Auto add new widgets for backend nodes

This commit is contained in:
space-nuko
2023-05-04 11:42:21 -05:00
parent 93cf2ed98a
commit 71a3abf518
13 changed files with 150 additions and 101 deletions

92
src/lib/ComfyGraph.ts Normal file
View File

@@ -0,0 +1,92 @@
import { LConnectionKind, LGraph, LGraphNode, type INodeSlot, type SlotIndex, LiteGraph, getStaticProperty, type LGraphAddNodeOptions } from "@litegraph-ts/core";
import GraphSync from "./GraphSync";
import EventEmitter from "events";
import type TypedEmitter from "typed-emitter";
import layoutState from "./stores/layoutState";
import uiState from "./stores/uiState";
import { get } from "svelte/store";
import type ComfyGraphNode from "./nodes/ComfyGraphNode";
import type IComfyInputSlot from "./IComfyInputSlot";
type ComfyGraphEvents = {
configured: (graph: LGraph) => void
nodeAdded: (node: LGraphNode) => void
nodeRemoved: (node: LGraphNode) => void
nodeConnectionChanged: (kind: LConnectionKind, node: LGraphNode, slot: SlotIndex, targetNode: LGraphNode, targetSlot: SlotIndex) => void
cleared: () => void
beforeChange: (graph: LGraph, param: any) => void
afterChange: (graph: LGraph, param: any) => void
}
export default class ComfyGraph extends LGraph {
graphSync: GraphSync;
eventBus: TypedEmitter<ComfyGraphEvents> = new EventEmitter() as TypedEmitter<ComfyGraphEvents>;
constructor() {
super();
this.graphSync = new GraphSync(this)
}
override onConfigure() {
console.debug("Configured");
this.eventBus.emit("configured", this);
}
override onBeforeChange(graph: LGraph, info: any) {
console.debug("BeforeChange", info);
this.eventBus.emit("beforeChange", graph, info);
}
override onAfterChange(graph: LGraph, info: any) {
console.debug("AfterChange", info);
this.eventBus.emit("afterChange", graph, info);
}
override onNodeAdded(node: LGraphNode, options: LGraphAddNodeOptions) {
layoutState.nodeAdded(node)
this.graphSync.onNodeAdded(node);
if ("comfyClass" in node // Is this a comfy node
&& !("svelteComponentType" in node) // ...and not also a ComfyWidgetNode
&& !options.addedByDeserialize // ...and we're not trying to deserialize an existing workflow
&& get(uiState).autoAddUI) {
console.debug("[ComfyGraph] AutoAdd UI")
const comfyNode = node as ComfyGraphNode;
const widgetNodesAdded = []
for (let index = 0; index < comfyNode.inputs.length; index++) {
const input = comfyNode.inputs[index];
if ("config" in input) {
const comfyInput = input as IComfyInputSlot;
if (comfyInput.defaultWidgetNode) {
const widgetNode = LiteGraph.createNode(comfyInput.defaultWidgetNode)
const inputPos = comfyNode.getConnectionPos(true, index);
this.add(widgetNode)
widgetNode.connect(0, comfyNode, index);
widgetNode.collapse();
widgetNode.pos = [inputPos[0] - 140, inputPos[1] + LiteGraph.NODE_SLOT_HEIGHT / 2];
widgetNodesAdded.push(widgetNode)
}
}
}
const dragItems = widgetNodesAdded.map(wn => get(layoutState).allItemsByNode[wn.id]?.dragItem).filter(di => di)
console.debug("[ComfyGraph] Group new widgets", dragItems)
layoutState.groupItems(dragItems, comfyNode.comfyClass)
}
console.debug("Added", node);
this.eventBus.emit("nodeAdded", node);
}
override onNodeRemoved(node: LGraphNode) {
layoutState.nodeRemoved(node);
this.graphSync.onNodeRemoved(node);
console.debug("Removed", node);
this.eventBus.emit("nodeRemoved", node);
}
override onNodeConnectionChange(kind: LConnectionKind, node: LGraphNode, slot: SlotIndex, targetNode: LGraphNode, targetSlot: SlotIndex) {
console.debug("ConnectionChange", node);
this.eventBus.emit("nodeConnectionChanged", kind, node, slot, targetNode, targetSlot);
}
}

