diff --git a/web/common.ts b/web/common.ts index 1dab183..69ad260 100644 --- a/web/common.ts +++ b/web/common.ts @@ -6,7 +6,7 @@ export const commonPrefix = '🪛'; export function displayContext( nodeType: TLGraphNode, appFromArg: ComfyApp, - index = 0, serialize_widgets = false, isVirtualNode = false + index = 0, serialize_widgets = false, isVirtualNode = false, ) { function populate(this: TLGraphNode, text: string | string[]) { if (this.widgets) { diff --git a/web/debugger.js b/web/debugger.js index 1f9e3c8..43b7817 100644 --- a/web/debugger.js +++ b/web/debugger.js @@ -2,7 +2,6 @@ import { app } from '/scripts/app.js'; import { api } from '/scripts/api.js'; import { commonPrefix, displayContext } from './common.js'; import { LiteGraph, TLGraphNode, ComfyWidgets } from './liteGraph.js'; -console.log('debugger.ts'); app.registerExtension({ name: 'Crystools.Debugger.ConsoleAny', beforeRegisterNodeDef(nodeType, nodeData, appFromArg) { diff --git a/web/debugger.ts b/web/debugger.ts index d6202b9..3faaf67 100644 --- a/web/debugger.ts +++ b/web/debugger.ts @@ -4,8 +4,6 @@ import { commonPrefix, displayContext } from './common.js'; import type { ComfyNode, ComfyApp } from './liteGraph.js'; import { LiteGraph, TLGraphNode, ComfyWidgets } from './liteGraph.js'; -console.log('debugger.ts'); - // "Show any" Node app.registerExtension({ name: 'Crystools.Debugger.ConsoleAny', diff --git a/web/extensions.d.ts b/web/extensions.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/web/extensions.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/web/extensions.js b/web/extensions.js new file mode 100644 index 0000000..15a9f68 --- /dev/null +++ b/web/extensions.js @@ -0,0 +1,20 @@ +import { app } from '/scripts/app.js'; +import { displayContext } from './common.js'; +const crystoolsExtensions = { + 'Get resolution [Crystools]': 'Crystools.Image.GetResolution', + 'Preview from image [Crystools]': 'Crystools.Image.PreviewFromImage', + 'Preview from metadata [Crystools]': 'Crystools.Image.PreviewFromMetadata', + 'Metadata comparator [Crystools]': 'Crystools.Metadata.MetadataComparator', + 'Stats system [Crystools]': 'Crystools.Utils.StatsSystem', + 'Show any to JSON [Crystools]': 'Crystools.Debugger.ConsoleAnyToJson', +}; +Object.keys(crystoolsExtensions).forEach(key => { + app.registerExtension({ + name: crystoolsExtensions[key], + beforeRegisterNodeDef(nodeType, nodeData, appFromArg) { + if (nodeData.name === key) { + displayContext(nodeType, appFromArg, 0); + } + }, + }); +}); diff --git a/web/extensions b/web/extensions.ts similarity index 74% rename from web/extensions rename to web/extensions.ts index 3aaaabe..25a6950 100644 --- a/web/extensions +++ b/web/extensions.ts @@ -1,7 +1,9 @@ import { app } from '/scripts/app.js'; import { displayContext } from './common.js'; +import type { ComfyNode, ComfyApp } from './liteGraph.types'; +import { TLGraphNode } from './liteGraph'; -const crystoolsExtensions = { +const crystoolsExtensions: Record = { 'Get resolution [Crystools]': 'Crystools.Image.GetResolution', 'Preview from image [Crystools]': 'Crystools.Image.PreviewFromImage', 'Preview from metadata [Crystools]': 'Crystools.Image.PreviewFromMetadata', @@ -13,7 +15,7 @@ const crystoolsExtensions = { Object.keys(crystoolsExtensions).forEach(key => { app.registerExtension({ name: crystoolsExtensions[key], - beforeRegisterNodeDef(nodeType, nodeData, appFromArg) { + beforeRegisterNodeDef(nodeType: ComfyNode, nodeData: TLGraphNode, appFromArg: ComfyApp) { if (nodeData.name === key) { displayContext(nodeType, appFromArg, 0); } diff --git a/web/liteGraph.js b/web/liteGraph.js index ddf1067..fca4a37 100644 --- a/web/liteGraph.js +++ b/web/liteGraph.js @@ -40,5 +40,4 @@ export class TLGraphNode extends LGraphNode { }); } } -console.log('liteGraph.ts'); export const LiteGraph = window.LiteGraph; diff --git a/web/liteGraph.ts b/web/liteGraph.ts index 264e3ff..95b8449 100644 --- a/web/liteGraph.ts +++ b/web/liteGraph.ts @@ -4,6 +4,7 @@ export type * from './liteGraph.types'; export * from '/scripts/widgets.js'; import type { IWidget as IWidgetOld, LGraphNode as TypeGraphNode, TypeLiteGraph } from './liteGraph.types'; + declare const LGraphNode: typeof TypeGraphNode; // just for get the type export interface IWidget extends IWidgetOld { @@ -28,7 +29,5 @@ export class TLGraphNode extends LGraphNode { prototype: TLGraphNode; // yes itself } -console.log('liteGraph.ts'); - // from globals export const LiteGraph: TypeLiteGraph = (window as any).LiteGraph; diff --git a/web/liteGraph.types.d.ts b/web/liteGraph.types.d.ts index a2d158e..bfce629 100644 --- a/web/liteGraph.types.d.ts +++ b/web/liteGraph.types.d.ts @@ -5,6 +5,7 @@ export type * from '/scripts/app'; export type { api } from '/scripts/api'; import { LGraph, LiteGraph } from '/types/litegraph'; + export declare type TypeLiteGraph = typeof LiteGraph & { graph: LGraph; }; diff --git a/web/monitor.d.ts b/web/monitor.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/web/monitor.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/web/monitor.js b/web/monitor.js new file mode 100644 index 0000000..842c6de --- /dev/null +++ b/web/monitor.js @@ -0,0 +1,577 @@ +import { app } from '/scripts/app.js'; +import { api } from '/scripts/api.js'; +import { commonPrefix } from './common.js'; +class CrystoolsMonitor { + constructor() { + Object.defineProperty(this, "idExtensionName", { + enumerable: true, + configurable: true, + writable: true, + value: 'Crystools.monitor' + }); + Object.defineProperty(this, "menuPrefix", { + enumerable: true, + configurable: true, + writable: true, + value: commonPrefix + }); + Object.defineProperty(this, "htmlIdCrystoolsRoot", { + enumerable: true, + configurable: true, + writable: true, + value: 'crystools-root' + }); + Object.defineProperty(this, "htmlIdCrystoolsMonitorContainer", { + enumerable: true, + configurable: true, + writable: true, + value: 'crystools-monitor-container' + }); + Object.defineProperty(this, "idInputRate", { + enumerable: true, + configurable: true, + writable: true, + value: 'Crystools.inputRate' + }); + Object.defineProperty(this, "defaultRate", { + enumerable: true, + configurable: true, + writable: true, + value: .5 + }); + Object.defineProperty(this, "idSwitchCPU", { + enumerable: true, + configurable: true, + writable: true, + value: 'Crystools.switchCPU' + }); + Object.defineProperty(this, "defaultSwitchCPU", { + enumerable: true, + configurable: true, + writable: true, + value: true + }); + Object.defineProperty(this, "htmlMonitorCPURef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorCPUSliderRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorCPULabelRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "cssColorCPU", { + enumerable: true, + configurable: true, + writable: true, + value: '#0AA015' + }); + Object.defineProperty(this, "idSwitchRAM", { + enumerable: true, + configurable: true, + writable: true, + value: 'Crystools.switchRAM' + }); + Object.defineProperty(this, "defaultSwitchRAM", { + enumerable: true, + configurable: true, + writable: true, + value: true + }); + Object.defineProperty(this, "htmlMonitorRAMRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorRAMSliderRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorRAMLabelRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "cssColorRAM", { + enumerable: true, + configurable: true, + writable: true, + value: '#07630D' + }); + Object.defineProperty(this, "idSwitchGPU", { + enumerable: true, + configurable: true, + writable: true, + value: 'Crystools.switchGPU' + }); + Object.defineProperty(this, "defaultSwitchGPU", { + enumerable: true, + configurable: true, + writable: true, + value: true + }); + Object.defineProperty(this, "htmlMonitorGPURef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorGPUSliderRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorGPULabelRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "cssColorGPU", { + enumerable: true, + configurable: true, + writable: true, + value: '#0C86F4' + }); + Object.defineProperty(this, "idSwitchVRAM", { + enumerable: true, + configurable: true, + writable: true, + value: 'Crystools.switchVRAM' + }); + Object.defineProperty(this, "defaultSwitchVRAM", { + enumerable: true, + configurable: true, + writable: true, + value: true + }); + Object.defineProperty(this, "htmlMonitorVRAMRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorVRAMSliderRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorVRAMLabelRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "cssColorVRAM", { + enumerable: true, + configurable: true, + writable: true, + value: '#176EC7' + }); + Object.defineProperty(this, "idSwitchHDD", { + enumerable: true, + configurable: true, + writable: true, + value: 'Crystools.switchHDD' + }); + Object.defineProperty(this, "defaultSwitchHDD", { + enumerable: true, + configurable: true, + writable: true, + value: false + }); + Object.defineProperty(this, "htmlMonitorHDDRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorHDDSliderRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlMonitorHDDLabelRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "cssColorHDD", { + enumerable: true, + configurable: true, + writable: true, + value: '#730F92' + }); + Object.defineProperty(this, "createSettings", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + app.ui.settings.addSetting({ + id: this.idSwitchCPU, + name: this.menuPrefix + 'Display CPU monitor [menu]', + type: 'boolean', + defaultValue: this.defaultSwitchCPU, + onChange: async (value) => { + this.updateWidget(this.htmlMonitorCPURef, value); + await this.updateServer({ + switchCPU: value, + }); + }, + }); + app.ui.settings.addSetting({ + id: this.idSwitchRAM, + name: this.menuPrefix + 'Display RAM monitor [menu]', + type: 'boolean', + defaultValue: this.defaultSwitchRAM, + onChange: async (value) => { + this.updateWidget(this.htmlMonitorRAMRef, value); + await this.updateServer({ + switchRAM: value, + }); + }, + }); + app.ui.settings.addSetting({ + id: this.idSwitchGPU, + name: this.menuPrefix + 'Display GPU monitor [menu]', + type: 'boolean', + defaultValue: this.defaultSwitchGPU, + onChange: async (value) => { + this.updateWidget(this.htmlMonitorGPURef, value); + await this.updateServer({ + switchGPU: value, + }); + }, + }); + app.ui.settings.addSetting({ + id: this.idSwitchVRAM, + name: this.menuPrefix + 'Display Video RAM monitor [menu]', + type: 'boolean', + defaultValue: this.defaultSwitchVRAM, + onChange: async (value) => { + this.updateWidget(this.htmlMonitorVRAMRef, value); + await this.updateServer({ + switchVRAM: value, + }); + }, + }); + app.ui.settings.addSetting({ + id: this.idSwitchHDD, + name: this.menuPrefix + 'Display Hard disk monitor [menu]', + tooltip: 'Only the drive where the comfyUI is installed', + type: 'boolean', + defaultValue: this.defaultSwitchHDD, + onChange: async (value) => { + this.updateWidget(this.htmlMonitorHDDRef, value); + await this.updateServer({ + switchHDD: value, + }); + }, + }); + app.ui.settings.addSetting({ + id: this.idInputRate, + name: this.menuPrefix + 'Monitors refresh rate (in seconds) [menu]', + tooltip: 'This is the time between each update of the monitors, 0 means no refresh', + type: 'slider', + attrs: { + min: 0, + max: 5, + step: 0.25, + }, + defaultValue: this.defaultRate, + onChange: async (value) => { + let valueNumber; + try { + valueNumber = parseFloat(value); + if (isNaN(valueNumber)) { + throw new Error('invalid value'); + } + } + catch (error) { + console.error(error); + return; + } + try { + await this.updateServer({ + rate: valueNumber, + }); + } + catch (error) { + console.error(error); + return; + } + if (valueNumber === 0) { + this.updateDisplay({ + cpu_utilization: 0, + device: 'cpu', + gpus: [ + { + gpu_utilization: 0, + vram_total: 0, + vram_used: 0, + vram_used_percent: 0, + }, + ], + hdd_total: 0, + hdd_used: 0, + hdd_used_percent: 0, + ram_total: 0, + ram_used: 0, + ram_used_percent: 0, + }); + } + if (this.htmlMonitorCPUSliderRef) { + value = valueNumber.toFixed(1); + const animationConfig = `width ${value}s`; + this.htmlMonitorCPUSliderRef.style.transition = animationConfig; + this.htmlMonitorGPUSliderRef.style.transition = animationConfig; + this.htmlMonitorHDDSliderRef.style.transition = animationConfig; + this.htmlMonitorRAMSliderRef.style.transition = animationConfig; + this.htmlMonitorVRAMSliderRef.style.transition = animationConfig; + this.htmlMonitorCPUSliderRef.style.width = value; + this.htmlMonitorGPUSliderRef.style.width = value; + this.htmlMonitorHDDSliderRef.style.width = value; + this.htmlMonitorRAMSliderRef.style.width = value; + this.htmlMonitorVRAMSliderRef.style.width = value; + } + }, + }); + } + }); + Object.defineProperty(this, "updateServer", { + enumerable: true, + configurable: true, + writable: true, + value: async (data) => { + const resp = await api.fetchApi('/crystools/monitor', { + method: 'PATCH', + body: JSON.stringify(data), + cache: 'no-store', + }); + if (resp.status === 200) { + return await resp.text(); + } + throw new Error(resp.statusText); + } + }); + Object.defineProperty(this, "updateAllWidget", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + this.updateWidget(this.htmlMonitorCPURef, app.ui.settings.getSettingValue(this.idSwitchCPU, this.defaultSwitchCPU)); + this.updateWidget(this.htmlMonitorGPURef, app.ui.settings.getSettingValue(this.idSwitchGPU, this.defaultSwitchGPU)); + this.updateWidget(this.htmlMonitorHDDRef, app.ui.settings.getSettingValue(this.idSwitchHDD, this.defaultSwitchHDD)); + this.updateWidget(this.htmlMonitorRAMRef, app.ui.settings.getSettingValue(this.idSwitchRAM, this.defaultSwitchRAM)); + this.updateWidget(this.htmlMonitorVRAMRef, app.ui.settings.getSettingValue(this.idSwitchVRAM, this.defaultSwitchVRAM)); + } + }); + Object.defineProperty(this, "updateWidget", { + enumerable: true, + configurable: true, + writable: true, + value: (container, value) => { + if (container) { + container.style.display = value ? 'flex' : 'none'; + } + } + }); + Object.defineProperty(this, "updateDisplay", { + enumerable: true, + configurable: true, + writable: true, + value: (data) => { + if (!this.htmlMonitorCPULabelRef) { + return; + } + this.htmlMonitorCPULabelRef.innerHTML = `${Math.floor(data.cpu_utilization)}%`; + this.htmlMonitorCPUSliderRef.style.width = this.htmlMonitorCPULabelRef.innerHTML; + const gpu = data.gpus[0]; + if (gpu) { + this.htmlMonitorGPULabelRef.innerHTML = `${Math.floor(gpu.gpu_utilization)}%`; + this.htmlMonitorGPUSliderRef.style.width = this.htmlMonitorGPULabelRef.innerHTML; + this.htmlMonitorVRAMLabelRef.innerHTML = `${Math.floor(gpu.vram_used_percent)}%`; + this.htmlMonitorVRAMSliderRef.style.width = this.htmlMonitorVRAMLabelRef.innerHTML; + } + else { + console.error('no gpu found'); + } + this.htmlMonitorHDDLabelRef.innerHTML = `${Math.floor(data.hdd_used_percent)}%`; + this.htmlMonitorHDDSliderRef.style.width = this.htmlMonitorHDDLabelRef.innerHTML; + this.htmlMonitorRAMLabelRef.innerHTML = `${Math.floor(data.ram_used_percent)}%`; + this.htmlMonitorRAMSliderRef.style.width = this.htmlMonitorRAMLabelRef.innerHTML; + } + }); + Object.defineProperty(this, "createMonitor", { + enumerable: true, + configurable: true, + writable: true, + value: (id, label, color) => { + const option = { + rootRef: null, + sliderRef: null, + labelRef: null, + }; + const htmlMain = document.createElement('div'); + htmlMain.setAttribute('id', id); + htmlMain.style.margin = '2px 10px'; + htmlMain.style.height = '12px'; + htmlMain.style.position = 'relative'; + htmlMain.style.display = 'flex'; + htmlMain.style.alignItems = 'center'; + htmlMain.style.flexDirection = 'row'; + option.rootRef = htmlMain; + const htmlMonitorText = document.createElement('div'); + htmlMonitorText.style.width = '35px'; + htmlMonitorText.style.fontSize = '10px'; + htmlMonitorText.innerHTML = label; + htmlMain.append(htmlMonitorText); + const htmlMonitorContent = document.createElement('div'); + htmlMonitorContent.style.height = '100%'; + htmlMonitorContent.style.flexGrow = '1'; + htmlMonitorContent.style.position = 'relative'; + htmlMonitorContent.style.backgroundColor = 'var(--bg-color)'; + htmlMain.append(htmlMonitorContent); + const htmlMonitorSlider = document.createElement('div'); + htmlMonitorSlider.style.position = 'absolute'; + htmlMonitorSlider.style.height = '100%'; + htmlMonitorSlider.style.width = '0'; + htmlMonitorSlider.style.backgroundColor = color; + htmlMonitorSlider.style.transition = 'width 0.5s'; + option.sliderRef = htmlMonitorSlider; + htmlMonitorContent.append(htmlMonitorSlider); + const htmlMonitorLabel = document.createElement('div'); + htmlMonitorLabel.style.position = 'relative'; + htmlMonitorLabel.style.width = '100%'; + htmlMonitorLabel.style.color = 'var(--drag-text)'; + htmlMonitorLabel.style.fontSize = '10px'; + htmlMonitorLabel.innerHTML = '0%'; + option.labelRef = htmlMonitorLabel; + htmlMonitorContent.append(htmlMonitorLabel); + return option; + } + }); + Object.defineProperty(this, "createMonitorCPU", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + const options = this.createMonitor('crystools-monitor-cpu', 'CPU', this.cssColorCPU); + this.htmlMonitorCPURef = options.rootRef; + this.htmlMonitorCPUSliderRef = options.sliderRef; + this.htmlMonitorCPULabelRef = options.labelRef; + return this.htmlMonitorCPURef; + } + }); + Object.defineProperty(this, "createMonitorRAM", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + const options = this.createMonitor('crystools-monitor-ram', 'RAM', this.cssColorRAM); + this.htmlMonitorRAMRef = options.rootRef; + this.htmlMonitorRAMSliderRef = options.sliderRef; + this.htmlMonitorRAMLabelRef = options.labelRef; + return this.htmlMonitorRAMRef; + } + }); + Object.defineProperty(this, "createMonitorGPU", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + const options = this.createMonitor('crystools-monitor-gpu', 'GPU', this.cssColorGPU); + this.htmlMonitorGPURef = options.rootRef; + this.htmlMonitorGPUSliderRef = options.sliderRef; + this.htmlMonitorGPULabelRef = options.labelRef; + return this.htmlMonitorGPURef; + } + }); + Object.defineProperty(this, "createMonitorVRAM", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + const options = this.createMonitor('crystools-monitor-vram', 'VRAM', this.cssColorVRAM); + this.htmlMonitorVRAMRef = options.rootRef; + this.htmlMonitorVRAMSliderRef = options.sliderRef; + this.htmlMonitorVRAMLabelRef = options.labelRef; + return this.htmlMonitorVRAMRef; + } + }); + Object.defineProperty(this, "createMonitorHDD", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + const options = this.createMonitor('crystools-monitor-hdd', 'HDD', this.cssColorHDD); + this.htmlMonitorHDDRef = options.rootRef; + this.htmlMonitorHDDSliderRef = options.sliderRef; + this.htmlMonitorHDDLabelRef = options.labelRef; + return this.htmlMonitorHDDRef; + } + }); + Object.defineProperty(this, "registerListeners", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + api.addEventListener('crystools.monitor', (event) => { + if (event?.detail === undefined) { + return; + } + this.updateDisplay(event.detail); + }, false); + } + }); + this.createSettings(); + } + setup() { + const parentElement = document.getElementById('queue-button'); + let ctoolsRoot = document.getElementById(this.htmlIdCrystoolsRoot); + if (!ctoolsRoot) { + ctoolsRoot = document.createElement('div'); + ctoolsRoot.setAttribute('id', this.htmlIdCrystoolsRoot); + ctoolsRoot.style.display = 'flex'; + ctoolsRoot.style.width = '100%'; + ctoolsRoot.style.flexDirection = 'column'; + parentElement.insertAdjacentElement('afterend', ctoolsRoot); + } + const htmlContainer = document.createElement('div'); + htmlContainer.setAttribute('id', this.htmlIdCrystoolsMonitorContainer); + htmlContainer.style.width = '100%'; + htmlContainer.style.cursor = 'crosshair'; + htmlContainer.style.order = '3'; + htmlContainer.style.margin = '4px 0'; + ctoolsRoot.append(htmlContainer); + htmlContainer.append(this.createMonitorCPU()); + htmlContainer.append(this.createMonitorRAM()); + htmlContainer.append(this.createMonitorGPU()); + htmlContainer.append(this.createMonitorVRAM()); + htmlContainer.append(this.createMonitorHDD()); + this.updateAllWidget(); + this.registerListeners(); + } +} +const crystoolsMonitor = new CrystoolsMonitor(); +app.registerExtension({ + name: crystoolsMonitor.idExtensionName, + setup: crystoolsMonitor.setup.bind(crystoolsMonitor), +}); diff --git a/web/monitor b/web/monitor.ts similarity index 99% rename from web/monitor rename to web/monitor.ts index bfc2042..105746a 100644 --- a/web/monitor +++ b/web/monitor.ts @@ -409,7 +409,7 @@ class CrystoolsMonitor { return; } this.updateDisplay(event.detail); - }); + }, false); }; } diff --git a/web/progressBar.d.ts b/web/progressBar.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/web/progressBar.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/web/progressBar.js b/web/progressBar.js new file mode 100644 index 0000000..8f641ee --- /dev/null +++ b/web/progressBar.js @@ -0,0 +1,244 @@ +import { api } from '/scripts/api.js'; +import { app } from '/scripts/app.js'; +import { commonPrefix } from './common.js'; +var EStatus; +(function (EStatus) { + EStatus["executing"] = "Executing"; + EStatus["executed"] = "Executed"; + EStatus["execution_error"] = "Execution error"; +})(EStatus || (EStatus = {})); +class CrystoolsProgressBar { + constructor() { + Object.defineProperty(this, "idExtensionName", { + enumerable: true, + configurable: true, + writable: true, + value: 'Crystools.progressBar' + }); + Object.defineProperty(this, "idShowProgressBar", { + enumerable: true, + configurable: true, + writable: true, + value: 'Crystools.showStatus' + }); + Object.defineProperty(this, "defaultShowStatus", { + enumerable: true, + configurable: true, + writable: true, + value: true + }); + Object.defineProperty(this, "menuPrefix", { + enumerable: true, + configurable: true, + writable: true, + value: commonPrefix + }); + Object.defineProperty(this, "htmlIdCrystoolsRoot", { + enumerable: true, + configurable: true, + writable: true, + value: 'crystools-root' + }); + Object.defineProperty(this, "htmlIdCrystoolsProgressBarContainer", { + enumerable: true, + configurable: true, + writable: true, + value: 'crystools-progress-bar-container' + }); + Object.defineProperty(this, "currentStatus", { + enumerable: true, + configurable: true, + writable: true, + value: EStatus.executed + }); + Object.defineProperty(this, "currentProgress", { + enumerable: true, + configurable: true, + writable: true, + value: 0 + }); + Object.defineProperty(this, "currentNode", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "timeStart", { + enumerable: true, + configurable: true, + writable: true, + value: 0 + }); + Object.defineProperty(this, "htmlProgressSliderRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "htmlProgressLabelRef", { + enumerable: true, + configurable: true, + writable: true, + value: null + }); + Object.defineProperty(this, "createSettings", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + app.ui.settings.addSetting({ + id: this.idShowProgressBar, + name: this.menuPrefix + 'Show progress bar [menu]', + type: 'boolean', + defaultValue: this.defaultShowStatus, + onChange: this.showProgressBar, + }); + } + }); + Object.defineProperty(this, "showProgressBar", { + enumerable: true, + configurable: true, + writable: true, + value: (value) => { + const container = document.getElementById(this.htmlIdCrystoolsProgressBarContainer); + if (container) { + container.style.display = value ? 'block' : 'none'; + } + } + }); + Object.defineProperty(this, "updateDisplay", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + if (this.currentStatus === EStatus.executed) { + this.htmlProgressLabelRef.innerHTML = 'cached'; + const timeElapsed = Date.now() - this.timeStart; + if (this.timeStart > 0 && timeElapsed > 0) { + this.htmlProgressLabelRef.innerHTML = new Date(timeElapsed).toISOString().substr(11, 8); + } + this.htmlProgressSliderRef.style.width = '0'; + } + else if (this.currentStatus === EStatus.execution_error) { + this.htmlProgressLabelRef.innerHTML = 'ERROR'; + this.htmlProgressSliderRef.style.backgroundColor = 'var(--error-text)'; + console.log('execution_error'); + } + else if (this.currentStatus === EStatus.executing) { + this.htmlProgressLabelRef.innerHTML = `${this.currentProgress}%`; + this.htmlProgressSliderRef.style.width = this.htmlProgressLabelRef.innerHTML; + this.htmlProgressSliderRef.style.backgroundColor = 'green'; + } + } + }); + Object.defineProperty(this, "registerListeners", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + api.addEventListener('status', ({ detail, }) => { + this.currentStatus = this.currentStatus === EStatus.execution_error ? EStatus.execution_error : EStatus.executed; + const queueRemaining = detail?.exec_info.queue_remaining; + if (queueRemaining) { + this.currentStatus = EStatus.executing; + } + this.updateDisplay(); + }, false); + api.addEventListener('progress', ({ detail, }) => { + const { value, max, node, } = detail; + const progress = Math.floor((value / max) * 100); + if (!isNaN(progress) && progress >= 0 && progress <= 100) { + this.currentProgress = progress; + this.currentNode = node; + } + this.updateDisplay(); + }, false); + api.addEventListener('executed', ({ detail, }) => { + if (detail?.node) { + this.currentNode = detail.node; + } + this.updateDisplay(); + }, false); + api.addEventListener('execution_start', ({ _detail, }) => { + console.log('execution_start', _detail); + this.currentStatus = EStatus.executing; + this.timeStart = Date.now(); + this.updateDisplay(); + }, false); + api.addEventListener('execution_error', ({ _detail, }) => { + console.log('execution_error', _detail); + this.currentStatus = EStatus.execution_error; + this.updateDisplay(); + }, false); + } + }); + Object.defineProperty(this, "centerNode", { + enumerable: true, + configurable: true, + writable: true, + value: () => { + const id = this.currentNode; + if (!id) { + return; + } + const node = app.graph.getNodeById(id); + if (!node) { + return; + } + app.canvas.centerOnNode(node); + } + }); + this.createSettings(); + } + setup() { + const parentElement = document.getElementById('queue-button'); + let ctoolsRoot = document.getElementById(this.htmlIdCrystoolsRoot); + if (!ctoolsRoot) { + ctoolsRoot = document.createElement('div'); + ctoolsRoot.setAttribute('id', this.htmlIdCrystoolsRoot); + ctoolsRoot.style.display = 'flex'; + ctoolsRoot.style.width = '100%'; + ctoolsRoot.style.flexDirection = 'column'; + parentElement.insertAdjacentElement('afterend', ctoolsRoot); + } + const htmlContainer = document.createElement('div'); + htmlContainer.setAttribute('id', this.htmlIdCrystoolsProgressBarContainer); + htmlContainer.setAttribute('title', 'click to see the current working node'); + htmlContainer.style.margin = '4px 0'; + htmlContainer.style.width = '100%'; + htmlContainer.style.cursor = 'pointer'; + htmlContainer.style.order = '1'; + htmlContainer.addEventListener('click', this.centerNode); + ctoolsRoot.append(htmlContainer); + const progressBar = document.createElement('div'); + progressBar.style.margin = '0 10px'; + progressBar.style.height = '18px'; + progressBar.style.position = 'relative'; + progressBar.style.backgroundColor = 'var(--bg-color)'; + htmlContainer.append(progressBar); + const progressSlider = document.createElement('div'); + progressSlider.style.position = 'absolute'; + progressSlider.style.height = '100%'; + progressSlider.style.width = '0'; + progressSlider.style.transition = 'width 0.2s'; + progressSlider.style.backgroundColor = 'green'; + this.htmlProgressSliderRef = progressSlider; + progressBar.append(this.htmlProgressSliderRef); + const progressLabel = document.createElement('div'); + progressLabel.style.position = 'absolute'; + progressLabel.style.margin = 'auto 0'; + progressLabel.style.width = '100%'; + progressLabel.style.color = 'var(--drag-text)'; + progressLabel.style.fontSize = '14px'; + progressLabel.innerHTML = '0%'; + this.htmlProgressLabelRef = progressLabel; + progressBar.append(this.htmlProgressLabelRef); + this.showProgressBar(app.ui.settings.getSettingValue(this.idShowProgressBar, this.defaultShowStatus)); + this.registerListeners(); + } +} +const crystoolsProgressBar = new CrystoolsProgressBar(); +app.registerExtension({ + name: crystoolsProgressBar.idExtensionName, + setup: crystoolsProgressBar.setup.bind(crystoolsProgressBar), +}); diff --git a/web/progressBar b/web/progressBar.ts similarity index 89% rename from web/progressBar rename to web/progressBar.ts index 63ff3b3..d7cebde 100644 --- a/web/progressBar +++ b/web/progressBar.ts @@ -2,11 +2,11 @@ import { api } from '/scripts/api.js'; import { app } from '/scripts/app.js'; import { commonPrefix } from './common.js'; -const status = { - executing: 'Executing', - executed: 'Executed', - execution_error: 'Execution error', -}; +enum EStatus { + executing = 'Executing', + executed = 'Executed', + execution_error = 'Execution error', +} class CrystoolsProgressBar { idExtensionName = 'Crystools.progressBar'; @@ -16,7 +16,7 @@ class CrystoolsProgressBar { htmlIdCrystoolsRoot = 'crystools-root'; htmlIdCrystoolsProgressBarContainer = 'crystools-progress-bar-container'; - currentStatus = status.executed; + currentStatus = EStatus.executed; currentProgress = 0; currentNode?: number = null; timeStart = 0; @@ -49,7 +49,7 @@ class CrystoolsProgressBar { }; updateDisplay = () => { - if (this.currentStatus === status.executed) { + if (this.currentStatus === EStatus.executed) { // finished this.htmlProgressLabelRef.innerHTML = 'cached'; @@ -59,13 +59,13 @@ class CrystoolsProgressBar { } this.htmlProgressSliderRef.style.width = '0'; - } else if (this.currentStatus === status.execution_error) { + } else if (this.currentStatus === EStatus.execution_error) { // an error occurred this.htmlProgressLabelRef.innerHTML = 'ERROR'; this.htmlProgressSliderRef.style.backgroundColor = 'var(--error-text)'; console.log('execution_error'); - } else if (this.currentStatus === status.executing) { + } else if (this.currentStatus === EStatus.executing) { // on going this.htmlProgressLabelRef.innerHTML = `${this.currentProgress}%`; this.htmlProgressSliderRef.style.width = this.htmlProgressLabelRef.innerHTML; @@ -130,14 +130,14 @@ class CrystoolsProgressBar { api.addEventListener('status', ({ detail, }: any) => { - this.currentStatus = this.currentStatus === status.execution_error ? status.execution_error : status.executed; + this.currentStatus = this.currentStatus === EStatus.execution_error ? EStatus.execution_error : EStatus.executed; const queueRemaining = detail?.exec_info.queue_remaining; if (queueRemaining) { - this.currentStatus = status.executing; + this.currentStatus = EStatus.executing; } this.updateDisplay(); - }); + }, false); api.addEventListener('progress', ({ detail, @@ -153,7 +153,7 @@ class CrystoolsProgressBar { } this.updateDisplay(); - }); + }, false); api.addEventListener('executed', ({ detail, @@ -163,26 +163,26 @@ class CrystoolsProgressBar { } this.updateDisplay(); - }); + }, false); api.addEventListener('execution_start', ({ _detail, }: any) => { console.log('execution_start', _detail); - this.currentStatus = status.executing; + this.currentStatus = EStatus.executing; this.timeStart = Date.now(); this.updateDisplay(); - }); + }, false); api.addEventListener('execution_error', ({ _detail, }: any) => { console.log('execution_error', _detail); - this.currentStatus = status.execution_error; + this.currentStatus = EStatus.execution_error; this.updateDisplay(); - }); + }, false); }; centerNode = () => {