constants

This commit is contained in:
robertvoy
2025-07-13 10:00:36 +10:00
parent f5aa01b48b
commit 9834a0f6e4
6 changed files with 106 additions and 56 deletions
+5 -3
View File
@@ -1,7 +1,9 @@
import { TIMEOUTS } from './constants.js';
export function createApiClient(baseUrl) {
const request = async (endpoint, options = {}, retries = 3) => {
const request = async (endpoint, options = {}, retries = TIMEOUTS.MAX_RETRIES) => {
let lastError;
let delay = 1000; // Initial delay for exponential backoff
let delay = TIMEOUTS.RETRY_DELAY; // Initial delay for exponential backoff
for (let attempt = 0; attempt < retries; attempt++) {
try {
@@ -114,7 +116,7 @@ export function createApiClient(baseUrl) {
},
// Status checking (with timeout)
async checkStatus(url, timeout = 5000) {
async checkStatus(url, timeout = TIMEOUTS.DEFAULT_FETCH) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
+50 -4
View File
@@ -5,7 +5,6 @@ export const BUTTON_STYLES = {
error: "background-color: #c04c4c;",
launch: "background-color: #4a7c4a;",
stop: "background-color: #7c4a4a;",
grey: "background-color: #555;",
};
export const STATUS_COLORS = {
@@ -15,6 +14,18 @@ export const STATUS_COLORS = {
PROCESSING_YELLOW: "#f0ad4e"
};
export const UI_COLORS = {
MUTED_TEXT: "#888",
SECONDARY_TEXT: "#ccc",
BORDER_LIGHT: "#555",
BORDER_DARK: "#444",
BORDER_DARKER: "#3a3a3a",
BACKGROUND_DARK: "#2a2a2a",
BACKGROUND_DARKER: "#1e1e1e",
ICON_COLOR: "#666",
ACCENT_COLOR: "#777"
};
export const PULSE_ANIMATION_CSS = `
@keyframes pulse {
0% {
@@ -73,11 +84,32 @@ export const UI_STYLES = {
formGroup: "display: flex; flex-direction: column; gap: 5px;",
formLabel: "font-size: 12px; color: #ccc; font-weight: 500;",
formInput: "padding: 6px 10px; background: #2a2a2a; border: 1px solid #444; color: white; font-size: 12px; border-radius: 4px; transition: border-color 0.2s;",
// Card styles
cardBase: "margin-bottom: 12px; border-radius: 6px; overflow: hidden; display: flex;",
workerCard: "margin-bottom: 12px; border-radius: 6px; overflow: hidden; display: flex; background: #2a2a2a;",
cardBlueprint: "border: 2px dashed #555; cursor: pointer; transition: all 0.2s ease; background: rgba(255, 255, 255, 0.02);",
cardAdd: "border: 1px dashed #444; cursor: pointer; transition: all 0.2s ease; background: transparent;",
// Column styles
columnBase: "display: flex; align-items: center; justify-content: center;",
checkboxColumn: "flex: 0 0 44px; display: flex; align-items: center; justify-content: center; border-right: 1px solid #3a3a3a; cursor: default; background: rgba(0,0,0,0.1);",
contentColumn: "flex: 1; display: flex; flex-direction: column; transition: background-color 0.2s ease;",
infoRow: "display: flex; align-items: center; padding: 12px; cursor: pointer;",
iconColumn: "width: 44px; flex-shrink: 0; font-size: 20px; color: #666;",
// Row and content styles
infoRow: "display: flex; align-items: center; padding: 12px; cursor: pointer; min-height: 64px;",
workerContent: "display: flex; align-items: center; gap: 10px; flex: 1;",
// Form and controls styles
buttonGroup: "display: flex; gap: 4px; margin-top: 10px;",
settingsForm: "display: flex; flex-direction: column; gap: 10px;",
checkboxGroup: "display: flex; align-items: center; gap: 8px; margin: 5px 0;",
formLabelClickable: "font-size: 12px; color: #ccc; cursor: pointer;",
settingsToggle: "display: flex; align-items: center; gap: 6px; padding: 4px 0; cursor: pointer; user-select: none;",
controlsWrapper: "display: flex; gap: 6px; align-items: stretch; width: 100%;",
// Existing styles
settingsArrow: "font-size: 12px; color: #888; transition: all 0.2s ease; margin-left: auto; padding: 4px;",
infoBox: "background-color: #333; color: #999; padding: 5px 14px; border-radius: 4px; font-size: 11px; text-align: center; flex: 1; font-weight: 500;",
workerSettings: "margin: 0 12px; padding: 0 12px; background: #1e1e1e; border-radius: 4px; border: 1px solid #2a2a2a;"
@@ -85,8 +117,22 @@ export const UI_STYLES = {
export const TIMEOUTS = {
DEFAULT_FETCH: 5000, // ms for general API calls
STATUS_CHECK: 2000, // ms for status checks
STATUS_CHECK: 500, // ms for status checks
LAUNCH: 90000, // ms for worker launch (longer for model loading)
RETRY_DELAY: 1000, // initial delay for exponential backoff
MAX_RETRIES: 3 // max retry attempts
MAX_RETRIES: 3, // max retry attempts
// UI feedback delays
BUTTON_RESET: 3000, // button text/state reset after actions
FLASH_SHORT: 1000, // brief success feedback
FLASH_MEDIUM: 1500, // medium error feedback
FLASH_LONG: 2000, // longer error feedback
// Operational delays
POST_ACTION_DELAY: 500, // delay after operations before status checks
STATUS_CHECK_DELAY: 100, // brief delay before status checks
// Background tasks
LOG_REFRESH: 2000, // log auto-refresh interval
IMAGE_CACHE_CLEAR: 30000 // delay before clearing image cache
};
+8 -3
View File
@@ -11,7 +11,7 @@ export function setupInterceptor(extension) {
if (hasCollector || hasDistUpscale) {
const result = await executeParallelDistributed(extension, prompt);
// Check status after dispatching jobs
setTimeout(() => extension.checkAllWorkerStatuses(), 500);
setTimeout(() => extension.checkAllWorkerStatuses(), TIMEOUTS.POST_ACTION_DELAY);
return result;
}
}
@@ -27,8 +27,13 @@ export async function executeParallelDistributed(extension, promptWrapper) {
// Pre-flight health check on all enabled workers
const activeWorkers = await performPreflightCheck(extension, enabledWorkers);
// Case: Enabled workers but all offline
if (activeWorkers.length === 0 && enabledWorkers.length > 0) {
extension.log("No active workers found. Running on master only.");
extension.log("No active workers found. All enabled workers are offline.");
if (extension.ui?.showToast) {
extension.ui.showToast(extension.app, "error", "All Workers Offline",
`${enabledWorkers.length} worker(s) enabled but all are offline or unreachable. Check worker connections and try again.`, 5000);
}
// Fall back to master-only execution
return extension.originalQueuePrompt(0, promptWrapper);
}
@@ -336,7 +341,7 @@ export async function loadImagesForWorker(extension, imageReferences) {
extension.log(`Clearing image cache (${extension._imageCache.size} images)`, "debug");
extension._imageCache.clear();
}
}, 30000); // Clear after 30 seconds
}, TIMEOUTS.IMAGE_CACHE_CLEAR); // Clear after 30 seconds
return images;
}
+13 -14
View File
@@ -16,6 +16,7 @@ class DistributedExtension {
this.statusCheckInterval = null;
this.logAutoRefreshInterval = null;
this.masterSettingsExpanded = false;
this.app = app; // Store app reference for toast notifications
// Initialize centralized state
this.state = createStateManager();
@@ -109,7 +110,7 @@ class DistributedExtension {
if (enabled) {
// Enabled: Start with checking state and trigger check
this.ui.updateStatusDot(workerId, STATUS_COLORS.OFFLINE_RED, "Checking status...", true);
setTimeout(() => this.checkWorkerStatus(worker), 100);
setTimeout(() => this.checkWorkerStatus(worker), TIMEOUTS.STATUS_CHECK_DELAY);
} else {
// Disabled: Set to gray
this.ui.updateStatusDot(workerId, STATUS_COLORS.DISABLED_GRAY, "Disabled", false);
@@ -192,11 +193,9 @@ class DistributedExtension {
}
startStatusChecking() {
// Start checking every 2 seconds for more responsive updates
// Don't check immediately since panel might not be open yet
this.statusCheckInterval = setInterval(() => {
this.checkAllWorkerStatuses();
}, 2000);
}, TIMEOUTS.STATUS_CHECK);
}
async checkAllWorkerStatuses() {
@@ -356,7 +355,7 @@ class DistributedExtension {
// Allow 90 seconds for worker to launch (model loading can take time)
setTimeout(() => {
this.state.setWorkerLaunching(workerId, false);
}, 90000);
}, TIMEOUTS.LAUNCH);
if (!launchBtn) return;
@@ -379,14 +378,14 @@ class DistributedExtension {
// Update controls immediately to hide launch button and show stop/log buttons
this.updateWorkerControls(workerId);
setTimeout(() => this.checkWorkerStatus(worker), 2000);
setTimeout(() => this.checkWorkerStatus(worker), TIMEOUTS.STATUS_CHECK);
}
} catch (error) {
// Check if worker was already running
if (error.message && error.message.includes("already running")) {
this.log(`Worker ${worker.name} is already running`, "info");
this.updateWorkerControls(workerId);
setTimeout(() => this.checkWorkerStatus(worker), 100);
setTimeout(() => this.checkWorkerStatus(worker), TIMEOUTS.STATUS_CHECK_DELAY);
} else {
this.log(`Error launching worker: ${error.message || error}`, "error");
@@ -425,11 +424,11 @@ class DistributedExtension {
stopBtn.textContent = "Stopped!";
setTimeout(() => {
this.updateWorkerControls(workerId);
}, 1000);
}, TIMEOUTS.FLASH_SHORT);
}
// Verify status after a short delay
setTimeout(() => this.checkWorkerStatus(worker), 2000);
setTimeout(() => this.checkWorkerStatus(worker), TIMEOUTS.STATUS_CHECK);
} else {
this.log(`Failed to stop worker: ${result.message}`, "error");
@@ -446,7 +445,7 @@ class DistributedExtension {
setTimeout(() => {
this.updateWorkerControls(workerId);
}, 1500);
}, TIMEOUTS.FLASH_MEDIUM);
}
}
} catch (error) {
@@ -458,7 +457,7 @@ class DistributedExtension {
stopBtn.textContent = "Error";
setTimeout(() => {
this.updateWorkerControls(workerId);
}, 1500);
}, TIMEOUTS.FLASH_MEDIUM);
}
}
}
@@ -473,7 +472,7 @@ class DistributedExtension {
}
// Generic async button action handler
async handleAsyncButtonAction(button, action, successText, errorText, resetDelay = 3000) {
async handleAsyncButtonAction(button, action, successText, errorText, resetDelay = TIMEOUTS.BUTTON_RESET) {
const originalText = button.textContent;
const originalStyle = button.style.cssText;
button.disabled = true;
@@ -638,7 +637,7 @@ class DistributedExtension {
logBtn.disabled = false;
logBtn.textContent = "View Log";
logBtn.style.backgroundColor = "#685434"; // Keep the yellow color
}, 2000);
}, TIMEOUTS.FLASH_LONG);
}
}
}
@@ -1121,7 +1120,7 @@ class DistributedExtension {
// Refresh every 2 seconds
this.logAutoRefreshInterval = setInterval(() => {
this.refreshLog(workerId, true); // silent mode
}, 2000);
}, TIMEOUTS.LOG_REFRESH);
}
stopLogAutoRefresh() {
+26 -28
View File
@@ -1,4 +1,4 @@
import { BUTTON_STYLES, UI_STYLES, STATUS_COLORS } from './constants.js';
import { BUTTON_STYLES, UI_STYLES, STATUS_COLORS, UI_COLORS, TIMEOUTS } from './constants.js';
const cardConfigs = {
master: {
@@ -19,7 +19,7 @@ const cardConfigs = {
const cudaDevice = extension.config?.master?.cuda_device ?? extension.masterCudaDevice;
const cudaInfo = cudaDevice !== undefined ? `CUDA ${cudaDevice} • ` : '';
const port = window.location.port || (window.location.protocol === 'https:' ? '443' : '80');
return `<strong id="master-name-display">${data?.name || extension.config?.master?.name || "Master"}</strong><br><small style="color: #888;"><span id="master-cuda-info">${cudaInfo}Port ${port}</span></small>`;
return `<strong id="master-name-display">${data?.name || extension.config?.master?.name || "Master"}</strong><br><small style="color: ${UI_COLORS.MUTED_TEXT};"><span id="master-cuda-info">${cudaInfo}Port ${port}</span></small>`;
},
controls: {
type: 'info',
@@ -50,10 +50,10 @@ const cardConfigs = {
infoText: (data, extension) => {
const isRemote = extension.isRemoteWorker(data);
if (isRemote) {
return `<strong>${data.name}</strong><br><small style="color: #888;">${data.host}:${data.port}</small>`;
return `<strong>${data.name}</strong><br><small style="color: ${UI_COLORS.MUTED_TEXT};">${data.host}:${data.port}</small>`;
} else {
const cudaInfo = data.cuda_device !== undefined ? `CUDA ${data.cuda_device} • ` : '';
return `<strong>${data.name}</strong><br><small style="color: #888;">${cudaInfo}Port ${data.port}</small>`;
return `<strong>${data.name}</strong><br><small style="color: ${UI_COLORS.MUTED_TEXT};">${cudaInfo}Port ${data.port}</small>`;
}
},
controls: {
@@ -73,17 +73,17 @@ const cardConfigs = {
type: 'icon',
content: '+',
width: 42,
style: 'border-right: 2px dashed #555; color: #777; font-size: 24px; font-weight: 500;'
style: `border-right: 2px dashed ${UI_COLORS.BORDER_LIGHT}; color: ${UI_COLORS.ACCENT_COLOR}; font-size: 24px; font-weight: 500;`
},
statusDot: {
color: 'transparent',
border: '1px solid #555'
border: `1px solid ${UI_COLORS.BORDER_LIGHT}`
},
infoText: () => `<strong style="color: #aaa; font-size: 16px;">Add New Worker</strong><br><small style="color: #555;">[CUDA] • [Port]</small>`,
infoText: () => `<strong style="color: #aaa; font-size: 16px;">Add New Worker</strong><br><small style="color: ${UI_COLORS.BORDER_LIGHT};">[CUDA] • [Port]</small>`,
controls: {
type: 'ghost',
text: 'Configure',
style: 'border: 1px solid #444; background: transparent; color: #555;'
style: `border: 1px solid ${UI_COLORS.BORDER_DARK}; background: transparent; color: ${UI_COLORS.BORDER_LIGHT};`
},
hover: 'placeholder',
expand: false,
@@ -94,13 +94,13 @@ const cardConfigs = {
type: 'icon',
content: '+',
width: 43,
style: 'border-right: 1px dashed #444; color: #555; font-size: 18px;'
style: `border-right: 1px dashed ${UI_COLORS.BORDER_DARK}; color: ${UI_COLORS.BORDER_LIGHT}; font-size: 18px;`
},
statusDot: {
color: 'transparent',
border: '1px solid #555'
border: `1px solid ${UI_COLORS.BORDER_LIGHT}`
},
infoText: () => `<span style="color: #666; font-weight: bold; font-size: 13px;">Add New Worker</span>`,
infoText: () => `<span style="color: ${UI_COLORS.ICON_COLOR}; font-weight: bold; font-size: 13px;">Add New Worker</span>`,
controls: null,
hover: 'placeholder',
expand: false,
@@ -134,7 +134,7 @@ export class DistributedUI {
createButtonGroup(buttons, style = "") {
const group = document.createElement("div");
group.style.cssText = "display: flex; gap: 4px; margin-top: 10px;" + style;
group.style.cssText = this.styles.buttonGroup + style;
buttons.forEach(button => group.appendChild(button));
return group;
}
@@ -207,20 +207,19 @@ export class DistributedUI {
createCard(type = 'worker', options = {}) {
const card = document.createElement("div");
const baseStyle = "margin-bottom: 12px; border-radius: 6px; overflow: hidden; display: flex;";
switch(type) {
case 'master':
case 'worker':
card.style.cssText = baseStyle + "background: #2a2a2a;";
card.style.cssText = this.styles.workerCard;
break;
case 'blueprint':
card.style.cssText = baseStyle + "border: 2px dashed #555; cursor: pointer; transition: all 0.2s ease; background: rgba(255, 255, 255, 0.02);";
card.style.cssText = this.styles.cardBase + this.styles.cardBlueprint;
if (options.onClick) card.onclick = options.onClick;
if (options.title) card.title = options.title;
break;
case 'add':
card.style.cssText = baseStyle + "border: 1px dashed #444; cursor: pointer; transition: all 0.2s ease; background: transparent;";
card.style.cssText = this.styles.cardBase + this.styles.cardAdd;
if (options.onClick) card.onclick = options.onClick;
if (options.title) card.title = options.title;
break;
@@ -238,18 +237,17 @@ export class DistributedUI {
createCardColumn(type = 'checkbox', options = {}) {
const column = document.createElement("div");
const baseStyle = "display: flex; align-items: center; justify-content: center;";
switch(type) {
case 'checkbox':
column.style.cssText = baseStyle + "flex: 0 0 44px; border-right: 1px solid #3a3a3a; cursor: default; background: rgba(0,0,0,0.1);";
column.style.cssText = this.styles.checkboxColumn;
if (options.title) column.title = options.title;
break;
case 'icon':
column.style.cssText = baseStyle + "width: 44px; flex-shrink: 0; font-size: 20px; color: #666;";
column.style.cssText = this.styles.columnBase + this.styles.iconColumn;
break;
case 'content':
column.style.cssText = "flex: 1; display: flex; flex-direction: column; transition: background-color 0.2s ease;";
column.style.cssText = this.styles.contentColumn;
break;
}
@@ -258,25 +256,25 @@ export class DistributedUI {
createInfoRow(options = {}) {
const row = document.createElement("div");
row.style.cssText = "display: flex; align-items: center; padding: 12px; cursor: pointer; min-height: 64px;";
row.style.cssText = this.styles.infoRow;
if (options.onClick) row.onclick = options.onClick;
return row;
}
createWorkerContent() {
const content = document.createElement("div");
content.style.cssText = "display: flex; align-items: center; gap: 10px; flex: 1;";
content.style.cssText = this.styles.workerContent;
return content;
}
createSettingsForm(fields = [], options = {}) {
const form = document.createElement("div");
form.style.cssText = "display: flex; flex-direction: column; gap: 10px;";
form.style.cssText = this.styles.settingsForm;
fields.forEach(field => {
if (field.type === 'checkbox') {
const group = document.createElement("div");
group.style.cssText = "display: flex; align-items: center; gap: 8px; margin: 5px 0;";
group.style.cssText = this.styles.checkboxGroup;
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
@@ -287,7 +285,7 @@ export class DistributedUI {
const label = document.createElement("label");
label.htmlFor = field.id;
label.textContent = field.label;
label.style.cssText = "font-size: 12px; color: #ccc; cursor: pointer;";
label.style.cssText = this.styles.formLabelClickable;
group.appendChild(checkbox);
group.appendChild(label);
@@ -613,7 +611,7 @@ export class DistributedUI {
createSettingsToggle() {
const settingsRow = document.createElement("div");
settingsRow.style.cssText = "display: flex; align-items: center; gap: 6px; padding: 4px 0; cursor: pointer; user-select: none;";
settingsRow.style.cssText = this.styles.settingsToggle;
const settingsTitle = document.createElement("h4");
settingsTitle.textContent = "Settings";
@@ -726,7 +724,7 @@ export class DistributedUI {
// Always create a wrapper div for consistent layout
const controlsWrapper = document.createElement("div");
controlsWrapper.style.cssText = "display: flex; gap: 6px; align-items: stretch; width: 100%;";
controlsWrapper.style.cssText = this.styles.controlsWrapper;
if (config.dynamic && data) {
if (isRemote) {
@@ -835,7 +833,7 @@ export class DistributedUI {
this.updateMasterDisplay(extension);
saveBtn.textContent = "Saved!";
setTimeout(() => { saveBtn.textContent = "Save"; }, 2000);
setTimeout(() => { saveBtn.textContent = "Save"; }, TIMEOUTS.FLASH_LONG);
}, "background-color: #4a7c4a;");
saveBtn.style.cssText += " padding: 6px 12px; font-size: 12px;";
+4 -4
View File
@@ -1,4 +1,4 @@
import { BUTTON_STYLES } from './constants.js';
import { BUTTON_STYLES, TIMEOUTS } from './constants.js';
export async function handleWorkerOperation(extension, button, operation, successText, errorText) {
const originalText = button.textContent;
@@ -20,7 +20,7 @@ export async function handleWorkerOperation(extension, button, operation, succes
button.textContent = originalText;
button.style.cssText = originalStyle;
button.disabled = false;
}, 3000);
}, TIMEOUTS.BUTTON_RESET);
return;
}
@@ -49,7 +49,7 @@ export async function handleWorkerOperation(extension, button, operation, succes
setTimeout(() => {
button.textContent = originalText;
button.style.cssText = originalStyle;
}, 3000);
}, TIMEOUTS.BUTTON_RESET);
} finally {
button.disabled = false;
}
@@ -60,7 +60,7 @@ export async function handleInterruptWorkers(extension, button) {
name: "Interrupt",
endpoint: "/interrupt",
loadingText: "Interrupting...",
onSuccess: () => setTimeout(() => extension.checkAllWorkerStatuses(), 500)
onSuccess: () => setTimeout(() => extension.checkAllWorkerStatuses(), TIMEOUTS.POST_ACTION_DELAY)
}, "Interrupted!", "Error! See Console");
}