View File

@@ -2,6 +2,7 @@ import type { LGraph, LGraphNode } from "@litegraph-ts/core";
import type ComfyApp from "./components/ComfyApp"; import type ComfyApp from "./components/ComfyApp";
import type { Unsubscriber, Writable } from "svelte/store"; import type { Unsubscriber, Writable } from "svelte/store";
import type { ComfyWidgetNode } from "./nodes"; import type { ComfyWidgetNode } from "./nodes";
import type ComfyGraph from "./ComfyGraph";
type WidgetSubStore = { type WidgetSubStore = {
store: WidgetUIStateStore, store: WidgetUIStateStore,
@@ -28,13 +29,11 @@ export default class GraphSync {
// nodeId -> widgetSubStore // nodeId -> widgetSubStore
private stores: Record<string, WidgetSubStore> = {} private stores: Record<string, WidgetSubStore> = {}
constructor(app: ComfyApp) { constructor(graph: ComfyGraph) {
this.graph = app.lGraph; this.graph = graph;
app.eventBus.on("nodeAdded", this.onNodeAdded.bind(this));
app.eventBus.on("nodeRemoved", this.onNodeRemoved.bind(this));
} }
private onNodeAdded(node: LGraphNode) { onNodeAdded(node: LGraphNode) {
// TODO assumes only a single graph's widget state. // TODO assumes only a single graph's widget state.
if ("svelteComponentType" in node) { if ("svelteComponentType" in node) {
@@ -44,7 +43,7 @@ export default class GraphSync {
this.graph.setDirtyCanvas(true, true); this.graph.setDirtyCanvas(true, true);
} }
private onNodeRemoved(node: LGraphNode) { onNodeRemoved(node: LGraphNode) {
if ("svelteComponentType" in node) { if ("svelteComponentType" in node) {
this.removeStore(node as ComfyWidgetNode); this.removeStore(node as ComfyWidgetNode);
} }

View File

@@ -1,4 +1,5 @@
import type { INodeInputSlot } from "@litegraph-ts/core"; import type { INodeInputSlot } from "@litegraph-ts/core";
import type { ComfyWidgetNode } from "./nodes";
// TODO generalize // TODO generalize
export type ComfyInputConfig = { export type ComfyInputConfig = {
@@ -12,6 +13,7 @@ export type ComfyInputConfig = {
} }
export default interface IComfyInputSlot extends INodeInputSlot { export default interface IComfyInputSlot extends INodeInputSlot {
serialize: boolean serialize: boolean,
config: ComfyInputConfig // stores range min/max/step, etc. defaultWidgetNode?: new (name?: string) => ComfyWidgetNode,
config: ComfyInputConfig, // stores range min/max/step, etc.
} }

View File

@@ -231,7 +231,7 @@ export default class ComfyAPI extends EventTarget {
}; };
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return { Running: [], Pending: [] }; return { Running: [], Pending: [], error };
} }
} }
@@ -245,7 +245,7 @@ export default class ComfyAPI extends EventTarget {
return { History: Object.values(await res.json()) }; return { History: Object.values(await res.json()) };
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return { History: [] }; return { History: [], error };
} }
} }

View File

@@ -107,9 +107,6 @@
}) })
} }
app.eventBus.on("nodeAdded", layoutState.nodeAdded);
app.eventBus.on("nodeRemoved", layoutState.nodeRemoved);
app.api.addEventListener("status", (ev: CustomEvent) => { app.api.addEventListener("status", (ev: CustomEvent) => {
queueState.statusUpdated(ev.detail as ComfyAPIStatus); queueState.statusUpdated(ev.detail as ComfyAPIStatus);
}); });
@@ -176,6 +173,7 @@
</Button> </Button>
<Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/> <Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/>
<Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/> <Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/>
<Checkbox label="Auto-Add UI" bind:value={$uiState.autoAddUI}/>
<label for="enable-ui-editing">Enable UI Editing</label> <label for="enable-ui-editing">Enable UI Editing</label>
<select id="enable-ui-editing" name="enable-ui-editing" bind:value={$uiState.uiEditMode}> <select id="enable-ui-editing" name="enable-ui-editing" bind:value={$uiState.uiEditMode}>
<option value="disabled">Disabled</option> <option value="disabled">Disabled</option>

