Update
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
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 type ComfyApp from "./components/ComfyApp";
|
||||
import type { Unsubscriber } from "svelte/store";
|
||||
|
||||
@@ -8,6 +9,11 @@ type WidgetSubStore = {
|
||||
unsubscribe: Unsubscriber
|
||||
}
|
||||
|
||||
type NodeSubStore = {
|
||||
store: NodeUIStateStore,
|
||||
unsubscribe: Unsubscriber
|
||||
}
|
||||
|
||||
/*
|
||||
* Responsible for watching for and synchronizing state changes from the
|
||||
* frontend to the litegraph instance.
|
||||
@@ -32,10 +38,12 @@ export default class GraphSync {
|
||||
|
||||
constructor(app: ComfyApp) {
|
||||
this.graph = app.lGraph;
|
||||
this._unsubscribe = widgetState.subscribe(this.onAllWidgetStateChanged.bind(this));
|
||||
this._unsubscribeWidget = widgetState.subscribe(this.onAllWidgetStateChanged.bind(this));
|
||||
this._unsubscribeNode = nodeState.subscribe(this.onAllNodeStateChanged.bind(this));
|
||||
this._finalizer = new FinalizationRegistry((id: number) => {
|
||||
console.log(`${this} has been garbage collected`);
|
||||
this._unsubscribe();
|
||||
this._unsubscribeWidget();
|
||||
this._unsubscribeNode();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -58,6 +66,16 @@ export default class GraphSync {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (this.stores[nodeId]) {
|
||||
console.warn("Stores already exist!", nodeId, this.stores[nodeId])
|
||||
@@ -67,7 +85,7 @@ export default class GraphSync {
|
||||
|
||||
for (const wuis of state[nodeId]) {
|
||||
const unsub = wuis.value.subscribe((v) => this.onWidgetStateChanged(wuis, v))
|
||||
this.stores[nodeId].push({ store: wuis.vlue, unsubscribe: unsub });
|
||||
this.stores[nodeId].push({ store: wuis.value, unsubscribe: unsub });
|
||||
}
|
||||
|
||||
console.log("NEWSTORES", this.stores[nodeId])
|
||||
@@ -179,7 +179,7 @@ export default class ComfyAPI extends EventTarget {
|
||||
prompt: output,
|
||||
extra_data: { extra_pnginfo: { workflow } },
|
||||
front: false,
|
||||
number: null
|
||||
number: number
|
||||
};
|
||||
|
||||
if (number === -1) {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
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";
|
||||
import { download } from "$lib/utils"
|
||||
|
||||
@@ -23,8 +25,6 @@
|
||||
let queue: ComfyQueue = undefined;
|
||||
let mainElem: HTMLDivElement;
|
||||
let containerElem: HTMLDivElement;
|
||||
let nodesLocked: boolean = false;
|
||||
let graphLocked: boolean = false;
|
||||
let resizeTimeout: typeof Timer = -1;
|
||||
|
||||
function refreshView(event?: Event) {
|
||||
@@ -37,8 +37,8 @@
|
||||
app.queuePrompt(0, 1);
|
||||
}
|
||||
|
||||
$: if (app) app.lCanvas.allow_dragnodes = !nodesLocked;
|
||||
$: if (app) app.lCanvas.allow_interaction = !graphLocked;
|
||||
$: if (app) app.lCanvas.allow_dragnodes = !$uiState.nodesLocked;
|
||||
$: if (app) app.lCanvas.allow_interaction = !$uiState.graphLocked;
|
||||
|
||||
let graphSize = null;
|
||||
|
||||
@@ -102,6 +102,12 @@
|
||||
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);
|
||||
@@ -167,8 +173,9 @@
|
||||
<Button variant="secondary" on:click={doSave}>
|
||||
Save
|
||||
</Button>
|
||||
<Checkbox label="Lock Nodes" bind:value={nodesLocked}/>
|
||||
<Checkbox label="Disable Interaction" bind:value={graphLocked}/>
|
||||
<Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/>
|
||||
<Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/>
|
||||
<Checkbox label="Enable UI Editing" bind:value={$uiState.unlocked}/>
|
||||
</div>
|
||||
<LightboxModal />
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
import TextWidget from "$lib/widgets/TextWidget.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";
|
||||
|
||||
import { dndzone, SHADOW_ITEM_MARKER_PROPERTY_NAME } from 'svelte-dnd-action';
|
||||
|
||||
@@ -21,6 +23,7 @@
|
||||
|
||||
export let dragItems: DragItem[] = [];
|
||||
let dragDisabled = true;
|
||||
let unlockUI = false;
|
||||
const flipDurationMs = 200;
|
||||
|
||||
const handleConsider = evt => {
|
||||
@@ -76,6 +79,11 @@
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function updateNodeName(node: LGraphNode, value: string) {
|
||||
console.log("CHA")
|
||||
nodeState.nodeStateChanged(node);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -89,11 +97,22 @@
|
||||
{@const id = node.id}
|
||||
<div class="animation-wrapper" class:is-executing={dragItem.isNodeExecuting} animate:flip={{duration:flipDurationMs}}>
|
||||
<Block>
|
||||
{#if $uiState.unlocked}
|
||||
<div class="handle" on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}>
|
||||
<Move/>
|
||||
</div>
|
||||
{/if}
|
||||
<label for={String(id)}>
|
||||
<BlockTitle>{node.title}</BlockTitle>
|
||||
<BlockTitle>
|
||||
{#if $uiState.unlocked}
|
||||
<input bind:value={dragItem.node.title} type="text" minlength="1" on:input="{(v) => { updateNodeName(node, v) }}"/>
|
||||
{:else}
|
||||
{node.title}
|
||||
{/if}
|
||||
{#if node.title !== node.type}
|
||||
<span class="node-type">({node.type})</span>
|
||||
{/if}
|
||||
</BlockTitle>
|
||||
</label>
|
||||
{#each $widgetState[id] as item}
|
||||
<svelte:component this={getComponentForWidgetState(item)} {item} />
|
||||
@@ -145,4 +164,9 @@
|
||||
opacity: 0.5;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.node-type {
|
||||
font-size: smaller;
|
||||
color: var(--neutral-400);
|
||||
}
|
||||
</style>
|
||||
|
||||
71
src/lib/stores/nodeState.ts
Normal file
71
src/lib/stores/nodeState.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { writable, get } from 'svelte/store';
|
||||
import type { LGraph, LGraphNode } from "@litegraph-ts/core";
|
||||
import type { Readable, Writable } from 'svelte/store';
|
||||
import type ComfyGraphNode from '$lib/nodes/ComfyGraphNode';
|
||||
|
||||
/** store for one node's state */
|
||||
export type NodeUIStateStore = Writable<any>
|
||||
|
||||
export type NodeUIState = {
|
||||
name: string,
|
||||
node: LGraphNode
|
||||
}
|
||||
|
||||
type NodeID = number;
|
||||
|
||||
type NodeStateOps = {
|
||||
nodeAdded: (node: LGraphNode) => void,
|
||||
nodeRemoved: (node: LGraphNode) => void,
|
||||
configureFinished: (graph: LGraph) => void,
|
||||
nodeStateChanged: (node: LGraphNode) => void,
|
||||
clear: () => void,
|
||||
}
|
||||
|
||||
export type NodeStateStore = Record<NodeID, NodeUIState>;
|
||||
type WritableNodeStateStore = Writable<NodeStateStore> & NodeStateOps;
|
||||
|
||||
const store: Writable<NodeStateStore> = writable({})
|
||||
|
||||
function clear() {
|
||||
store.set({})
|
||||
}
|
||||
|
||||
function nodeAdded(node: LGraphNode) {
|
||||
let state = get(store)
|
||||
state[node.id] = { node: node, name: node.name }
|
||||
store.set(state);
|
||||
}
|
||||
|
||||
function nodeRemoved(node: LGraphNode) {
|
||||
const state = get(store)
|
||||
delete state[node.id]
|
||||
store.set(state)
|
||||
}
|
||||
|
||||
function nodeStateChanged(node: LGraphNode) {
|
||||
const state = get(store)
|
||||
const nodeState = state[node.id]
|
||||
nodeState.name = node.name
|
||||
store.set(state);
|
||||
}
|
||||
|
||||
function configureFinished(graph: LGraph) {
|
||||
let state = get(store);
|
||||
|
||||
for (const node of graph.computeExecutionOrder(false, null)) {
|
||||
state[node.id].name = name;
|
||||
}
|
||||
|
||||
store.set(state)
|
||||
}
|
||||
|
||||
const nodeStateStore: WritableNodeStateStore =
|
||||
{
|
||||
...store,
|
||||
nodeAdded,
|
||||
nodeRemoved,
|
||||
nodeStateChanged,
|
||||
configureFinished,
|
||||
clear
|
||||
}
|
||||
export default nodeStateStore;
|
||||
14
src/lib/stores/uiState.ts
Normal file
14
src/lib/stores/uiState.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import type { Readable, Writable } from 'svelte/store';
|
||||
|
||||
export type UIState = {
|
||||
unlocked: boolean,
|
||||
}
|
||||
|
||||
const store: Writable<UIState> = writable({ unlocked: false })
|
||||
|
||||
const uiStateStore: WritableUIStateStore =
|
||||
{
|
||||
...store
|
||||
}
|
||||
export default uiStateStore;
|
||||
@@ -4,8 +4,6 @@ import type { Readable, Writable } from 'svelte/store';
|
||||
import type ComfyGraphNode from '$lib/nodes/ComfyGraphNode';
|
||||
import type ComfyWidget from '$lib/widgets/ComfyWidget';
|
||||
|
||||
import { subStore } from "immer-loves-svelte"
|
||||
|
||||
/** store for one widget's state */
|
||||
export type WidgetUIStateStore = Writable<any>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user