From c7b2bc85ca15a559c4458178e63084a889617d5c Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Sat, 3 Aug 2024 21:55:32 +0100 Subject: [PATCH 1/3] Add support for node/input/output tooltips --- src/components/graph/GraphCanvas.vue | 2 + src/components/graph/NodeTooltip.vue | 168 ++++++++++++++++++++++++++ src/extensions/core/widgetInputs.ts | 3 +- src/scripts/domWidget.ts | 7 ++ src/scripts/ui.ts | 9 +- src/scripts/widgets.ts | 4 + src/stores/nodeDefStore.ts | 14 ++- src/stores/settingStore.ts | 2 +- src/types/apiTypes.ts | 1 + src/types/litegraph-augmentation.d.ts | 24 ++++ 10 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 src/components/graph/NodeTooltip.vue diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index 685a8a39..e0a2cb2d 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -8,12 +8,14 @@ + + + diff --git a/src/extensions/core/widgetInputs.ts b/src/extensions/core/widgetInputs.ts index 7b786c82..9b2e6b7e 100644 --- a/src/extensions/core/widgetInputs.ts +++ b/src/extensions/core/widgetInputs.ts @@ -571,7 +571,8 @@ export function mergeIfValid( k !== 'forceInput' && k !== 'defaultInput' && k !== 'control_after_generate' && - k !== 'multiline' + k !== 'multiline' && + k !== 'tooltip' ) { let v1 = config1[1][k] let v2 = config2[1]?.[k] diff --git a/src/scripts/domWidget.ts b/src/scripts/domWidget.ts index 2deaa20c..dfdda45b 100644 --- a/src/scripts/domWidget.ts +++ b/src/scripts/domWidget.ts @@ -282,6 +282,13 @@ LGraphNode.prototype.addDOMWidget = function ( document.addEventListener('mousedown', mouseDownHandler) } + const { nodeData } = this.constructor + const tooltip = (nodeData?.input.required?.[name] ?? + nodeData?.input.optional?.[name])?.[1]?.tooltip + if (tooltip && !element.title) { + element.title = tooltip + } + const widget: DOMWidget = { type, name, diff --git a/src/scripts/ui.ts b/src/scripts/ui.ts index 4b047f6b..0f8fa456 100644 --- a/src/scripts/ui.ts +++ b/src/scripts/ui.ts @@ -428,6 +428,13 @@ export class ComfyUI { defaultValue: 'default' }) + this.settings.addSetting({ + id: 'Comfy.EnableTooltips', + name: 'Enable Tooltips', + type: 'boolean', + defaultValue: true + }) + const fileInput = $el('input', { id: 'comfy-file-input', type: 'file', @@ -437,7 +444,7 @@ export class ComfyUI { onchange: () => { app.handleFile(fileInput.files[0]) } - }) as HTMLInputElement + }) this.loadFile = () => fileInput.click() diff --git a/src/scripts/widgets.ts b/src/scripts/widgets.ts index dda65da3..78fcfc3b 100644 --- a/src/scripts/widgets.ts +++ b/src/scripts/widgets.ts @@ -113,6 +113,8 @@ export function addValueControlWidgets( serialize: false // Don't include this in prompt. } ) + valueControl.tooltip = + 'Allows the linked widget to be changed automatically, for example randomizing the noise seed.' valueControl[IS_CONTROL_WIDGET] = true updateControlWidgetLabel(valueControl) widgets.push(valueControl) @@ -133,6 +135,8 @@ export function addValueControlWidgets( } ) updateControlWidgetLabel(comboFilter) + comboFilter.tooltip = + "Allows for filtering the list of values when changing the value via the control generate mode. Allows for RegEx matches in the format /abc/ to only filter to values containing 'abc'." widgets.push(comboFilter) } diff --git a/src/stores/nodeDefStore.ts b/src/stores/nodeDefStore.ts index e588fd84..dda58ca5 100644 --- a/src/stores/nodeDefStore.ts +++ b/src/stores/nodeDefStore.ts @@ -8,7 +8,7 @@ import { TreeNode } from 'primevue/treenode' export class BaseInputSpec { name: string type: string - + tooltip?: string default?: T @Type(() => Boolean) @@ -131,6 +131,10 @@ export class ComfyInputsSpec { get all() { return [...Object.values(this.required), ...Object.values(this.optional)] } + + getInput(name: string): BaseInputSpec | undefined { + return this.required[name] ?? this.optional[name] + } } export class ComfyOutputSpec { @@ -140,7 +144,8 @@ export class ComfyOutputSpec { public name: string, public type: string, public is_list: boolean, - public comboOptions?: any[] + public comboOptions?: any[], + public tooltip?: string ) {} } @@ -166,7 +171,7 @@ export class ComfyNodeDefImpl { output: ComfyOutputsSpec private static transformOutputSpec(obj: any): ComfyOutputsSpec { - const { output, output_is_list, output_name } = obj + const { output, output_is_list, output_name, output_tooltips } = obj const result = output.map((type: string | any[], index: number) => { const typeString = Array.isArray(type) ? 'COMBO' : type @@ -175,7 +180,8 @@ export class ComfyNodeDefImpl { output_name[index], typeString, output_is_list[index], - Array.isArray(type) ? type : undefined + Array.isArray(type) ? type : undefined, + output_tooltips?.[index] ) }) return new ComfyOutputsSpec(result) diff --git a/src/stores/settingStore.ts b/src/stores/settingStore.ts index 6f1df0e0..3cc06d8d 100644 --- a/src/stores/settingStore.ts +++ b/src/stores/settingStore.ts @@ -32,7 +32,7 @@ export const useSettingStore = defineStore('setting', { app.ui.settings.setSettingValue(key, value) }, - get(key: string) { + get(key: string): T { return ( this.settingValues[key] ?? app.ui.settings.getSettingDefaultValue(key) ) diff --git a/src/types/apiTypes.ts b/src/types/apiTypes.ts index 58a6bf24..b9aaf8b1 100644 --- a/src/types/apiTypes.ts +++ b/src/types/apiTypes.ts @@ -272,6 +272,7 @@ const zComfyNodeDef = z.object({ output: zComfyOutputTypesSpec, output_is_list: z.array(z.boolean()), output_name: z.array(z.string()), + output_tooltips: z.optional(z.array(z.string())), name: z.string(), display_name: z.string(), description: z.string(), diff --git a/src/types/litegraph-augmentation.d.ts b/src/types/litegraph-augmentation.d.ts index a5befe4e..359f9487 100644 --- a/src/types/litegraph-augmentation.d.ts +++ b/src/types/litegraph-augmentation.d.ts @@ -21,6 +21,13 @@ declare module '@comfyorg/litegraph' { * Allows for additional cleanup when removing a widget when converting to input. */ onRemove?(): void + + /** + * DOM element used for the widget + */ + element?: HTMLElement + + tooltip?: string } interface INodeOutputSlot { @@ -43,4 +50,21 @@ declare module '@comfyorg/litegraph' { interface LGraphNode { widgets_values?: unknown[] } + + interface LGraphCanvas { + /** This is in the litegraph types but has incorrect return type */ + isOverNodeInput( + node: LGraphNode, + canvasX: number, + canvasY: number, + slotPos: Vector2 + ): number + + isOverNodeOutput( + node: LGraphNode, + canvasX: number, + canvasY: number, + slotPos: Vector2 + ): number + } } From 8f90e72a32d00da814dfef902deba2ff0975d673 Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:20:53 +0100 Subject: [PATCH 2/3] pr feedback --- src/components/graph/NodeTooltip.vue | 4 ++-- src/types/apiTypes.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/graph/NodeTooltip.vue b/src/components/graph/NodeTooltip.vue index f4f9613a..88f0e558 100644 --- a/src/components/graph/NodeTooltip.vue +++ b/src/components/graph/NodeTooltip.vue @@ -16,7 +16,7 @@ import { app as comfyApp } from '@/scripts/app' import { useNodeDefStore } from '@/stores/nodeDefStore' import { useSettingStore } from '@/stores/settingStore' -let idleTimeout: NodeJS.Timeout +let idleTimeout: number const nodeDefStore = useNodeDefStore() const settingStore = useSettingStore() const tooltipRef = ref() @@ -126,7 +126,7 @@ const onMouseMove = (e: MouseEvent) => { clearTimeout(idleTimeout) if ((e.target as Node).nodeName !== 'CANVAS') return - idleTimeout = setTimeout(onIdle, 500) + idleTimeout = window.setTimeout(onIdle, 500) } watch( diff --git a/src/types/apiTypes.ts b/src/types/apiTypes.ts index b9aaf8b1..a77b7b19 100644 --- a/src/types/apiTypes.ts +++ b/src/types/apiTypes.ts @@ -272,7 +272,7 @@ const zComfyNodeDef = z.object({ output: zComfyOutputTypesSpec, output_is_list: z.array(z.boolean()), output_name: z.array(z.string()), - output_tooltips: z.optional(z.array(z.string())), + output_tooltips: z.array(z.string()).optional(), name: z.string(), display_name: z.string(), description: z.string(), From 1561f09da826e31dd6b9ea293f92678e5ee21787 Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:26:33 +0100 Subject: [PATCH 3/3] Remove --- src/scripts/domWidget.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/scripts/domWidget.ts b/src/scripts/domWidget.ts index dd6aff77..146f214b 100644 --- a/src/scripts/domWidget.ts +++ b/src/scripts/domWidget.ts @@ -226,11 +226,8 @@ LGraphCanvas.prototype.computeVisibleNodes = function (): LGraphNode[] { if (elementWidgets.has(node)) { const hidden = visibleNodes.indexOf(node) === -1 for (const w of node.widgets) { - // @ts-expect-error if (w.element) { - // @ts-expect-error w.element.hidden = hidden - // @ts-expect-error w.element.style.display = hidden ? 'none' : undefined if (hidden) { w.options.onHide?.(w)