View File

@@ -13,13 +13,13 @@ import * as nodes from "$lib/nodes/index"
import ComfyGraphCanvas, { type SerializedGraphCanvasState } from "$lib/ComfyGraphCanvas"; import ComfyGraphCanvas, { type SerializedGraphCanvasState } from "$lib/ComfyGraphCanvas";
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode"; import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
import * as widgets from "$lib/widgets/index" import * as widgets from "$lib/widgets/index"
import type ComfyWidget from "$lib/widgets/ComfyWidget";
import queueState from "$lib/stores/queueState"; import queueState from "$lib/stores/queueState";
import GraphSync from "$lib/GraphSync";
import type { SvelteComponentDev } from "svelte/internal"; import type { SvelteComponentDev } from "svelte/internal";
import type IComfyInputSlot from "$lib/IComfyInputSlot"; import type IComfyInputSlot from "$lib/IComfyInputSlot";
import type { SerializedLayoutState } from "$lib/stores/layoutState"; import type { SerializedLayoutState } from "$lib/stores/layoutState";
import layoutState from "$lib/stores/layoutState"; import layoutState from "$lib/stores/layoutState";
import { toast } from '@zerodevx/svelte-toast'
import ComfyGraph from "$lib/ComfyGraph";
export const COMFYBOX_SERIAL_VERSION = 1; export const COMFYBOX_SERIAL_VERSION = 1;
@@ -40,17 +40,6 @@ export type SerializedAppState = {
canvas: SerializedGraphCanvasState canvas: SerializedGraphCanvasState
} }
type ComfyAppEvents = {
configured: (graph: LGraph) => void
nodeAdded: (node: LGraphNode) => void
nodeRemoved: (node: LGraphNode) => void
nodeConnectionChanged: (kind: LConnectionKind, node: LGraphNode, slot: INodeSlot, targetNode: LGraphNode, targetSlot: INodeSlot) => void
cleared: () => void
beforeChange: (graph: LGraph, param: any) => void
afterChange: (graph: LGraph, param: any) => void
restored: (workflow: SerializedAppState) => void
}
export type Progress = { export type Progress = {
value: number, value: number,
max: number max: number
@@ -61,12 +50,10 @@ export default class ComfyApp {
rootEl: HTMLDivElement | null = null; rootEl: HTMLDivElement | null = null;
canvasEl: HTMLCanvasElement | null = null; canvasEl: HTMLCanvasElement | null = null;
canvasCtx: CanvasRenderingContext2D | null = null; canvasCtx: CanvasRenderingContext2D | null = null;
lGraph: LGraph | null = null; lGraph: ComfyGraph | null = null;
lCanvas: ComfyGraphCanvas | null = null; lCanvas: ComfyGraphCanvas | null = null;
dropZone: HTMLElement | null = null; dropZone: HTMLElement | null = null;
nodeOutputs: Record<string, any> = {}; nodeOutputs: Record<string, any> = {};
eventBus: TypedEmitter<ComfyAppEvents> = new EventEmitter() as TypedEmitter<ComfyAppEvents>;
graphSync: GraphSync;
dragOverNode: LGraphNode | null = null; dragOverNode: LGraphNode | null = null;
shiftDown: boolean = false; shiftDown: boolean = false;
@@ -82,12 +69,9 @@ export default class ComfyApp {
async setup(): Promise<void> { async setup(): Promise<void> {
this.rootEl = document.getElementById("main") as HTMLDivElement; this.rootEl = document.getElementById("main") as HTMLDivElement;
this.canvasEl = document.getElementById("graph-canvas") as HTMLCanvasElement; this.canvasEl = document.getElementById("graph-canvas") as HTMLCanvasElement;
this.lGraph = new LGraph(); this.lGraph = new ComfyGraph();
this.lCanvas = new ComfyGraphCanvas(this, this.canvasEl); this.lCanvas = new ComfyGraphCanvas(this, this.canvasEl);
this.canvasCtx = this.canvasEl.getContext("2d"); this.canvasCtx = this.canvasEl.getContext("2d");
this.graphSync = new GraphSync(this);
this.addGraphLifecycleHooks();
LiteGraph.release_link_on_empty_shows_menu = true; LiteGraph.release_link_on_empty_shows_menu = true;
LiteGraph.alt_drag_do_clone_nodes = true; LiteGraph.alt_drag_do_clone_nodes = true;
@@ -118,7 +102,7 @@ export default class ComfyApp {
} }
// Save current workflow automatically // Save current workflow automatically
setInterval(this.saveStateToLocalStorage.bind(this), 1000); // setInterval(this.saveStateToLocalStorage.bind(this), 1000);
this.addApiUpdateHandlers(); this.addApiUpdateHandlers();
this.addDropHandler(); this.addDropHandler();
@@ -147,58 +131,12 @@ export default class ComfyApp {
this.lCanvas.draw(true, true); this.lCanvas.draw(true, true);
} }
private graphOnConfigure() {
console.debug("Configured");
this.eventBus.emit("configured", this.lGraph);
}
private graphOnBeforeChange(graph: LGraph, info: any) {
console.debug("BeforeChange", info);
this.eventBus.emit("beforeChange", graph, info);
}
private graphOnAfterChange(graph: LGraph, info: any) {
console.debug("AfterChange", info);
this.eventBus.emit("afterChange", graph, info);
}
private graphOnNodeAdded(node: LGraphNode) {
console.debug("Added", node);
this.eventBus.emit("nodeAdded", node);
}
private graphOnNodeRemoved(node: LGraphNode) {
console.debug("Removed", node);
this.eventBus.emit("nodeRemoved", node);
}
private graphOnNodeConnectionChange(kind: LConnectionKind, node: LGraphNode, slot: INodeSlot, targetNode: LGraphNode, targetSlot: INodeSlot) {
console.debug("ConnectionChange", node);
this.eventBus.emit("nodeConnectionChanged", kind, node, slot, targetNode, targetSlot);
}
private canvasOnClear() {
console.debug("CanvasClear");
this.eventBus.emit("cleared");
}
saveStateToLocalStorage() { saveStateToLocalStorage() {
const savedWorkflow = this.serialize(); const savedWorkflow = this.serialize();
const json = JSON.stringify(savedWorkflow); const json = JSON.stringify(savedWorkflow);
localStorage.setItem("workflow", json) localStorage.setItem("workflow", json)
} }
private addGraphLifecycleHooks() {
this.lGraph.onConfigure = this.graphOnConfigure.bind(this);
this.lGraph.onBeforeChange = this.graphOnBeforeChange.bind(this);
this.lGraph.onAfterChange = this.graphOnAfterChange.bind(this);
this.lGraph.onNodeAdded = this.graphOnNodeAdded.bind(this);
this.lGraph.onNodeRemoved = this.graphOnNodeRemoved.bind(this);
this.lGraph.onNodeConnectionChange = this.graphOnNodeConnectionChange.bind(this);
this.lCanvas.onClear = this.canvasOnClear.bind(this);
}
static node_type_overrides: Record<string, typeof ComfyGraphNode> = {} static node_type_overrides: Record<string, typeof ComfyGraphNode> = {}
static widget_type_overrides: Record<string, typeof SvelteComponentDev> = {} static widget_type_overrides: Record<string, typeof SvelteComponentDev> = {}
@@ -642,7 +580,13 @@ export default class ComfyApp {
await this.api.queuePrompt(num, p); await this.api.queuePrompt(num, p);
} catch (error) { } catch (error) {
// this.ui.dialog.show(error.response || error.toString()); // this.ui.dialog.show(error.response || error.toString());
console.error(error.response || error.toString()) const mes = error.response || error.toString()
toast.push(`Error queuing prompt:\n${mes}`, {
theme: {
'--toastBackground': 'var(--color-red-500)',
}
})
console.error("Error queuing prompt", mes, num, p)
break; break;
} }

View File

@@ -32,7 +32,7 @@
&& !$layoutState.isMenuOpen && !$layoutState.isMenuOpen
$: if ($queueState && widget) { $: if ($queueState && widget && widget.node) {
dragItem.isNodeExecuting = $queueState.runningNodeId === widget.node.id; dragItem.isNodeExecuting = $queueState.runningNodeId === widget.node.id;
} }
</script> </script>
@@ -40,7 +40,7 @@
{#if container} {#if container}
<BlockContainer {container} {classes} {zIndex} {showHandles} /> <BlockContainer {container} {classes} {zIndex} {showHandles} />
{:else if widget} {:else if widget && widget.node}
<div class="widget" class:widget-edit-outline={$uiState.uiEditMode === "widgets" && zIndex > 1} <div class="widget" class:widget-edit-outline={$uiState.uiEditMode === "widgets" && zIndex > 1}
class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(widget.id)} class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(widget.id)}
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.id} class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.id}

