diff --git a/src/lib/components/ComfyProperties.svelte b/src/lib/components/ComfyProperties.svelte index 1ca6009..4bdf50c 100644 --- a/src/lib/components/ComfyProperties.svelte +++ b/src/lib/components/ComfyProperties.svelte @@ -6,9 +6,9 @@ import uiState from "$lib/stores/uiState" import selectionState from "$lib/stores/selectionState" import { get, type Writable, writable } from "svelte/store" - import type { ComfyWidgetNode } from "$lib/nodes"; import ComfyNumberProperty from "./ComfyNumberProperty.svelte"; import ComfyComboProperty from "./ComfyComboProperty.svelte"; + import type { ComfyWidgetNode } from "$lib/nodes/widgets"; let target: IDragItem | null = null; let node: LGraphNode | null = null; @@ -146,9 +146,13 @@ if (spec.deserialize) value = spec.deserialize(value) + const prevValue = target.attrs[name] target.attrs[name] = value target.attrsChanged.set(get(target.attrsChanged) + 1) + if (spec.onChanged) + spec.onChanged(target, value, prevValue) + if (node && "propsChanged" in node) { const comfyNode = node as ComfyWidgetNode comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1) @@ -180,8 +184,12 @@ if (spec.deserialize) value = spec.deserialize(value) + const prevValue = node.properties[name] node.properties[name] = value; + if (spec.onChanged) + spec.onChanged(node, value, prevValue) + if ("propsChanged" in node) { const comfyNode = node as ComfyWidgetNode comfyNode.notifyPropsChanged(); @@ -211,8 +219,12 @@ if (spec.deserialize) value = spec.deserialize(value) + const prevValue = node[name] node[name] = value; + if (spec.onChanged) + spec.onChanged(node, value, prevValue) + if ("propsChanged" in node) { const comfyNode = node as ComfyWidgetNode comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1) @@ -240,9 +252,13 @@ const name = spec.name console.warn("[ComfyProperties] updateWorkflowAttribute", name, value) + const prevValue = value $layoutState.attrs[name] = value $layoutState = $layoutState + if (spec.onChanged) + spec.onChanged($layoutState, value, prevValue) + if (spec.refreshPanelOnChange) doRefreshPanel() } diff --git a/src/lib/stores/layoutState.ts b/src/lib/stores/layoutState.ts index cbff7ab..d86eecf 100644 --- a/src/lib/stores/layoutState.ts +++ b/src/lib/stores/layoutState.ts @@ -1,7 +1,7 @@ import { get, writable } from 'svelte/store'; import type { Writable } from 'svelte/store'; import type ComfyApp from "$lib/components/ComfyApp" -import { type LGraphNode, type IWidget, type LGraph, NodeMode, type LGraphRemoveNodeOptions, type LGraphAddNodeOptions, type UUID, type NodeID } from "@litegraph-ts/core" +import { type LGraphNode, type IWidget, type LGraph, NodeMode, type LGraphRemoveNodeOptions, type LGraphAddNodeOptions, type UUID, type NodeID, LiteGraph } from "@litegraph-ts/core" import { SHADOW_PLACEHOLDER_ITEM_ID } from 'svelte-dnd-action'; import type { ComfyWidgetNode } from '$lib/nodes'; import type { ComfyNodeID } from '$lib/api'; @@ -250,7 +250,12 @@ export type AttributesSpec = { * This should be used if there's a canShow dependent on this property so * the pane can be updated with the new list of valid properties. */ - refreshPanelOnChange?: boolean + refreshPanelOnChange?: boolean, + + /* + * Callback run when this value is changed. + */ + onChanged?: (arg: IDragItem | LGraphNode | LayoutState, value: any, prevValue: any) => void, } /* @@ -274,6 +279,24 @@ const deserializeStringArray = (arg: string) => { return arg.split(",").map(s => s.trim()) } +const setNodeTitle = (arg: IDragItem, value: any) => { + if (arg.type !== "widget") + return + + const widget = arg as WidgetLayout; + if (widget.node == null) + return; + + const reg = LiteGraph.registered_node_types[widget.node.type]; + if (reg == null) + return + + if (value) + widget.node.title = `${reg.title} (${value})` + else + widget.node.title = reg.title +} + /* * Attributes that will show up in the properties panel. * Their order in the list is the order they'll appear in the panel. @@ -288,6 +311,7 @@ const ALL_ATTRIBUTES: AttributesSpecList = [ location: "widget", defaultValue: "", editable: true, + onChanged: setNodeTitle }, { name: "hidden", @@ -570,6 +594,7 @@ for (const cat of Object.values(ALL_ATTRIBUTES)) { export { ALL_ATTRIBUTES }; +// TODO Should be nested by category for name uniqueness? const defaultWidgetAttributes: Attributes = {} as any const defaultWorkflowAttributes: LayoutAttributes = {} as any for (const cat of Object.values(ALL_ATTRIBUTES)) { @@ -694,6 +719,16 @@ function findDefaultContainerForInsertion(): ContainerLayout | null { return null } +function runOnChangedForWidgetDefaults(dragItem: IDragItem) { + for (const cat of Object.values(ALL_ATTRIBUTES)) { + for (const spec of Object.values(cat.specs)) { + if (defaultWidgetAttributes[spec.name] !== undefined && spec.onChanged != null) { + spec.onChanged(dragItem, dragItem.attrs[spec.name], dragItem.attrs[spec.name]) + } + } + } +} + function addContainer(parent: ContainerLayout | null, attrs: Partial = {}, index?: number): ContainerLayout { const state = get(store); const dragItem: ContainerLayout = { @@ -707,14 +742,17 @@ function addContainer(parent: ContainerLayout | null, attrs: Partial } } const entry: DragItemEntry = { dragItem, children: [], parent: null }; - if (state.allItemsByNode[dragItem.id] !== null) + + if (state.allItemsByNode[dragItem.id] != null) throw new Error(`Container with ID ${dragItem.id} already registered!!!`) state.allItems[dragItem.id] = entry; + if (parent) { moveItem(dragItem, parent, index) } console.debug("[layoutState] addContainer", state) store.set(state) + runOnChangedForWidgetDefaults(dragItem) return dragItem; } @@ -735,14 +773,18 @@ function addWidget(parent: ContainerLayout, node: ComfyWidgetNode, attrs: Partia } const parentEntry = state.allItems[parent.id] const entry: DragItemEntry = { dragItem, children: [], parent: null }; - if (state.allItemsByNode[dragItem.id] !== null) + + if (state.allItems[dragItem.id] != null) throw new Error(`Widget with ID ${dragItem.id} already registered!!!`) state.allItems[dragItem.id] = entry; - if (state.allItemsByNode[node.id] !== null) + + if (state.allItemsByNode[node.id] != null) throw new Error(`Widget's node with ID ${node.id} already registered!!!`) state.allItemsByNode[node.id] = entry; + console.debug("[layoutState] addWidget", state) moveItem(dragItem, parent, index) + runOnChangedForWidgetDefaults(dragItem) return dragItem; }