Update
This commit is contained in:
@@ -27,7 +27,7 @@
|
|||||||
"svelte-check": "^3.2.0",
|
"svelte-check": "^3.2.0",
|
||||||
"svelte-dnd-action": "^0.9.22",
|
"svelte-dnd-action": "^0.9.22",
|
||||||
"typescript": "^5.0.3",
|
"typescript": "^5.0.3",
|
||||||
"vite": "^4.2.1",
|
"vite": "^4.3.1",
|
||||||
"vite-tsconfig-paths": "^4.0.8",
|
"vite-tsconfig-paths": "^4.0.8",
|
||||||
"vitest": "^0.25.8"
|
"vitest": "^0.25.8"
|
||||||
},
|
},
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
"@gradio/theme": "workspace:*",
|
"@gradio/theme": "workspace:*",
|
||||||
"@gradio/upload": "workspace:*",
|
"@gradio/upload": "workspace:*",
|
||||||
"@gradio/utils": "workspace:*",
|
"@gradio/utils": "workspace:*",
|
||||||
|
"@litegraph-ts/tsconfig": "workspace:*",
|
||||||
"@litegraph-ts/core": "workspace:*",
|
"@litegraph-ts/core": "workspace:*",
|
||||||
"@litegraph-ts/nodes-basic": "workspace:*",
|
"@litegraph-ts/nodes-basic": "workspace:*",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
|
|||||||
6214
pnpm-lock.yaml
generated
6214
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -3,3 +3,4 @@ packages:
|
|||||||
- 'gradio/client/js'
|
- 'gradio/client/js'
|
||||||
- 'litegraph/packages/core'
|
- 'litegraph/packages/core'
|
||||||
- 'litegraph/packages/nodes-basic'
|
- 'litegraph/packages/nodes-basic'
|
||||||
|
- 'litegraph/packages/tsconfig'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { LGraph } from "@litegraph-ts/core";
|
import type { LGraph } from "@litegraph-ts/core";
|
||||||
import widgetState, { type WidgetStateStore, type WidgetUIState, type WidgetUIStateStore } from "./stores/widgetState";
|
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 ComfyApp from "./components/ComfyApp";
|
||||||
import type { Unsubscriber } from "svelte/store";
|
import type { Unsubscriber } from "svelte/store";
|
||||||
|
|
||||||
@@ -8,6 +9,11 @@ type WidgetSubStore = {
|
|||||||
unsubscribe: Unsubscriber
|
unsubscribe: Unsubscriber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NodeSubStore = {
|
||||||
|
store: NodeUIStateStore,
|
||||||
|
unsubscribe: Unsubscriber
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Responsible for watching for and synchronizing state changes from the
|
* Responsible for watching for and synchronizing state changes from the
|
||||||
* frontend to the litegraph instance.
|
* frontend to the litegraph instance.
|
||||||
@@ -32,10 +38,12 @@ export default class GraphSync {
|
|||||||
|
|
||||||
constructor(app: ComfyApp) {
|
constructor(app: ComfyApp) {
|
||||||
this.graph = app.lGraph;
|
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) => {
|
this._finalizer = new FinalizationRegistry((id: number) => {
|
||||||
console.log(`${this} has been garbage collected`);
|
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) {
|
private addStores(state: WidgetStateStore, nodeId: string) {
|
||||||
if (this.stores[nodeId]) {
|
if (this.stores[nodeId]) {
|
||||||
console.warn("Stores already exist!", nodeId, 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]) {
|
for (const wuis of state[nodeId]) {
|
||||||
const unsub = wuis.value.subscribe((v) => this.onWidgetStateChanged(wuis, v))
|
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])
|
console.log("NEWSTORES", this.stores[nodeId])
|
||||||
@@ -179,7 +179,7 @@ export default class ComfyAPI extends EventTarget {
|
|||||||
prompt: output,
|
prompt: output,
|
||||||
extra_data: { extra_pnginfo: { workflow } },
|
extra_data: { extra_pnginfo: { workflow } },
|
||||||
front: false,
|
front: false,
|
||||||
number: null
|
number: number
|
||||||
};
|
};
|
||||||
|
|
||||||
if (number === -1) {
|
if (number === -1) {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
import ComfyApp, { type SerializedAppState } from "./ComfyApp";
|
import ComfyApp, { type SerializedAppState } from "./ComfyApp";
|
||||||
import { Checkbox } from "@gradio/form"
|
import { Checkbox } from "@gradio/form"
|
||||||
import widgetState from "$lib/stores/widgetState";
|
import widgetState from "$lib/stores/widgetState";
|
||||||
|
import nodeState from "$lib/stores/nodeState";
|
||||||
|
import uiState from "$lib/stores/uiState";
|
||||||
import { ImageViewer } from "$lib/ImageViewer";
|
import { ImageViewer } from "$lib/ImageViewer";
|
||||||
import { download } from "$lib/utils"
|
import { download } from "$lib/utils"
|
||||||
|
|
||||||
@@ -23,8 +25,6 @@
|
|||||||
let queue: ComfyQueue = undefined;
|
let queue: ComfyQueue = undefined;
|
||||||
let mainElem: HTMLDivElement;
|
let mainElem: HTMLDivElement;
|
||||||
let containerElem: HTMLDivElement;
|
let containerElem: HTMLDivElement;
|
||||||
let nodesLocked: boolean = false;
|
|
||||||
let graphLocked: boolean = false;
|
|
||||||
let resizeTimeout: typeof Timer = -1;
|
let resizeTimeout: typeof Timer = -1;
|
||||||
|
|
||||||
function refreshView(event?: Event) {
|
function refreshView(event?: Event) {
|
||||||
@@ -37,8 +37,8 @@
|
|||||||
app.queuePrompt(0, 1);
|
app.queuePrompt(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (app) app.lCanvas.allow_dragnodes = !nodesLocked;
|
$: if (app) app.lCanvas.allow_dragnodes = !$uiState.nodesLocked;
|
||||||
$: if (app) app.lCanvas.allow_interaction = !graphLocked;
|
$: if (app) app.lCanvas.allow_interaction = !$uiState.graphLocked;
|
||||||
|
|
||||||
let graphSize = null;
|
let graphSize = null;
|
||||||
|
|
||||||
@@ -102,6 +102,12 @@
|
|||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
app = new ComfyApp();
|
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("nodeAdded", widgetState.nodeAdded);
|
||||||
app.eventBus.on("nodeRemoved", widgetState.nodeRemoved);
|
app.eventBus.on("nodeRemoved", widgetState.nodeRemoved);
|
||||||
app.eventBus.on("configured", widgetState.configureFinished);
|
app.eventBus.on("configured", widgetState.configureFinished);
|
||||||
@@ -167,8 +173,9 @@
|
|||||||
<Button variant="secondary" on:click={doSave}>
|
<Button variant="secondary" on:click={doSave}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
<Checkbox label="Lock Nodes" bind:value={nodesLocked}/>
|
<Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/>
|
||||||
<Checkbox label="Disable Interaction" bind:value={graphLocked}/>
|
<Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/>
|
||||||
|
<Checkbox label="Enable UI Editing" bind:value={$uiState.unlocked}/>
|
||||||
</div>
|
</div>
|
||||||
<LightboxModal />
|
<LightboxModal />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
import TextWidget from "$lib/widgets/TextWidget.svelte";
|
import TextWidget from "$lib/widgets/TextWidget.svelte";
|
||||||
import widgetState, { type WidgetDrawState, type WidgetUIState } from "$lib/stores/widgetState";
|
import widgetState, { type WidgetDrawState, type WidgetUIState } from "$lib/stores/widgetState";
|
||||||
import queueState from "$lib/stores/queueState";
|
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';
|
import { dndzone, SHADOW_ITEM_MARKER_PROPERTY_NAME } from 'svelte-dnd-action';
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@
|
|||||||
|
|
||||||
export let dragItems: DragItem[] = [];
|
export let dragItems: DragItem[] = [];
|
||||||
let dragDisabled = true;
|
let dragDisabled = true;
|
||||||
|
let unlockUI = false;
|
||||||
const flipDurationMs = 200;
|
const flipDurationMs = 200;
|
||||||
|
|
||||||
const handleConsider = evt => {
|
const handleConsider = evt => {
|
||||||
@@ -76,6 +79,11 @@
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateNodeName(node: LGraphNode, value: string) {
|
||||||
|
console.log("CHA")
|
||||||
|
nodeState.nodeStateChanged(node);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@@ -89,11 +97,22 @@
|
|||||||
{@const id = node.id}
|
{@const id = node.id}
|
||||||
<div class="animation-wrapper" class:is-executing={dragItem.isNodeExecuting} animate:flip={{duration:flipDurationMs}}>
|
<div class="animation-wrapper" class:is-executing={dragItem.isNodeExecuting} animate:flip={{duration:flipDurationMs}}>
|
||||||
<Block>
|
<Block>
|
||||||
|
{#if $uiState.unlocked}
|
||||||
<div class="handle" on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}>
|
<div class="handle" on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}>
|
||||||
<Move/>
|
<Move/>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
<label for={String(id)}>
|
<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>
|
</label>
|
||||||
{#each $widgetState[id] as item}
|
{#each $widgetState[id] as item}
|
||||||
<svelte:component this={getComponentForWidgetState(item)} {item} />
|
<svelte:component this={getComponentForWidgetState(item)} {item} />
|
||||||
@@ -145,4 +164,9 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.node-type {
|
||||||
|
font-size: smaller;
|
||||||
|
color: var(--neutral-400);
|
||||||
|
}
|
||||||
</style>
|
</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 ComfyGraphNode from '$lib/nodes/ComfyGraphNode';
|
||||||
import type ComfyWidget from '$lib/widgets/ComfyWidget';
|
import type ComfyWidget from '$lib/widgets/ComfyWidget';
|
||||||
|
|
||||||
import { subStore } from "immer-loves-svelte"
|
|
||||||
|
|
||||||
/** store for one widget's state */
|
/** store for one widget's state */
|
||||||
export type WidgetUIStateStore = Writable<any>
|
export type WidgetUIStateStore = Writable<any>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import FullReload from 'vite-plugin-full-reload';
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
plugins: [
|
plugins: [
|
||||||
tsconfigPaths({ loose: true }),
|
// tsconfigPaths({ loose: true }),
|
||||||
sveltekit(),
|
sveltekit(),
|
||||||
FullReload(["src/**/*.{js,ts,svelte}"])
|
FullReload(["src/**/*.{js,ts,svelte}"])
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user