From 772d6b771a5658924b6f5052281e9a274350ea30 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 29 Apr 2023 02:16:27 -0700 Subject: [PATCH] Merge node/widget state --- src/App.svelte | 365 --------------------- src/AppMobile.svelte | 1 - src/lib/GraphSync.ts | 29 +- src/lib/components/ComfyApp.svelte | 6 - src/lib/components/ComfyApp.ts | 1 - src/lib/components/ComfyPane.svelte | 5 +- src/lib/components/ComfyUIPane.svelte | 6 +- src/lib/stores/nodeState.ts | 95 +++++- src/lib/stores/widgetState.ts | 132 -------- src/lib/utils.ts | 2 +- src/lib/widgets/ComboWidget.svelte | 2 +- src/lib/widgets/ComfyGalleryWidget.svelte | 2 +- src/lib/widgets/ComfyValueControlWidget.ts | 4 +- src/lib/widgets/ComfyWidget.ts | 4 +- src/lib/widgets/RangeWidget.svelte | 2 +- src/lib/widgets/TextWidget.svelte | 2 +- src/mobile/GenToolbar.svelte | 1 - src/mobile/routes/home.svelte | 6 - src/mobile/routes/list-subworkflows.svelte | 1 - src/mobile/routes/subworkflow.svelte | 5 +- tsconfig.json | 7 +- 21 files changed, 119 insertions(+), 559 deletions(-) delete mode 100644 src/lib/stores/widgetState.ts diff --git a/src/App.svelte b/src/App.svelte index 8e91652..a075214 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -9,369 +9,4 @@ diff --git a/src/AppMobile.svelte b/src/AppMobile.svelte index e08e4e3..21f50df 100644 --- a/src/AppMobile.svelte +++ b/src/AppMobile.svelte @@ -4,7 +4,6 @@ import { Button } from "@gradio/button"; import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp"; import { Checkbox } from "@gradio/form" - import widgetState from "$lib/stores/widgetState"; import nodeState from "$lib/stores/nodeState"; import uiState from "$lib/stores/uiState"; import { ImageViewer } from "$lib/ImageViewer"; diff --git a/src/lib/GraphSync.ts b/src/lib/GraphSync.ts index 024e83b..e941355 100644 --- a/src/lib/GraphSync.ts +++ b/src/lib/GraphSync.ts @@ -1,6 +1,5 @@ import type { LGraph } from "@litegraph-ts/core"; -import widgetState, { type WidgetStateStore, type WidgetUIState, type WidgetUIStateStore } from "./stores/widgetState"; -import nodeState, { type NodeStateStore, type NodeUIState, type NodeUIStateStore } from "./stores/nodeState"; +import nodeState, { type WidgetUIState, type WidgetUIStateStore, type NodeStateStore, type NodeUIState, type NodeUIStateStore } from "./stores/nodeState"; import type ComfyApp from "./components/ComfyApp"; import type { Unsubscriber } from "svelte/store"; @@ -38,24 +37,20 @@ export default class GraphSync { constructor(app: ComfyApp) { this.graph = app.lGraph; - this._unsubscribeWidget = widgetState.subscribe(this.onAllWidgetStateChanged.bind(this)); - this._unsubscribeNode = nodeState.subscribe(this.onAllNodeStateChanged.bind(this)); + this._unsubscribe = nodeState.subscribe(this.onAllNodeStateChanged.bind(this)); this._finalizer = new FinalizationRegistry((id: number) => { console.log(`${this} has been garbage collected`); - this._unsubscribeWidget(); - this._unsubscribeNode(); + this._unsubscribe(); }); } - /* - * Fired when the entire widget graph changes. - */ - private onAllWidgetStateChanged(state: WidgetStateStore) { + private onAllNodeStateChanged(state: NodeStateStore) { // TODO assumes only a single graph's widget state. for (let nodeId in state) { + state[nodeId].node.title = state[nodeId].name; if (!this.stores[nodeId]) { - this.addStores(state, nodeId); + this.addStores(state[nodeId].widgetStates, nodeId); } } @@ -64,26 +59,18 @@ export default class GraphSync { this.removeStores(nodeId); } } - } - - private onAllNodeStateChanged(state: NodeStateStore) { - // TODO assumes only a single graph's widget state. - - for (let nodeId in state) { - state[nodeId].node.name = state[nodeId].name; - } this.graph.setDirtyCanvas(true, true); } - private addStores(state: WidgetStateStore, nodeId: string) { + private addStores(widgetStates: WidgetUIState[], nodeId: string) { if (this.stores[nodeId]) { console.warn("Stores already exist!", nodeId, this.stores[nodeId]) } this.stores[nodeId] = [] - for (const wuis of state[nodeId]) { + for (const wuis of widgetStates) { const unsub = wuis.value.subscribe((v) => this.onWidgetStateChanged(wuis, v)) this.stores[nodeId].push({ store: wuis.value, unsubscribe: unsub }); } diff --git a/src/lib/components/ComfyApp.svelte b/src/lib/components/ComfyApp.svelte index b4e5887..b13a927 100644 --- a/src/lib/components/ComfyApp.svelte +++ b/src/lib/components/ComfyApp.svelte @@ -6,7 +6,6 @@ import ComfyUIPane from "./ComfyUIPane.svelte"; import ComfyApp, { type SerializedAppState } from "./ComfyApp"; import { Checkbox } from "@gradio/form" - import widgetState from "$lib/stores/widgetState"; import nodeState from "$lib/stores/nodeState"; import uiState from "$lib/stores/uiState"; import { ImageViewer } from "$lib/ImageViewer"; @@ -102,16 +101,11 @@ onMount(async () => { app = new ComfyApp(); - // TODO dedup app.eventBus.on("nodeAdded", nodeState.nodeAdded); app.eventBus.on("nodeRemoved", nodeState.nodeRemoved); app.eventBus.on("configured", nodeState.configureFinished); app.eventBus.on("cleared", nodeState.clear); - app.eventBus.on("nodeAdded", widgetState.nodeAdded); - app.eventBus.on("nodeRemoved", widgetState.nodeRemoved); - app.eventBus.on("configured", widgetState.configureFinished); - app.eventBus.on("cleared", widgetState.clear); app.eventBus.on("autosave", doAutosave); app.eventBus.on("restored", doRestore); diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index 14bb305..a67eb66 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -12,7 +12,6 @@ import * as basic from "@litegraph-ts/nodes-basic" import * as nodes from "$lib/nodes/index" import ComfyGraphCanvas from "$lib/ComfyGraphCanvas"; import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode"; -import type { WidgetStateStore, WidgetUIState } from "$lib/stores/widgetState"; import * as widgets from "$lib/widgets/index" import type ComfyWidget from "$lib/widgets/ComfyWidget"; import queueState from "$lib/stores/queueState"; diff --git a/src/lib/components/ComfyPane.svelte b/src/lib/components/ComfyPane.svelte index 34b229d..0233f0f 100644 --- a/src/lib/components/ComfyPane.svelte +++ b/src/lib/components/ComfyPane.svelte @@ -3,7 +3,6 @@ import { get } from "svelte/store" import { Block, BlockTitle } from "@gradio/atoms"; import { Move } from 'radix-icons-svelte'; - import widgetState, { type WidgetDrawState, type WidgetUIState } from "$lib/stores/widgetState"; import queueState from "$lib/stores/queueState"; import nodeState from "$lib/stores/nodeState"; import uiState from "$lib/stores/uiState"; @@ -47,7 +46,7 @@ dragDisabled = true; }; - const unsubscribe = widgetState.subscribe(state => { + const unsubscribe = nodeState.subscribe(state => { dragItems = dragItems.filter(item => item.node.id in state); }); @@ -88,7 +87,7 @@ {/if} - {#each $widgetState[id] as item} + {#each $nodeState[id].widgetStates as item} {#if dragItem[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
diff --git a/src/lib/components/ComfyUIPane.svelte b/src/lib/components/ComfyUIPane.svelte index 7d29903..962c878 100644 --- a/src/lib/components/ComfyUIPane.svelte +++ b/src/lib/components/ComfyUIPane.svelte @@ -5,7 +5,7 @@ import ComfyApp from "./ComfyApp"; import type { SerializedPanes } from "./ComfyApp" import ComfyPane from "./ComfyPane.svelte"; - import widgetState from "$lib/stores/widgetState"; + import nodeState from "$lib/stores/nodeState"; import type { DragItem } from "./ComfyUIPane"; export let app: ComfyApp; @@ -17,12 +17,12 @@ function findLeastPopulatedPaneIndex(): number { let minWidgetCount = 2 ** 64; let minIndex = 0; - let state = get(widgetState); + let state = get(nodeState); for (let i = 0; i < dragItems.length; i++) { let widgetCount = 0; for (let j = 0; j < dragItems[i].length; j++) { const nodeID = dragItems[i][j].node.id; - widgetCount += state[nodeID].length; + widgetCount += state[nodeID].widgetStates.length; } if (widgetCount < minWidgetCount) { minWidgetCount = widgetCount diff --git a/src/lib/stores/nodeState.ts b/src/lib/stores/nodeState.ts index ccd6eae..38c9c71 100644 --- a/src/lib/stores/nodeState.ts +++ b/src/lib/stores/nodeState.ts @@ -1,23 +1,51 @@ import { writable, get } from 'svelte/store'; -import type { LGraph, LGraphNode } from "@litegraph-ts/core"; +import type { LGraph, LGraphNode, IWidget } from "@litegraph-ts/core"; import type { Readable, Writable } from 'svelte/store'; import type ComfyGraphNode from '$lib/nodes/ComfyGraphNode'; +import type ComfyWidget from '$lib/widgets/ComfyWidget'; /** store for one node's state */ export type NodeUIStateStore = Writable export type NodeUIState = { name: string, - node: LGraphNode + node: LGraphNode, + widgetStates: WidgetUIState[] } -type NodeID = number; +export type WidgetDrawState = { + isNodeExecuting: boolean +} + +/** store for one widget's state */ +export type WidgetUIStateStore = Writable + +export type WidgetUIState = { + /** position in the node's list of widgets */ + index: number, + /** parent node containing the widget */ + node: LGraphNode, + /** actual widget instance */ + widget: IWidget, + /** widget value as a store, to react to changes */ + value: WidgetUIStateStore, + /** + * true if this widget was added purely from the frontend. what this means: + * - this widget's state will not be saved to the workflow + * - the widget was added on startup by some subclass of ComfyGraphNode + */ + isVirtual: boolean +} + +export type NodeID = number; type NodeStateOps = { nodeAdded: (node: LGraphNode) => void, nodeRemoved: (node: LGraphNode) => void, configureFinished: (graph: LGraph) => void, nodeStateChanged: (node: LGraphNode) => void, + widgetStateChanged: (nodeId: number, widget: IWidget) => void, + findWidgetByName: (nodeId: number, name: string) => WidgetUIState | null, clear: () => void, } @@ -32,8 +60,22 @@ function clear() { function nodeAdded(node: LGraphNode) { let state = get(store) - state[node.id] = { node: node, name: node.name } + + const widgets = []; + + if (node.widgets) { + for (const [index, widget] of node.widgets.entries()) { + let isVirtual = false; + if ("isVirtual" in widget) + isVirtual = (widget as ComfyWidget).isVirtual; + widgets.push({ index, widget, value: writable(widget.value), isVirtual: isVirtual }) + } + } + + state[node.id] = { node: node, name: node.title, widgetStates: widgets } store.set(state); + + console.debug("call nodeAdded", state[node.id]) } function nodeRemoved(node: LGraphNode) { @@ -45,7 +87,7 @@ function nodeRemoved(node: LGraphNode) { function nodeStateChanged(node: LGraphNode) { const state = get(store) const nodeState = state[node.id] - nodeState.name = node.name + nodeState.name = node.title store.set(state); } @@ -53,12 +95,51 @@ function configureFinished(graph: LGraph) { let state = get(store); for (const node of graph.computeExecutionOrder(false, null)) { - state[node.id].name = name; + state[node.id].name = node.title; + + const widgetStates = state[node.id].widgetStates; + if (node.widgets_values) { + for (const [i, value] of node.widgets_values.entries()) { + if (i < widgetStates.length && !widgetStates[i].isVirtual) { + widgetStates[i].value.set(value); + } + else { + console.log("Skip virtual widget", node.id, node.type, widgetStates[i].widget) + } + } + } } store.set(state) } +function widgetStateChanged(nodeId: number, widget: IWidget) { + const state = get(store) + const entries = state[nodeId].widgetStates + if (entries) { + let widgetState = entries.find(e => e.widget === widget); + if (widgetState) { + widgetState.value.set(widget.value); + store.set(state); + } + else { + console.error("Widget state changed and node was found, but widget was not found in state!", widget, state[nodeId].node, entries) + } + } + else { + console.error("Widget state changed but node was not found in state!", widget, state[nodeId].node) + } +} + +function findWidgetByName(nodeId: NodeID, name: string): WidgetUIState | null { + let state = get(store); + + if (!(nodeId in state)) + return null; + + return state[nodeId].widgetStates.find((v) => v.widget.name === name); +} + const nodeStateStore: WritableNodeStateStore = { ...store, @@ -66,6 +147,8 @@ const nodeStateStore: WritableNodeStateStore = nodeRemoved, nodeStateChanged, configureFinished, + widgetStateChanged, + findWidgetByName, clear } export default nodeStateStore; diff --git a/src/lib/stores/widgetState.ts b/src/lib/stores/widgetState.ts deleted file mode 100644 index 06084b9..0000000 --- a/src/lib/stores/widgetState.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { writable, get } from 'svelte/store'; -import type { LGraph, LGraphNode, IWidget } from "@litegraph-ts/core"; -import type { Readable, Writable } from 'svelte/store'; -import type ComfyGraphNode from '$lib/nodes/ComfyGraphNode'; -import type ComfyWidget from '$lib/widgets/ComfyWidget'; - -/** store for one widget's state */ -export type WidgetUIStateStore = Writable - -export type WidgetUIState = { - /** position in the node's list of widgets */ - index: number, - /** parent node containing the widget */ - node: LGraphNode, - /** actual widget instance */ - widget: IWidget, - /** widget value as a store, to react to changes */ - value: WidgetUIStateStore, - /** - * true if this widget was added purely from the frontend. what this means: - * - this widget's state will not be saved to the workflow - * - the widget was added on startup by some subclass of ComfyGraphNode - */ - isVirtual: boolean -} - -export type WidgetDrawState = { - isNodeExecuting: boolean -} - -type NodeID = number; - -type WidgetStateOps = { - nodeAdded: (node: LGraphNode) => void, - nodeRemoved: (node: LGraphNode) => void, - configureFinished: (graph: LGraph) => void, - widgetStateChanged: (nodeId: number, widget: IWidget) => void, - findWidgetByName: (nodeId: number, name: string) => WidgetUIState | null, - clear: () => void, -} - -export type WidgetStateStore = Record; -type WritableWidgetStateStore = Writable & WidgetStateOps; - -const store: Writable = writable({}) - -function clear() { - store.set({}) -} - -function nodeAdded(node: LGraphNode) { - let state = get(store) - - if (node.widgets) { - for (const [index, widget] of node.widgets.entries()) { - if (!state[node.id]) - state[node.id] = [] - let isVirtual = false; - if ("isVirtual" in widget) - isVirtual = (widget as ComfyWidget).isVirtual; - state[node.id].push({ index, node, widget, value: writable(widget.value), isVirtual: isVirtual }) - } - } - - console.debug("NODEADDED", state) - - store.set(state); -} - -function nodeRemoved(node: LGraphNode) { - const state = get(store) - delete state[node.id] - store.set(state) -} - -function widgetStateChanged(nodeId: number, widget: IWidget) { - const state = get(store) - const entries = state[nodeId] - if (entries) { - let widgetState = entries.find(e => e.widget === widget); - if (widgetState) { - widgetState.value.set(widget.value); - store.set(state); - } - else { - console.error("Widget state changed and node was found, but widget was not found in state!", widget, widget.node, entries) - } - } - else { - console.error("Widget state changed but node was not found in state!", widget, widget.node) - } -} - -function configureFinished(graph: LGraph) { - let state = get(store); - - for (const node of graph.computeExecutionOrder(false, null)) { - if (node.widgets_values) { - for (const [i, value] of node.widgets_values.entries()) { - if (i < state[node.id].length && !state[node.id][i].isVirtual) { - state[node.id][i].value.set(value); - } - else { - console.log("Skip virtual widget", node.id, node.type, state[node.id][i].widget) - } - } - } - } - - store.set(state) -} - -function findWidgetByName(nodeId: number, name: string): WidgetUIState | null { - let state = get(store); - - if (!(nodeId in state)) - return null; - - return state[nodeId].find((v) => v.widget.name === name); -} - -const widgetStateStore: WritableWidgetStateStore = -{ - ...store, - nodeAdded, - nodeRemoved, - widgetStateChanged, - configureFinished, - findWidgetByName, - clear -} -export default widgetStateStore; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 6f24cad..c495cea 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,7 +2,7 @@ import ComfyApp from "./components/ComfyApp"; import ComboWidget from "$lib/widgets/ComboWidget.svelte"; import RangeWidget from "$lib/widgets/RangeWidget.svelte"; import TextWidget from "$lib/widgets/TextWidget.svelte"; - import widgetState, { type WidgetDrawState, type WidgetUIState } from "$lib/stores/widgetState"; +import { type WidgetUIState } from "$lib/stores/nodeState"; export function download(filename: string, text: string, type: string = "text/plain") { const blob = new Blob([text], { type: type }); diff --git a/src/lib/widgets/ComboWidget.svelte b/src/lib/widgets/ComboWidget.svelte index 36a0103..e49693a 100644 --- a/src/lib/widgets/ComboWidget.svelte +++ b/src/lib/widgets/ComboWidget.svelte @@ -1,5 +1,5 @@