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
+ }
}