View File

@@ -29,8 +29,6 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
/** Svelte class for the frontend logic */ /** Svelte class for the frontend logic */
abstract svelteComponentType: typeof SvelteComponentDev abstract svelteComponentType: typeof SvelteComponentDev
/** Compatible litegraph widget types that can be connected to this node */
abstract inputWidgetTypes: string[]
/** If false, user manually set min/max/step, and should not be autoinherited from connected input */ /** If false, user manually set min/max/step, and should not be autoinherited from connected input */
autoConfig: boolean = true; autoConfig: boolean = true;
@@ -159,7 +157,6 @@ export class ComfySliderNode extends ComfyWidgetNode<number> {
} }
override svelteComponentType = RangeWidget override svelteComponentType = RangeWidget
override inputWidgetTypes = ["number", "slider"]
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
outputs: [ outputs: [
@@ -203,7 +200,6 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
} }
override svelteComponentType = ComboWidget override svelteComponentType = ComboWidget
override inputWidgetTypes = ["combo", "enum"]
constructor(name?: string) { constructor(name?: string) {
super(name, "A") super(name, "A")
@@ -269,7 +265,6 @@ export class ComfyTextNode extends ComfyWidgetNode<string> {
} }
override svelteComponentType = TextWidget override svelteComponentType = TextWidget
override inputWidgetTypes = ["text"]
constructor(name?: string) { constructor(name?: string) {
super(name, "") super(name, "")

View File

@@ -14,6 +14,7 @@ type DragItemEntry = {
export type LayoutState = { export type LayoutState = {
root: IDragItem | null, root: IDragItem | null,
allItems: Record<DragItemID, DragItemEntry>, allItems: Record<DragItemID, DragItemEntry>,
allItemsByNode: Record<number, DragItemEntry>,
currentId: number, currentId: number,
currentSelection: DragItemID[], currentSelection: DragItemID[],
isConfiguring: boolean, isConfiguring: boolean,
@@ -94,7 +95,7 @@ type LayoutStateOps = {
updateChildren: (parent: IDragItem, children: IDragItem[]) => IDragItem[], updateChildren: (parent: IDragItem, children: IDragItem[]) => IDragItem[],
nodeAdded: (node: LGraphNode) => void, nodeAdded: (node: LGraphNode) => void,
nodeRemoved: (node: LGraphNode) => void, nodeRemoved: (node: LGraphNode) => void,
groupItems: (dragItems: IDragItem[]) => ContainerLayout, groupItems: (dragItems: IDragItem[], title: string) => ContainerLayout,
ungroup: (container: ContainerLayout) => void, ungroup: (container: ContainerLayout) => void,
getCurrentSelection: () => IDragItem[], getCurrentSelection: () => IDragItem[],
findLayoutForNode: (nodeId: number) => IDragItem | null; findLayoutForNode: (nodeId: number) => IDragItem | null;
@@ -108,6 +109,7 @@ export type WritableLayoutStateStore = Writable<LayoutState> & LayoutStateOps;
const store: Writable<LayoutState> = writable({ const store: Writable<LayoutState> = writable({
root: null, root: null,
allItems: {}, allItems: {},
allItemsByNode: {},
currentId: 0, currentId: 0,
currentSelection: [], currentSelection: [],
isMenuOpen: false, isMenuOpen: false,
@@ -175,6 +177,7 @@ function addWidget(parent: ContainerLayout, node: ComfyWidgetNode, attrs: Partia
const parentEntry = state.allItems[parent.id] const parentEntry = state.allItems[parent.id]
const entry: DragItemEntry = { dragItem, children: [], parent: null }; const entry: DragItemEntry = { dragItem, children: [], parent: null };
state.allItems[dragItem.id] = entry; state.allItems[dragItem.id] = entry;
state.allItemsByNode[node.id] = entry;
console.debug("[layoutState] addWidget", state) console.debug("[layoutState] addWidget", state)
moveItem(dragItem, parent) moveItem(dragItem, parent)
return dragItem; return dragItem;
@@ -230,6 +233,10 @@ function removeEntry(state: LayoutState, id: DragItemID) {
const parentEntry = state.allItems[parent.id]; const parentEntry = state.allItems[parent.id];
parentEntry.children = parentEntry.children.filter(item => item.id !== id) parentEntry.children = parentEntry.children.filter(item => item.id !== id)
} }
if (entry.dragItem.type === "widget") {
const widget = entry.dragItem as WidgetLayout;
delete state.allItemsByNode[widget.node.id]
}
delete state.allItems[id] delete state.allItems[id]
} }
@@ -286,7 +293,7 @@ function getCurrentSelection(): IDragItem[] {
return state.currentSelection.map(id => state.allItems[id].dragItem) return state.currentSelection.map(id => state.allItems[id].dragItem)
} }
function groupItems(dragItems: IDragItem[]): ContainerLayout { function groupItems(dragItems: IDragItem[], title: string = "Group"): ContainerLayout {
if (dragItems.length === 0) if (dragItems.length === 0)
return; return;
@@ -303,7 +310,7 @@ function groupItems(dragItems: IDragItem[]): ContainerLayout {
index = indexFound index = indexFound
} }
const container = addContainer(parent as ContainerLayout, { title: "Group" }, index) const container = addContainer(parent as ContainerLayout, { title }, index)
for (const item of dragItems) { for (const item of dragItems) {
moveItem(item, container) moveItem(item, container)
@@ -420,22 +427,29 @@ function serialize(): SerializedLayoutState {
function deserialize(data: SerializedLayoutState, graph: LGraph) { function deserialize(data: SerializedLayoutState, graph: LGraph) {
const allItems: Record<DragItemID, DragItemEntry> = {} const allItems: Record<DragItemID, DragItemEntry> = {}
const allItemsByNode: Record<number, DragItemEntry> = {}
for (const pair of Object.entries(data.allItems)) { for (const pair of Object.entries(data.allItems)) {
const [id, entry] = pair; const [id, entry] = pair;
const dragItem: IDragItem = { const dragItem: IDragItem = {
type: entry.dragItem.type, type: entry.dragItem.type,
id: entry.dragItem.id, id: entry.dragItem.id,
attrs: entry.dragItem.attrs attrs: entry.dragItem.attrs
}; };
if (dragItem.type === "widget") {
const widget = dragItem as WidgetLayout; const dragEntry: DragItemEntry = {
widget.node = graph.getNodeById(entry.dragItem.nodeId) as ComfyWidgetNode
}
allItems[id] = {
dragItem, dragItem,
children: [], children: [],
parent: null parent: null
} }
allItems[id] = dragEntry
if (dragItem.type === "widget") {
const widget = dragItem as WidgetLayout;
widget.node = graph.getNodeById(entry.dragItem.nodeId) as ComfyWidgetNode
allItemsByNode[entry.dragItem.nodeId] = dragEntry
}
} }
// reconnect parent/child tree // reconnect parent/child tree
@@ -457,6 +471,7 @@ function deserialize(data: SerializedLayoutState, graph: LGraph) {
const state: LayoutState = { const state: LayoutState = {
root, root,
allItems, allItems,
allItemsByNode,
currentId: data.currentId, currentId: data.currentId,
currentSelection: [], currentSelection: [],
isMenuOpen: false, isMenuOpen: false,

View File

@@ -8,6 +8,7 @@ export type UIState = {
app: ComfyApp, app: ComfyApp,
nodesLocked: boolean, nodesLocked: boolean,
graphLocked: boolean, graphLocked: boolean,
autoAddUI: boolean,
uiEditMode: UIEditMode uiEditMode: UIEditMode
} }
@@ -17,6 +18,7 @@ const store: WritableUIStateStore = writable(
app: null, app: null,
graphLocked: false, graphLocked: false,
nodesLocked: false, nodesLocked: false,
autoAddUI: true,
uiEditMode: "disabled", uiEditMode: "disabled",
}) })

View File

@@ -4,6 +4,7 @@ import ComfyValueControlWidget from "./widgets/ComfyValueControlWidget";
import type { ComfyInputConfig } from "./IComfyInputSlot"; import type { ComfyInputConfig } from "./IComfyInputSlot";
import type IComfyInputSlot from "./IComfyInputSlot"; import type IComfyInputSlot from "./IComfyInputSlot";
import { BuiltInSlotShape } from "@litegraph-ts/core"; import { BuiltInSlotShape } from "@litegraph-ts/core";
import { ComfyComboNode, ComfySliderNode, ComfyTextNode } from "./nodes";
type WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp) => IComfyInputSlot; type WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp) => IComfyInputSlot;
@@ -29,19 +30,19 @@ function addComfyInput(node: LGraphNode, inputName: string, extraInfo: Partial<I
const FLOAT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => { const FLOAT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => {
const config = getNumberDefaults(inputData, 0.5); const config = getNumberDefaults(inputData, 0.5);
return addComfyInput(node, inputName, { type: "number", config }) return addComfyInput(node, inputName, { type: "number", config, defaultWidgetNode: ComfySliderNode })
} }
const INT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => { const INT: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => {
const config = getNumberDefaults(inputData, 1); const config = getNumberDefaults(inputData, 1);
return addComfyInput(node, inputName, { type: "number", config }) return addComfyInput(node, inputName, { type: "number", config, defaultWidgetNode: ComfySliderNode })
}; };
const STRING: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp): IComfyInputSlot => { const STRING: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app: ComfyApp): IComfyInputSlot => {
const defaultValue = inputData[1].default || ""; const defaultValue = inputData[1].default || "";
const multiline = !!inputData[1].multiline; const multiline = !!inputData[1].multiline;
return addComfyInput(node, inputName, { type: "string", config: { defaultValue, multiline } }) return addComfyInput(node, inputName, { type: "string", config: { defaultValue, multiline }, defaultWidgetNode: ComfyTextNode })
}; };
const COMBO: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => { const COMBO: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any): IComfyInputSlot => {
@@ -50,7 +51,7 @@ const COMBO: WidgetFactory = (node: LGraphNode, inputName: string, inputData: an
if (inputData[1] && inputData[1].default) { if (inputData[1] && inputData[1].default) {
defaultValue = inputData[1].default; defaultValue = inputData[1].default;
} }
return addComfyInput(node, inputName, { type: "string", config: { values: type, defaultValue } }) return addComfyInput(node, inputName, { type: "string", config: { values: type, defaultValue }, defaultWidgetNode: ComfyComboNode })
} }
const IMAGEUPLOAD: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app): IComfyInputSlot => { const IMAGEUPLOAD: WidgetFactory = (node: LGraphNode, inputName: string, inputData: any, app): IComfyInputSlot => {

View File

@@ -13,6 +13,7 @@
$: widget && setNodeValue(widget); $: widget && setNodeValue(widget);
$: if (nodeValue !== null && (!$propsChanged || $propsChanged)) { $: if (nodeValue !== null && (!$propsChanged || $propsChanged)) {
$nodeValue = option
setOption($nodeValue) setOption($nodeValue)
setNodeValue(widget) setNodeValue(widget)
node.properties = node.properties node.properties = node.properties