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 2403be68..ddd3272b 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 5a6373a6..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) @@ -282,6 +279,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 64194cc7..4d06bf69 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 58d0e764..5ca51c9b 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.array(z.string()).optional(), 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 + } }