Merge branch 'main' into dev
This commit is contained in:
+3
-3
@@ -58,10 +58,10 @@ module.exports = {
|
||||
// false positive https://github.com/typescript-eslint/typescript-eslint/issues/2483
|
||||
'no-shadow': 'off',
|
||||
'object-curly-newline': ["error", {
|
||||
"ObjectExpression": { "multiline": true, "minProperties": 1 },
|
||||
"ObjectPattern": { "multiline": true, "minProperties": 1 },
|
||||
"ObjectExpression": { "multiline": true, "minProperties": 8 },
|
||||
"ObjectPattern": { "multiline": true, "minProperties": 8 },
|
||||
"ImportDeclaration": {"multiline": true, "minProperties": 8 },
|
||||
"ExportDeclaration": { "multiline": true, "minProperties": 1 }
|
||||
"ExportDeclaration": { "multiline": true, "minProperties": 8 }
|
||||
}],
|
||||
"@typescript-eslint/explicit-function-return-type": 'error',
|
||||
'@typescript-eslint/triple-slash-reference': 'off',
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
<env name="CRYSTOOLS_LOGLEVEL" value="10" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="SDK_NAME" value="Python 3.11 (.env)" />
|
||||
<option name="WORKING_DIRECTORY" value="E:\AI\SD\comfyUI\ComfyUI" />
|
||||
<option name="IS_MODULE_SDK" value="false" />
|
||||
<option name="WORKING_DIRECTORY" value="X:\comfy\ComfyUI" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/main.py" />
|
||||
<option name="PARAMETERS" value="--windows-standalone-build --extra-model-paths-config ..\extra_model_paths.yaml --output-directory ..\..\comfyUI.output --input-directory ..\..\comfyUI.input --disable-auto-launch" />
|
||||
<option name="PARAMETERS" value="--extra-model-paths-config ..\extra_model_paths.yaml --output-directory ..\..\comfyUI.output --input-directory ..\..\comfyUI.input --disable-auto-launch --preview-size 1024 --front-end-version aComfy-Org/ComfyUI_frontend@latest" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
# REMEMBER TO COPY THIS FILE INTO .git/hooks/pre-commit
|
||||
|
||||
cd ../comfyUI/ComfyUI
|
||||
cd ../comfy/ComfyUI
|
||||
rm ./custom_nodes/ComfyUI-Crystools/web/*.js -f
|
||||
|
||||
LOCAL_COMMAND="npm --prefix ./custom_nodes/ComfyUI-Crystools run validate"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="typescript" type="js.build_tools.npm">
|
||||
<package-json value="$PROJECT_DIR$/custom_nodes/ComfyUI-Crystools/package.json" />
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="start" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<node-interpreter value="W:/!Workstation/nodejs/node" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
@@ -27,7 +27,11 @@ This provides better nodes to load/save images, previews, etc, and see "hidden"
|
||||
|
||||
**🎉Finally, you can see the resources used by ComfyUI (CPU, GPU, RAM, VRAM, GPU Temp and space) on the menu in real-time!**
|
||||
|
||||

|
||||
Horizontal:
|
||||

|
||||
|
||||
Vertical:
|
||||

|
||||
|
||||
Now you can identify the bottlenecks in your workflow and know when it's time to restart the server, unload models or even close some tabs!
|
||||
|
||||
@@ -512,6 +516,21 @@ You have predefined switches (string, latent, image, conditioning) but you can u
|
||||
|
||||
### Crystools
|
||||
|
||||
### 1.20.0 (21/10/2024)
|
||||
- BETA of JSON file reader and extractor, to allow you to read your own JSON files and extract the values to use in your workflow
|
||||
|
||||
### 1.19.0 (06/10/2024)
|
||||
- HORIZONTAL UI! New version is ready! 🎉
|
||||
|
||||
### 1.18.0 (21/09/2024)
|
||||
- HORIZONTAL UI! 🎉
|
||||
- Configurable size of monitors on settings menu
|
||||
|
||||
### 1.17.0 (21/09/2024)
|
||||
- Settings menu reorganized
|
||||
- Preparing for horizontal UI
|
||||
- Update from ComfyUI (typescript and new features)
|
||||
|
||||
### 1.16.0 (31/07/2024)
|
||||
- Rollback of AMD support by manager does not support other repository parameter (https://test.pypi.org/simple by pyrsmi)
|
||||
|
||||
|
||||
+8
-2
@@ -2,7 +2,7 @@
|
||||
@author: Crystian
|
||||
@title: Crystools
|
||||
@nickname: Crystools
|
||||
@version: 1.16.6
|
||||
@version: 1.20.0
|
||||
@project: "https://github.com/crystian/ComfyUI-Crystools",
|
||||
@description: Plugins for multiples uses, mainly for debugging, you need them! IG: https://www.instagram.com/crystian.ia
|
||||
"""
|
||||
@@ -21,6 +21,7 @@ from .nodes.list import CListAny, CListString
|
||||
from .nodes.pipe import CPipeToAny, CPipeFromAny
|
||||
from .nodes.utils import CUtilsCompareJsons, CUtilsStatSystem
|
||||
from .nodes.metadata import CMetadataExtractor, CMetadataCompare
|
||||
from .nodes.parameters import CJsonFile, CJsonExtractor
|
||||
from .server import *
|
||||
from .general import *
|
||||
|
||||
@@ -56,7 +57,9 @@ NODE_CLASS_MAPPINGS = {
|
||||
CLASSES.CMETADATA_EXTRACTOR_NAME.value: CMetadataExtractor,
|
||||
CLASSES.CMETADATA_COMPARATOR_NAME.value: CMetadataCompare,
|
||||
CLASSES.CUTILS_JSON_COMPARATOR_NAME.value: CUtilsCompareJsons,
|
||||
CLASSES.CUTILS_STAT_SYSTEM_NAME.value: CUtilsStatSystem
|
||||
CLASSES.CUTILS_STAT_SYSTEM_NAME.value: CUtilsStatSystem,
|
||||
CLASSES.CJSONFILE_NAME.value: CJsonFile,
|
||||
CLASSES.CJSONEXTRACTOR_NAME.value: CJsonExtractor,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -93,6 +96,9 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
|
||||
CLASSES.CUTILS_JSON_COMPARATOR_NAME.value: CLASSES.CUTILS_JSON_COMPARATOR_DESC.value,
|
||||
CLASSES.CUTILS_STAT_SYSTEM_NAME.value: CLASSES.CUTILS_STAT_SYSTEM_DESC.value,
|
||||
|
||||
CLASSES.CJSONFILE_NAME.value: CLASSES.CJSONFILE_DESC.value,
|
||||
CLASSES.CJSONEXTRACTOR_NAME.value: CLASSES.CJSONEXTRACTOR_DESC.value,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -105,3 +105,13 @@ def get_size(path):
|
||||
return f"{round(size / (pow(1024, 2)), 2)} MB"
|
||||
elif size < pow(1024, 4):
|
||||
return f"{round(size / (pow(1024, 3)), 2)} GB"
|
||||
|
||||
|
||||
def get_nested_value(data, dotted_key, default=None):
|
||||
keys = dotted_key.split('.')
|
||||
for key in keys:
|
||||
if isinstance(data, dict) and key in data:
|
||||
data = data[key]
|
||||
else:
|
||||
return default
|
||||
return data
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
version = "1.16.6"
|
||||
version = "1.20.0"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
+10
-10
@@ -110,11 +110,11 @@ class CGPUInfo:
|
||||
if self.cudaDevice == 'cpu':
|
||||
gpuType = 'cpu'
|
||||
gpus.append({
|
||||
'gpu_utilization': 0,
|
||||
'gpu_temperature': 0,
|
||||
'vram_total': 0,
|
||||
'vram_used': 0,
|
||||
'vram_used_percent': 0,
|
||||
'gpu_utilization': -1,
|
||||
'gpu_temperature': -1,
|
||||
'vram_total': -1,
|
||||
'vram_used': -1,
|
||||
'vram_used_percent': -1,
|
||||
})
|
||||
else:
|
||||
gpuType = self.cudaDevice
|
||||
@@ -126,11 +126,11 @@ class CGPUInfo:
|
||||
for deviceIndex in range(self.cudaDevicesFound):
|
||||
deviceHandle = self.deviceGetHandleByIndex(deviceIndex)
|
||||
|
||||
gpuUtilization = 0
|
||||
vramPercent = 0
|
||||
vramUsed = 0
|
||||
vramTotal = 0
|
||||
gpuTemperature = 0
|
||||
gpuUtilization = -1
|
||||
vramPercent = -1
|
||||
vramUsed = -1
|
||||
vramTotal = -1
|
||||
gpuTemperature = -1
|
||||
|
||||
# GPU Utilization
|
||||
if self.switchGPU and self.gpusUtilization[deviceIndex]:
|
||||
|
||||
+11
-2
@@ -63,7 +63,16 @@ class CLASSES(Enum):
|
||||
CMETADATA_COMPARATOR_NAME = 'Metadata comparator [Crystools]'
|
||||
CMETADATA_COMPARATOR_DESC = prefix + 'Metadata comparator'
|
||||
|
||||
CUTILS_JSON_COMPARATOR_NAME = 'Json comparator [Crystools]'
|
||||
CUTILS_JSON_COMPARATOR_DESC = prefix + 'Json comparator'
|
||||
CUTILS_JSON_COMPARATOR_NAME = 'JSON comparator [Crystools]'
|
||||
CUTILS_JSON_COMPARATOR_DESC = prefix + 'JSON comparator'
|
||||
CUTILS_STAT_SYSTEM_NAME = 'Stats system [Crystools]'
|
||||
CUTILS_STAT_SYSTEM_DESC = prefix + 'Stats system (powered by WAS)'
|
||||
|
||||
# CPARAMETERS_NAME = 'External parameter from JSON file [Crystools]'
|
||||
# CPARAMETERS_DESC = prefix + 'External parameters from JSON file'
|
||||
|
||||
CJSONFILE_NAME = 'Read JSON file [Crystools]'
|
||||
CJSONFILE_DESC = prefix + 'Read JSON file (BETA)'
|
||||
|
||||
CJSONEXTRACTOR_NAME = 'JSON extractor [Crystools]'
|
||||
CJSONEXTRACTOR_DESC = prefix + 'JSON extractor (BETA)'
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import json
|
||||
from ..core import CONFIG, any, JSON_WIDGET, CATEGORY, STRING, INT, FLOAT, BOOLEAN, logger, get_nested_value
|
||||
|
||||
# class CParameter:
|
||||
# def __init__(self):
|
||||
# pass
|
||||
#
|
||||
# @classmethod
|
||||
# def INPUT_TYPES(cls):
|
||||
# return {
|
||||
# "required": {
|
||||
# },
|
||||
# "optional": {
|
||||
# "path_to_json": STRING,
|
||||
# "key": STRING,
|
||||
# "default": STRING,
|
||||
# },
|
||||
# }
|
||||
#
|
||||
# CATEGORY = CATEGORY.MAIN.value + CATEGORY.UTILS.value
|
||||
# INPUT_IS_LIST = False
|
||||
#
|
||||
# RETURN_TYPES = (any,)
|
||||
# RETURN_NAMES = ("any",)
|
||||
#
|
||||
# FUNCTION = "execute"
|
||||
#
|
||||
# def execute(self, path_to_json=None, key=True, default=None):
|
||||
# text = default
|
||||
# value = text
|
||||
#
|
||||
# if path_to_json is not None and path_to_json != "":
|
||||
# logger.debug(f"External parameter from: '{path_to_json}'")
|
||||
# try:
|
||||
# with open(path_to_json, 'r') as file:
|
||||
# data = json.load(file)
|
||||
# logger.debug(f"File found, data: '{data}'")
|
||||
#
|
||||
# result = get_value(data, key, default)
|
||||
# text = result["text"]
|
||||
# value = result["value"]
|
||||
#
|
||||
# except Exception as e:
|
||||
# logger.error(e)
|
||||
# text = f"Error reading file: {e}\nReturning default value: '{default}'"
|
||||
# value = default
|
||||
#
|
||||
# return {"ui": {"text": [text]}, "result": [value]}
|
||||
|
||||
class CJsonFile:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
},
|
||||
"optional": {
|
||||
"path_to_json": STRING,
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = CATEGORY.MAIN.value + CATEGORY.UTILS.value
|
||||
INPUT_IS_LIST = False
|
||||
|
||||
RETURN_TYPES = ("JSON",)
|
||||
RETURN_NAMES = ("json",)
|
||||
|
||||
FUNCTION = "execute"
|
||||
|
||||
def IS_CHANGED(path_to_json=None):
|
||||
return True
|
||||
|
||||
def execute(self, path_to_json=None):
|
||||
text = ""
|
||||
data = {}
|
||||
|
||||
if path_to_json is not None and path_to_json != "":
|
||||
logger.debug(f"Open json file: '{path_to_json}'")
|
||||
try:
|
||||
with open(path_to_json, 'r') as file:
|
||||
data = json.load(file)
|
||||
text = json.dumps(data, indent=CONFIG["indent"])
|
||||
logger.debug(f"File found, data: '{str(data)}'")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
text = f"Error reading file: {e}"
|
||||
|
||||
return {"ui": {"text": [text]}, "result": [data]}
|
||||
|
||||
class CJsonExtractor:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"json": JSON_WIDGET,
|
||||
},
|
||||
"optional": {
|
||||
"key": STRING,
|
||||
"default": STRING,
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = CATEGORY.MAIN.value + CATEGORY.UTILS.value
|
||||
INPUT_IS_LIST = False
|
||||
|
||||
RETURN_TYPES = (any, "STRING", "INT", "FLOAT", "BOOLEAN")
|
||||
RETURN_NAMES = ("any", "string", "int", "float", "boolean")
|
||||
|
||||
# OUTPUT_IS_LIST = (False,)
|
||||
|
||||
FUNCTION = "execute"
|
||||
|
||||
def execute(cls, json=None, key=True, default=None):
|
||||
result = get_value(json, key, default)
|
||||
|
||||
result["any"] = result["value"]
|
||||
try:
|
||||
result["string"] = str(result["value"])
|
||||
except Exception as e:
|
||||
result["string"] = result["value"]
|
||||
|
||||
try:
|
||||
result["int"] = int(result["value"])
|
||||
except Exception as e:
|
||||
result["int"] = result["value"]
|
||||
|
||||
try:
|
||||
result["float"] = float(result["value"])
|
||||
except Exception as e:
|
||||
result["float"] = result["value"]
|
||||
|
||||
try:
|
||||
result["boolean"] = result["value"].lower() == "true"
|
||||
except Exception as e:
|
||||
result["boolean"] = result["value"]
|
||||
|
||||
return {
|
||||
"ui": {"text": [result["text"]]},
|
||||
"result": [
|
||||
result["any"],
|
||||
result["string"],
|
||||
result["int"],
|
||||
result["float"],
|
||||
result["boolean"]
|
||||
]
|
||||
}
|
||||
|
||||
def get_value(data, key, default=None):
|
||||
text = ""
|
||||
val = ""
|
||||
|
||||
if key is not None and key != "":
|
||||
val = get_nested_value(data, key, default)
|
||||
if default != val:
|
||||
text = f"Key found, return value: '{val}'"
|
||||
else:
|
||||
text = f"Key no found, return default value: '{val}'"
|
||||
else:
|
||||
text = f"Key is empty, return default value: '{val}'"
|
||||
|
||||
return {
|
||||
"text": text,
|
||||
"value": val
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-crystools"
|
||||
description = "With this suit, you can see the resources monitor, progress bar & time elapsed, metadata and compare between two images, compare between two JSONs, show any value to console/display, pipes, and more!\nThis provides better nodes to load/save images, previews, etc, and see \"hidden\" data without loading a new workflow."
|
||||
version = "1.16.6"
|
||||
version = "1.20.0"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = ["deepdiff", "torch", "numpy", "Pillow", "pynvml", "py-cpuinfo"]
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
deepdiff
|
||||
torch
|
||||
numpy
|
||||
Pillow
|
||||
Pillow>=9.5.0
|
||||
pynvml
|
||||
py-cpuinfo
|
||||
piexif
|
||||
|
||||
+13
-12
@@ -6,19 +6,16 @@
|
||||
"typeRoots": [
|
||||
// "./web/ts/typings",
|
||||
],
|
||||
"baseUrl": ".",
|
||||
// "rootDir": "../",
|
||||
"baseUrl": "../../web",
|
||||
// "baseUrl": "./",
|
||||
"paths": {
|
||||
"/scripts/*": ["../../web/scripts/*"],
|
||||
"/lib/*": ["../../web/lib/*"],
|
||||
"/extensions/*": ["../../web/extensions/*"],
|
||||
"/types/*": ["../../web/types/*"]
|
||||
// "/scripts/*": ["../../web/scripts/*"], // works
|
||||
},
|
||||
// "rootDir": "./web/src",
|
||||
// "rootDir": "../../",
|
||||
// "rootDirs": [
|
||||
// "../../web",
|
||||
// "web/src"
|
||||
// ],
|
||||
"rootDirs": [
|
||||
"../../web",
|
||||
// "web"
|
||||
],
|
||||
"preserveSymlinks": true,
|
||||
"checkJs": false,
|
||||
"allowJs": true,
|
||||
@@ -58,7 +55,11 @@
|
||||
"noEmit": false
|
||||
},
|
||||
"include": [
|
||||
"./web/*.ts"
|
||||
// "./custom_nodes/ComfyUI-Crystools/web/*.ts",
|
||||
// "../../../web/*.ts",
|
||||
// "../../web/*.ts",
|
||||
"web/*.ts",
|
||||
// "web/types.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
export type * from './scripts.js';
|
||||
export * from './scripts.js';
|
||||
export type { ComfyApp } from './typings/comfy.js';
|
||||
export * from './liteGraph.js';
|
||||
export type * from './liteGraph.js';
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './scripts.js';
|
||||
export * from './liteGraph.js';
|
||||
@@ -0,0 +1,5 @@
|
||||
export type * from './scripts.js';
|
||||
export * from './scripts.js';
|
||||
export type { ComfyApp } from './typings/comfy.js';
|
||||
export * from './liteGraph.js';
|
||||
export type * from './liteGraph.js';
|
||||
+2
-5
@@ -1,6 +1,5 @@
|
||||
export type * from './liteGraph.types';
|
||||
export * from '/scripts/widgets.js';
|
||||
import type { IWidget as IWidgetOld, LGraphNode as TypeGraphNode, TypeLiteGraph } from './liteGraph.types';
|
||||
export type * from './liteGraph.types.js';
|
||||
import type { IWidget as IWidgetOld, LGraphNode as TypeGraphNode, TypeLiteGraph } from './liteGraph.types.js';
|
||||
declare const LGraphNode: typeof TypeGraphNode;
|
||||
export interface IWidget extends IWidgetOld {
|
||||
onRemove?: () => void;
|
||||
@@ -12,9 +11,7 @@ export declare class TLGraphNode extends LGraphNode {
|
||||
static color: string;
|
||||
static bgcolor: string;
|
||||
static collapsable: boolean;
|
||||
widgets?: IWidget[];
|
||||
isVirtualNode?: boolean;
|
||||
onResize?: (size: [number, number]) => void;
|
||||
widgets_values?: any[];
|
||||
name?: string;
|
||||
prototype: TLGraphNode;
|
||||
@@ -1,25 +1,12 @@
|
||||
export * from '/scripts/widgets.js';
|
||||
export class TLGraphNode extends LGraphNode {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
Object.defineProperty(this, "widgets", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "isVirtualNode", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "onResize", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "widgets_values", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
@@ -1,9 +1,8 @@
|
||||
// / <reference path="/types/litegraph.d.ts" />
|
||||
// A LOTS OF PATCHES FOR LITEGRAPH TYPES ¯\_(ツ)_/¯
|
||||
export type * from './liteGraph.types';
|
||||
export * from '/scripts/widgets.js';
|
||||
export type * from './liteGraph.types.js';
|
||||
|
||||
import type { IWidget as IWidgetOld, LGraphNode as TypeGraphNode, TypeLiteGraph } from './liteGraph.types';
|
||||
import type { IWidget as IWidgetOld, LGraphNode as TypeGraphNode, TypeLiteGraph } from './liteGraph.types.js';
|
||||
|
||||
declare const LGraphNode: typeof TypeGraphNode; // just for get the type
|
||||
|
||||
@@ -20,9 +19,9 @@ export class TLGraphNode extends LGraphNode {
|
||||
static bgcolor: string;
|
||||
static collapsable: boolean;
|
||||
|
||||
widgets?: IWidget[];
|
||||
// widgets?: IWidget[];
|
||||
isVirtualNode?: boolean;
|
||||
onResize?: (size: [number, number]) => void;
|
||||
// override onResize?: (size: [number, number]) => void;
|
||||
widgets_values?: any[];
|
||||
name?: string;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// A LOTS OF PATCHES FOR LITEGRAPH TYPES ¯\_(ツ)_/¯
|
||||
export type * from '/types/litegraph';
|
||||
export type * from '/scripts/widgets';
|
||||
export type * from '/scripts/app';
|
||||
export type { api } from '/scripts/api';
|
||||
import { LGraph } from './typings/litegraph.js';
|
||||
|
||||
import { LGraph, LiteGraph } from '/types/litegraph';
|
||||
export type * from './typings/litegraph.js';
|
||||
|
||||
export declare type TypeLiteGraph = typeof LiteGraph & {
|
||||
graph: LGraph;
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
export { ComfyWidgets } from '../../../scripts/widgets.js';
|
||||
export { app } from '../../../scripts/app.js';
|
||||
export { api } from '../../../scripts/api.js';
|
||||
export * as utils from '../../../scripts/utils.js';
|
||||
export { ComfyButtonGroup } from '../../../scripts/ui/components/buttonGroup.js';
|
||||
@@ -0,0 +1,5 @@
|
||||
export { ComfyWidgets } from '../../../scripts/widgets.js';
|
||||
export { app } from '../../../scripts/app.js';
|
||||
export { api } from '../../../scripts/api.js';
|
||||
export * as utils from '../../../scripts/utils.js';
|
||||
export { ComfyButtonGroup } from '../../../scripts/ui/components/buttonGroup.js';
|
||||
@@ -0,0 +1,10 @@
|
||||
// @ts-expect-error I could not find a way to make this work
|
||||
export { ComfyWidgets } from '../../../scripts/widgets.js';
|
||||
// @ts-expect-error I could not find a way to make this work
|
||||
export { app } from '../../../scripts/app.js';
|
||||
// @ts-expect-error I could not find a way to make this work
|
||||
export { api } from '../../../scripts/api.js';
|
||||
// @ts-expect-error I could not find a way to make this work
|
||||
export * as utils from '../../../scripts/utils.js';
|
||||
// @ts-expect-error I could not find a way to make this work
|
||||
export { ComfyButtonGroup } from '../../../scripts/ui/components/buttonGroup.js';
|
||||
@@ -0,0 +1,5 @@
|
||||
COPIED FROM rgthree
|
||||
|
||||
The typings in node_modules or in ComfyUI's web/ directory were not that well covered. These typings are hacked together with some of the inconsistencies I found.
|
||||
|
||||
To be honest, I have no idea why I needed a bizarre workaround for litegraph's types. Usually the '/// <reference>' comment should have picked up the types, but it wasn't having it. ¯\_(ツ)_/¯
|
||||
Vendored
+227
@@ -0,0 +1,227 @@
|
||||
import type { LGraphGroup as TLGraphGroup, LGraphNode as TLGraphNode, IWidget, SerializedLGraphNode, LGraph as TLGraph, LGraphCanvas as TLGraphCanvas, LiteGraph as TLiteGraph } from "./litegraph.js";
|
||||
import type {Constructor, SerializedGraph} from './index.js';
|
||||
|
||||
declare global {
|
||||
const LiteGraph: typeof TLiteGraph;
|
||||
const LGraph: typeof TLGraph;
|
||||
const LGraphNode: typeof TLGraphNode;
|
||||
const LGraphCanvas: typeof TLGraphCanvas;
|
||||
const LGraphGroup: typeof TLGraphGroup;
|
||||
}
|
||||
|
||||
// @rgthree: Types on ComfyApp as needed.
|
||||
export interface ComfyApp {
|
||||
extensions: ComfyExtension[];
|
||||
async queuePrompt(number?: number, batchCount = 1): Promise<void>;
|
||||
graph: TLGraph;
|
||||
canvas: TLGraphCanvas;
|
||||
clean() : void;
|
||||
registerExtension(extension: ComfyExtension): void;
|
||||
getPreviewFormatParam(): string;
|
||||
getRandParam(): string;
|
||||
loadApiJson(apiData: {}, fileName: string): void;
|
||||
async graphToPrompt(graph?: TLGraph, clean?: boolean): Promise<void>;
|
||||
// workflow: ComfyWorkflowInstance ???
|
||||
async loadGraphData(graphData: {}, clean?: boolean, restore_view?: boolean, workflow?: any|null): Promise<void>
|
||||
ui: {
|
||||
settings: {
|
||||
addSetting(config: {id: string, name: string, type: () => HTMLElement}) : void;
|
||||
}
|
||||
}
|
||||
// Just marking as any for now.
|
||||
menu?: any;
|
||||
}
|
||||
|
||||
export interface ComfyWidget extends IWidget {
|
||||
// https://github.com/comfyanonymous/ComfyUI/issues/2193 Changes from SerializedLGraphNode to
|
||||
// LGraphNode...
|
||||
serializeValue(nodeType: TLGraphNode, index: number): Promise<TValue>;
|
||||
afterQueued(): void;
|
||||
inputEl?: HTMLTextAreaElement;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface ComfyGraphNode extends TLGraphNode {
|
||||
getExtraMenuOptions: (node: TLGraphNode, options: ContextMenuItem[]) => void;
|
||||
onExecuted(message: any): void;
|
||||
}
|
||||
|
||||
export interface ComfyNode extends TLGraphNode {
|
||||
comfyClass: string;
|
||||
}
|
||||
|
||||
// @rgthree
|
||||
export interface ComfyNodeConstructor extends Constructor<ComfyNode> {
|
||||
static title: string;
|
||||
static type?: string;
|
||||
static comfyClass: string;
|
||||
}
|
||||
|
||||
export type NodeMode = 0|1|2|3|4|undefined;
|
||||
|
||||
|
||||
export interface ComfyExtension {
|
||||
/**
|
||||
* The name of the extension
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Allows any initialisation, e.g. loading resources. Called after the canvas is created but before nodes are added
|
||||
* @param app The ComfyUI app instance
|
||||
*/
|
||||
init?(app: ComfyApp): Promise<void>;
|
||||
/**
|
||||
* Allows any additonal setup, called after the application is fully set up and running
|
||||
* @param app The ComfyUI app instance
|
||||
*/
|
||||
setup?(app: ComfyApp): Promise<void>;
|
||||
/**
|
||||
* Called before nodes are registered with the graph
|
||||
* @param defs The collection of node definitions, add custom ones or edit existing ones
|
||||
* @param app The ComfyUI app instance
|
||||
*/
|
||||
addCustomNodeDefs?(defs: Record<string, ComfyObjectInfo>, app: ComfyApp): Promise<void>;
|
||||
/**
|
||||
* Allows the extension to add custom widgets
|
||||
* @param app The ComfyUI app instance
|
||||
* @returns An array of {[widget name]: widget data}
|
||||
*/
|
||||
getCustomWidgets?(
|
||||
app: ComfyApp
|
||||
): Promise<
|
||||
Record<string, (node, inputName, inputData, app) => { widget?: IWidget; minWidth?: number; minHeight?: number }>
|
||||
>;
|
||||
/**
|
||||
* Allows the extension to add additional handling to the node before it is registered with LGraph
|
||||
* @rgthree changed nodeType from `typeof LGraphNode` to `ComfyNodeConstructor`
|
||||
* @param nodeType The node class (not an instance)
|
||||
* @param nodeData The original node object info config object
|
||||
* @param app The ComfyUI app instance
|
||||
*/
|
||||
beforeRegisterNodeDef?(nodeType: ComfyNodeConstructor, nodeData: ComfyObjectInfo, app: ComfyApp): Promise<void>;
|
||||
/**
|
||||
* Allows the extension to register additional nodes with LGraph after standard nodes are added
|
||||
* @param app The ComfyUI app instance
|
||||
*/
|
||||
// @rgthree - add void for non async
|
||||
registerCustomNodes?(app: ComfyApp): void|Promise<void>;
|
||||
/**
|
||||
* Allows the extension to modify a node that has been reloaded onto the graph.
|
||||
* If you break something in the backend and want to patch workflows in the frontend
|
||||
* This is the place to do this
|
||||
* @param node The node that has been loaded
|
||||
* @param app The ComfyUI app instance
|
||||
*/
|
||||
loadedGraphNode?(node: TLGraphNode, app: ComfyApp);
|
||||
/**
|
||||
* Allows the extension to run code after the constructor of the node
|
||||
* @param node The node that has been created
|
||||
* @param app The ComfyUI app instance
|
||||
*/
|
||||
nodeCreated?(node: TLGraphNode, app: ComfyApp);
|
||||
}
|
||||
|
||||
export type ComfyObjectInfo = {
|
||||
name: string;
|
||||
display_name?: string;
|
||||
description?: string;
|
||||
category: string;
|
||||
input?: {
|
||||
required?: Record<string, ComfyObjectInfoConfig>;
|
||||
optional?: Record<string, ComfyObjectInfoConfig>;
|
||||
hidden?: Record<string, ComfyObjectInfoConfig>;
|
||||
};
|
||||
output?: string[];
|
||||
output_name: string[];
|
||||
// @rgthree
|
||||
output_node?: boolean;
|
||||
};
|
||||
|
||||
export type ComfyObjectInfoConfig = [string | any[]] | [string | any[], any];
|
||||
|
||||
// @rgthree
|
||||
type ComfyApiInputLink = [
|
||||
/** The id string of the connected node. */
|
||||
string,
|
||||
/** The output index. */
|
||||
number,
|
||||
]
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiFormatNode = {
|
||||
"inputs": {
|
||||
[input_name: string]: string|number|boolean|ComfyApiInputLink,
|
||||
},
|
||||
"class_type": string,
|
||||
"_meta": {
|
||||
"title": string,
|
||||
}
|
||||
}
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiFormat = {
|
||||
[node_id: string]: ComfyApiFormatNode
|
||||
}
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiPrompt = {
|
||||
workflow: SerializedGraph,
|
||||
output: ComfyApiFormat,
|
||||
}
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiEventDetailStatus = {
|
||||
exec_info: {
|
||||
queue_remaining: number;
|
||||
};
|
||||
};
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiEventDetailExecutionStart = {
|
||||
prompt_id: string;
|
||||
};
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiEventDetailExecuting = null | string;
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiEventDetailProgress = {
|
||||
node: string;
|
||||
prompt_id: string;
|
||||
max: number;
|
||||
value: number;
|
||||
};
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiEventDetailExecuted = {
|
||||
node: string;
|
||||
prompt_id: string;
|
||||
output: any;
|
||||
};
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiEventDetailCached = {
|
||||
nodes: string[];
|
||||
prompt_id: string;
|
||||
};
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiEventDetailExecuted = {
|
||||
prompt_id: string;
|
||||
node: string;
|
||||
output: any;
|
||||
};
|
||||
|
||||
// @rgthree
|
||||
export type ComfyApiEventDetailError = {
|
||||
prompt_id: string;
|
||||
exception_type: string;
|
||||
exception_message: string;
|
||||
node_id: string;
|
||||
node_type: string;
|
||||
node_id: string;
|
||||
traceback: string;
|
||||
executed: any[];
|
||||
current_inputs: {[key: string]: (number[]|string[])};
|
||||
current_outputs: {[key: string]: (number[]|string[])};
|
||||
}
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
import { LGraph } from "./litegraph.js";
|
||||
|
||||
export type Constructor<T> = new(...args: any[]) => T;
|
||||
|
||||
export type SerializedLink = [
|
||||
number, // this.id,
|
||||
number, // this.origin_id,
|
||||
number, // this.origin_slot,
|
||||
number, // this.target_id,
|
||||
number, // this.target_slot,
|
||||
string, // this.type
|
||||
];
|
||||
|
||||
export interface SerializedNodeInput {
|
||||
name: string;
|
||||
type: string;
|
||||
link: number;
|
||||
}
|
||||
export interface SerializedNodeOutput {
|
||||
name: string;
|
||||
type: string;
|
||||
link: number;
|
||||
slot_index: number;
|
||||
links: number[];
|
||||
}
|
||||
export interface SerializedNode {
|
||||
id: number;
|
||||
inputs: SerializedNodeInput[];
|
||||
outputs: SerializedNodeOutput[];
|
||||
mode: number;
|
||||
order: number;
|
||||
pos: [number, number];
|
||||
properties: any;
|
||||
size: [number, number];
|
||||
type: string;
|
||||
widgets_values: Array<number | string>;
|
||||
}
|
||||
|
||||
export interface SerializedGraph {
|
||||
config: any;
|
||||
extra: any;
|
||||
groups: any;
|
||||
last_link_id: number;
|
||||
last_node_id: number;
|
||||
links: SerializedLink[];
|
||||
nodes: SerializedNode[];
|
||||
}
|
||||
|
||||
export interface BadLinksData<T = SerializedGraph|LGraph> {
|
||||
hasBadLinks: boolean;
|
||||
fixed: boolean;
|
||||
graph: T;
|
||||
patched: number;
|
||||
deleted: number;
|
||||
}
|
||||
Vendored
+1750
File diff suppressed because it is too large
Load Diff
Vendored
+2
-1
@@ -1,3 +1,4 @@
|
||||
import type { ComfyApp, TLGraphNode } from './liteGraph.js';
|
||||
import type { TLGraphNode } from './comfy/index.js';
|
||||
import { ComfyApp } from './comfy/index.js';
|
||||
export declare const commonPrefix = "\uD83E\uDE9B";
|
||||
export declare function displayContext(nodeType: TLGraphNode, appFromArg: ComfyApp, index?: number, serialize_widgets?: boolean, isVirtualNode?: boolean): void;
|
||||
|
||||
+2
-11
@@ -1,4 +1,4 @@
|
||||
import { ComfyWidgets } from './liteGraph.js';
|
||||
import { ComfyWidgets } from './comfy/index.js';
|
||||
export const commonPrefix = '🪛';
|
||||
export function displayContext(nodeType, appFromArg, index = 0, serialize_widgets = false, isVirtualNode = false) {
|
||||
function populate(text) {
|
||||
@@ -14,9 +14,7 @@ export function displayContext(nodeType, appFromArg, index = 0, serialize_widget
|
||||
this.serialize_widgets = serialize_widgets;
|
||||
this.isVirtualNode = isVirtualNode;
|
||||
const widget = ComfyWidgets.STRING(this, 'text', [
|
||||
'STRING', {
|
||||
multiline: true,
|
||||
},
|
||||
'STRING', { multiline: true },
|
||||
], appFromArg).widget;
|
||||
widget.inputEl.readOnly = true;
|
||||
widget.inputEl.style.opacity = 0.6;
|
||||
@@ -50,10 +48,3 @@ export function displayContext(nodeType, appFromArg, index = 0, serialize_widget
|
||||
}
|
||||
};
|
||||
}
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `
|
||||
#comfy-settings-dialog label[for^=Crystools-] {
|
||||
white-space: pre-line;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
+6
-16
@@ -1,5 +1,5 @@
|
||||
import type { ComfyApp, TLGraphNode } from './liteGraph.js';
|
||||
import { ComfyWidgets } from './liteGraph.js';
|
||||
import type { TLGraphNode } from './comfy/index.js';
|
||||
import { ComfyWidgets, ComfyApp } from './comfy/index.js';
|
||||
|
||||
export const commonPrefix = '🪛';
|
||||
|
||||
@@ -25,9 +25,7 @@ export function displayContext(
|
||||
this.isVirtualNode = isVirtualNode;
|
||||
|
||||
const widget = ComfyWidgets.STRING(this, 'text', [
|
||||
'STRING', {
|
||||
multiline: true,
|
||||
},
|
||||
'STRING', { multiline: true },
|
||||
], appFromArg).widget;
|
||||
widget.inputEl.readOnly = true;
|
||||
widget.inputEl.style.opacity = 0.6;
|
||||
@@ -53,10 +51,12 @@ export function displayContext(
|
||||
}
|
||||
|
||||
// When the node is executed we will be sent the input text, display this in the widget
|
||||
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
const onExecutedOriginal = nodeType.prototype.onExecuted;
|
||||
// @ts-ignore
|
||||
nodeType.prototype.onExecuted = function(message: { text: string }): void {
|
||||
// @ts-ignore
|
||||
onExecutedOriginal?.apply(this, arguments);
|
||||
populate.call(this, message.text);
|
||||
};
|
||||
@@ -90,13 +90,3 @@ export function displayContext(
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// HACKS
|
||||
// for settings using break lines on label
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `
|
||||
#comfy-settings-dialog label[for^=Crystools-] {
|
||||
white-space: pre-line;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
+6
-16
@@ -1,7 +1,5 @@
|
||||
import { app } from '/scripts/app.js';
|
||||
import { api } from '/scripts/api.js';
|
||||
import { app, api, ComfyWidgets, LiteGraph, TLGraphNode } from './comfy/index.js';
|
||||
import { commonPrefix, displayContext } from './common.js';
|
||||
import { LiteGraph, TLGraphNode, ComfyWidgets } from './liteGraph.js';
|
||||
app.registerExtension({
|
||||
name: 'Crystools.Debugger.ConsoleAny',
|
||||
beforeRegisterNodeDef(nodeType, nodeData, appFromArg) {
|
||||
@@ -20,7 +18,7 @@ app.registerExtension({
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: async () => {
|
||||
value: () => {
|
||||
return app.graphToPrompt()
|
||||
.then((workflow) => {
|
||||
let result = 'inactive';
|
||||
@@ -56,25 +54,17 @@ app.registerExtension({
|
||||
this.serialize_widgets = false;
|
||||
this.isVirtualNode = true;
|
||||
const widget = ComfyWidgets.STRING(this, '', [
|
||||
'', {
|
||||
default: '', multiline: true,
|
||||
},
|
||||
'', { default: '', multiline: true },
|
||||
], app).widget;
|
||||
widget.inputEl.readOnly = true;
|
||||
ComfyWidgets.BOOLEAN(this, 'Active', [
|
||||
'', {
|
||||
default: true,
|
||||
},
|
||||
'', { default: true },
|
||||
]);
|
||||
ComfyWidgets.BOOLEAN(this, 'Parsed', [
|
||||
'', {
|
||||
default: true,
|
||||
},
|
||||
'', { default: true },
|
||||
]);
|
||||
ComfyWidgets.COMBO(this, 'What', [
|
||||
['Prompt', 'Workflow'], {
|
||||
default: 'Prompt',
|
||||
},
|
||||
['Prompt', 'Workflow'], { default: 'Prompt' },
|
||||
]);
|
||||
api.addEventListener('executed', this.fillMetadataWidget, false);
|
||||
}
|
||||
|
||||
+9
-19
@@ -1,8 +1,6 @@
|
||||
import { app } from '/scripts/app.js';
|
||||
import { api } from '/scripts/api.js';
|
||||
import type { ComfyNode } from './comfy/index.js';
|
||||
import { app, api, ComfyWidgets, LiteGraph, TLGraphNode, ComfyApp } from './comfy/index.js';
|
||||
import { commonPrefix, displayContext } from './common.js';
|
||||
import type { ComfyNode, ComfyApp } from './liteGraph.js';
|
||||
import { LiteGraph, TLGraphNode, ComfyWidgets } from './liteGraph.js';
|
||||
|
||||
// "Show any" Node
|
||||
app.registerExtension({
|
||||
@@ -25,36 +23,28 @@ app.registerExtension({
|
||||
this.isVirtualNode = true;
|
||||
|
||||
const widget = ComfyWidgets.STRING(this, '', [
|
||||
'', {
|
||||
default: '', multiline: true,
|
||||
},
|
||||
'', {default: '', multiline: true},
|
||||
], app).widget;
|
||||
widget.inputEl.readOnly = true;
|
||||
ComfyWidgets.BOOLEAN(this, 'Active', [
|
||||
'', {
|
||||
default: true,
|
||||
},
|
||||
'', {default: true},
|
||||
]);
|
||||
ComfyWidgets.BOOLEAN(this, 'Parsed', [
|
||||
'', {
|
||||
default: true,
|
||||
},
|
||||
'', {default: true},
|
||||
]);
|
||||
ComfyWidgets.COMBO(this, 'What', [
|
||||
['Prompt', 'Workflow'], {
|
||||
default: 'Prompt',
|
||||
},
|
||||
['Prompt', 'Workflow'], {default: 'Prompt'},
|
||||
]);
|
||||
|
||||
// It runs at finish on each prompt queue
|
||||
api.addEventListener('executed', this.fillMetadataWidget, false);
|
||||
}
|
||||
|
||||
fillMetadataWidget = async (): Promise<string> => {
|
||||
fillMetadataWidget = (): Promise<string> => {
|
||||
return app.graphToPrompt()
|
||||
.then((workflow): string => {
|
||||
.then((workflow: any): string => {
|
||||
let result = 'inactive';
|
||||
if(this.widgets?.length !== 4) {
|
||||
if (this.widgets?.length !== 4) {
|
||||
console.error('Something is wrong with the widgets, should be 4!');
|
||||
return 'error';
|
||||
}
|
||||
|
||||
+14
-2
@@ -1,5 +1,9 @@
|
||||
import { app } from '/scripts/app.js';
|
||||
import { app } from './comfy/index.js';
|
||||
import { displayContext } from './common.js';
|
||||
const crystoolsExtensionsSerialized = {
|
||||
'Read JSON file [Crystools]': 'Crystools.Utils.ReadJsonFile',
|
||||
'JSON extractor [Crystools]': 'Crystools.Utils.JsonExtractor',
|
||||
};
|
||||
const crystoolsExtensions = {
|
||||
'Get resolution [Crystools]': 'Crystools.Image.GetResolution',
|
||||
'Preview from image [Crystools]': 'Crystools.Image.PreviewFromImage',
|
||||
@@ -8,12 +12,20 @@ const crystoolsExtensions = {
|
||||
'Stats system [Crystools]': 'Crystools.Utils.StatsSystem',
|
||||
'Show any to JSON [Crystools]': 'Crystools.Debugger.ConsoleAnyToJson',
|
||||
};
|
||||
Object.keys(crystoolsExtensionsSerialized).forEach(prop => {
|
||||
crystoolsExtensions[prop] = crystoolsExtensionsSerialized[prop];
|
||||
});
|
||||
Object.keys(crystoolsExtensions).forEach(key => {
|
||||
app.registerExtension({
|
||||
name: crystoolsExtensions[key],
|
||||
beforeRegisterNodeDef(nodeType, nodeData, appFromArg) {
|
||||
if (nodeData.name === key) {
|
||||
displayContext(nodeType, appFromArg, 0);
|
||||
if (nodeData.name in crystoolsExtensionsSerialized) {
|
||||
displayContext(nodeType, appFromArg, 0, true);
|
||||
}
|
||||
else {
|
||||
displayContext(nodeType, appFromArg, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
+18
-4
@@ -1,7 +1,12 @@
|
||||
import { app } from '/scripts/app.js';
|
||||
import { app, TLGraphNode, ComfyApp } from './comfy/index.js';
|
||||
import type { ComfyNode } from './comfy/index.js';
|
||||
import { displayContext } from './common.js';
|
||||
import type { ComfyNode, ComfyApp } from './liteGraph.types';
|
||||
import { TLGraphNode } from './liteGraph';
|
||||
|
||||
const crystoolsExtensionsSerialized: Record<string, string> = {
|
||||
// 'External parameter from JSON file [Crystools]': 'Crystools.Utils.ExternalParameterFromJson',
|
||||
'Read JSON file [Crystools]': 'Crystools.Utils.ReadJsonFile',
|
||||
'JSON extractor [Crystools]': 'Crystools.Utils.JsonExtractor',
|
||||
};
|
||||
|
||||
const crystoolsExtensions: Record<string, string> = {
|
||||
'Get resolution [Crystools]': 'Crystools.Image.GetResolution',
|
||||
@@ -12,12 +17,21 @@ const crystoolsExtensions: Record<string, string> = {
|
||||
'Show any to JSON [Crystools]': 'Crystools.Debugger.ConsoleAnyToJson',
|
||||
};
|
||||
|
||||
Object.keys(crystoolsExtensionsSerialized).forEach(prop => {
|
||||
// @ts-ignore
|
||||
crystoolsExtensions[prop] = crystoolsExtensionsSerialized[prop];
|
||||
});
|
||||
|
||||
Object.keys(crystoolsExtensions).forEach(key => {
|
||||
app.registerExtension({
|
||||
name: crystoolsExtensions[key],
|
||||
beforeRegisterNodeDef(nodeType: ComfyNode, nodeData: TLGraphNode, appFromArg: ComfyApp) {
|
||||
if (nodeData.name === key) {
|
||||
displayContext(nodeType, appFromArg, 0);
|
||||
if (nodeData.name in crystoolsExtensionsSerialized) {
|
||||
displayContext(nodeType, appFromArg, 0, true); // serialize_widgets = true
|
||||
} else {
|
||||
displayContext(nodeType, appFromArg, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/* Vertical UI */
|
||||
.comfy-menu {
|
||||
#crystools-monitors-root {
|
||||
/*background-color: red;*/
|
||||
box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
margin: 7px;
|
||||
padding: 5px 0;
|
||||
cursor: crosshair;
|
||||
|
||||
/* MONITORS */
|
||||
|
||||
.crystools-monitor {
|
||||
margin: 1px 10px;
|
||||
height: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.crystools-text {
|
||||
width: 40px;
|
||||
font-size: 10px;
|
||||
text-align: right;
|
||||
margin-right: 3px;
|
||||
|
||||
&:hover {
|
||||
font-weight: 600;
|
||||
color: var(--input-text);
|
||||
}
|
||||
}
|
||||
|
||||
.crystools-label {
|
||||
&:hover {
|
||||
font-weight: 700;
|
||||
color: var(--input-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.crystools-content {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
background-color: var(--content-hover-bg)
|
||||
}
|
||||
|
||||
.crystools-slider {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
box-shadow: inset 2px 2px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.crystools-label {
|
||||
position: relative;
|
||||
color: var(--input-text);
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* PROGRESS BAR */
|
||||
|
||||
#crystools-progressBar-root {
|
||||
cursor: pointer;
|
||||
margin: 0 12px 4px;
|
||||
height: 18px;
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
background-color: var(--comfy-input-bg);
|
||||
|
||||
.crystools-slider {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
transition: width 0.2s;
|
||||
background-color: green;
|
||||
box-shadow: inset 2px 2px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.crystools-label {
|
||||
position: absolute;
|
||||
margin: auto 0;
|
||||
width: 100%;
|
||||
color: var(--input-text);
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.comfyui-queue-button {
|
||||
/*width: unset !important;*/
|
||||
}
|
||||
|
||||
/* Horizontal UI */
|
||||
.comfyui-menu #crystools-monitors-root {
|
||||
/*background-color: red;*/
|
||||
/*display: none;*/
|
||||
flex-direction: row;
|
||||
/*flex-grow: 5;*/
|
||||
justify-content: flex-end;
|
||||
/*flex: auto;*/
|
||||
flex-shrink: 1;
|
||||
/*min-width: max-content;*/
|
||||
/*height: 100%;*/
|
||||
/* max-width: 40vw;*/
|
||||
/* min-width: 10vw;*/
|
||||
/*gap: 5px;*/
|
||||
/*margin: 0 auto;*/
|
||||
/*align-items: center;*/
|
||||
/*align-self: center;*/
|
||||
/*width: min-content;*/
|
||||
/*width: 100%;*/
|
||||
cursor: crosshair;
|
||||
/*margin: 4px 0;*/
|
||||
display: flex; /* by progress bar base */
|
||||
flex-wrap: wrap;
|
||||
/*flex-direction: row;*/
|
||||
/*justify-content: flex-end;*/
|
||||
/*align-items: center;*/
|
||||
gap: 5px;
|
||||
align-self: center;
|
||||
|
||||
.crystools-monitor {
|
||||
background-color: var(--comfy-input-bg);
|
||||
position: relative;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
/*width: 60px;*/
|
||||
/*height: 100%;*/
|
||||
|
||||
.crystools-text {
|
||||
font-size: 10px;
|
||||
text-align: right;
|
||||
margin-left: 3px;
|
||||
position: absolute;
|
||||
font-weight: 100;
|
||||
bottom: 2px;
|
||||
z-index: 10;
|
||||
|
||||
/*&:hover {*/
|
||||
/* font-weight: 600;*/
|
||||
/* color: var(--input-text);*/
|
||||
/*}*/
|
||||
}
|
||||
|
||||
/*.crystools-label {*/
|
||||
/* &:hover {*/
|
||||
/* font-weight: 800;*/
|
||||
/* color: var(--input-text);*/
|
||||
/* }*/
|
||||
/*}*/
|
||||
}
|
||||
|
||||
.crystools-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.crystools-slider {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
box-shadow: inset 2px 2px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.crystools-label {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
color: var(--input-text);
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
right: 2px;
|
||||
top: 2px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
+461
-402
@@ -1,6 +1,9 @@
|
||||
import { app } from '/scripts/app.js';
|
||||
import { api } from '/scripts/api.js';
|
||||
import { app, api, ComfyButtonGroup } from './comfy/index.js';
|
||||
import { commonPrefix } from './common.js';
|
||||
import { MonitorUI } from './monitorUI.js';
|
||||
import { Colors } from './styles.js';
|
||||
import { convertNumberToPascalCase } from './utils.js';
|
||||
import { ComfyKeyMenuDisplayOption, MenuDisplayOptions } from './progressBarUIBase.js';
|
||||
class CrystoolsMonitor {
|
||||
constructor() {
|
||||
Object.defineProperty(this, "idExtensionName", {
|
||||
@@ -15,41 +18,59 @@ class CrystoolsMonitor {
|
||||
writable: true,
|
||||
value: commonPrefix
|
||||
});
|
||||
Object.defineProperty(this, "htmlIdCrystoolsRoot", {
|
||||
Object.defineProperty(this, "menuDisplayOption", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 'crystools-root'
|
||||
value: MenuDisplayOptions.Disabled
|
||||
});
|
||||
Object.defineProperty(this, "htmlIdCrystoolsMonitorContainer", {
|
||||
Object.defineProperty(this, "crystoolsButtonGroup", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 'crystools-monitor-container'
|
||||
value: null
|
||||
});
|
||||
Object.defineProperty(this, "idInputRate", {
|
||||
Object.defineProperty(this, "settingsRate", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 'Crystools.inputRate'
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "defaultRate", {
|
||||
Object.defineProperty(this, "settingsMonitorHeight", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: .5
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "idWhichHDD", {
|
||||
Object.defineProperty(this, "settingsMonitorWidth", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 'Crystools.whichHDD'
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "defaultWhichHDD", {
|
||||
Object.defineProperty(this, "monitorCPUElement", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: '/'
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "monitorRAMElement", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "monitorHDDElement", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "settingsHDD", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "monitorGPUSettings", {
|
||||
enumerable: true,
|
||||
@@ -69,95 +90,53 @@ class CrystoolsMonitor {
|
||||
writable: true,
|
||||
value: []
|
||||
});
|
||||
Object.defineProperty(this, "monitorCPUElement", {
|
||||
Object.defineProperty(this, "monitorUI", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
id: 'Crystools.switchCPU',
|
||||
name: this.menuPrefix + ' [monitor] CPU Usage',
|
||||
type: 'boolean',
|
||||
label: 'CPU',
|
||||
symbol: '%',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#0AA015',
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(this.monitorCPUElement);
|
||||
await this.updateServer({
|
||||
switchCPU: value,
|
||||
});
|
||||
},
|
||||
}
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "monitorRAMElement", {
|
||||
Object.defineProperty(this, "monitorWidthId", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
id: 'Crystools.switchRAM',
|
||||
name: this.menuPrefix + ' [monitor] RAM Used',
|
||||
type: 'boolean',
|
||||
label: 'RAM',
|
||||
symbol: '%',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#07630D',
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(this.monitorRAMElement);
|
||||
await this.updateServer({
|
||||
switchRAM: value,
|
||||
});
|
||||
},
|
||||
}
|
||||
value: 'Crystools.MonitorWidth'
|
||||
});
|
||||
Object.defineProperty(this, "monitorHDDElement", {
|
||||
Object.defineProperty(this, "monitorWidth", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
id: 'Crystools.switchHDD',
|
||||
name: this.menuPrefix + ' [monitor] HDD Used',
|
||||
type: 'boolean',
|
||||
label: 'HDD',
|
||||
symbol: '%',
|
||||
tooltip: 'See Partition to show (HDD)',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#730F92',
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(this.monitorHDDElement);
|
||||
await this.updateServer({
|
||||
switchHDD: value,
|
||||
});
|
||||
},
|
||||
}
|
||||
value: 60
|
||||
});
|
||||
Object.defineProperty(this, "createSettings", {
|
||||
Object.defineProperty(this, "monitorHeightId", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 'Crystools.MonitorHeight'
|
||||
});
|
||||
Object.defineProperty(this, "monitorHeight", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 30
|
||||
});
|
||||
Object.defineProperty(this, "createSettingsRate", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
app.ui.settings.addSetting(this.monitorCPUElement);
|
||||
app.ui.settings.addSetting(this.monitorRAMElement);
|
||||
app.ui.settings.addSetting(this.monitorHDDElement);
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idInputRate,
|
||||
name: this.menuPrefix + '[monitor] Refresh rate',
|
||||
this.settingsRate = {
|
||||
id: 'Crystools.RefreshRate',
|
||||
name: 'Refresh per second',
|
||||
category: ['Crystools', this.menuPrefix + ' Configuration', 'refresh'],
|
||||
tooltip: 'This is the time (in seconds) between each update of the monitors, 0 means no refresh',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 0,
|
||||
max: 5,
|
||||
step: 0.25,
|
||||
max: 2,
|
||||
step: .25,
|
||||
},
|
||||
defaultValue: this.defaultRate,
|
||||
defaultValue: .5,
|
||||
onChange: async (value) => {
|
||||
let valueNumber;
|
||||
try {
|
||||
@@ -171,143 +150,408 @@ class CrystoolsMonitor {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.updateServer({
|
||||
rate: valueNumber,
|
||||
});
|
||||
await this.updateServer({ rate: valueNumber });
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
const data = {
|
||||
cpu_utilization: 0,
|
||||
device: 'cpu',
|
||||
gpus: [
|
||||
{
|
||||
gpu_utilization: 0,
|
||||
gpu_temperature: 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 (valueNumber === 0) {
|
||||
this.updateAllMonitors({
|
||||
cpu_utilization: 0,
|
||||
device: 'cpu',
|
||||
gpus: [
|
||||
{
|
||||
gpu_utilization: 0,
|
||||
gpu_temperature: 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,
|
||||
});
|
||||
this.monitorUI.updateDisplay(data);
|
||||
}
|
||||
this.updateAllAnimationDuration(valueNumber);
|
||||
this.monitorUI?.updateAllAnimationDuration(valueNumber);
|
||||
},
|
||||
});
|
||||
void this.getHDDsFromServer().then((data) => {
|
||||
const which = app.ui.settings.getSettingValue(this.idWhichHDD, this.defaultWhichHDD);
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idWhichHDD,
|
||||
name: this.menuPrefix + '[monitor] Partition to show',
|
||||
type: 'combo',
|
||||
defaultValue: this.defaultWhichHDD,
|
||||
options: (value) => data.map((m) => ({
|
||||
};
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createSettingsCPU", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.monitorCPUElement = {
|
||||
id: 'Crystools.ShowCpu',
|
||||
name: 'CPU Usage',
|
||||
category: ['Crystools', this.menuPrefix + ' Hardware', 'Cpu'],
|
||||
type: 'boolean',
|
||||
label: 'CPU',
|
||||
symbol: '%',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.CPU,
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(this.monitorCPUElement);
|
||||
await this.updateServer({ switchCPU: value });
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createSettingsRAM", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.monitorRAMElement = {
|
||||
id: 'Crystools.ShowRam',
|
||||
name: 'RAM Used',
|
||||
category: ['Crystools', this.menuPrefix + ' Hardware', 'Ram'],
|
||||
type: 'boolean',
|
||||
label: 'RAM',
|
||||
symbol: '%',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.RAM,
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(this.monitorRAMElement);
|
||||
await this.updateServer({ switchRAM: value });
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createSettingsGPUUsage", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (name, index, moreThanOneGPU) => {
|
||||
if (name === undefined || index === undefined) {
|
||||
console.warn('getGPUsFromServer: name or index undefined', name, index);
|
||||
return;
|
||||
}
|
||||
let label = 'GPU ';
|
||||
label += moreThanOneGPU ? index : '';
|
||||
const monitorGPUNElement = {
|
||||
id: 'Crystools.ShowGpuUsage' + convertNumberToPascalCase(index),
|
||||
name: ' Usage',
|
||||
category: ['Crystools', `${this.menuPrefix} Show GPU [${index}] ${name}`, 'Usage'],
|
||||
type: 'boolean',
|
||||
label,
|
||||
symbol: '%',
|
||||
monitorTitle: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.GPU,
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(monitorGPUNElement);
|
||||
void await this.updateServerGPU(index, { utilization: value });
|
||||
},
|
||||
};
|
||||
this.monitorGPUSettings[index] = monitorGPUNElement;
|
||||
app.ui.settings.addSetting(this.monitorGPUSettings[index]);
|
||||
this.monitorUI.createDOMGPUMonitor(this.monitorGPUSettings[index]);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createSettingsGPUVRAM", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (name, index, moreThanOneGPU) => {
|
||||
if (name === undefined || index === undefined) {
|
||||
console.warn('getGPUsFromServer: name or index undefined', name, index);
|
||||
return;
|
||||
}
|
||||
let label = 'VRAM ';
|
||||
label += moreThanOneGPU ? index : '';
|
||||
const monitorVRAMNElement = {
|
||||
id: 'Crystools.ShowGpuVram' + convertNumberToPascalCase(index),
|
||||
name: 'VRAM',
|
||||
category: ['Crystools', `${this.menuPrefix} Show GPU [${index}] ${name}`, 'VRAM'],
|
||||
type: 'boolean',
|
||||
label: label,
|
||||
symbol: '%',
|
||||
monitorTitle: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.VRAM,
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(monitorVRAMNElement);
|
||||
void await this.updateServerGPU(index, { vram: value });
|
||||
},
|
||||
};
|
||||
this.monitorVRAMSettings[index] = monitorVRAMNElement;
|
||||
app.ui.settings.addSetting(this.monitorVRAMSettings[index]);
|
||||
this.monitorUI.createDOMGPUMonitor(this.monitorVRAMSettings[index]);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createSettingsGPUTemp", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (name, index, moreThanOneGPU) => {
|
||||
if (name === undefined || index === undefined) {
|
||||
console.warn('getGPUsFromServer: name or index undefined', name, index);
|
||||
return;
|
||||
}
|
||||
let label = 'Temp ';
|
||||
label += moreThanOneGPU ? index : '';
|
||||
const monitorTemperatureNElement = {
|
||||
id: 'Crystools.ShowGpuTemperature' + convertNumberToPascalCase(index),
|
||||
name: 'Temperature',
|
||||
category: ['Crystools', `${this.menuPrefix} Show GPU [${index}] ${name}`, 'Temperature'],
|
||||
type: 'boolean',
|
||||
label: label,
|
||||
symbol: '°',
|
||||
monitorTitle: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.TEMP_START,
|
||||
cssColorFinal: Colors.TEMP_END,
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(monitorTemperatureNElement);
|
||||
void await this.updateServerGPU(index, { temperature: value });
|
||||
},
|
||||
};
|
||||
this.monitorTemperatureSettings[index] = monitorTemperatureNElement;
|
||||
app.ui.settings.addSetting(this.monitorTemperatureSettings[index]);
|
||||
this.monitorUI.createDOMGPUMonitor(this.monitorTemperatureSettings[index]);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createSettingsHDD", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.monitorHDDElement = {
|
||||
id: 'Crystools.ShowHdd',
|
||||
name: 'Show HDD Used',
|
||||
category: ['Crystools', this.menuPrefix + ' Show Hard Disk', 'Show'],
|
||||
type: 'boolean',
|
||||
label: 'HDD',
|
||||
symbol: '%',
|
||||
defaultValue: false,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.DISK,
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(this.monitorHDDElement);
|
||||
await this.updateServer({ switchHDD: value });
|
||||
},
|
||||
};
|
||||
this.settingsHDD = {
|
||||
id: 'Crystools.WhichHdd',
|
||||
name: 'Partition to show',
|
||||
category: ['Crystools', this.menuPrefix + ' Show Hard Disk', 'Which'],
|
||||
type: 'combo',
|
||||
defaultValue: '/',
|
||||
data: [],
|
||||
options: (value) => {
|
||||
const which = app.ui.settings.getSettingValue(this.settingsHDD.id, this.settingsHDD.defaultValue);
|
||||
return this.settingsHDD.data.map((m) => ({
|
||||
value: m,
|
||||
text: m,
|
||||
selected: !value ? m === which : m === value,
|
||||
})),
|
||||
onChange: async (value) => {
|
||||
await this.updateServer({
|
||||
whichHDD: value,
|
||||
});
|
||||
},
|
||||
});
|
||||
}));
|
||||
},
|
||||
onChange: async (value) => {
|
||||
await this.updateServer({ whichHDD: value });
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createSettingsMonitorWidth", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.settingsMonitorWidth = {
|
||||
id: this.monitorWidthId,
|
||||
name: 'Pixel Width',
|
||||
category: ['Crystools', this.menuPrefix + ' Configuration', 'width'],
|
||||
tooltip: 'The width of the monitor in pixels on the UI (only on floating UI)',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 60,
|
||||
max: 100,
|
||||
step: 1,
|
||||
},
|
||||
defaultValue: this.monitorWidth,
|
||||
onChange: (value) => {
|
||||
let valueNumber;
|
||||
try {
|
||||
valueNumber = parseInt(value);
|
||||
if (isNaN(valueNumber)) {
|
||||
throw new Error('invalid value');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
const h = app.ui.settings.getSettingValue(this.monitorHeightId, this.monitorHeight);
|
||||
this.monitorUI?.updateMonitorSize(valueNumber, h);
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createSettingsMonitorHeight", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.settingsMonitorHeight = {
|
||||
id: this.monitorHeightId,
|
||||
name: 'Pixel Height',
|
||||
category: ['Crystools', this.menuPrefix + ' Configuration', 'height'],
|
||||
tooltip: 'The height of the monitor in pixels on the UI (only on floating UI)',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 16,
|
||||
max: 50,
|
||||
step: 1,
|
||||
},
|
||||
defaultValue: this.monitorHeight,
|
||||
onChange: async (value) => {
|
||||
let valueNumber;
|
||||
try {
|
||||
valueNumber = parseInt(value);
|
||||
if (isNaN(valueNumber)) {
|
||||
throw new Error('invalid value');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
const w = await app.ui.settings.getSettingValue(this.monitorWidthId, this.monitorWidth);
|
||||
this.monitorUI?.updateMonitorSize(w, valueNumber);
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createSettings", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
app.ui.settings.addSetting(this.settingsRate);
|
||||
app.ui.settings.addSetting(this.settingsMonitorHeight);
|
||||
app.ui.settings.addSetting(this.settingsMonitorWidth);
|
||||
app.ui.settings.addSetting(this.monitorRAMElement);
|
||||
app.ui.settings.addSetting(this.monitorCPUElement);
|
||||
void this.getHDDsFromServer().then((data) => {
|
||||
this.settingsHDD.data = data;
|
||||
app.ui.settings.addSetting(this.settingsHDD);
|
||||
});
|
||||
app.ui.settings.addSetting(this.monitorHDDElement);
|
||||
void this.getGPUsFromServer().then((gpus) => {
|
||||
let moreThanOneGPU = false;
|
||||
if (gpus.length > 1) {
|
||||
moreThanOneGPU = true;
|
||||
}
|
||||
gpus?.forEach(({ name, index }) => {
|
||||
if (name === undefined || index === undefined) {
|
||||
console.warn('getGPUsFromServer: name or index undefined', name, index);
|
||||
return;
|
||||
}
|
||||
let label = 'GPU';
|
||||
let labelVRAM = 'VRAM';
|
||||
let labelTemperature = 'Temp';
|
||||
if (moreThanOneGPU) {
|
||||
label = 'GPU ' + index;
|
||||
labelVRAM = 'VRAM' + index;
|
||||
labelTemperature = 'Temp ' + index;
|
||||
}
|
||||
const monitorGPUNElement = {
|
||||
id: 'Crystools.switchGPU' + index,
|
||||
name: this.menuPrefix + `[menu] Display GPU\r\n[${index}] ${name}`,
|
||||
type: 'boolean',
|
||||
label,
|
||||
symbol: '%',
|
||||
title: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#0C86F4',
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(monitorGPUNElement);
|
||||
void await this.updateServerGPU(index, {
|
||||
utilization: value
|
||||
});
|
||||
},
|
||||
};
|
||||
const monitorVRAMNElement = {
|
||||
id: 'Crystools.switchVRAM' + index,
|
||||
name: this.menuPrefix + `[menu] Display GPU VRAM\r\n[${index}] ${name}`,
|
||||
type: 'boolean',
|
||||
label: labelVRAM,
|
||||
symbol: '%',
|
||||
title: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#176EC7',
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(monitorVRAMNElement);
|
||||
void await this.updateServerGPU(index, {
|
||||
vram: value
|
||||
});
|
||||
},
|
||||
};
|
||||
const monitorTemperatureNElement = {
|
||||
id: 'Crystools.switchTemperature' + index,
|
||||
name: this.menuPrefix + `[menu] Display GPU Temperature\r\n[${index}] ${name}`,
|
||||
type: 'boolean',
|
||||
label: labelTemperature,
|
||||
symbol: '°',
|
||||
title: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#00ff00',
|
||||
cssColorFinal: '#ff0000',
|
||||
onChange: async (value) => {
|
||||
this.updateWidget(monitorTemperatureNElement);
|
||||
void await this.updateServerGPU(index, {
|
||||
temperature: value
|
||||
});
|
||||
},
|
||||
};
|
||||
this.monitorGPUSettings[index] = monitorGPUNElement;
|
||||
this.monitorVRAMSettings[index] = monitorVRAMNElement;
|
||||
this.monitorTemperatureSettings[index] = monitorTemperatureNElement;
|
||||
app.ui.settings.addSetting(this.monitorGPUSettings[index]);
|
||||
app.ui.settings.addSetting(this.monitorVRAMSettings[index]);
|
||||
app.ui.settings.addSetting(this.monitorTemperatureSettings[index]);
|
||||
this.createSettingsGPUTemp(name, index, moreThanOneGPU);
|
||||
this.createSettingsGPUVRAM(name, index, moreThanOneGPU);
|
||||
this.createSettingsGPUUsage(name, index, moreThanOneGPU);
|
||||
});
|
||||
this.finishedLoad();
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "finishedLoad", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.monitorUI.orderMonitors();
|
||||
this.updateAllWidget();
|
||||
this.moveMonitor(this.menuDisplayOption);
|
||||
const w = app.ui.settings.getSettingValue(this.monitorWidthId, this.monitorWidth);
|
||||
const h = app.ui.settings.getSettingValue(this.monitorHeightId, this.monitorHeight);
|
||||
this.monitorUI.updateMonitorSize(w, h);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateDisplay", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (value) => {
|
||||
if (value !== this.menuDisplayOption) {
|
||||
this.menuDisplayOption = value;
|
||||
this.moveMonitor(this.menuDisplayOption);
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "moveMonitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (position) => {
|
||||
let parentElement;
|
||||
switch (position) {
|
||||
case MenuDisplayOptions.Disabled:
|
||||
parentElement = document.getElementById('queue-button');
|
||||
if (parentElement && this.monitorUI.rootElement) {
|
||||
parentElement.insertAdjacentElement('afterend', this.crystoolsButtonGroup.element);
|
||||
}
|
||||
else {
|
||||
console.error('Crystools: parentElement to move monitors not found!', parentElement);
|
||||
}
|
||||
break;
|
||||
case MenuDisplayOptions.Top:
|
||||
case MenuDisplayOptions.Bottom:
|
||||
app.menu?.settingsGroup.element.before(this.crystoolsButtonGroup.element);
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateAllWidget", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.updateWidget(this.monitorCPUElement);
|
||||
this.updateWidget(this.monitorRAMElement);
|
||||
this.updateWidget(this.monitorHDDElement);
|
||||
this.monitorGPUSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateWidget", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings) => {
|
||||
if (this.monitorUI) {
|
||||
const value = app.ui.settings.getSettingValue(monitorSettings.id, monitorSettings.defaultValue);
|
||||
this.monitorUI.showMonitor(monitorSettings, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateServer", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
@@ -371,183 +615,31 @@ class CrystoolsMonitor {
|
||||
throw new Error(resp.statusText);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateAllWidget", {
|
||||
Object.defineProperty(this, "setup", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.updateWidget(this.monitorCPUElement);
|
||||
this.updateWidget(this.monitorRAMElement);
|
||||
this.updateWidget(this.monitorHDDElement);
|
||||
this.monitorGPUSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateWidget", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings) => {
|
||||
const value = app.ui.settings.getSettingValue(monitorSettings.id, monitorSettings.defaultValue);
|
||||
if (monitorSettings.htmlMonitorRef) {
|
||||
monitorSettings.htmlMonitorRef.style.display = value ? 'flex' : 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateAllMonitors", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (data) => {
|
||||
this.updateMonitor(this.monitorCPUElement, data.cpu_utilization);
|
||||
this.updateMonitor(this.monitorRAMElement, data.ram_used_percent);
|
||||
this.updateMonitor(this.monitorHDDElement, data.hdd_used_percent);
|
||||
if (data.gpus === undefined || data.gpus.length === 0) {
|
||||
console.warn('UpdateAllMonitors: no GPU data');
|
||||
if (this.monitorUI) {
|
||||
return;
|
||||
}
|
||||
this.monitorGPUSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
return;
|
||||
}
|
||||
this.updateMonitor(monitorSettings, gpu.gpu_utilization);
|
||||
}
|
||||
else {
|
||||
}
|
||||
this.createSettingsRate();
|
||||
this.createSettingsMonitorHeight();
|
||||
this.createSettingsMonitorWidth();
|
||||
this.createSettingsCPU();
|
||||
this.createSettingsRAM();
|
||||
this.createSettingsHDD();
|
||||
this.createSettings();
|
||||
const currentRate = parseFloat(app.ui.settings.getSettingValue(this.settingsRate.id, this.settingsRate.defaultValue));
|
||||
this.menuDisplayOption = app.ui.settings.getSettingValue(ComfyKeyMenuDisplayOption, MenuDisplayOptions.Disabled);
|
||||
app.ui.settings.addEventListener(`${ComfyKeyMenuDisplayOption}.change`, (e) => {
|
||||
this.updateDisplay(e.detail.value);
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
return;
|
||||
}
|
||||
this.updateMonitor(monitorSettings, gpu.vram_used_percent);
|
||||
}
|
||||
else {
|
||||
}
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
return;
|
||||
}
|
||||
this.updateMonitor(monitorSettings, gpu.gpu_temperature);
|
||||
if (monitorSettings.cssColorFinal && monitorSettings.htmlMonitorSliderRef) {
|
||||
monitorSettings.htmlMonitorSliderRef.style.backgroundColor =
|
||||
`color-mix(in srgb, ${monitorSettings.cssColorFinal} ${gpu.gpu_temperature}%, ${monitorSettings.cssColor})`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateMonitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings, percent) => {
|
||||
if (!(monitorSettings.htmlMonitorSliderRef && monitorSettings.htmlMonitorLabelRef)) {
|
||||
return;
|
||||
}
|
||||
monitorSettings.htmlMonitorLabelRef.innerHTML = `${Math.floor(percent)}${monitorSettings.symbol}`;
|
||||
monitorSettings.htmlMonitorSliderRef.style.width = `${Math.floor(percent)}%`;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateAllAnimationDuration", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (value) => {
|
||||
this.updatedAnimationDuration(this.monitorCPUElement, value);
|
||||
this.updatedAnimationDuration(this.monitorRAMElement, value);
|
||||
this.updatedAnimationDuration(this.monitorHDDElement, value);
|
||||
this.monitorGPUSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updatedAnimationDuration", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings, value) => {
|
||||
const slider = monitorSettings.htmlMonitorSliderRef;
|
||||
if (!slider) {
|
||||
return;
|
||||
}
|
||||
slider.style.transition = `width ${value.toFixed(1)}s`;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createMonitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings) => {
|
||||
if (!monitorSettings) {
|
||||
return document.createElement('div');
|
||||
}
|
||||
const htmlMain = document.createElement('div');
|
||||
htmlMain.setAttribute('id', monitorSettings.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';
|
||||
monitorSettings.htmlMonitorRef = htmlMain;
|
||||
if (monitorSettings.title) {
|
||||
htmlMain.title = monitorSettings.title;
|
||||
}
|
||||
const htmlMonitorText = document.createElement('div');
|
||||
htmlMonitorText.style.width = '35px';
|
||||
htmlMonitorText.style.fontSize = '10px';
|
||||
htmlMonitorText.innerHTML = monitorSettings.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';
|
||||
if (monitorSettings.cssColorFinal) {
|
||||
htmlMonitorSlider.style.backgroundColor =
|
||||
`color-mix(in srgb, ${monitorSettings.cssColorFinal} 0%, ${monitorSettings.cssColor})`;
|
||||
}
|
||||
else {
|
||||
htmlMonitorSlider.style.backgroundColor = monitorSettings.cssColor;
|
||||
}
|
||||
monitorSettings.htmlMonitorSliderRef = 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%';
|
||||
monitorSettings.htmlMonitorLabelRef = htmlMonitorLabel;
|
||||
htmlMonitorContent.append(htmlMonitorLabel);
|
||||
return monitorSettings.htmlMonitorRef;
|
||||
this.crystoolsButtonGroup = new ComfyButtonGroup();
|
||||
app.menu?.settingsGroup.element.before(this.crystoolsButtonGroup.element);
|
||||
this.monitorUI = new MonitorUI(this.crystoolsButtonGroup.element, this.monitorCPUElement, this.monitorRAMElement, this.monitorHDDElement, this.monitorGPUSettings, this.monitorVRAMSettings, this.monitorTemperatureSettings, currentRate);
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
this.registerListeners();
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "registerListeners", {
|
||||
@@ -559,47 +651,14 @@ class CrystoolsMonitor {
|
||||
if (event?.detail === undefined) {
|
||||
return;
|
||||
}
|
||||
this.updateAllMonitors(event.detail);
|
||||
this.monitorUI.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.createMonitor(this.monitorCPUElement));
|
||||
htmlContainer.append(this.createMonitor(this.monitorRAMElement));
|
||||
this.monitorGPUSettings.forEach((_monitorSettings, index) => {
|
||||
this.monitorGPUSettings[index] && htmlContainer.append(this.createMonitor(this.monitorGPUSettings[index]));
|
||||
this.monitorVRAMSettings[index] && htmlContainer.append(this.createMonitor(this.monitorVRAMSettings[index]));
|
||||
this.monitorTemperatureSettings[index] &&
|
||||
htmlContainer.append(this.createMonitor(this.monitorTemperatureSettings[index]));
|
||||
});
|
||||
htmlContainer.append(this.createMonitor(this.monitorHDDElement));
|
||||
const currentRate = parseFloat(app.ui.settings.getSettingValue(this.idInputRate, this.defaultRate));
|
||||
this.updateAllAnimationDuration(currentRate);
|
||||
this.updateAllWidget();
|
||||
this.registerListeners();
|
||||
}
|
||||
}
|
||||
const crystoolsMonitor = new CrystoolsMonitor();
|
||||
app.registerExtension({
|
||||
name: crystoolsMonitor.idExtensionName,
|
||||
setup: crystoolsMonitor.setup.bind(crystoolsMonitor),
|
||||
setup: crystoolsMonitor.setup,
|
||||
});
|
||||
|
||||
+431
-438
@@ -1,103 +1,50 @@
|
||||
import { app } from '/scripts/app.js';
|
||||
import { api } from '/scripts/api.js';
|
||||
import { app, api, ComfyButtonGroup } from './comfy/index.js';
|
||||
import { commonPrefix } from './common.js';
|
||||
import { MonitorUI } from './monitorUI.js';
|
||||
import { Colors } from './styles.js';
|
||||
import { convertNumberToPascalCase } from './utils.js';
|
||||
import { ComfyKeyMenuDisplayOption, MenuDisplayOptions } from './progressBarUIBase.js';
|
||||
|
||||
class CrystoolsMonitor {
|
||||
idExtensionName = 'Crystools.monitor';
|
||||
menuPrefix = commonPrefix;
|
||||
htmlIdCrystoolsRoot = 'crystools-root';
|
||||
htmlIdCrystoolsMonitorContainer = 'crystools-monitor-container';
|
||||
readonly idExtensionName = 'Crystools.monitor';
|
||||
private readonly menuPrefix = commonPrefix;
|
||||
private menuDisplayOption: MenuDisplayOptions = MenuDisplayOptions.Disabled;
|
||||
private crystoolsButtonGroup: ComfyButtonGroup = null;
|
||||
|
||||
idInputRate = 'Crystools.inputRate';
|
||||
defaultRate = .5;
|
||||
idWhichHDD = 'Crystools.whichHDD';
|
||||
defaultWhichHDD = '/';
|
||||
monitorGPUSettings: TMonitorSettings[] = [];
|
||||
monitorVRAMSettings: TMonitorSettings[] = [];
|
||||
monitorTemperatureSettings: TMonitorSettings[] = [];
|
||||
private settingsRate: TMonitorSettings;
|
||||
private settingsMonitorHeight: TMonitorSettings;
|
||||
private settingsMonitorWidth: TMonitorSettings;
|
||||
private monitorCPUElement: TMonitorSettings;
|
||||
private monitorRAMElement: TMonitorSettings;
|
||||
private monitorHDDElement: TMonitorSettings;
|
||||
private settingsHDD: TMonitorSettings;
|
||||
private monitorGPUSettings: TMonitorSettings[] = [];
|
||||
private monitorVRAMSettings: TMonitorSettings[] = [];
|
||||
private monitorTemperatureSettings: TMonitorSettings[] = [];
|
||||
|
||||
// CPU Variables
|
||||
monitorCPUElement: TMonitorSettings = {
|
||||
id: 'Crystools.switchCPU',
|
||||
name: this.menuPrefix + ' [monitor] CPU Usage',
|
||||
type: 'boolean',
|
||||
label: 'CPU',
|
||||
symbol: '%',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#0AA015',
|
||||
onChange: async(value: boolean) => {
|
||||
this.updateWidget(this.monitorCPUElement);
|
||||
await this.updateServer({
|
||||
switchCPU: value,
|
||||
});
|
||||
},
|
||||
};
|
||||
private monitorUI: MonitorUI;
|
||||
|
||||
// RAM Variables
|
||||
monitorRAMElement: TMonitorSettings = {
|
||||
id: 'Crystools.switchRAM',
|
||||
name: this.menuPrefix + ' [monitor] RAM Used',
|
||||
type: 'boolean',
|
||||
label: 'RAM',
|
||||
symbol: '%',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#07630D',
|
||||
onChange: async(value: boolean) => {
|
||||
this.updateWidget(this.monitorRAMElement);
|
||||
await this.updateServer({
|
||||
switchRAM: value,
|
||||
});
|
||||
},
|
||||
};
|
||||
private readonly monitorWidthId = 'Crystools.MonitorWidth';
|
||||
private readonly monitorWidth = 60;
|
||||
private readonly monitorHeightId = 'Crystools.MonitorHeight';
|
||||
private readonly monitorHeight = 30;
|
||||
|
||||
// HDD Variables
|
||||
monitorHDDElement: TMonitorSettings = {
|
||||
id: 'Crystools.switchHDD',
|
||||
name: this.menuPrefix + ' [monitor] HDD Used',
|
||||
type: 'boolean',
|
||||
label: 'HDD',
|
||||
symbol: '%',
|
||||
tooltip: 'See Partition to show (HDD)',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#730F92',
|
||||
onChange: async(value: boolean) => {
|
||||
this.updateWidget(this.monitorHDDElement);
|
||||
await this.updateServer({
|
||||
switchHDD: value,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.createSettings();
|
||||
}
|
||||
|
||||
createSettings = (): void => {
|
||||
app.ui.settings.addSetting(this.monitorCPUElement);
|
||||
app.ui.settings.addSetting(this.monitorRAMElement);
|
||||
app.ui.settings.addSetting(this.monitorHDDElement);
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idInputRate,
|
||||
name: this.menuPrefix + '[monitor] Refresh rate',
|
||||
createSettingsRate = (): void => {
|
||||
this.settingsRate = {
|
||||
id: 'Crystools.RefreshRate',
|
||||
name: 'Refresh per second',
|
||||
category: ['Crystools', this.menuPrefix + ' Configuration', 'refresh'],
|
||||
tooltip: 'This is the time (in seconds) between each update of the monitors, 0 means no refresh',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 0,
|
||||
max: 5,
|
||||
step: 0.25,
|
||||
max: 2,
|
||||
step: .25,
|
||||
},
|
||||
defaultValue: this.defaultRate,
|
||||
onChange: async(value: string) => {
|
||||
defaultValue: .5,
|
||||
|
||||
// @ts-ignore
|
||||
onChange: async(value: string): Promise<void> => {
|
||||
let valueNumber: number;
|
||||
|
||||
try {
|
||||
@@ -110,62 +57,313 @@ class CrystoolsMonitor {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.updateServer({
|
||||
rate: valueNumber,
|
||||
});
|
||||
await this.updateServer({rate: valueNumber});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (valueNumber === 0) {
|
||||
this.updateAllMonitors({
|
||||
cpu_utilization: 0,
|
||||
device: 'cpu',
|
||||
const data = {
|
||||
cpu_utilization: 0,
|
||||
device: 'cpu',
|
||||
|
||||
gpus: [
|
||||
{
|
||||
gpu_utilization: 0,
|
||||
gpu_temperature: 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,
|
||||
});
|
||||
gpus: [
|
||||
{
|
||||
gpu_utilization: 0,
|
||||
gpu_temperature: 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 (valueNumber === 0) {
|
||||
this.monitorUI.updateDisplay(data);
|
||||
}
|
||||
|
||||
this.updateAllAnimationDuration(valueNumber);
|
||||
this.monitorUI?.updateAllAnimationDuration(valueNumber);
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
createSettingsCPU = (): void => {
|
||||
// CPU Variables
|
||||
this.monitorCPUElement = {
|
||||
id: 'Crystools.ShowCpu',
|
||||
name: 'CPU Usage',
|
||||
category: ['Crystools', this.menuPrefix + ' Hardware', 'Cpu'],
|
||||
type: 'boolean',
|
||||
label: 'CPU',
|
||||
symbol: '%',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.CPU,
|
||||
// @ts-ignore
|
||||
onChange: async(value: boolean): Promise<void> => {
|
||||
this.updateWidget(this.monitorCPUElement);
|
||||
await this.updateServer({switchCPU: value});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
createSettingsRAM = (): void => {
|
||||
// RAM Variables
|
||||
this.monitorRAMElement = {
|
||||
id: 'Crystools.ShowRam',
|
||||
name: 'RAM Used',
|
||||
category: ['Crystools', this.menuPrefix + ' Hardware', 'Ram'],
|
||||
type: 'boolean',
|
||||
label: 'RAM',
|
||||
symbol: '%',
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.RAM,
|
||||
// @ts-ignore
|
||||
onChange: async(value: boolean): Promise<void> => {
|
||||
this.updateWidget(this.monitorRAMElement);
|
||||
await this.updateServer({switchRAM: value});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
createSettingsGPUUsage = (name: string, index: number, moreThanOneGPU: boolean): void => {
|
||||
if (name === undefined || index === undefined) {
|
||||
console.warn('getGPUsFromServer: name or index undefined', name, index);
|
||||
return;
|
||||
}
|
||||
|
||||
let label = 'GPU ';
|
||||
label += moreThanOneGPU ? index : '';
|
||||
|
||||
const monitorGPUNElement: TMonitorSettings = {
|
||||
id: 'Crystools.ShowGpuUsage' + convertNumberToPascalCase(index),
|
||||
name: ' Usage',
|
||||
category: ['Crystools', `${this.menuPrefix} Show GPU [${index}] ${name}`, 'Usage'],
|
||||
type: 'boolean',
|
||||
label,
|
||||
symbol: '%',
|
||||
monitorTitle: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.GPU,
|
||||
// @ts-ignore
|
||||
onChange: async(value: boolean): Promise<void> => {
|
||||
this.updateWidget(monitorGPUNElement);
|
||||
void await this.updateServerGPU(index, {utilization: value});
|
||||
},
|
||||
};
|
||||
|
||||
this.monitorGPUSettings[index] = monitorGPUNElement;
|
||||
app.ui.settings.addSetting(this.monitorGPUSettings[index]);
|
||||
this.monitorUI.createDOMGPUMonitor(this.monitorGPUSettings[index]);
|
||||
};
|
||||
|
||||
createSettingsGPUVRAM = (name: string, index: number, moreThanOneGPU: boolean): void => {
|
||||
if (name === undefined || index === undefined) {
|
||||
console.warn('getGPUsFromServer: name or index undefined', name, index);
|
||||
return;
|
||||
}
|
||||
|
||||
let label = 'VRAM ';
|
||||
label += moreThanOneGPU ? index : '';
|
||||
|
||||
// GPU VRAM Variables
|
||||
const monitorVRAMNElement: TMonitorSettings = {
|
||||
id: 'Crystools.ShowGpuVram' + convertNumberToPascalCase(index),
|
||||
name: 'VRAM',
|
||||
category: ['Crystools', `${this.menuPrefix} Show GPU [${index}] ${name}`, 'VRAM'],
|
||||
type: 'boolean',
|
||||
label: label,
|
||||
symbol: '%',
|
||||
monitorTitle: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.VRAM,
|
||||
// @ts-ignore
|
||||
onChange: async(value: boolean): Promise<void> => {
|
||||
this.updateWidget(monitorVRAMNElement);
|
||||
void await this.updateServerGPU(index, {vram: value});
|
||||
},
|
||||
};
|
||||
|
||||
this.monitorVRAMSettings[index] = monitorVRAMNElement;
|
||||
app.ui.settings.addSetting(this.monitorVRAMSettings[index]);
|
||||
this.monitorUI.createDOMGPUMonitor(this.monitorVRAMSettings[index]);
|
||||
};
|
||||
|
||||
createSettingsGPUTemp = (name: string, index: number, moreThanOneGPU: boolean): void => {
|
||||
if (name === undefined || index === undefined) {
|
||||
console.warn('getGPUsFromServer: name or index undefined', name, index);
|
||||
return;
|
||||
}
|
||||
|
||||
let label = 'Temp ';
|
||||
label += moreThanOneGPU ? index : '';
|
||||
|
||||
// GPU Temperature Variables
|
||||
const monitorTemperatureNElement: TMonitorSettings = {
|
||||
id: 'Crystools.ShowGpuTemperature' + convertNumberToPascalCase(index),
|
||||
name: 'Temperature',
|
||||
category: ['Crystools', `${this.menuPrefix} Show GPU [${index}] ${name}`, 'Temperature'],
|
||||
type: 'boolean',
|
||||
label: label,
|
||||
symbol: '°',
|
||||
monitorTitle: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.TEMP_START,
|
||||
cssColorFinal: Colors.TEMP_END,
|
||||
// @ts-ignore
|
||||
onChange: async(value: boolean): Promise<void> => {
|
||||
this.updateWidget(monitorTemperatureNElement);
|
||||
void await this.updateServerGPU(index, {temperature: value});
|
||||
},
|
||||
};
|
||||
|
||||
this.monitorTemperatureSettings[index] = monitorTemperatureNElement;
|
||||
app.ui.settings.addSetting(this.monitorTemperatureSettings[index]);
|
||||
this.monitorUI.createDOMGPUMonitor(this.monitorTemperatureSettings[index]);
|
||||
};
|
||||
|
||||
createSettingsHDD = (): void => {
|
||||
// HDD Variables
|
||||
this.monitorHDDElement = {
|
||||
id: 'Crystools.ShowHdd',
|
||||
name: 'Show HDD Used',
|
||||
category: ['Crystools', this.menuPrefix + ' Show Hard Disk', 'Show'],
|
||||
type: 'boolean',
|
||||
label: 'HDD',
|
||||
symbol: '%',
|
||||
// tooltip: 'See Partition to show (HDD)',
|
||||
defaultValue: false,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: Colors.DISK,
|
||||
// @ts-ignore
|
||||
onChange: async(value: boolean): Promise<void> => {
|
||||
this.updateWidget(this.monitorHDDElement);
|
||||
await this.updateServer({switchHDD: value});
|
||||
},
|
||||
};
|
||||
|
||||
this.settingsHDD = {
|
||||
id: 'Crystools.WhichHdd',
|
||||
name: 'Partition to show',
|
||||
category: ['Crystools', this.menuPrefix + ' Show Hard Disk', 'Which'],
|
||||
type: 'combo',
|
||||
defaultValue: '/',
|
||||
data: [],
|
||||
// @ts-ignore bad definition from comfyUI: `options?: undefined;`??
|
||||
options: (value: string): any => {
|
||||
const which = app.ui.settings.getSettingValue(this.settingsHDD.id, this.settingsHDD.defaultValue);
|
||||
return this.settingsHDD.data.map((m: any) => ({
|
||||
value: m,
|
||||
text: m,
|
||||
selected: !value ? m === which : m === value,
|
||||
}));
|
||||
},
|
||||
// @ts-ignore
|
||||
onChange: async(value: string): Promise<void> => {
|
||||
await this.updateServer({whichHDD: value});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
createSettingsMonitorWidth = (): void => {
|
||||
this.settingsMonitorWidth = {
|
||||
id: this.monitorWidthId,
|
||||
name: 'Pixel Width',
|
||||
category: ['Crystools', this.menuPrefix + ' Configuration', 'width'],
|
||||
tooltip: 'The width of the monitor in pixels on the UI (only on floating UI)',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 60,
|
||||
max: 100,
|
||||
step: 1,
|
||||
},
|
||||
defaultValue: this.monitorWidth,
|
||||
// @ts-ignore
|
||||
onChange: (value: string): void => {
|
||||
let valueNumber: number;
|
||||
|
||||
try {
|
||||
valueNumber = parseInt(value);
|
||||
if (isNaN(valueNumber)) {
|
||||
throw new Error('invalid value');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const h = app.ui.settings.getSettingValue(this.monitorHeightId, this.monitorHeight);
|
||||
this.monitorUI?.updateMonitorSize(valueNumber, h);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
createSettingsMonitorHeight = (): void => {
|
||||
this.settingsMonitorHeight = {
|
||||
id: this.monitorHeightId,
|
||||
name: 'Pixel Height',
|
||||
category: ['Crystools', this.menuPrefix + ' Configuration', 'height'],
|
||||
tooltip: 'The height of the monitor in pixels on the UI (only on floating UI)',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 16,
|
||||
max: 50,
|
||||
step: 1,
|
||||
},
|
||||
defaultValue: this.monitorHeight,
|
||||
// @ts-ignore
|
||||
onChange: async(value: string): void => {
|
||||
let valueNumber: number;
|
||||
|
||||
try {
|
||||
valueNumber = parseInt(value);
|
||||
if (isNaN(valueNumber)) {
|
||||
throw new Error('invalid value');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const w = await app.ui.settings.getSettingValue(this.monitorWidthId, this.monitorWidth);
|
||||
this.monitorUI?.updateMonitorSize(w, valueNumber);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
createSettings = (): void => {
|
||||
app.ui.settings.addSetting(this.settingsRate);
|
||||
app.ui.settings.addSetting(this.settingsMonitorHeight);
|
||||
app.ui.settings.addSetting(this.settingsMonitorWidth);
|
||||
app.ui.settings.addSetting(this.monitorRAMElement);
|
||||
app.ui.settings.addSetting(this.monitorCPUElement);
|
||||
|
||||
void this.getHDDsFromServer().then((data: string[]): void => {
|
||||
const which = app.ui.settings.getSettingValue(this.idWhichHDD, this.defaultWhichHDD);
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idWhichHDD,
|
||||
name: this.menuPrefix + '[monitor] Partition to show',
|
||||
type: 'combo',
|
||||
defaultValue: this.defaultWhichHDD,
|
||||
// @ts-ignore bad definition from comfyUI: `options?: undefined;`??
|
||||
options: (value: string) =>
|
||||
data.map((m) => ({
|
||||
value: m,
|
||||
text: m,
|
||||
selected: !value ? m === which : m === value,
|
||||
})),
|
||||
onChange: async(value: string) => {
|
||||
await this.updateServer({
|
||||
whichHDD: value,
|
||||
});
|
||||
},
|
||||
});
|
||||
this.settingsHDD.data = data;
|
||||
app.ui.settings.addSetting(this.settingsHDD);
|
||||
});
|
||||
app.ui.settings.addSetting(this.monitorHDDElement);
|
||||
|
||||
void this.getGPUsFromServer().then((gpus: TGpuName[]): void => {
|
||||
let moreThanOneGPU = false;
|
||||
@@ -173,101 +371,79 @@ class CrystoolsMonitor {
|
||||
moreThanOneGPU = true;
|
||||
}
|
||||
|
||||
gpus?.forEach(({
|
||||
name, index
|
||||
}) => {
|
||||
|
||||
if(name === undefined || index === undefined) {
|
||||
console.warn('getGPUsFromServer: name or index undefined', name, index);
|
||||
return;
|
||||
}
|
||||
|
||||
let label = 'GPU';
|
||||
let labelVRAM = 'VRAM';
|
||||
let labelTemperature = 'Temp';
|
||||
if (moreThanOneGPU) {
|
||||
label = 'GPU '+index;
|
||||
labelVRAM = 'VRAM'+index;
|
||||
labelTemperature = 'Temp '+index;
|
||||
}
|
||||
|
||||
// GPU Utilization Variables
|
||||
const monitorGPUNElement: TMonitorSettings = {
|
||||
id: 'Crystools.switchGPU' + index,
|
||||
name: this.menuPrefix + `[menu] Display GPU\r\n[${index}] ${name}`,
|
||||
type: 'boolean',
|
||||
label,
|
||||
symbol: '%',
|
||||
title: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#0C86F4',
|
||||
onChange: async(value: boolean) => {
|
||||
this.updateWidget(monitorGPUNElement);
|
||||
void await this.updateServerGPU(index,{
|
||||
utilization: value
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// GPU VRAM Variables
|
||||
const monitorVRAMNElement: TMonitorSettings = {
|
||||
id: 'Crystools.switchVRAM' + index,
|
||||
name: this.menuPrefix + `[menu] Display GPU VRAM\r\n[${index}] ${name}`,
|
||||
type: 'boolean',
|
||||
label: labelVRAM,
|
||||
symbol: '%',
|
||||
title: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#176EC7',
|
||||
onChange: async(value: boolean) => {
|
||||
this.updateWidget(monitorVRAMNElement);
|
||||
void await this.updateServerGPU(index,{
|
||||
vram: value
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// GPU Temperature Variables
|
||||
const monitorTemperatureNElement: TMonitorSettings = {
|
||||
id: 'Crystools.switchTemperature' + index,
|
||||
name: this.menuPrefix + `[menu] Display GPU Temperature\r\n[${index}] ${name}`,
|
||||
type: 'boolean',
|
||||
label: labelTemperature,
|
||||
symbol: '°',
|
||||
title: `${index}: ${name}`,
|
||||
defaultValue: true,
|
||||
htmlMonitorRef: undefined,
|
||||
htmlMonitorSliderRef: undefined,
|
||||
htmlMonitorLabelRef: undefined,
|
||||
cssColor: '#00ff00',
|
||||
cssColorFinal: '#ff0000',
|
||||
onChange: async(value: boolean) => {
|
||||
this.updateWidget(monitorTemperatureNElement);
|
||||
void await this.updateServerGPU(index,{
|
||||
temperature: value
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
this.monitorGPUSettings[index] = monitorGPUNElement;
|
||||
this.monitorVRAMSettings[index] = monitorVRAMNElement;
|
||||
this.monitorTemperatureSettings[index] = monitorTemperatureNElement;
|
||||
// @ts-ignore
|
||||
app.ui.settings.addSetting(this.monitorGPUSettings[index]);
|
||||
// @ts-ignore
|
||||
app.ui.settings.addSetting(this.monitorVRAMSettings[index]);
|
||||
// @ts-ignore
|
||||
app.ui.settings.addSetting(this.monitorTemperatureSettings[index]);
|
||||
gpus?.forEach(({name, index}) => {
|
||||
this.createSettingsGPUTemp(name, index, moreThanOneGPU);
|
||||
this.createSettingsGPUVRAM(name, index, moreThanOneGPU);
|
||||
this.createSettingsGPUUsage(name, index, moreThanOneGPU);
|
||||
});
|
||||
|
||||
this.finishedLoad();
|
||||
});
|
||||
};
|
||||
|
||||
finishedLoad = (): void => {
|
||||
this.monitorUI.orderMonitors();
|
||||
this.updateAllWidget();
|
||||
this.moveMonitor(this.menuDisplayOption);
|
||||
|
||||
const w = app.ui.settings.getSettingValue(this.monitorWidthId, this.monitorWidth);
|
||||
const h = app.ui.settings.getSettingValue(this.monitorHeightId, this.monitorHeight);
|
||||
this.monitorUI.updateMonitorSize(w, h);
|
||||
};
|
||||
|
||||
updateDisplay = (value: MenuDisplayOptions): void => {
|
||||
if (value !== this.menuDisplayOption) {
|
||||
this.menuDisplayOption = value;
|
||||
this.moveMonitor(this.menuDisplayOption);
|
||||
}
|
||||
};
|
||||
|
||||
moveMonitor = (position: MenuDisplayOptions): void => {
|
||||
let parentElement: Element | null | undefined;
|
||||
|
||||
switch (position) {
|
||||
case MenuDisplayOptions.Disabled:
|
||||
parentElement = document.getElementById('queue-button');
|
||||
if (parentElement && this.monitorUI.rootElement) {
|
||||
parentElement.insertAdjacentElement('afterend', this.crystoolsButtonGroup.element);
|
||||
} else {
|
||||
console.error('Crystools: parentElement to move monitors not found!', parentElement);
|
||||
}
|
||||
break;
|
||||
|
||||
case MenuDisplayOptions.Top:
|
||||
case MenuDisplayOptions.Bottom:
|
||||
app.menu?.settingsGroup.element.before(this.crystoolsButtonGroup.element);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
updateAllWidget = (): void => {
|
||||
this.updateWidget(this.monitorCPUElement);
|
||||
this.updateWidget(this.monitorRAMElement);
|
||||
this.updateWidget(this.monitorHDDElement);
|
||||
|
||||
this.monitorGPUSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* for the settings menu
|
||||
* @param monitorSettings
|
||||
*/
|
||||
updateWidget = (monitorSettings: TMonitorSettings): void => {
|
||||
if (this.monitorUI) {
|
||||
const value = app.ui.settings.getSettingValue(monitorSettings.id, monitorSettings.defaultValue);
|
||||
this.monitorUI.showMonitor(monitorSettings, value);
|
||||
}
|
||||
};
|
||||
|
||||
updateServer = async(data: TStatsSettings): Promise<string> => {
|
||||
const resp = await api.fetchApi('/crystools/monitor', {
|
||||
@@ -301,7 +477,7 @@ class CrystoolsMonitor {
|
||||
return this.getDataFromServer<TGpuName>('GPU');
|
||||
};
|
||||
|
||||
getDataFromServer = async<T>(what: string): Promise<T[]> => {
|
||||
getDataFromServer = async <T>(what: string): Promise<T[]> => {
|
||||
const resp = await api.fetchApi(`/crystools/monitor/${what}`, {
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
@@ -312,227 +488,44 @@ class CrystoolsMonitor {
|
||||
throw new Error(resp.statusText);
|
||||
};
|
||||
|
||||
updateAllWidget = (): void => {
|
||||
this.updateWidget(this.monitorCPUElement);
|
||||
this.updateWidget(this.monitorRAMElement);
|
||||
this.updateWidget(this.monitorHDDElement);
|
||||
|
||||
this.monitorGPUSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updateWidget(monitorSettings);
|
||||
});
|
||||
};
|
||||
|
||||
updateWidget = (monitorSettings: TMonitorSettings): void => {
|
||||
const value = app.ui.settings.getSettingValue(monitorSettings.id, monitorSettings.defaultValue);
|
||||
if (monitorSettings.htmlMonitorRef) {
|
||||
monitorSettings.htmlMonitorRef.style.display = value ? 'flex' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
updateAllMonitors = (data: TStatsData): void => {
|
||||
// console.log('updateAllMonitors', data);
|
||||
this.updateMonitor(this.monitorCPUElement, data.cpu_utilization);
|
||||
this.updateMonitor(this.monitorRAMElement, data.ram_used_percent);
|
||||
this.updateMonitor(this.monitorHDDElement, data.hdd_used_percent);
|
||||
|
||||
if (data.gpus === undefined || data.gpus.length === 0) {
|
||||
console.warn('UpdateAllMonitors: no GPU data');
|
||||
setup = (): void => {
|
||||
if (this.monitorUI) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.monitorGPUSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
// console.error('UpdateAllMonitors: no GPU data for index', index);
|
||||
return;
|
||||
}
|
||||
this.createSettingsRate();
|
||||
this.createSettingsMonitorHeight();
|
||||
this.createSettingsMonitorWidth();
|
||||
this.createSettingsCPU();
|
||||
this.createSettingsRAM();
|
||||
this.createSettingsHDD();
|
||||
this.createSettings();
|
||||
|
||||
this.updateMonitor(monitorSettings, gpu.gpu_utilization);
|
||||
} else {
|
||||
// console.error('UpdateAllMonitors: no GPU data for index', index);
|
||||
}
|
||||
});
|
||||
const currentRate =
|
||||
parseFloat(app.ui.settings.getSettingValue(this.settingsRate.id, this.settingsRate.defaultValue));
|
||||
|
||||
this.monitorVRAMSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
// console.error('UpdateAllMonitors: no GPU VRAM data for index', index);
|
||||
return;
|
||||
}
|
||||
this.menuDisplayOption = app.ui.settings.getSettingValue(ComfyKeyMenuDisplayOption, MenuDisplayOptions.Disabled);
|
||||
app.ui.settings.addEventListener(`${ComfyKeyMenuDisplayOption}.change`, (e: any) => {
|
||||
this.updateDisplay(e.detail.value);
|
||||
},
|
||||
);
|
||||
|
||||
this.updateMonitor(monitorSettings, gpu.vram_used_percent);
|
||||
} else {
|
||||
// console.error('UpdateAllMonitors: no GPU VRAM data for index', index);
|
||||
}
|
||||
});
|
||||
this.crystoolsButtonGroup = new ComfyButtonGroup();
|
||||
app.menu?.settingsGroup.element.before(this.crystoolsButtonGroup.element);
|
||||
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
// console.error('UpdateAllMonitors: no GPU VRAM data for index', index);
|
||||
return;
|
||||
}
|
||||
this.monitorUI = new MonitorUI(
|
||||
this.crystoolsButtonGroup.element,
|
||||
this.monitorCPUElement,
|
||||
this.monitorRAMElement,
|
||||
this.monitorHDDElement,
|
||||
this.monitorGPUSettings,
|
||||
this.monitorVRAMSettings,
|
||||
this.monitorTemperatureSettings,
|
||||
currentRate,
|
||||
);
|
||||
|
||||
this.updateMonitor(monitorSettings, gpu.gpu_temperature);
|
||||
if(monitorSettings.cssColorFinal && monitorSettings.htmlMonitorSliderRef){
|
||||
monitorSettings.htmlMonitorSliderRef.style.backgroundColor =
|
||||
`color-mix(in srgb, ${monitorSettings.cssColorFinal} ${gpu.gpu_temperature}%, ${monitorSettings.cssColor})`;
|
||||
}
|
||||
} else {
|
||||
// console.error('UpdateAllMonitors: no GPU VRAM data for index', index);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
updateMonitor = (monitorSettings: TMonitorSettings, percent: number): void => {
|
||||
if (!(monitorSettings.htmlMonitorSliderRef && monitorSettings.htmlMonitorLabelRef)) {
|
||||
return;
|
||||
}
|
||||
|
||||
monitorSettings.htmlMonitorLabelRef.innerHTML = `${Math.floor(percent)}${monitorSettings.symbol}`;
|
||||
monitorSettings.htmlMonitorSliderRef.style.width = `${Math.floor(percent)}%`;
|
||||
};
|
||||
|
||||
updateAllAnimationDuration = (value: number): void => {
|
||||
this.updatedAnimationDuration(this.monitorCPUElement, value);
|
||||
this.updatedAnimationDuration(this.monitorRAMElement, value);
|
||||
this.updatedAnimationDuration(this.monitorHDDElement, value);
|
||||
this.monitorGPUSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
};
|
||||
|
||||
updatedAnimationDuration = (monitorSettings: TMonitorSettings, value: number): void => {
|
||||
const slider = monitorSettings.htmlMonitorSliderRef;
|
||||
if (!slider) {
|
||||
return;
|
||||
}
|
||||
|
||||
slider.style.transition = `width ${value.toFixed(1)}s`;
|
||||
};
|
||||
|
||||
setup(): void {
|
||||
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.createMonitor(this.monitorCPUElement));
|
||||
htmlContainer.append(this.createMonitor(this.monitorRAMElement));
|
||||
|
||||
// gpu0 > gpu1 > vram0 > vram1
|
||||
// this.monitorGPUSettings.forEach((monitorSettings) => {
|
||||
// monitorSettings && htmlContainer.append(this.createMonitor(monitorSettings));
|
||||
// });
|
||||
// this.monitorVRAMSettings.forEach((monitorSettings) => {
|
||||
// monitorSettings && htmlContainer.append(this.createMonitor(monitorSettings));
|
||||
// });
|
||||
// this.monitorTemperatureSettings.forEach((monitorSettings) => {
|
||||
// monitorSettings && htmlContainer.append(this.createMonitor(monitorSettings));
|
||||
// });
|
||||
|
||||
// gpu0 > vram0 > gpu1 > vram1
|
||||
this.monitorGPUSettings.forEach((_monitorSettings, index) => {
|
||||
this.monitorGPUSettings[index] && htmlContainer.append(this.createMonitor(this.monitorGPUSettings[index]));
|
||||
this.monitorVRAMSettings[index] && htmlContainer.append(this.createMonitor(this.monitorVRAMSettings[index]));
|
||||
this.monitorTemperatureSettings[index] &&
|
||||
htmlContainer.append(this.createMonitor(this.monitorTemperatureSettings[index]));
|
||||
});
|
||||
|
||||
htmlContainer.append(this.createMonitor(this.monitorHDDElement));
|
||||
|
||||
const currentRate = parseFloat(app.ui.settings.getSettingValue(this.idInputRate, this.defaultRate));
|
||||
this.updateAllAnimationDuration(currentRate);
|
||||
this.updateAllWidget();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
createMonitor = (monitorSettings?: TMonitorSettings): HTMLDivElement => {
|
||||
if (!monitorSettings) {
|
||||
// just for typescript
|
||||
return document.createElement('div');
|
||||
}
|
||||
const htmlMain = document.createElement('div');
|
||||
htmlMain.setAttribute('id', monitorSettings.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';
|
||||
monitorSettings.htmlMonitorRef = htmlMain;
|
||||
|
||||
if (monitorSettings.title) {
|
||||
htmlMain.title = monitorSettings.title;
|
||||
}
|
||||
|
||||
const htmlMonitorText = document.createElement('div');
|
||||
htmlMonitorText.style.width = '35px';
|
||||
htmlMonitorText.style.fontSize = '10px';
|
||||
htmlMonitorText.innerHTML = monitorSettings.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';
|
||||
if(monitorSettings.cssColorFinal){
|
||||
htmlMonitorSlider.style.backgroundColor =
|
||||
`color-mix(in srgb, ${monitorSettings.cssColorFinal} 0%, ${monitorSettings.cssColor})`;
|
||||
} else {
|
||||
htmlMonitorSlider.style.backgroundColor = monitorSettings.cssColor;
|
||||
}
|
||||
monitorSettings.htmlMonitorSliderRef = 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%';
|
||||
monitorSettings.htmlMonitorLabelRef = htmlMonitorLabel;
|
||||
htmlMonitorContent.append(htmlMonitorLabel);
|
||||
|
||||
return monitorSettings.htmlMonitorRef;
|
||||
};
|
||||
|
||||
registerListeners = (): void => {
|
||||
@@ -540,7 +533,7 @@ class CrystoolsMonitor {
|
||||
if (event?.detail === undefined) {
|
||||
return;
|
||||
}
|
||||
this.updateAllMonitors(event.detail);
|
||||
this.monitorUI.updateDisplay(event.detail);
|
||||
}, false);
|
||||
};
|
||||
}
|
||||
@@ -548,5 +541,5 @@ class CrystoolsMonitor {
|
||||
const crystoolsMonitor = new CrystoolsMonitor();
|
||||
app.registerExtension({
|
||||
name: crystoolsMonitor.idExtensionName,
|
||||
setup: crystoolsMonitor.setup.bind(crystoolsMonitor),
|
||||
setup: crystoolsMonitor.setup,
|
||||
});
|
||||
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
import { ProgressBarUIBase } from './progressBarUIBase.js';
|
||||
export declare class MonitorUI extends ProgressBarUIBase {
|
||||
rootElement: HTMLElement;
|
||||
private monitorCPUElement;
|
||||
private monitorRAMElement;
|
||||
private monitorHDDElement;
|
||||
private monitorGPUSettings;
|
||||
private monitorVRAMSettings;
|
||||
private monitorTemperatureSettings;
|
||||
private currentRate;
|
||||
lastMonitor: number;
|
||||
styleSheet: HTMLStyleElement;
|
||||
constructor(rootElement: HTMLElement, monitorCPUElement: TMonitorSettings, monitorRAMElement: TMonitorSettings, monitorHDDElement: TMonitorSettings, monitorGPUSettings: TMonitorSettings[], monitorVRAMSettings: TMonitorSettings[], monitorTemperatureSettings: TMonitorSettings[], currentRate: number);
|
||||
createDOM: () => void;
|
||||
createDOMGPUMonitor: (monitorSettings?: TMonitorSettings) => void;
|
||||
orderMonitors: () => void;
|
||||
updateDisplay: (data: TStatsData) => void;
|
||||
updateMonitor: (monitorSettings: TMonitorSettings, percent: number, used?: number, total?: number) => void;
|
||||
updateAllAnimationDuration: (value: number) => void;
|
||||
updatedAnimationDuration: (monitorSettings: TMonitorSettings, value: number) => void;
|
||||
createMonitor: (monitorSettings?: TMonitorSettings) => HTMLDivElement;
|
||||
updateMonitorSize: (width: number, height: number) => void;
|
||||
showMonitor: (monitorSettings: TMonitorSettings, value: boolean) => void;
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
import { ProgressBarUIBase } from './progressBarUIBase.js';
|
||||
import { createStyleSheet, formatBytes } from './utils.js';
|
||||
export class MonitorUI extends ProgressBarUIBase {
|
||||
constructor(rootElement, monitorCPUElement, monitorRAMElement, monitorHDDElement, monitorGPUSettings, monitorVRAMSettings, monitorTemperatureSettings, currentRate) {
|
||||
super('crystools-monitors-root', rootElement);
|
||||
Object.defineProperty(this, "rootElement", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: rootElement
|
||||
});
|
||||
Object.defineProperty(this, "monitorCPUElement", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: monitorCPUElement
|
||||
});
|
||||
Object.defineProperty(this, "monitorRAMElement", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: monitorRAMElement
|
||||
});
|
||||
Object.defineProperty(this, "monitorHDDElement", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: monitorHDDElement
|
||||
});
|
||||
Object.defineProperty(this, "monitorGPUSettings", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: monitorGPUSettings
|
||||
});
|
||||
Object.defineProperty(this, "monitorVRAMSettings", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: monitorVRAMSettings
|
||||
});
|
||||
Object.defineProperty(this, "monitorTemperatureSettings", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: monitorTemperatureSettings
|
||||
});
|
||||
Object.defineProperty(this, "currentRate", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: currentRate
|
||||
});
|
||||
Object.defineProperty(this, "lastMonitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 1
|
||||
});
|
||||
Object.defineProperty(this, "styleSheet", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "createDOM", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
if (!this.rootElement) {
|
||||
throw Error('Crystools: MonitorUI - Container not found');
|
||||
}
|
||||
this.rootElement.appendChild(this.createMonitor(this.monitorCPUElement));
|
||||
this.rootElement.appendChild(this.createMonitor(this.monitorRAMElement));
|
||||
this.rootElement.appendChild(this.createMonitor(this.monitorHDDElement));
|
||||
this.updateAllAnimationDuration(this.currentRate);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createDOMGPUMonitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings) => {
|
||||
if (!(monitorSettings && this.rootElement)) {
|
||||
return;
|
||||
}
|
||||
this.rootElement.appendChild(this.createMonitor(monitorSettings));
|
||||
this.updateAllAnimationDuration(this.currentRate);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "orderMonitors", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
try {
|
||||
this.monitorCPUElement.htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
this.monitorRAMElement.htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
this.monitorGPUSettings.forEach((_monitorSettings, index) => {
|
||||
this.monitorGPUSettings[index].htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
this.monitorVRAMSettings[index].htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
this.monitorTemperatureSettings[index].htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
});
|
||||
this.monitorHDDElement.htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('orderMonitors', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateDisplay", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (data) => {
|
||||
this.updateMonitor(this.monitorCPUElement, data.cpu_utilization);
|
||||
this.updateMonitor(this.monitorRAMElement, data.ram_used_percent, data.ram_used, data.ram_total);
|
||||
this.updateMonitor(this.monitorHDDElement, data.hdd_used_percent, data.hdd_used, data.hdd_total);
|
||||
if (data.gpus === undefined || data.gpus.length === 0) {
|
||||
console.warn('UpdateAllMonitors: no GPU data');
|
||||
return;
|
||||
}
|
||||
this.monitorGPUSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
return;
|
||||
}
|
||||
this.updateMonitor(monitorSettings, gpu.gpu_utilization);
|
||||
}
|
||||
else {
|
||||
}
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
return;
|
||||
}
|
||||
this.updateMonitor(monitorSettings, gpu.vram_used_percent, gpu.vram_used, gpu.vram_total);
|
||||
}
|
||||
else {
|
||||
}
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
return;
|
||||
}
|
||||
this.updateMonitor(monitorSettings, gpu.gpu_temperature);
|
||||
if (monitorSettings.cssColorFinal && monitorSettings.htmlMonitorSliderRef) {
|
||||
monitorSettings.htmlMonitorSliderRef.style.backgroundColor =
|
||||
`color-mix(in srgb, ${monitorSettings.cssColorFinal} ${gpu.gpu_temperature}%, ${monitorSettings.cssColor})`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateMonitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings, percent, used, total) => {
|
||||
if (!(monitorSettings.htmlMonitorSliderRef && monitorSettings.htmlMonitorLabelRef)) {
|
||||
return;
|
||||
}
|
||||
if (percent < 0) {
|
||||
return;
|
||||
}
|
||||
const prefix = monitorSettings.monitorTitle ? monitorSettings.monitorTitle + ' - ' : '';
|
||||
let title = `${Math.floor(percent)}${monitorSettings.symbol}`;
|
||||
let postfix = '';
|
||||
if (used !== undefined && total !== undefined) {
|
||||
postfix = ` - ${formatBytes(used)} / ${formatBytes(total)} GB`;
|
||||
}
|
||||
title = `${prefix}${title}${postfix}`;
|
||||
if (monitorSettings.htmlMonitorRef) {
|
||||
monitorSettings.htmlMonitorRef.title = title;
|
||||
}
|
||||
monitorSettings.htmlMonitorLabelRef.innerHTML = `${Math.floor(percent)}${monitorSettings.symbol}`;
|
||||
monitorSettings.htmlMonitorSliderRef.style.width = `${Math.floor(percent)}%`;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateAllAnimationDuration", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (value) => {
|
||||
this.updatedAnimationDuration(this.monitorCPUElement, value);
|
||||
this.updatedAnimationDuration(this.monitorRAMElement, value);
|
||||
this.updatedAnimationDuration(this.monitorHDDElement, value);
|
||||
this.monitorGPUSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updatedAnimationDuration", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings, value) => {
|
||||
const slider = monitorSettings.htmlMonitorSliderRef;
|
||||
if (!slider) {
|
||||
return;
|
||||
}
|
||||
slider.style.transition = `width ${value.toFixed(1)}s`;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "createMonitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings) => {
|
||||
if (!monitorSettings) {
|
||||
return document.createElement('div');
|
||||
}
|
||||
const htmlMain = document.createElement('div');
|
||||
htmlMain.classList.add(monitorSettings.id);
|
||||
htmlMain.classList.add('crystools-monitor');
|
||||
monitorSettings.htmlMonitorRef = htmlMain;
|
||||
if (monitorSettings.title) {
|
||||
htmlMain.title = monitorSettings.title;
|
||||
}
|
||||
const htmlMonitorText = document.createElement('div');
|
||||
htmlMonitorText.classList.add('crystools-text');
|
||||
htmlMonitorText.innerHTML = monitorSettings.label;
|
||||
htmlMain.append(htmlMonitorText);
|
||||
const htmlMonitorContent = document.createElement('div');
|
||||
htmlMonitorContent.classList.add('crystools-content');
|
||||
htmlMain.append(htmlMonitorContent);
|
||||
const htmlMonitorSlider = document.createElement('div');
|
||||
htmlMonitorSlider.classList.add('crystools-slider');
|
||||
if (monitorSettings.cssColorFinal) {
|
||||
htmlMonitorSlider.style.backgroundColor =
|
||||
`color-mix(in srgb, ${monitorSettings.cssColorFinal} 0%, ${monitorSettings.cssColor})`;
|
||||
}
|
||||
else {
|
||||
htmlMonitorSlider.style.backgroundColor = monitorSettings.cssColor;
|
||||
}
|
||||
monitorSettings.htmlMonitorSliderRef = htmlMonitorSlider;
|
||||
htmlMonitorContent.append(htmlMonitorSlider);
|
||||
const htmlMonitorLabel = document.createElement('div');
|
||||
htmlMonitorLabel.classList.add('crystools-label');
|
||||
monitorSettings.htmlMonitorLabelRef = htmlMonitorLabel;
|
||||
htmlMonitorContent.append(htmlMonitorLabel);
|
||||
htmlMonitorLabel.innerHTML = '0%';
|
||||
return monitorSettings.htmlMonitorRef;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateMonitorSize", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (width, height) => {
|
||||
this.styleSheet.innerText = `.comfyui-menu #crystools-monitors-root .crystools-monitor .crystools-content {height: ${height}px; width: ${width}px;}`;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "showMonitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (monitorSettings, value) => {
|
||||
if (monitorSettings.htmlMonitorRef) {
|
||||
monitorSettings.htmlMonitorRef.style.display = value ? 'flex' : 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
this.createDOM();
|
||||
this.styleSheet = createStyleSheet('crystools-monitors-size');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
import { ProgressBarUIBase } from './progressBarUIBase.js';
|
||||
import { createStyleSheet, formatBytes } from './utils.js';
|
||||
|
||||
export class MonitorUI extends ProgressBarUIBase {
|
||||
lastMonitor = 1; // just for order on monitors section
|
||||
styleSheet: HTMLStyleElement;
|
||||
|
||||
constructor(
|
||||
public override rootElement: HTMLElement,
|
||||
private monitorCPUElement: TMonitorSettings,
|
||||
private monitorRAMElement: TMonitorSettings,
|
||||
private monitorHDDElement: TMonitorSettings,
|
||||
private monitorGPUSettings: TMonitorSettings[],
|
||||
private monitorVRAMSettings: TMonitorSettings[],
|
||||
private monitorTemperatureSettings: TMonitorSettings[],
|
||||
private currentRate: number,
|
||||
) {
|
||||
super('crystools-monitors-root', rootElement);
|
||||
this.createDOM();
|
||||
|
||||
this.styleSheet = createStyleSheet('crystools-monitors-size');
|
||||
}
|
||||
|
||||
createDOM = (): void => {
|
||||
if (!this.rootElement) {
|
||||
throw Error('Crystools: MonitorUI - Container not found');
|
||||
}
|
||||
|
||||
// this.container.style.order = '2';
|
||||
this.rootElement.appendChild(this.createMonitor(this.monitorCPUElement));
|
||||
this.rootElement.appendChild(this.createMonitor(this.monitorRAMElement));
|
||||
this.rootElement.appendChild(this.createMonitor(this.monitorHDDElement));
|
||||
this.updateAllAnimationDuration(this.currentRate);
|
||||
};
|
||||
|
||||
createDOMGPUMonitor = (monitorSettings?: TMonitorSettings): void => {
|
||||
if (!(monitorSettings && this.rootElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.rootElement.appendChild(this.createMonitor(monitorSettings));
|
||||
this.updateAllAnimationDuration(this.currentRate);
|
||||
};
|
||||
|
||||
orderMonitors = (): void => {
|
||||
try {
|
||||
// @ts-ignore
|
||||
this.monitorCPUElement.htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
// @ts-ignore
|
||||
this.monitorRAMElement.htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
// @ts-ignore
|
||||
this.monitorGPUSettings.forEach((_monitorSettings, index) => {
|
||||
// @ts-ignore
|
||||
this.monitorGPUSettings[index].htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
// @ts-ignore
|
||||
this.monitorVRAMSettings[index].htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
// @ts-ignore
|
||||
this.monitorTemperatureSettings[index].htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
});
|
||||
// @ts-ignore
|
||||
this.monitorHDDElement.htmlMonitorRef.style.order = '' + this.lastMonitor++;
|
||||
} catch (error) {
|
||||
console.error('orderMonitors', error);
|
||||
}
|
||||
};
|
||||
|
||||
updateDisplay = (data: TStatsData): void => {
|
||||
this.updateMonitor(this.monitorCPUElement, data.cpu_utilization);
|
||||
this.updateMonitor(this.monitorRAMElement, data.ram_used_percent, data.ram_used, data.ram_total);
|
||||
this.updateMonitor(this.monitorHDDElement, data.hdd_used_percent, data.hdd_used, data.hdd_total);
|
||||
|
||||
if (data.gpus === undefined || data.gpus.length === 0) {
|
||||
console.warn('UpdateAllMonitors: no GPU data');
|
||||
return;
|
||||
}
|
||||
|
||||
this.monitorGPUSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
// console.error('UpdateAllMonitors: no GPU data for index', index);
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateMonitor(monitorSettings, gpu.gpu_utilization);
|
||||
} else {
|
||||
// console.error('UpdateAllMonitors: no GPU data for index', index);
|
||||
}
|
||||
});
|
||||
|
||||
this.monitorVRAMSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
// console.error('UpdateAllMonitors: no GPU VRAM data for index', index);
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateMonitor(monitorSettings, gpu.vram_used_percent, gpu.vram_used, gpu.vram_total);
|
||||
} else {
|
||||
// console.error('UpdateAllMonitors: no GPU VRAM data for index', index);
|
||||
}
|
||||
});
|
||||
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings, index) => {
|
||||
if (data.gpus[index]) {
|
||||
const gpu = data.gpus[index];
|
||||
if (gpu === undefined) {
|
||||
// console.error('UpdateAllMonitors: no GPU VRAM data for index', index);
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateMonitor(monitorSettings, gpu.gpu_temperature);
|
||||
if (monitorSettings.cssColorFinal && monitorSettings.htmlMonitorSliderRef) {
|
||||
monitorSettings.htmlMonitorSliderRef.style.backgroundColor =
|
||||
`color-mix(in srgb, ${monitorSettings.cssColorFinal} ${gpu.gpu_temperature}%, ${monitorSettings.cssColor})`;
|
||||
}
|
||||
} else {
|
||||
// console.error('UpdateAllMonitors: no GPU VRAM data for index', index);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
updateMonitor = (monitorSettings: TMonitorSettings, percent: number, used?: number, total?: number): void => {
|
||||
if (!(monitorSettings.htmlMonitorSliderRef && monitorSettings.htmlMonitorLabelRef)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (percent < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log('updateMonitor', monitorSettings.id, percent);
|
||||
|
||||
const prefix = monitorSettings.monitorTitle ? monitorSettings.monitorTitle + ' - ' : '';
|
||||
let title = `${Math.floor(percent)}${monitorSettings.symbol}`;
|
||||
let postfix = '';
|
||||
|
||||
if (used !== undefined && total !== undefined) {
|
||||
postfix = ` - ${formatBytes(used)} / ${formatBytes(total)} GB`;
|
||||
}
|
||||
title = `${prefix}${title}${postfix}`;
|
||||
|
||||
if (monitorSettings.htmlMonitorRef) {
|
||||
monitorSettings.htmlMonitorRef.title = title;
|
||||
}
|
||||
monitorSettings.htmlMonitorLabelRef.innerHTML = `${Math.floor(percent)}${monitorSettings.symbol}`;
|
||||
monitorSettings.htmlMonitorSliderRef.style.width = `${Math.floor(percent)}%`;
|
||||
};
|
||||
|
||||
updateAllAnimationDuration = (value: number): void => {
|
||||
this.updatedAnimationDuration(this.monitorCPUElement, value);
|
||||
this.updatedAnimationDuration(this.monitorRAMElement, value);
|
||||
this.updatedAnimationDuration(this.monitorHDDElement, value);
|
||||
this.monitorGPUSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
this.monitorVRAMSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
this.monitorTemperatureSettings.forEach((monitorSettings) => {
|
||||
monitorSettings && this.updatedAnimationDuration(monitorSettings, value);
|
||||
});
|
||||
};
|
||||
|
||||
updatedAnimationDuration = (monitorSettings: TMonitorSettings, value: number): void => {
|
||||
const slider = monitorSettings.htmlMonitorSliderRef;
|
||||
if (!slider) {
|
||||
return;
|
||||
}
|
||||
|
||||
slider.style.transition = `width ${value.toFixed(1)}s`;
|
||||
};
|
||||
|
||||
createMonitor = (monitorSettings?: TMonitorSettings): HTMLDivElement => {
|
||||
if (!monitorSettings) {
|
||||
// just for typescript
|
||||
return document.createElement('div');
|
||||
}
|
||||
|
||||
const htmlMain = document.createElement('div');
|
||||
htmlMain.classList.add(monitorSettings.id);
|
||||
htmlMain.classList.add('crystools-monitor');
|
||||
|
||||
monitorSettings.htmlMonitorRef = htmlMain;
|
||||
|
||||
if (monitorSettings.title) {
|
||||
htmlMain.title = monitorSettings.title;
|
||||
}
|
||||
|
||||
const htmlMonitorText = document.createElement('div');
|
||||
htmlMonitorText.classList.add('crystools-text');
|
||||
htmlMonitorText.innerHTML = monitorSettings.label;
|
||||
htmlMain.append(htmlMonitorText);
|
||||
|
||||
const htmlMonitorContent = document.createElement('div');
|
||||
htmlMonitorContent.classList.add('crystools-content');
|
||||
htmlMain.append(htmlMonitorContent);
|
||||
|
||||
const htmlMonitorSlider = document.createElement('div');
|
||||
htmlMonitorSlider.classList.add('crystools-slider');
|
||||
if (monitorSettings.cssColorFinal) {
|
||||
htmlMonitorSlider.style.backgroundColor =
|
||||
`color-mix(in srgb, ${monitorSettings.cssColorFinal} 0%, ${monitorSettings.cssColor})`;
|
||||
} else {
|
||||
htmlMonitorSlider.style.backgroundColor = monitorSettings.cssColor;
|
||||
}
|
||||
monitorSettings.htmlMonitorSliderRef = htmlMonitorSlider;
|
||||
htmlMonitorContent.append(htmlMonitorSlider);
|
||||
|
||||
const htmlMonitorLabel = document.createElement('div');
|
||||
htmlMonitorLabel.classList.add('crystools-label');
|
||||
monitorSettings.htmlMonitorLabelRef = htmlMonitorLabel;
|
||||
htmlMonitorContent.append(htmlMonitorLabel);
|
||||
htmlMonitorLabel.innerHTML = '0%';
|
||||
return monitorSettings.htmlMonitorRef;
|
||||
};
|
||||
|
||||
updateMonitorSize = (width: number, height: number): void => {
|
||||
// eslint-disable-next-line max-len
|
||||
this.styleSheet.innerText = `.comfyui-menu #crystools-monitors-root .crystools-monitor .crystools-content {height: ${height}px; width: ${width}px;}`;
|
||||
};
|
||||
|
||||
showMonitor = (monitorSettings: TMonitorSettings, value: boolean): void => {
|
||||
if (monitorSettings.htmlMonitorRef) {
|
||||
monitorSettings.htmlMonitorRef.style.display = value ? 'flex' : 'none';
|
||||
}
|
||||
};
|
||||
}
|
||||
+58
-118
@@ -1,12 +1,7 @@
|
||||
import { api } from '/scripts/api.js';
|
||||
import { app } from '/scripts/app.js';
|
||||
import { app, api } from './comfy/index.js';
|
||||
import { commonPrefix } from './common.js';
|
||||
var EStatus;
|
||||
(function (EStatus) {
|
||||
EStatus["executing"] = "Executing";
|
||||
EStatus["executed"] = "Executed";
|
||||
EStatus["execution_error"] = "Execution error";
|
||||
})(EStatus || (EStatus = {}));
|
||||
import { ProgressBarUI } from './progressBarUI.js';
|
||||
import { ComfyKeyMenuDisplayOption, EStatus, MenuDisplayOptions } from './progressBarUIBase.js';
|
||||
class CrystoolsProgressBar {
|
||||
constructor() {
|
||||
Object.defineProperty(this, "idExtensionName", {
|
||||
@@ -19,7 +14,7 @@ class CrystoolsProgressBar {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 'Crystools.showStatus'
|
||||
value: 'Crystools.ProgressBar'
|
||||
});
|
||||
Object.defineProperty(this, "defaultShowStatus", {
|
||||
enumerable: true,
|
||||
@@ -33,17 +28,11 @@ class CrystoolsProgressBar {
|
||||
writable: true,
|
||||
value: commonPrefix
|
||||
});
|
||||
Object.defineProperty(this, "htmlIdCrystoolsRoot", {
|
||||
Object.defineProperty(this, "menuDisplayOption", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 'crystools-root'
|
||||
});
|
||||
Object.defineProperty(this, "htmlIdCrystoolsProgressBarContainer", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 'crystools-progress-bar-container'
|
||||
value: MenuDisplayOptions.Disabled
|
||||
});
|
||||
Object.defineProperty(this, "currentStatus", {
|
||||
enumerable: true,
|
||||
@@ -69,17 +58,11 @@ class CrystoolsProgressBar {
|
||||
writable: true,
|
||||
value: 0
|
||||
});
|
||||
Object.defineProperty(this, "htmlProgressSliderRef", {
|
||||
Object.defineProperty(this, "progressBarUI", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined
|
||||
});
|
||||
Object.defineProperty(this, "htmlProgressLabelRef", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "createSettings", {
|
||||
enumerable: true,
|
||||
@@ -88,50 +71,58 @@ class CrystoolsProgressBar {
|
||||
value: () => {
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idShowProgressBar,
|
||||
name: this.menuPrefix + '[monitor] Show progress bar',
|
||||
name: 'Show progress bar',
|
||||
category: ['Crystools', this.menuPrefix + ' Progress Bar', 'Show'],
|
||||
tooltip: 'This apply only on "Disabled" (old) menu',
|
||||
type: 'boolean',
|
||||
defaultValue: this.defaultShowStatus,
|
||||
onChange: this.showProgressBar,
|
||||
onChange: this.progressBarUI.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: (menuDisplayOption) => {
|
||||
if (menuDisplayOption !== this.menuDisplayOption) {
|
||||
this.menuDisplayOption = menuDisplayOption;
|
||||
this.progressBarUI.showSection(this.menuDisplayOption === MenuDisplayOptions.Disabled);
|
||||
}
|
||||
if (this.menuDisplayOption === MenuDisplayOptions.Disabled && this.progressBarUI.showProgressBarFlag) {
|
||||
console.log('updateDisplay progress bar');
|
||||
this.progressBarUI.updateDisplay(this.currentStatus, this.timeStart, this.currentProgress);
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "setup", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
if (!(this.htmlProgressLabelRef && this.htmlProgressSliderRef)) {
|
||||
console.error('htmlProgressLabelRef or htmlProgressSliderRef is undefined');
|
||||
if (this.progressBarUI) {
|
||||
this.progressBarUI
|
||||
.showProgressBar(app.ui.settings.getSettingValue(this.idShowProgressBar, this.defaultShowStatus));
|
||||
return;
|
||||
}
|
||||
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';
|
||||
this.menuDisplayOption = app.ui.settings.getSettingValue(ComfyKeyMenuDisplayOption, MenuDisplayOptions.Disabled);
|
||||
app.ui.settings.addEventListener(`${ComfyKeyMenuDisplayOption}.change`, (e) => {
|
||||
console.log('Comfy.UseNewMenu.change desde progress bar', e.detail.value);
|
||||
this.updateDisplay(e.detail.value);
|
||||
});
|
||||
const progressBarElement = document.createElement('div');
|
||||
progressBarElement.classList.add('crystools-monitors-container');
|
||||
this.progressBarUI = new ProgressBarUI(progressBarElement, (this.menuDisplayOption === MenuDisplayOptions.Disabled), this.centerNode);
|
||||
const parentElement = document.getElementById('queue-button');
|
||||
if (parentElement) {
|
||||
parentElement.insertAdjacentElement('afterend', progressBarElement);
|
||||
}
|
||||
else if (this.currentStatus === EStatus.execution_error) {
|
||||
this.htmlProgressLabelRef.innerHTML = 'ERROR';
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'var(--error-text)';
|
||||
}
|
||||
else if (this.currentStatus === EStatus.executing) {
|
||||
this.htmlProgressLabelRef.innerHTML = `${this.currentProgress}%`;
|
||||
this.htmlProgressSliderRef.style.width = this.htmlProgressLabelRef.innerHTML;
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'green';
|
||||
else {
|
||||
console.error('Crystools: parentElement to move monitors not found!', parentElement);
|
||||
}
|
||||
this.createSettings();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
this.registerListeners();
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "registerListeners", {
|
||||
@@ -139,37 +130,37 @@ class CrystoolsProgressBar {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
api.addEventListener('status', ({ detail, }) => {
|
||||
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();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
api.addEventListener('progress', ({ detail, }) => {
|
||||
const { value, max, node, } = detail;
|
||||
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();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
api.addEventListener('executed', ({ detail, }) => {
|
||||
api.addEventListener('executed', ({ detail }) => {
|
||||
if (detail?.node) {
|
||||
this.currentNode = detail.node;
|
||||
}
|
||||
this.updateDisplay();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
api.addEventListener('execution_start', ({ _detail, }) => {
|
||||
api.addEventListener('execution_start', ({ _detail }) => {
|
||||
this.currentStatus = EStatus.executing;
|
||||
this.timeStart = Date.now();
|
||||
this.updateDisplay();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
api.addEventListener('execution_error', ({ _detail, }) => {
|
||||
api.addEventListener('execution_error', ({ _detail }) => {
|
||||
this.currentStatus = EStatus.execution_error;
|
||||
this.updateDisplay();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
@@ -189,61 +180,10 @@ class CrystoolsProgressBar {
|
||||
app.canvas.centerOnNode(node);
|
||||
}
|
||||
});
|
||||
this.createSettings();
|
||||
}
|
||||
setup() {
|
||||
const parentElement = document.getElementById('queue-button');
|
||||
if (!parentElement) {
|
||||
console.error('queue-button not found');
|
||||
return;
|
||||
}
|
||||
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),
|
||||
setup: crystoolsProgressBar.setup,
|
||||
});
|
||||
|
||||
+55
-124
@@ -1,158 +1,95 @@
|
||||
import { api } from '/scripts/api.js';
|
||||
import { app } from '/scripts/app.js';
|
||||
import { app, api } from './comfy/index.js';
|
||||
import { commonPrefix } from './common.js';
|
||||
|
||||
enum EStatus {
|
||||
executing = 'Executing',
|
||||
executed = 'Executed',
|
||||
execution_error = 'Execution error',
|
||||
}
|
||||
import { ProgressBarUI } from './progressBarUI.js';
|
||||
import { ComfyKeyMenuDisplayOption, EStatus, MenuDisplayOptions } from './progressBarUIBase.js';
|
||||
|
||||
class CrystoolsProgressBar {
|
||||
idExtensionName = 'Crystools.progressBar';
|
||||
idShowProgressBar = 'Crystools.showStatus';
|
||||
idShowProgressBar = 'Crystools.ProgressBar';
|
||||
defaultShowStatus = true;
|
||||
menuPrefix = commonPrefix;
|
||||
htmlIdCrystoolsRoot = 'crystools-root';
|
||||
htmlIdCrystoolsProgressBarContainer = 'crystools-progress-bar-container';
|
||||
menuDisplayOption: MenuDisplayOptions = MenuDisplayOptions.Disabled;
|
||||
|
||||
currentStatus = EStatus.executed;
|
||||
currentProgress = 0;
|
||||
currentNode?: number = undefined;
|
||||
timeStart = 0;
|
||||
|
||||
htmlProgressSliderRef?: HTMLDivElement = undefined;
|
||||
htmlProgressLabelRef?: HTMLDivElement = undefined;
|
||||
|
||||
constructor() {
|
||||
this.createSettings();
|
||||
}
|
||||
progressBarUI: ProgressBarUI;
|
||||
|
||||
// not on setup because this affect the order on settings, I prefer to options at first
|
||||
createSettings = (): void => {
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idShowProgressBar,
|
||||
name: this.menuPrefix + '[monitor] Show progress bar',
|
||||
name: 'Show progress bar',
|
||||
category: ['Crystools', this.menuPrefix + ' Progress Bar', 'Show'],
|
||||
tooltip: 'This apply only on "Disabled" (old) menu',
|
||||
type: 'boolean',
|
||||
defaultValue: this.defaultShowStatus,
|
||||
onChange: this.showProgressBar,
|
||||
onChange: this.progressBarUI.showProgressBar,
|
||||
});
|
||||
};
|
||||
|
||||
showProgressBar = (value: boolean): void => {
|
||||
const container = document.getElementById(this.htmlIdCrystoolsProgressBarContainer);
|
||||
|
||||
// validation because this run before setup
|
||||
if (container) {
|
||||
container.style.display = value ? 'block' : 'none';
|
||||
updateDisplay = (menuDisplayOption: MenuDisplayOptions): void => {
|
||||
if (menuDisplayOption !== this.menuDisplayOption) {
|
||||
this.menuDisplayOption = menuDisplayOption;
|
||||
this.progressBarUI.showSection(this.menuDisplayOption === MenuDisplayOptions.Disabled);
|
||||
}
|
||||
if (this.menuDisplayOption === MenuDisplayOptions.Disabled && this.progressBarUI.showProgressBarFlag) {
|
||||
console.log('updateDisplay progress bar');
|
||||
this.progressBarUI.updateDisplay(this.currentStatus, this.timeStart, this.currentProgress);
|
||||
}
|
||||
};
|
||||
|
||||
updateDisplay = (): void => {
|
||||
if (!(this.htmlProgressLabelRef && this.htmlProgressSliderRef)) {
|
||||
console.error('htmlProgressLabelRef or htmlProgressSliderRef is undefined');
|
||||
// automatically called by ComfyUI
|
||||
setup = (): void => {
|
||||
if (this.progressBarUI) {
|
||||
this.progressBarUI
|
||||
.showProgressBar(app.ui.settings.getSettingValue(this.idShowProgressBar, this.defaultShowStatus));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentStatus === EStatus.executed) {
|
||||
// finished
|
||||
this.htmlProgressLabelRef.innerHTML = 'cached';
|
||||
this.menuDisplayOption = app.ui.settings.getSettingValue(ComfyKeyMenuDisplayOption, MenuDisplayOptions.Disabled);
|
||||
app.ui.settings.addEventListener(`${ComfyKeyMenuDisplayOption}.change`, (e: any) => {
|
||||
console.log('Comfy.UseNewMenu.change desde progress bar', e.detail.value);
|
||||
this.updateDisplay(e.detail.value);
|
||||
},
|
||||
);
|
||||
|
||||
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';
|
||||
const progressBarElement = document.createElement('div');
|
||||
progressBarElement.classList.add('crystools-monitors-container');
|
||||
|
||||
} else if (this.currentStatus === EStatus.execution_error) {
|
||||
// an error occurred
|
||||
this.htmlProgressLabelRef.innerHTML = 'ERROR';
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'var(--error-text)';
|
||||
this.progressBarUI = new ProgressBarUI(
|
||||
progressBarElement,
|
||||
(this.menuDisplayOption === MenuDisplayOptions.Disabled),
|
||||
this.centerNode,
|
||||
);
|
||||
|
||||
} else if (this.currentStatus === EStatus.executing) {
|
||||
// on going
|
||||
this.htmlProgressLabelRef.innerHTML = `${this.currentProgress}%`;
|
||||
this.htmlProgressSliderRef.style.width = this.htmlProgressLabelRef.innerHTML;
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'green'; // by reset the color
|
||||
}
|
||||
};
|
||||
|
||||
setup(): void {
|
||||
const parentElement = document.getElementById('queue-button');
|
||||
if (!parentElement) {
|
||||
console.error('queue-button not found');
|
||||
return;
|
||||
if (parentElement) {
|
||||
parentElement.insertAdjacentElement('afterend', progressBarElement);
|
||||
} else {
|
||||
console.error('Crystools: parentElement to move monitors not found!', parentElement);
|
||||
}
|
||||
|
||||
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.createSettings();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
this.registerListeners();
|
||||
}
|
||||
};
|
||||
|
||||
registerListeners = (): void => {
|
||||
api.addEventListener('status', ({
|
||||
detail,
|
||||
}: any) => {
|
||||
api.addEventListener('status', ({detail}: any) => {
|
||||
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();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
|
||||
api.addEventListener('progress', ({
|
||||
detail,
|
||||
}: any) => {
|
||||
const {
|
||||
value, max, node,
|
||||
} = detail;
|
||||
api.addEventListener('progress', ({detail}: any) => {
|
||||
const {value, max, node} = detail;
|
||||
const progress = Math.floor((value / max) * 100);
|
||||
|
||||
if (!isNaN(progress) && progress >= 0 && progress <= 100) {
|
||||
@@ -160,34 +97,28 @@ class CrystoolsProgressBar {
|
||||
this.currentNode = node;
|
||||
}
|
||||
|
||||
this.updateDisplay();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
|
||||
api.addEventListener('executed', ({
|
||||
detail,
|
||||
}: any) => {
|
||||
api.addEventListener('executed', ({detail}: any) => {
|
||||
if (detail?.node) {
|
||||
this.currentNode = detail.node;
|
||||
}
|
||||
|
||||
this.updateDisplay();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
|
||||
api.addEventListener('execution_start', ({
|
||||
_detail,
|
||||
}: any) => {
|
||||
api.addEventListener('execution_start', ({_detail}: any) => {
|
||||
this.currentStatus = EStatus.executing;
|
||||
this.timeStart = Date.now();
|
||||
|
||||
this.updateDisplay();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
|
||||
api.addEventListener('execution_error', ({
|
||||
_detail,
|
||||
}: any) => {
|
||||
api.addEventListener('execution_error', ({_detail}: any) => {
|
||||
this.currentStatus = EStatus.execution_error;
|
||||
|
||||
this.updateDisplay();
|
||||
this.updateDisplay(this.menuDisplayOption);
|
||||
}, false);
|
||||
};
|
||||
|
||||
@@ -207,5 +138,5 @@ class CrystoolsProgressBar {
|
||||
const crystoolsProgressBar = new CrystoolsProgressBar();
|
||||
app.registerExtension({
|
||||
name: crystoolsProgressBar.idExtensionName,
|
||||
setup: crystoolsProgressBar.setup.bind(crystoolsProgressBar),
|
||||
setup: crystoolsProgressBar.setup,
|
||||
});
|
||||
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
import { EStatus, ProgressBarUIBase } from './progressBarUIBase.js';
|
||||
export declare class ProgressBarUI extends ProgressBarUIBase {
|
||||
rootElement: HTMLElement;
|
||||
showSectionFlag: boolean;
|
||||
private centerNode;
|
||||
htmlProgressSliderRef: HTMLDivElement;
|
||||
htmlProgressLabelRef: HTMLDivElement;
|
||||
currentStatus: EStatus;
|
||||
timeStart: number;
|
||||
currentProgress: number;
|
||||
showProgressBarFlag: boolean;
|
||||
constructor(rootElement: HTMLElement, showSectionFlag: boolean, centerNode: () => void);
|
||||
createDOM: () => void;
|
||||
updateDisplay: (currentStatus: EStatus, timeStart: number, currentProgress: number) => void;
|
||||
showSection: (value: boolean) => void;
|
||||
showProgressBar: (value: boolean) => void;
|
||||
private displaySection;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import { EStatus, ProgressBarUIBase } from './progressBarUIBase.js';
|
||||
export class ProgressBarUI extends ProgressBarUIBase {
|
||||
constructor(rootElement, showSectionFlag, centerNode) {
|
||||
super('crystools-progressBar-root', rootElement);
|
||||
Object.defineProperty(this, "rootElement", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: rootElement
|
||||
});
|
||||
Object.defineProperty(this, "showSectionFlag", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: showSectionFlag
|
||||
});
|
||||
Object.defineProperty(this, "centerNode", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: centerNode
|
||||
});
|
||||
Object.defineProperty(this, "htmlProgressSliderRef", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "htmlProgressLabelRef", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "currentStatus", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "timeStart", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "currentProgress", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "showProgressBarFlag", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "createDOM", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.rootElement.setAttribute('title', 'click to see the current working node');
|
||||
this.rootElement.addEventListener('click', this.centerNode);
|
||||
const progressBar = document.createElement('div');
|
||||
progressBar.classList.add('crystools-progress-bar');
|
||||
this.rootElement.append(progressBar);
|
||||
const progressSlider = document.createElement('div');
|
||||
this.htmlProgressSliderRef = progressSlider;
|
||||
progressSlider.classList.add('crystools-slider');
|
||||
progressBar.append(this.htmlProgressSliderRef);
|
||||
const progressLabel = document.createElement('div');
|
||||
progressLabel.classList.add('crystools-label');
|
||||
progressLabel.innerHTML = '0%';
|
||||
this.htmlProgressLabelRef = progressLabel;
|
||||
progressBar.append(this.htmlProgressLabelRef);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "updateDisplay", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (currentStatus, timeStart, currentProgress) => {
|
||||
if (!(this.showSectionFlag && this.showProgressBarFlag)) {
|
||||
return;
|
||||
}
|
||||
if (!(this.htmlProgressLabelRef && this.htmlProgressSliderRef)) {
|
||||
console.error('htmlProgressLabelRef or htmlProgressSliderRef is undefined');
|
||||
return;
|
||||
}
|
||||
this.currentStatus = currentStatus;
|
||||
this.timeStart = timeStart;
|
||||
this.currentProgress = currentProgress;
|
||||
if (currentStatus === EStatus.executed) {
|
||||
this.htmlProgressLabelRef.innerHTML = 'cached';
|
||||
const timeElapsed = Date.now() - timeStart;
|
||||
if (timeStart > 0 && timeElapsed > 0) {
|
||||
this.htmlProgressLabelRef.innerHTML = new Date(timeElapsed).toISOString().substr(11, 8);
|
||||
}
|
||||
this.htmlProgressSliderRef.style.width = '0';
|
||||
}
|
||||
else if (currentStatus === EStatus.execution_error) {
|
||||
this.htmlProgressLabelRef.innerHTML = 'ERROR';
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'var(--error-text)';
|
||||
}
|
||||
else if (currentStatus === EStatus.executing) {
|
||||
this.htmlProgressLabelRef.innerHTML = `${currentProgress}%`;
|
||||
this.htmlProgressSliderRef.style.width = this.htmlProgressLabelRef.innerHTML;
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'green';
|
||||
}
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "showSection", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (value) => {
|
||||
this.showSectionFlag = value;
|
||||
this.displaySection();
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "showProgressBar", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: (value) => {
|
||||
this.showProgressBarFlag = value;
|
||||
this.displaySection();
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, "displaySection", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
this.rootElement.style.display = (this.showSectionFlag && this.showProgressBarFlag) ? 'block' : 'none';
|
||||
}
|
||||
});
|
||||
this.createDOM();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { EStatus, ProgressBarUIBase } from './progressBarUIBase.js';
|
||||
|
||||
export class ProgressBarUI extends ProgressBarUIBase {
|
||||
htmlProgressSliderRef: HTMLDivElement;
|
||||
htmlProgressLabelRef: HTMLDivElement;
|
||||
currentStatus: EStatus;
|
||||
timeStart: number;
|
||||
currentProgress: number;
|
||||
showProgressBarFlag: boolean;
|
||||
|
||||
constructor(
|
||||
public override rootElement: HTMLElement,
|
||||
public showSectionFlag: boolean,
|
||||
private centerNode: () => void,
|
||||
) {
|
||||
super('crystools-progressBar-root', rootElement);
|
||||
this.createDOM();
|
||||
}
|
||||
|
||||
createDOM = (): void => {
|
||||
this.rootElement.setAttribute('title', 'click to see the current working node');
|
||||
this.rootElement.addEventListener('click', this.centerNode);
|
||||
|
||||
const progressBar = document.createElement('div');
|
||||
progressBar.classList.add('crystools-progress-bar');
|
||||
this.rootElement.append(progressBar);
|
||||
|
||||
const progressSlider = document.createElement('div');
|
||||
this.htmlProgressSliderRef = progressSlider;
|
||||
progressSlider.classList.add('crystools-slider');
|
||||
progressBar.append(this.htmlProgressSliderRef);
|
||||
|
||||
const progressLabel = document.createElement('div');
|
||||
progressLabel.classList.add('crystools-label');
|
||||
progressLabel.innerHTML = '0%';
|
||||
this.htmlProgressLabelRef = progressLabel;
|
||||
progressBar.append(this.htmlProgressLabelRef);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
updateDisplay = (currentStatus: EStatus, timeStart: number, currentProgress: number): void => {
|
||||
if (!(this.showSectionFlag && this.showProgressBarFlag)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(this.htmlProgressLabelRef && this.htmlProgressSliderRef)) {
|
||||
console.error('htmlProgressLabelRef or htmlProgressSliderRef is undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log('only if showSection and progressBar', timeStart, currentProgress);
|
||||
|
||||
this.currentStatus = currentStatus;
|
||||
this.timeStart = timeStart;
|
||||
this.currentProgress = currentProgress;
|
||||
|
||||
if (currentStatus === EStatus.executed) {
|
||||
// finished
|
||||
this.htmlProgressLabelRef.innerHTML = 'cached';
|
||||
|
||||
const timeElapsed = Date.now() - timeStart;
|
||||
if (timeStart > 0 && timeElapsed > 0) {
|
||||
this.htmlProgressLabelRef.innerHTML = new Date(timeElapsed).toISOString().substr(11, 8);
|
||||
}
|
||||
this.htmlProgressSliderRef.style.width = '0';
|
||||
|
||||
} else if (currentStatus === EStatus.execution_error) {
|
||||
// an error occurred
|
||||
this.htmlProgressLabelRef.innerHTML = 'ERROR';
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'var(--error-text)';
|
||||
|
||||
} else if (currentStatus === EStatus.executing) {
|
||||
// on going
|
||||
this.htmlProgressLabelRef.innerHTML = `${currentProgress}%`;
|
||||
this.htmlProgressSliderRef.style.width = this.htmlProgressLabelRef.innerHTML;
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'green'; // by reset the color
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public showSection = (value: boolean): void => {
|
||||
this.showSectionFlag = value;
|
||||
this.displaySection();
|
||||
};
|
||||
|
||||
// remember it can't have more parameters because it is used on settings automatically
|
||||
public showProgressBar = (value: boolean): void => {
|
||||
this.showProgressBarFlag = value;
|
||||
this.displaySection();
|
||||
};
|
||||
|
||||
private displaySection = (): void => {
|
||||
this.rootElement.style.display = (this.showSectionFlag && this.showProgressBarFlag) ? 'block' : 'none';
|
||||
};
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
export declare enum EStatus {
|
||||
executing = "Executing",
|
||||
executed = "Executed",
|
||||
execution_error = "Execution error"
|
||||
}
|
||||
export declare const ComfyKeyMenuDisplayOption = "Comfy.UseNewMenu";
|
||||
export declare enum MenuDisplayOptions {
|
||||
'Disabled' = "Disabled",
|
||||
'Top' = "Top",
|
||||
'Bottom' = "Bottom"
|
||||
}
|
||||
export declare abstract class ProgressBarUIBase {
|
||||
rootId: string;
|
||||
rootElement: HTMLElement | null | undefined;
|
||||
protected htmlClassMonitor: string;
|
||||
protected constructor(rootId: string, rootElement: HTMLElement | null | undefined);
|
||||
abstract createDOM(): void;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
export var EStatus;
|
||||
(function (EStatus) {
|
||||
EStatus["executing"] = "Executing";
|
||||
EStatus["executed"] = "Executed";
|
||||
EStatus["execution_error"] = "Execution error";
|
||||
})(EStatus || (EStatus = {}));
|
||||
export const ComfyKeyMenuDisplayOption = 'Comfy.UseNewMenu';
|
||||
export var MenuDisplayOptions;
|
||||
(function (MenuDisplayOptions) {
|
||||
MenuDisplayOptions["Disabled"] = "Disabled";
|
||||
MenuDisplayOptions["Top"] = "Top";
|
||||
MenuDisplayOptions["Bottom"] = "Bottom";
|
||||
})(MenuDisplayOptions || (MenuDisplayOptions = {}));
|
||||
export class ProgressBarUIBase {
|
||||
constructor(rootId, rootElement) {
|
||||
Object.defineProperty(this, "rootId", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: rootId
|
||||
});
|
||||
Object.defineProperty(this, "rootElement", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: rootElement
|
||||
});
|
||||
Object.defineProperty(this, "htmlClassMonitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 'crystools-monitors-container'
|
||||
});
|
||||
if (this.rootElement && this.rootElement.children.length === 0) {
|
||||
this.rootElement.setAttribute('id', this.rootId);
|
||||
this.rootElement.classList.add(this.htmlClassMonitor);
|
||||
this.rootElement.classList.add(this.constructor.name);
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
export enum EStatus {
|
||||
executing = 'Executing',
|
||||
executed = 'Executed',
|
||||
execution_error = 'Execution error',
|
||||
}
|
||||
|
||||
export const ComfyKeyMenuDisplayOption = 'Comfy.UseNewMenu';
|
||||
export enum MenuDisplayOptions {
|
||||
'Disabled' = 'Disabled',
|
||||
'Top' = 'Top',
|
||||
'Bottom' = 'Bottom',
|
||||
}
|
||||
|
||||
export abstract class ProgressBarUIBase {
|
||||
protected htmlClassMonitor = 'crystools-monitors-container';
|
||||
|
||||
protected constructor(
|
||||
public rootId: string,
|
||||
public rootElement: HTMLElement | null | undefined,
|
||||
) {
|
||||
// IMPORTANT duplicate on crystools-save
|
||||
if (this.rootElement && this.rootElement.children.length === 0) {
|
||||
this.rootElement.setAttribute('id', this.rootId);
|
||||
this.rootElement.classList.add(this.htmlClassMonitor);
|
||||
this.rootElement.classList.add(this.constructor.name);
|
||||
} else {
|
||||
// it was created before
|
||||
}
|
||||
}
|
||||
|
||||
abstract createDOM(): void;
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
export declare enum Styles {
|
||||
'BARS' = "BARS"
|
||||
}
|
||||
export declare enum Colors {
|
||||
'CPU' = "#0AA015",
|
||||
'RAM' = "#07630D",
|
||||
'DISK' = "#730F92",
|
||||
'GPU' = "#0C86F4",
|
||||
'VRAM' = "#176EC7",
|
||||
'TEMP_START' = "#00ff00",
|
||||
'TEMP_END' = "#ff0000"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { utils } from './comfy/index.js';
|
||||
utils.addStylesheet('extensions/ComfyUI-Crystools/monitor.css');
|
||||
export var Styles;
|
||||
(function (Styles) {
|
||||
Styles["BARS"] = "BARS";
|
||||
})(Styles || (Styles = {}));
|
||||
export var Colors;
|
||||
(function (Colors) {
|
||||
Colors["CPU"] = "#0AA015";
|
||||
Colors["RAM"] = "#07630D";
|
||||
Colors["DISK"] = "#730F92";
|
||||
Colors["GPU"] = "#0C86F4";
|
||||
Colors["VRAM"] = "#176EC7";
|
||||
Colors["TEMP_START"] = "#00ff00";
|
||||
Colors["TEMP_END"] = "#ff0000";
|
||||
})(Colors || (Colors = {}));
|
||||
@@ -0,0 +1,17 @@
|
||||
import { utils } from './comfy/index.js';
|
||||
|
||||
utils.addStylesheet('extensions/ComfyUI-Crystools/monitor.css');
|
||||
|
||||
export enum Styles {
|
||||
'BARS' = 'BARS'
|
||||
}
|
||||
|
||||
export enum Colors {
|
||||
'CPU' = '#0AA015',
|
||||
'RAM' = '#07630D',
|
||||
'DISK' = '#730F92',
|
||||
'GPU' = '#0C86F4',
|
||||
'VRAM' = '#176EC7',
|
||||
'TEMP_START' = '#00ff00',
|
||||
'TEMP_END' = '#ff0000',
|
||||
}
|
||||
+7
-3
@@ -1,3 +1,4 @@
|
||||
|
||||
type TGpuStatData = {
|
||||
gpu_utilization: number,
|
||||
gpu_temperature: number,
|
||||
@@ -42,13 +43,16 @@ type TStatsSettings = {
|
||||
type TMonitorSettings = {
|
||||
id: string,
|
||||
name: string,
|
||||
category: string[], // for settings location
|
||||
label: string, // on monitor
|
||||
symbol: string, // on monitor
|
||||
monitorTitle?: string, // on monitor
|
||||
title?: string, // on monitor
|
||||
tooltip?: string, // on settings
|
||||
type: 'boolean' | 'number' | 'string',
|
||||
defaultValue: boolean,
|
||||
onChange: (value: boolean) => Promise<void>,
|
||||
type: 'boolean' | 'number' | 'string' | 'slider' | 'combo',
|
||||
defaultValue: boolean | number | string,
|
||||
data?: any,
|
||||
onChange: (value: boolean | number | string) => Promise<void>,
|
||||
htmlMonitorRef?: HTMLDivElement,
|
||||
htmlMonitorSliderRef?: HTMLDivElement,
|
||||
htmlMonitorLabelRef?: HTMLDivElement,
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
export declare function numberToWords(num: number): string;
|
||||
export declare function toPascalCase(strings: string[]): string;
|
||||
export declare function convertNumberToPascalCase(num: number): string;
|
||||
export declare function formatBytes(bytes: number): string;
|
||||
export declare function createStyleSheet(id: string): HTMLStyleElement;
|
||||
@@ -0,0 +1,56 @@
|
||||
export function numberToWords(num) {
|
||||
if (num === 0)
|
||||
return 'zero';
|
||||
const belowTwenty = [
|
||||
'', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
|
||||
'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'
|
||||
];
|
||||
const tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'];
|
||||
const thousands = ['', 'thousand', 'million', 'billion'];
|
||||
function helper(n) {
|
||||
if (n === 0)
|
||||
return '';
|
||||
else if (n < 20)
|
||||
return belowTwenty[n] + ' ';
|
||||
else if (n < 100)
|
||||
return tens[Math.floor(n / 10)] + ' ' + helper(n % 10);
|
||||
return belowTwenty[Math.floor(n / 100)] + ' hundred ' + helper(n % 100);
|
||||
}
|
||||
let word = '';
|
||||
let i = 0;
|
||||
while (num > 0) {
|
||||
if (num % 1000 !== 0) {
|
||||
word = helper(num % 1000) + thousands[i] + ' ' + word;
|
||||
}
|
||||
num = Math.floor(num / 1000);
|
||||
i++;
|
||||
}
|
||||
return word.trim();
|
||||
}
|
||||
export function toPascalCase(strings) {
|
||||
if (!Array.isArray(strings))
|
||||
return '';
|
||||
function capitalize(word) {
|
||||
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
||||
}
|
||||
return strings.map(capitalize).join('');
|
||||
}
|
||||
export function convertNumberToPascalCase(num) {
|
||||
return toPascalCase(numberToWords(num).split(' '));
|
||||
}
|
||||
export function formatBytes(bytes) {
|
||||
if (bytes === 0)
|
||||
return '0 Bytes';
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
const formattedSize = (bytes / Math.pow(1024, i)).toFixed(2);
|
||||
return `${formattedSize} ${sizes[i]}`;
|
||||
}
|
||||
export function createStyleSheet(id) {
|
||||
const style = document.createElement('style');
|
||||
style.setAttribute('id', id);
|
||||
style.setAttribute('rel', 'stylesheet');
|
||||
style.setAttribute('type', 'text/css');
|
||||
document.head.appendChild(style);
|
||||
return style;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
export function numberToWords(num: number): string {
|
||||
if (num === 0) return 'zero';
|
||||
|
||||
const belowTwenty = [
|
||||
'', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
|
||||
'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'
|
||||
];
|
||||
const tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'];
|
||||
const thousands = ['', 'thousand', 'million', 'billion'];
|
||||
|
||||
function helper(n: number): string {
|
||||
if (n === 0) return '';
|
||||
else if (n < 20) return belowTwenty[n] + ' ';
|
||||
else if (n < 100) return tens[Math.floor(n / 10)] + ' ' + helper(n % 10);
|
||||
return belowTwenty[Math.floor(n / 100)] + ' hundred ' + helper(n % 100);
|
||||
}
|
||||
|
||||
let word = '';
|
||||
let i = 0;
|
||||
|
||||
while (num > 0) {
|
||||
if (num % 1000 !== 0) {
|
||||
word = helper(num % 1000) + thousands[i] + ' ' + word;
|
||||
}
|
||||
num = Math.floor(num / 1000);
|
||||
i++;
|
||||
}
|
||||
|
||||
return word.trim();
|
||||
}
|
||||
|
||||
export function toPascalCase(strings: string[]): string {
|
||||
if (!Array.isArray(strings)) return '';
|
||||
|
||||
function capitalize(word: string): string {
|
||||
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
||||
}
|
||||
|
||||
return strings.map(capitalize).join('');
|
||||
}
|
||||
|
||||
|
||||
export function convertNumberToPascalCase(num: number): string {
|
||||
return toPascalCase(numberToWords(num).split(' '));
|
||||
}
|
||||
|
||||
export function formatBytes(bytes: number): string {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
const formattedSize = (bytes / Math.pow(1024, i)).toFixed(2);
|
||||
|
||||
return `${formattedSize} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
export function createStyleSheet(id: string): HTMLStyleElement {
|
||||
const style = document.createElement('style');
|
||||
style.setAttribute('id', id);
|
||||
style.setAttribute('rel', 'stylesheet');
|
||||
style.setAttribute('type', 'text/css');
|
||||
document.head.appendChild(style);
|
||||
return style;
|
||||
}
|
||||
Reference in New Issue
Block a user