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 @@