ts added
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
# Change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
max_line_length = 120
|
||||
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:@typescript-eslint/recommended-type-checked',
|
||||
'plugin:@typescript-eslint/stylistic-type-checked'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: [],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: {
|
||||
// experimentalObjectRestSpread: true,
|
||||
modules: true,
|
||||
legacyDecorators: true,
|
||||
jsx: true,
|
||||
},
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
rules: {
|
||||
'import/extensions': [
|
||||
'error', 'always', {
|
||||
pattern: {
|
||||
'ts': 'never',
|
||||
'js': 'never',
|
||||
},
|
||||
},
|
||||
],
|
||||
'import/no-unresolved': ['off'],
|
||||
'class-methods-use-this': ['off'],
|
||||
'radix': ['off'],
|
||||
'import/prefer-default-export': ['off'],
|
||||
'implicit-arrow-linebreak': ['off'],
|
||||
'no-trailing-spaces': [
|
||||
'error', {
|
||||
'skipBlankLines': true,
|
||||
'ignoreComments': true,
|
||||
},
|
||||
],
|
||||
'max-len': [
|
||||
'error', {
|
||||
'code': 120,
|
||||
},
|
||||
],
|
||||
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error', {
|
||||
'devDependencies': ['**/__tests__/*.ts', '**/__mocks__/*.ts'],
|
||||
},
|
||||
],
|
||||
|
||||
'one-var': ['off'],
|
||||
|
||||
// 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 },
|
||||
"ImportDeclaration": {"multiline": true, "minProperties": 8 },
|
||||
"ExportDeclaration": { "multiline": true, "minProperties": 1 }
|
||||
}],
|
||||
'@typescript-eslint/no-shadow': 'error',
|
||||
'@typescript-eslint/no-unsafe-argument': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': [
|
||||
'error',
|
||||
{
|
||||
'allowShortCircuit': true,
|
||||
'allowTernary': true,
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-use-before-define': ['off'],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{'argsIgnorePattern': '^_'},
|
||||
],
|
||||
'@typescript-eslint/explicit-module-boundary-types': [
|
||||
'error', {
|
||||
allowArgumentsExplicitlyTypedAsAny: true,
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{'ts-expect-error': 'allow-with-description'},
|
||||
],
|
||||
'@typescript-eslint/no-empty-function': [
|
||||
'error',
|
||||
{
|
||||
'allow': [
|
||||
'methods',
|
||||
'asyncMethods',
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
'lines-between-class-members': 'off',
|
||||
'@typescript-eslint/lines-between-class-members': 'off',
|
||||
|
||||
'curly': ['error', 'multi-line', 'consistent'],
|
||||
'no-unused-vars': 'off',
|
||||
'jest/no-test-prefixes': 'off',
|
||||
'jest/no-focused-tests': 'off',
|
||||
'comma-dangle': ['error', 'only-multiline'],
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
'quotes': ['error', 'single'],
|
||||
'semi': ['error', 'always'],
|
||||
'eqeqeq': ['error', 'always'],
|
||||
'complexity': [
|
||||
'error', {
|
||||
max: 6,
|
||||
},
|
||||
],
|
||||
'block-scoped-var': 'error',
|
||||
'no-else-return': [
|
||||
'error', {
|
||||
allowElseIf: true,
|
||||
},
|
||||
],
|
||||
'no-eval': 'error',
|
||||
'no-lone-blocks': 'error',
|
||||
'no-multi-spaces': 'error',
|
||||
'no-useless-return': 'error',
|
||||
'no-var': 'error',
|
||||
'no-console': [
|
||||
// 'error', {
|
||||
// allow: ['warn', 'error'],
|
||||
// },
|
||||
'off',
|
||||
],
|
||||
'no-throw-literal': 'error',
|
||||
'newline-per-chained-call': [
|
||||
'error', {
|
||||
ignoreChainWithDepth: 4,
|
||||
},
|
||||
],
|
||||
'no-extra-boolean-cast': [
|
||||
'error', {
|
||||
enforceForLogicalOperands: true,
|
||||
},
|
||||
],
|
||||
'no-fallthrough': 'error',
|
||||
'no-use-before-define': 'off',
|
||||
'no-case-declarations': 'off',
|
||||
},
|
||||
ignorePatterns: [
|
||||
'node_modules'
|
||||
],
|
||||
globals: {},
|
||||
}
|
||||
@@ -153,3 +153,4 @@ cython_debug/
|
||||
*.pyc
|
||||
|
||||
.idea
|
||||
/node_modules
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import shutil
|
||||
import glob
|
||||
import json
|
||||
|
||||
print('Running __dev__.py')
|
||||
# from config import RGTHREE_CONFIG
|
||||
#
|
||||
# THIS_DIR=os.path.dirname(os.path.abspath(__file__))
|
||||
# DIR_WEB = os.path.abspath(f'{THIS_DIR}/web/')
|
||||
# DIR_WEB_COMFYUI = os.path.abspath(f'{DIR_WEB}/comfyui/')
|
||||
#
|
||||
# js_files = glob.glob(os.path.join(THIS_DIR, 'web/**/*.js'))
|
||||
# for file in js_files:
|
||||
# os.remove(file)
|
||||
#
|
||||
# subprocess.run(["./node_modules/typescript/bin/tsc"])
|
||||
#
|
||||
# with open(os.path.join(DIR_WEB_COMFYUI, 'rgthree_config.js'), 'w', encoding = 'UTF-8') as file:
|
||||
# file.write('export const rgthreeConfig = ' + json.dumps(RGTHREE_CONFIG))
|
||||
+1
-1
@@ -94,4 +94,4 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
}
|
||||
|
||||
|
||||
WEB_DIRECTORY = "./web"
|
||||
WEB_DIRECTORY = "./web/js"
|
||||
|
||||
+8
-3
@@ -3,11 +3,16 @@ import cpuinfo
|
||||
import psutil
|
||||
import torch
|
||||
import pynvml
|
||||
from comfy.model_management import get_torch_device_name, get_torch_device
|
||||
import comfy.model_management
|
||||
|
||||
from ..core import logger
|
||||
|
||||
|
||||
class CStats:
|
||||
"""
|
||||
This is only class to get information from hardware.
|
||||
Specially for share it to other software.
|
||||
"""
|
||||
nvidia = False
|
||||
pynvmlLoaded = False
|
||||
cudaAvailable = False
|
||||
@@ -127,12 +132,12 @@ class CStats:
|
||||
if self.pynvmlLoaded and pynvml.nvmlDeviceGetCount() > 0:
|
||||
self.cudaDevicesFound = pynvml.nvmlDeviceGetCount()
|
||||
self.nvidia = True
|
||||
logger.info(f'NVIDIA Driver detected - {pynvml.nvmlSystemGetDriverVersion()} - {get_torch_device_name(get_torch_device())}')
|
||||
logger.info(f'NVIDIA Driver detected - {pynvml.nvmlSystemGetDriverVersion()} - {comfy.model_management.get_torch_device_name(comfy.model_management.get_torch_device())}')
|
||||
else:
|
||||
logger.warn('No NVIDIA GPU detected.')
|
||||
|
||||
try:
|
||||
self.torchDevice = get_torch_device_name(get_torch_device())
|
||||
self.torchDevice = comfy.model_management.get_torch_device_name(comfy.model_management.get_torch_device())
|
||||
except Exception as e:
|
||||
logger.error('Could not pick default device.' + str(e))
|
||||
|
||||
|
||||
Generated
+1148
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "crystools-typescript",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "tsc",
|
||||
"build": "tsc && vite build --emptyOutDir",
|
||||
"tsc": "tsc",
|
||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"lint-fix": "eslint src --fix",
|
||||
"validate": "npm test && npm run coverage && npm run lintcss && npm run tsc && npm run lint && npm run tsc",
|
||||
"fix": "npm run lint-fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.56.0",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
// "typeRoots": [
|
||||
// "./ts/typings",
|
||||
// ],
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"*": ["web/ts/typings/*"],
|
||||
},
|
||||
"outDir": "web/",
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"useUnknownInCatchVariables": true,
|
||||
"alwaysStrict": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
"exactOptionalPropertyTypes": false,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"allowUnusedLabels": true,
|
||||
"skipLibCheck": true,
|
||||
|
||||
"useDefineForClassFields": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": [
|
||||
"web/ts/*.ts", "web/ts/**/*.ts", "web/ts/typings/index.d.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts",
|
||||
"node_modules/**/*.ts"
|
||||
],
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js';
|
||||
|
||||
export const commonPrefix = '🪛';
|
||||
|
||||
export function displayContext(nodeType, app, index = 0, serialize_widgets = false, isVirtualNode = false) {
|
||||
function populate(text) {
|
||||
if (this.widgets) {
|
||||
const pos = this.widgets.findIndex((w) => w.name === 'text');
|
||||
if (pos !== -1) {
|
||||
for (let i = pos; i < this.widgets.length; i++) {
|
||||
this.widgets[i].onRemove?.();
|
||||
}
|
||||
this.widgets.length = pos;
|
||||
}
|
||||
}
|
||||
// if want to not save properties in the node (be careful with F5)
|
||||
// BUG on isVirtualNode, with "true", it ignores OUTPUT_NODE on py file!
|
||||
this.serialize_widgets = serialize_widgets;
|
||||
this.isVirtualNode = isVirtualNode;
|
||||
|
||||
const w = ComfyWidgets['STRING'](this, 'text', ['STRING', {multiline: true}], app).widget;
|
||||
w.inputEl.readOnly = true;
|
||||
w.inputEl.style.opacity = 0.6;
|
||||
if (Array.isArray(text)) {
|
||||
text = text[index];
|
||||
}
|
||||
w.value = text || '';
|
||||
w.serializeValue = async() => {}; // just for no serialized to itself!
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const sz = this.computeSize();
|
||||
if (sz[0] < this.size[0]) {
|
||||
sz[0] = this.size[0];
|
||||
}
|
||||
if (sz[1] < this.size[1]) {
|
||||
sz[1] = this.size[1];
|
||||
}
|
||||
this.onResize?.(sz);
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
});
|
||||
}
|
||||
|
||||
// When the node is executed we will be sent the input text, display this in the widget
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function(message) {
|
||||
onExecuted?.apply(this, arguments);
|
||||
populate.call(this, message.text);
|
||||
};
|
||||
|
||||
const onConfigure = nodeType.prototype.onConfigure;
|
||||
nodeType.prototype.onConfigure = function() {
|
||||
onConfigure?.apply(this, arguments);
|
||||
if (this.widgets_values?.length) {
|
||||
populate.call(this, this.widgets_values);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// propagate the output value to the dependents nodes, it does not work with some nodes ¯\_(ツ)_/¯
|
||||
const propagateOutputToDependentsNodes = function(output, value) {
|
||||
if (output.links && output.links.length) {
|
||||
for (const l of output.links) {
|
||||
const link_info = app.graph.links[l];
|
||||
const outNode = app.graph.getNodeById(link_info.target_id);
|
||||
const outIn = outNode && outNode.inputs && outNode.inputs[link_info.target_slot];
|
||||
if (outIn.widget) {
|
||||
const w = outNode.widgets.find((w) => w.name === outIn.widget.name);
|
||||
if (!w) {
|
||||
continue;
|
||||
}
|
||||
w.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
import { app } from '../../../scripts/app.js';
|
||||
import { api } from '../../../scripts/api.js';
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js';
|
||||
import { commonPrefix, displayContext } from './common.js';
|
||||
|
||||
// "Show any" Node
|
||||
app.registerExtension({
|
||||
name: 'Crystools.Debugger.ConsoleAny',
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === 'Show any [Crystools]') {
|
||||
// 3 is the index of the text field in the node
|
||||
displayContext(nodeType, app, 3);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Crystools.Debugger.Metadata',
|
||||
|
||||
registerCustomNodes() {
|
||||
class MetadataNode {
|
||||
constructor() {
|
||||
this.serialize_widgets = false;
|
||||
this.isVirtualNode = true;
|
||||
|
||||
const w = ComfyWidgets.STRING(this, '', ['', {default: '', multiline: true}], app).widget;
|
||||
w.inputEl.readOnly = true;
|
||||
ComfyWidgets.BOOLEAN(this, 'Active', ['', {default: true}], app);
|
||||
ComfyWidgets.BOOLEAN(this, 'Parsed', ['', {default: true}], app);
|
||||
ComfyWidgets.COMBO(this, 'What', [['Prompt', 'Workflow'], {default: 'Prompt'}]);
|
||||
|
||||
// It runs at finish on each prompt queue
|
||||
api.addEventListener('executed', this.fillMetadataWidget);
|
||||
}
|
||||
|
||||
fillMetadataWidget = () => {
|
||||
app.graphToPrompt()
|
||||
.then(workflow => {
|
||||
let result = 'inactive';
|
||||
// debugger
|
||||
const output = this.widgets[0];
|
||||
const active = this.widgets[1].value;
|
||||
const parsed = this.widgets[2].value;
|
||||
let what = this.widgets[3].value.toLowerCase();
|
||||
|
||||
if (active) {
|
||||
what = what === 'prompt' ? 'output' : what; // little fix for better understanding
|
||||
result = workflow[what];
|
||||
if (parsed) {
|
||||
result = JSON.stringify(result, null, 2);
|
||||
} else {
|
||||
result = JSON.stringify(result);
|
||||
}
|
||||
}
|
||||
|
||||
output.value = result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// I'm not sure for what they're using prototype and lots of black magic, don't change the order!
|
||||
LiteGraph.registerNodeType('Show Metadata [Crystools]', MetadataNode);
|
||||
MetadataNode.category = `crystools ${commonPrefix}/Debugger`;
|
||||
MetadataNode.shape = LiteGraph.BOX_SHAPE;
|
||||
MetadataNode.title = `Show Metadata ${commonPrefix}`;
|
||||
// MetadataNode.collapsable = false;
|
||||
// MetadataNode.color = "#FF2222";
|
||||
// MetadataNode.bgcolor = "#000000";
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { app } from '../../../scripts/app.js';
|
||||
import { displayContext } from './common.js';
|
||||
|
||||
let crystoolsExtensions = {
|
||||
'Get resolution [Crystools]': 'Crystools.Image.GetResolution',
|
||||
'Preview from image [Crystools]': 'Crystools.Image.PreviewFromImage',
|
||||
'Preview from metadata [Crystools]': 'Crystools.Image.PreviewFromMetadata',
|
||||
'Metadata comparator [Crystools]': 'Crystools.Metadata.MetadataComparator',
|
||||
'Stats system [Crystools]': 'Crystools.Utils.StatsSystem',
|
||||
'Show any to JSON [Crystools]': 'Crystools.Debugger.ConsoleAnyToJson',
|
||||
};
|
||||
|
||||
Object.keys(crystoolsExtensions).forEach(key => {
|
||||
app.registerExtension({
|
||||
name: crystoolsExtensions[key],
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === key) {
|
||||
displayContext(nodeType, app, 0);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,371 @@
|
||||
import { app } from '../../../scripts/app.js';
|
||||
import { api } from '../../../scripts/api.js';
|
||||
import { commonPrefix } from './common.js';
|
||||
|
||||
class CrystoolsMonitor {
|
||||
idExtensionName = 'Crystools.monitor';
|
||||
menuPrefix = commonPrefix;
|
||||
htmlIdCrystoolsRoot = 'crystools-root';
|
||||
htmlIdCrystoolsMonitorContainer = 'crystools-monitor-container';
|
||||
|
||||
idInputRate = 'Crystools.inputRate';
|
||||
defaultRate = .5;
|
||||
|
||||
/** MONITORS */
|
||||
// CPU Variables
|
||||
idSwitchCPU = 'Crystools.switchCPU';
|
||||
defaultSwitchCPU = true;
|
||||
htmlMonitorCPURef = null;
|
||||
htmlMonitorCPUSliderRef = null;
|
||||
htmlMonitorCPULabelRef = null;
|
||||
cssColorCPU = '#0AA015';
|
||||
|
||||
// RAM Variables
|
||||
idSwitchRAM = 'Crystools.switchRAM';
|
||||
defaultSwitchRAM = true;
|
||||
htmlMonitorRAMRef = null;
|
||||
htmlMonitorRAMSliderRef = null;
|
||||
htmlMonitorRAMLabelRef = null;
|
||||
cssColorRAM = '#07630D';
|
||||
|
||||
// GPU Variables
|
||||
idSwitchGPU = 'Crystools.switchGPU';
|
||||
defaultSwitchGPU = true;
|
||||
htmlMonitorGPURef = null;
|
||||
htmlMonitorGPUSliderRef = null;
|
||||
htmlMonitorGPULabelRef = null;
|
||||
cssColorGPU = '#0C86F4';
|
||||
|
||||
// VRAM Variables
|
||||
idSwitchVRAM = 'Crystools.switchVRAM';
|
||||
defaultSwitchVRAM = true;
|
||||
htmlMonitorVRAMRef = null;
|
||||
htmlMonitorVRAMSliderRef = null;
|
||||
htmlMonitorVRAMLabelRef = null;
|
||||
cssColorVRAM = '#176EC7';
|
||||
|
||||
// HDD Variables
|
||||
idSwitchHDD = 'Crystools.switchHDD';
|
||||
defaultSwitchHDD = false;
|
||||
htmlMonitorHDDRef = null;
|
||||
htmlMonitorHDDSliderRef = null;
|
||||
htmlMonitorHDDLabelRef = null;
|
||||
cssColorHDD = '#730F92';
|
||||
|
||||
constructor() {
|
||||
this.createSettings();
|
||||
}
|
||||
|
||||
createSettings = () => {
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idSwitchCPU,
|
||||
name: this.menuPrefix + 'Display CPU monitor [menu]',
|
||||
type: 'boolean',
|
||||
defaultValue: this.defaultSwitchCPU,
|
||||
onChange: async(value) => {
|
||||
this.updateWidget(this.htmlMonitorCPURef, value);
|
||||
await this.updateServer({switchCPU: value});
|
||||
},
|
||||
});
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idSwitchRAM,
|
||||
name: this.menuPrefix + 'Display RAM monitor [menu]',
|
||||
type: 'boolean',
|
||||
defaultValue: this.defaultSwitchRAM,
|
||||
onChange: async(value) => {
|
||||
this.updateWidget(this.htmlMonitorRAMRef, value);
|
||||
await this.updateServer({switchRAM: value});
|
||||
},
|
||||
});
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idSwitchGPU,
|
||||
name: this.menuPrefix + 'Display GPU monitor [menu]',
|
||||
type: 'boolean',
|
||||
defaultValue: this.defaultSwitchGPU,
|
||||
onChange: async(value) => {
|
||||
this.updateWidget(this.htmlMonitorGPURef, value);
|
||||
await this.updateServer({switchGPU: value});
|
||||
},
|
||||
});
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idSwitchVRAM,
|
||||
name: this.menuPrefix + 'Display Video RAM monitor [menu]',
|
||||
type: 'boolean',
|
||||
defaultValue: this.defaultSwitchVRAM,
|
||||
onChange: async(value) => {
|
||||
this.updateWidget(this.htmlMonitorVRAMRef, value);
|
||||
await this.updateServer({switchVRAM: value});
|
||||
},
|
||||
});
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idSwitchHDD,
|
||||
name: this.menuPrefix + 'Display Hard disk monitor [menu]',
|
||||
tooltip: 'Only the drive where the comfyUI is installed',
|
||||
type: 'boolean',
|
||||
defaultValue: this.defaultSwitchHDD,
|
||||
onChange: async(value) => {
|
||||
this.updateWidget(this.htmlMonitorHDDRef, value);
|
||||
await this.updateServer({switchHDD: value});
|
||||
},
|
||||
});
|
||||
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idInputRate,
|
||||
name: this.menuPrefix + 'Monitors refresh rate (in seconds) [menu]',
|
||||
tooltip: 'This is the time between each update of the monitors, 0 means no refresh',
|
||||
type: 'slider',
|
||||
attrs: {
|
||||
min: 0,
|
||||
max: 5,
|
||||
step: 0.25,
|
||||
},
|
||||
defaultValue: this.defaultRate,
|
||||
onChange: async(value) => {
|
||||
try {
|
||||
value = parseFloat(value);
|
||||
if (isNaN(value)) {
|
||||
throw new Error('invalid value');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.updateServer({rate: value});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value === 0) {
|
||||
this.updateDisplay({
|
||||
cpu_utilization: 0,
|
||||
device: 'cpu',
|
||||
|
||||
gpus: [{
|
||||
gpu_utilization: 0,
|
||||
vram_total: 0,
|
||||
vram_used: 0,
|
||||
vram_used_percent: 0,
|
||||
}],
|
||||
hdd_total: 0,
|
||||
hdd_used: 0,
|
||||
hdd_used_percent: 0,
|
||||
ram_total: 0,
|
||||
ram_used: 0,
|
||||
ram_used_percent: 0,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.htmlMonitorCPUSliderRef) { // validation because this run before setup
|
||||
value = value.toFixed(1);
|
||||
const animationConfig = `width ${value}s`;
|
||||
this.htmlMonitorCPUSliderRef.style.transition = animationConfig;
|
||||
this.htmlMonitorGPUSliderRef.style.transition = animationConfig;
|
||||
this.htmlMonitorHDDSliderRef.style.transition = animationConfig;
|
||||
this.htmlMonitorRAMSliderRef.style.transition = animationConfig;
|
||||
this.htmlMonitorVRAMSliderRef.style.transition = animationConfig;
|
||||
this.htmlMonitorCPUSliderRef.style.width = value;
|
||||
this.htmlMonitorGPUSliderRef.style.width = value;
|
||||
this.htmlMonitorHDDSliderRef.style.width = value;
|
||||
this.htmlMonitorRAMSliderRef.style.width = value;
|
||||
this.htmlMonitorVRAMSliderRef.style.width = value;
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
updateServer = async(data) => {
|
||||
const resp = await api.fetchApi('/crystools/monitor', {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(data),
|
||||
cache: 'no-store',
|
||||
});
|
||||
if (resp.status === 200) {
|
||||
return await resp.text();
|
||||
} else {
|
||||
throw new Error(resp.statusText);
|
||||
}
|
||||
};
|
||||
|
||||
updateAllWidget = () => {
|
||||
this.updateWidget(this.htmlMonitorCPURef, app.ui.settings.getSettingValue(this.idSwitchCPU, this.defaultSwitchCPU));
|
||||
this.updateWidget(this.htmlMonitorGPURef, app.ui.settings.getSettingValue(this.idSwitchGPU, this.defaultSwitchGPU));
|
||||
this.updateWidget(this.htmlMonitorHDDRef, app.ui.settings.getSettingValue(this.idSwitchHDD, this.defaultSwitchHDD));
|
||||
this.updateWidget(this.htmlMonitorRAMRef, app.ui.settings.getSettingValue(this.idSwitchRAM, this.defaultSwitchRAM));
|
||||
this.updateWidget(this.htmlMonitorVRAMRef, app.ui.settings.getSettingValue(this.idSwitchVRAM, this.defaultSwitchVRAM));
|
||||
};
|
||||
|
||||
updateWidget = (container, value) => {
|
||||
if (container) {
|
||||
container.style.display = value ? 'flex' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
updateDisplay = (data) => {
|
||||
// console.log('updateDisplay', data);
|
||||
if (!this.htmlMonitorCPULabelRef) {
|
||||
return;
|
||||
}
|
||||
this.htmlMonitorCPULabelRef.innerHTML = `${Math.floor(data.cpu_utilization)}%`;
|
||||
this.htmlMonitorCPUSliderRef.style.width = this.htmlMonitorCPULabelRef.innerHTML;
|
||||
|
||||
const gpu = data.gpus[0];
|
||||
if (gpu) {
|
||||
this.htmlMonitorGPULabelRef.innerHTML = `${Math.floor(gpu.gpu_utilization)}%`;
|
||||
this.htmlMonitorGPUSliderRef.style.width = this.htmlMonitorGPULabelRef.innerHTML;
|
||||
this.htmlMonitorVRAMLabelRef.innerHTML = `${Math.floor(gpu.vram_used_percent)}%`;
|
||||
this.htmlMonitorVRAMSliderRef.style.width = this.htmlMonitorVRAMLabelRef.innerHTML;
|
||||
} else {
|
||||
console.error('no gpu found');
|
||||
}
|
||||
|
||||
this.htmlMonitorHDDLabelRef.innerHTML = `${Math.floor(data.hdd_used_percent)}%`;
|
||||
this.htmlMonitorHDDSliderRef.style.width = this.htmlMonitorHDDLabelRef.innerHTML;
|
||||
|
||||
this.htmlMonitorRAMLabelRef.innerHTML = `${Math.floor(data.ram_used_percent)}%`;
|
||||
this.htmlMonitorRAMSliderRef.style.width = this.htmlMonitorRAMLabelRef.innerHTML;
|
||||
};
|
||||
|
||||
setup() {
|
||||
const parentElement = document.getElementById('queue-button');
|
||||
|
||||
let ctoolsRoot = document.getElementById(this.htmlIdCrystoolsRoot);
|
||||
if (!ctoolsRoot) {
|
||||
ctoolsRoot = document.createElement('div');
|
||||
ctoolsRoot.setAttribute('id', this.htmlIdCrystoolsRoot);
|
||||
ctoolsRoot.style.display = 'flex';
|
||||
ctoolsRoot.style.width = '100%';
|
||||
ctoolsRoot.style.flexDirection = 'column';
|
||||
parentElement.insertAdjacentElement('afterend', ctoolsRoot);
|
||||
}
|
||||
|
||||
const htmlContainer = document.createElement('div');
|
||||
htmlContainer.setAttribute('id', this.htmlIdCrystoolsMonitorContainer);
|
||||
htmlContainer.style.width = '100%';
|
||||
htmlContainer.style.cursor = 'crosshair';
|
||||
htmlContainer.style.order = '3';
|
||||
htmlContainer.style.margin = '4px 0';
|
||||
ctoolsRoot.append(htmlContainer);
|
||||
|
||||
htmlContainer.append(this.createMonitorCPU());
|
||||
htmlContainer.append(this.createMonitorRAM());
|
||||
htmlContainer.append(this.createMonitorGPU());
|
||||
htmlContainer.append(this.createMonitorVRAM());
|
||||
htmlContainer.append(this.createMonitorHDD());
|
||||
|
||||
this.updateAllWidget();
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
createMonitor = (id, label, color) => {
|
||||
const option = {
|
||||
rootRef: null,
|
||||
sliderRef: null,
|
||||
labelRef: null,
|
||||
};
|
||||
|
||||
const htmlMain = document.createElement('div');
|
||||
htmlMain.setAttribute('id', id);
|
||||
htmlMain.style.margin = '2px 10px';
|
||||
htmlMain.style.height = '12px';
|
||||
htmlMain.style.position = 'relative';
|
||||
htmlMain.style.display = 'flex';
|
||||
htmlMain.style.alignItems = 'center';
|
||||
htmlMain.style.flexDirection = 'row';
|
||||
option.rootRef = htmlMain;
|
||||
|
||||
const htmlMonitorText = document.createElement('div');
|
||||
htmlMonitorText.style.width = '35px';
|
||||
htmlMonitorText.style.fontSize = '10px';
|
||||
htmlMonitorText.innerHTML = label;
|
||||
htmlMain.append(htmlMonitorText);
|
||||
|
||||
const htmlMonitorContent = document.createElement('div');
|
||||
htmlMonitorContent.style.height = '100%';
|
||||
htmlMonitorContent.style.flexGrow = '1';
|
||||
htmlMonitorContent.style.position = 'relative';
|
||||
htmlMonitorContent.style.backgroundColor = 'var(--bg-color)';
|
||||
htmlMain.append(htmlMonitorContent);
|
||||
|
||||
const htmlMonitorSlider = document.createElement('div');
|
||||
htmlMonitorSlider.style.position = 'absolute';
|
||||
htmlMonitorSlider.style.height = '100%';
|
||||
htmlMonitorSlider.style.width = '0';
|
||||
htmlMonitorSlider.style.backgroundColor = color;
|
||||
htmlMonitorSlider.style.transition = 'width 0.5s';
|
||||
option.sliderRef = htmlMonitorSlider;
|
||||
htmlMonitorContent.append(htmlMonitorSlider);
|
||||
|
||||
const htmlMonitorLabel = document.createElement('div');
|
||||
htmlMonitorLabel.style.position = 'relative';
|
||||
htmlMonitorLabel.style.width = '100%';
|
||||
htmlMonitorLabel.style.color = 'var(--drag-text)';
|
||||
htmlMonitorLabel.style.fontSize = '10px';
|
||||
htmlMonitorLabel.innerHTML = '0%';
|
||||
option.labelRef = htmlMonitorLabel;
|
||||
htmlMonitorContent.append(htmlMonitorLabel);
|
||||
|
||||
return option;
|
||||
};
|
||||
|
||||
createMonitorCPU = () => {
|
||||
const options = this.createMonitor('crystools-monitor-cpu', 'CPU', this.cssColorCPU);
|
||||
|
||||
this.htmlMonitorCPURef = options.rootRef;
|
||||
this.htmlMonitorCPUSliderRef = options.sliderRef;
|
||||
this.htmlMonitorCPULabelRef = options.labelRef;
|
||||
return this.htmlMonitorCPURef;
|
||||
};
|
||||
|
||||
createMonitorRAM = () => {
|
||||
const options = this.createMonitor('crystools-monitor-ram', 'RAM', this.cssColorRAM);
|
||||
|
||||
this.htmlMonitorRAMRef = options.rootRef;
|
||||
this.htmlMonitorRAMSliderRef = options.sliderRef;
|
||||
this.htmlMonitorRAMLabelRef = options.labelRef;
|
||||
return this.htmlMonitorRAMRef;
|
||||
};
|
||||
|
||||
createMonitorGPU = () => {
|
||||
const options = this.createMonitor('crystools-monitor-gpu', 'GPU', this.cssColorGPU);
|
||||
|
||||
this.htmlMonitorGPURef = options.rootRef;
|
||||
this.htmlMonitorGPUSliderRef = options.sliderRef;
|
||||
this.htmlMonitorGPULabelRef = options.labelRef;
|
||||
return this.htmlMonitorGPURef;
|
||||
};
|
||||
|
||||
createMonitorVRAM = () => {
|
||||
const options = this.createMonitor('crystools-monitor-vram', 'VRAM', this.cssColorVRAM);
|
||||
|
||||
this.htmlMonitorVRAMRef = options.rootRef;
|
||||
this.htmlMonitorVRAMSliderRef = options.sliderRef;
|
||||
this.htmlMonitorVRAMLabelRef = options.labelRef;
|
||||
return this.htmlMonitorVRAMRef;
|
||||
};
|
||||
|
||||
createMonitorHDD = () => {
|
||||
const options = this.createMonitor('crystools-monitor-hdd', 'HDD', this.cssColorHDD);
|
||||
|
||||
this.htmlMonitorHDDRef = options.rootRef;
|
||||
this.htmlMonitorHDDSliderRef = options.sliderRef;
|
||||
this.htmlMonitorHDDLabelRef = options.labelRef;
|
||||
return this.htmlMonitorHDDRef;
|
||||
};
|
||||
|
||||
registerListeners = () => {
|
||||
api.addEventListener('crystools.monitor', (event) => {
|
||||
if (event?.detail === undefined) {
|
||||
return;
|
||||
}
|
||||
this.updateDisplay(event.detail);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const crystoolsMonitor = new CrystoolsMonitor();
|
||||
app.registerExtension({
|
||||
name: crystoolsMonitor.idExtensionName,
|
||||
setup: crystoolsMonitor.setup.bind(crystoolsMonitor),
|
||||
});
|
||||
@@ -0,0 +1,191 @@
|
||||
import { api } from '../../../scripts/api.js';
|
||||
import { app } from '../../../scripts/app.js';
|
||||
import { commonPrefix } from './common.js';
|
||||
|
||||
const status = {
|
||||
executing: 'Executing',
|
||||
executed: 'Executed',
|
||||
execution_error: 'Execution error',
|
||||
};
|
||||
|
||||
class CrystoolsProgressBar {
|
||||
idExtensionName = 'Crystools.progressBar';
|
||||
idShowProgressBar = 'Crystools.showStatus';
|
||||
defaultShowStatus = true;
|
||||
menuPrefix = commonPrefix;
|
||||
htmlIdCrystoolsRoot = 'crystools-root';
|
||||
htmlIdCrystoolsProgressBarContainer = 'crystools-progress-bar-container';
|
||||
|
||||
currentStatus = status.executed;
|
||||
currentProgress = 0;
|
||||
currentNode = null;
|
||||
timeStart = 0;
|
||||
|
||||
htmlProgressSliderRef = null;
|
||||
htmlProgressLabelRef = null;
|
||||
|
||||
constructor() {
|
||||
this.createSettings();
|
||||
}
|
||||
|
||||
// not on setup because this affect the order on settings, I prefer to options at first
|
||||
createSettings = () => {
|
||||
app.ui.settings.addSetting({
|
||||
id: this.idShowProgressBar,
|
||||
name: this.menuPrefix + 'Show progress bar [menu]',
|
||||
type: 'boolean',
|
||||
defaultValue: this.defaultShowStatus,
|
||||
onChange: this.showProgressBar,
|
||||
});
|
||||
};
|
||||
|
||||
showProgressBar = (value) => {
|
||||
const container = document.getElementById(this.htmlIdCrystoolsProgressBarContainer);
|
||||
|
||||
// validation because this run before setup
|
||||
if (container) {
|
||||
container.style.display = value ? 'block' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
updateDisplay = () => {
|
||||
if (this.currentStatus === status.executed) {
|
||||
// finished
|
||||
this.htmlProgressLabelRef.innerHTML = 'cached';
|
||||
|
||||
const timeElapsed = Date.now() - this.timeStart;
|
||||
if (this.timeStart > 0 && timeElapsed > 0) {
|
||||
this.htmlProgressLabelRef.innerHTML = new Date(timeElapsed).toISOString().substr(11, 8);
|
||||
}
|
||||
this.htmlProgressSliderRef.style.width = '0';
|
||||
|
||||
} else if (this.currentStatus === status.execution_error) {
|
||||
// an error occurred
|
||||
this.htmlProgressLabelRef.innerHTML = 'ERROR';
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'var(--error-text)';
|
||||
console.log('execution_error');
|
||||
|
||||
} else if (this.currentStatus === status.executing) {
|
||||
// on going
|
||||
this.htmlProgressLabelRef.innerHTML = `${this.currentProgress}%`;
|
||||
this.htmlProgressSliderRef.style.width = this.htmlProgressLabelRef.innerHTML;
|
||||
this.htmlProgressSliderRef.style.backgroundColor = 'green'; // by reset the color
|
||||
}
|
||||
};
|
||||
|
||||
setup() {
|
||||
const parentElement= document.getElementById('queue-button');
|
||||
|
||||
let ctoolsRoot = document.getElementById(this.htmlIdCrystoolsRoot);
|
||||
if(!ctoolsRoot){
|
||||
ctoolsRoot = document.createElement('div');
|
||||
ctoolsRoot.setAttribute('id', this.htmlIdCrystoolsRoot);
|
||||
ctoolsRoot.style.display = 'flex';
|
||||
ctoolsRoot.style.width = '100%';
|
||||
ctoolsRoot.style.flexDirection = 'column';
|
||||
parentElement.insertAdjacentElement('afterend', ctoolsRoot);
|
||||
}
|
||||
|
||||
const htmlContainer = document.createElement('div');
|
||||
htmlContainer.setAttribute('id', this.htmlIdCrystoolsProgressBarContainer);
|
||||
htmlContainer.setAttribute('title', 'click to see the current working node');
|
||||
htmlContainer.style.margin = '4px 0';
|
||||
htmlContainer.style.width = '100%';
|
||||
htmlContainer.style.cursor = 'pointer';
|
||||
htmlContainer.style.order = '1';
|
||||
htmlContainer.addEventListener('click', this.centerNode);
|
||||
ctoolsRoot.append(htmlContainer);
|
||||
|
||||
const progressBar = document.createElement('div');
|
||||
progressBar.style.margin = '0 10px';
|
||||
progressBar.style.height = '18px';
|
||||
progressBar.style.position = 'relative';
|
||||
progressBar.style.backgroundColor = 'var(--bg-color)';
|
||||
htmlContainer.append(progressBar);
|
||||
|
||||
const progressSlider = document.createElement('div');
|
||||
progressSlider.style.position = 'absolute';
|
||||
progressSlider.style.height = '100%';
|
||||
progressSlider.style.width = '0';
|
||||
progressSlider.style.transition = 'width 0.2s';
|
||||
progressSlider.style.backgroundColor = 'green';
|
||||
this.htmlProgressSliderRef = progressSlider;
|
||||
progressBar.append(this.htmlProgressSliderRef);
|
||||
|
||||
const progressLabel = document.createElement('div');
|
||||
progressLabel.style.position = 'absolute';
|
||||
progressLabel.style.margin = 'auto 0';
|
||||
progressLabel.style.width = '100%';
|
||||
progressLabel.style.color = 'var(--drag-text)';
|
||||
progressLabel.style.fontSize = '14px';
|
||||
progressLabel.innerHTML = '0%';
|
||||
this.htmlProgressLabelRef = progressLabel;
|
||||
progressBar.append(this.htmlProgressLabelRef);
|
||||
|
||||
this.showProgressBar(app.ui.settings.getSettingValue(this.idShowProgressBar, this.defaultShowStatus));
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
registerListeners = () => {
|
||||
api.addEventListener('status', ({detail}) => {
|
||||
this.currentStatus = this.currentStatus === status.execution_error ? status.execution_error : status.executed;
|
||||
let queueRemaining = detail && detail.exec_info.queue_remaining;
|
||||
|
||||
if (queueRemaining) {
|
||||
this.currentStatus = status.executing;
|
||||
}
|
||||
this.updateDisplay();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
api.addEventListener('executed', ({detail}) => {
|
||||
if (detail?.node) {
|
||||
this.currentNode = detail.node;
|
||||
}
|
||||
|
||||
this.updateDisplay();
|
||||
});
|
||||
|
||||
api.addEventListener('execution_start', ({detail}) => {
|
||||
this.currentStatus = status.executing;
|
||||
this.timeStart = Date.now();
|
||||
|
||||
this.updateDisplay();
|
||||
});
|
||||
|
||||
api.addEventListener('execution_error', ({detail}) => {
|
||||
this.currentStatus = status.execution_error;
|
||||
|
||||
this.updateDisplay();
|
||||
});
|
||||
};
|
||||
|
||||
centerNode = () => {
|
||||
const id = this.currentNode;
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
const node = app.graph.getNodeById(id);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
app.canvas.centerOnNode(node);
|
||||
};
|
||||
}
|
||||
|
||||
const crystoolsProgressBar = new CrystoolsProgressBar();
|
||||
app.registerExtension({
|
||||
name: crystoolsProgressBar.idExtensionName,
|
||||
setup: crystoolsProgressBar.setup.bind(crystoolsProgressBar),
|
||||
})
|
||||
Reference in New Issue
Block a user