Bind widget values to special widget nodes
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
import { Button } from "@gradio/button";
|
import { Button } from "@gradio/button";
|
||||||
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
||||||
import { Checkbox } from "@gradio/form"
|
import { Checkbox } from "@gradio/form"
|
||||||
import nodeState from "$lib/stores/nodeState";
|
|
||||||
import uiState from "$lib/stores/uiState";
|
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"
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
import type { LGraph } from "@litegraph-ts/core";
|
import type { LGraph, LGraphNode } from "@litegraph-ts/core";
|
||||||
import nodeState, { type WidgetUIState, type WidgetUIStateStore, 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, Writable } from "svelte/store";
|
||||||
|
import type { ComfyWidgetNode } from "./nodes";
|
||||||
|
|
||||||
type WidgetSubStore = {
|
type WidgetSubStore = {
|
||||||
store: WidgetUIStateStore,
|
store: WidgetUIStateStore,
|
||||||
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.
|
||||||
@@ -29,68 +24,55 @@ type NodeSubStore = {
|
|||||||
*/
|
*/
|
||||||
export default class GraphSync {
|
export default class GraphSync {
|
||||||
graph: LGraph;
|
graph: LGraph;
|
||||||
private _unsubscribe: Unsubscriber;
|
|
||||||
private _finalizer: FinalizationRegistry<number>;
|
|
||||||
|
|
||||||
// nodeId -> widgetSubStores[]
|
// nodeId -> widgetSubStore
|
||||||
private stores: Record<string, WidgetSubStore[]> = {}
|
private stores: Record<string, WidgetSubStore> = {}
|
||||||
|
|
||||||
constructor(app: ComfyApp) {
|
constructor(app: ComfyApp) {
|
||||||
this.graph = app.lGraph;
|
this.graph = app.lGraph;
|
||||||
this._unsubscribe = nodeState.subscribe(this.onAllNodeStateChanged.bind(this));
|
app.eventBus.on("nodeAdded", this.onNodeAdded.bind(this));
|
||||||
this._finalizer = new FinalizationRegistry((id: number) => {
|
app.eventBus.on("nodeRemoved", this.onNodeRemoved.bind(this));
|
||||||
console.log(`${this} has been garbage collected`);
|
|
||||||
this._unsubscribe();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAllNodeStateChanged(state: NodeStateStore) {
|
private onNodeAdded(node: LGraphNode) {
|
||||||
// TODO assumes only a single graph's widget state.
|
// TODO assumes only a single graph's widget state.
|
||||||
|
|
||||||
for (let nodeId in state) {
|
if ("svelteComponentType" in node) {
|
||||||
state[nodeId].node.title = state[nodeId].name;
|
this.addStore(node as ComfyWidgetNode);
|
||||||
if (!this.stores[nodeId]) {
|
|
||||||
this.addStores(state[nodeId].widgetStates, nodeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let nodeId in this.stores) {
|
|
||||||
if (!state[nodeId]) {
|
|
||||||
this.removeStores(nodeId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.graph.setDirtyCanvas(true, true);
|
this.graph.setDirtyCanvas(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private addStores(widgetStates: WidgetUIState[], nodeId: string) {
|
private onNodeRemoved(node: LGraphNode) {
|
||||||
if (this.stores[nodeId]) {
|
if ("svelteComponentType" in node) {
|
||||||
console.warn("Stores already exist!", nodeId, this.stores[nodeId])
|
this.removeStore(node as ComfyWidgetNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stores[nodeId] = []
|
this.graph.setDirtyCanvas(true, true);
|
||||||
|
|
||||||
for (const wuis of widgetStates) {
|
|
||||||
const unsub = wuis.value.subscribe((v) => this.onWidgetStateChanged(wuis, v))
|
|
||||||
this.stores[nodeId].push({ store: wuis.value, unsubscribe: unsub });
|
|
||||||
}
|
|
||||||
|
|
||||||
console.debug("NEWSTORES", this.stores[nodeId])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeStores(nodeId: string) {
|
private addStore(node: ComfyWidgetNode) {
|
||||||
console.debug("DELSTORES", this.stores[nodeId])
|
if (this.stores[node.id]) {
|
||||||
for (const ss of this.stores[nodeId]) {
|
console.warn("[GraphSync] Stores already exist!", node.id, this.stores[node.id])
|
||||||
ss.unsubscribe();
|
|
||||||
}
|
}
|
||||||
delete this.stores[nodeId]
|
|
||||||
|
const unsub = node.value.subscribe((v) => this.onWidgetStateChanged(node, v))
|
||||||
|
this.stores[node.id] = ({ store: node.value, unsubscribe: unsub });
|
||||||
|
|
||||||
|
console.debug("[GraphSync] NEWSTORE", this.stores[node.id])
|
||||||
|
}
|
||||||
|
|
||||||
|
private removeStore(node: ComfyWidgetNode) {
|
||||||
|
console.debug("[GraphSync] DELSTORE", this.stores[node.id])
|
||||||
|
this.stores[node.id].unsubscribe()
|
||||||
|
delete this.stores[node.id]
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fired when a single widget's value changes.
|
* Fired when a single widget's value changes.
|
||||||
*/
|
*/
|
||||||
private onWidgetStateChanged(wuis: WidgetUIState, value: any) {
|
private onWidgetStateChanged(node: ComfyWidgetNode, value: any) {
|
||||||
wuis.widget.value = value;
|
|
||||||
this.graph.setDirtyCanvas(true, true);
|
this.graph.setDirtyCanvas(true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
import ComfyUIPane from "./ComfyUIPane.svelte";
|
import ComfyUIPane from "./ComfyUIPane.svelte";
|
||||||
import ComfyApp, { type SerializedAppState } from "./ComfyApp";
|
import ComfyApp, { type SerializedAppState } from "./ComfyApp";
|
||||||
import { Checkbox } from "@gradio/form"
|
import { Checkbox } from "@gradio/form"
|
||||||
import nodeState from "$lib/stores/nodeState";
|
|
||||||
import uiState from "$lib/stores/uiState";
|
import uiState from "$lib/stores/uiState";
|
||||||
import layoutState from "$lib/stores/layoutState";
|
import layoutState from "$lib/stores/layoutState";
|
||||||
import { ImageViewer } from "$lib/ImageViewer";
|
import { ImageViewer } from "$lib/ImageViewer";
|
||||||
@@ -105,11 +104,6 @@
|
|||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
app = new ComfyApp();
|
app = new ComfyApp();
|
||||||
|
|
||||||
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", layoutState.nodeAdded);
|
app.eventBus.on("nodeAdded", layoutState.nodeAdded);
|
||||||
app.eventBus.on("nodeRemoved", layoutState.nodeRemoved);
|
app.eventBus.on("nodeRemoved", layoutState.nodeRemoved);
|
||||||
app.eventBus.on("configured", layoutState.configureFinished);
|
app.eventBus.on("configured", layoutState.configureFinished);
|
||||||
|
|||||||
@@ -433,7 +433,7 @@ export default class ComfyApp {
|
|||||||
this.clean();
|
this.clean();
|
||||||
|
|
||||||
if (!graphData) {
|
if (!graphData) {
|
||||||
// graphData = structuredClone(defaultGraph.workflow)
|
graphData = structuredClone(defaultGraph.workflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
|
// Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
import ComfyApp from "./ComfyApp";
|
import ComfyApp from "./ComfyApp";
|
||||||
import type { SerializedPanes } from "./ComfyApp"
|
import type { SerializedPanes } from "./ComfyApp"
|
||||||
import WidgetContainer from "./WidgetContainer.svelte";
|
import WidgetContainer from "./WidgetContainer.svelte";
|
||||||
import nodeState from "$lib/stores/nodeState";
|
|
||||||
import layoutState, { type ContainerLayout, type DragItem } from "$lib/stores/layoutState";
|
import layoutState, { type ContainerLayout, type DragItem } from "$lib/stores/layoutState";
|
||||||
import uiState from "$lib/stores/uiState";
|
import uiState from "$lib/stores/uiState";
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import queueState from "$lib/stores/queueState";
|
import queueState from "$lib/stores/queueState";
|
||||||
import nodeState, { type WidgetUIState } from "$lib/stores/nodeState";
|
|
||||||
import uiState from "$lib/stores/uiState";
|
import uiState from "$lib/stores/uiState";
|
||||||
|
|
||||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||||
import { startDrag, stopDrag, getComponentForWidgetState } from "$lib/utils"
|
import { startDrag, stopDrag } from "$lib/utils"
|
||||||
import BlockContainer from "./BlockContainer.svelte"
|
import BlockContainer from "./BlockContainer.svelte"
|
||||||
|
|
||||||
export let dragItem: IDragItem | null = null;
|
export let dragItem: IDragItem | null = null;
|
||||||
@@ -12,14 +11,12 @@
|
|||||||
export let classes: string[] = [];
|
export let classes: string[] = [];
|
||||||
let container: ContainerLayout | null = null;
|
let container: ContainerLayout | null = null;
|
||||||
let widget: WidgetLayout | null = null;
|
let widget: WidgetLayout | null = null;
|
||||||
let widgetState: WidgetUIState | null = null;
|
|
||||||
let showHandles: boolean = false;
|
let showHandles: boolean = false;
|
||||||
|
|
||||||
$: if (!dragItem || !$layoutState.allItems[dragItem.id]) {
|
$: if (!dragItem || !$layoutState.allItems[dragItem.id]) {
|
||||||
dragItem = null;
|
dragItem = null;
|
||||||
container = null;
|
container = null;
|
||||||
widget = null;
|
widget = null;
|
||||||
widgetState = null;
|
|
||||||
}
|
}
|
||||||
else if (dragItem.type === "container") {
|
else if (dragItem.type === "container") {
|
||||||
container = dragItem as ContainerLayout;
|
container = dragItem as ContainerLayout;
|
||||||
@@ -48,7 +45,7 @@
|
|||||||
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}
|
||||||
>
|
>
|
||||||
<svelte:component this={widget.node.svelteComponentType} node={widget.node} />
|
<svelte:component this={widget.node.svelteComponentType} {widget} />
|
||||||
</div>
|
</div>
|
||||||
{#if showHandles}
|
{#if showHandles}
|
||||||
<div class="handle handle-widget" style="z-index: {zIndex+100}" data-drag-item-id={widget.id} on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}/>
|
<div class="handle handle-widget" style="z-index: {zIndex+100}" data-drag-item-id={widget.id} on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}/>
|
||||||
|
|||||||
@@ -4,398 +4,16 @@ const defaultGraph: SerializedAppState = {
|
|||||||
createdBy: "ComfyBox",
|
createdBy: "ComfyBox",
|
||||||
version: 1,
|
version: 1,
|
||||||
workflow: {
|
workflow: {
|
||||||
last_node_id: 9,
|
last_node_id: 0,
|
||||||
last_link_id: 9,
|
last_link_id: 0,
|
||||||
nodes: [
|
nodes: [],
|
||||||
{
|
links: [],
|
||||||
id: 7,
|
|
||||||
type: "CLIPTextEncode",
|
|
||||||
pos: [
|
|
||||||
413,
|
|
||||||
389
|
|
||||||
],
|
|
||||||
size: [
|
|
||||||
425.27801513671875,
|
|
||||||
180.6060791015625
|
|
||||||
],
|
|
||||||
flags: {},
|
|
||||||
order: 3,
|
|
||||||
mode: 0,
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
name: "clip",
|
|
||||||
type: "CLIP",
|
|
||||||
link: 5
|
|
||||||
}
|
|
||||||
],
|
|
||||||
outputs: [
|
|
||||||
{
|
|
||||||
name: "CONDITIONING",
|
|
||||||
type: "CONDITIONING",
|
|
||||||
links: [
|
|
||||||
6
|
|
||||||
],
|
|
||||||
slot_index: 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
title: "CLIPTextEncode",
|
|
||||||
properties: {},
|
|
||||||
widgets_values: [
|
|
||||||
"bad hands"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
type: "CLIPTextEncode",
|
|
||||||
pos: [
|
|
||||||
415,
|
|
||||||
186
|
|
||||||
],
|
|
||||||
size: [
|
|
||||||
422.84503173828125,
|
|
||||||
164.31304931640625
|
|
||||||
],
|
|
||||||
flags: {},
|
|
||||||
order: 2,
|
|
||||||
mode: 0,
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
name: "clip",
|
|
||||||
type: "CLIP",
|
|
||||||
link: 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
outputs: [
|
|
||||||
{
|
|
||||||
name: "CONDITIONING",
|
|
||||||
type: "CONDITIONING",
|
|
||||||
links: [
|
|
||||||
4
|
|
||||||
],
|
|
||||||
slot_index: 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
title: "CLIPTextEncode",
|
|
||||||
properties: {},
|
|
||||||
widgets_values: [
|
|
||||||
"masterpiece best quality girl"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
type: "EmptyLatentImage",
|
|
||||||
pos: [
|
|
||||||
473,
|
|
||||||
609
|
|
||||||
],
|
|
||||||
size: [
|
|
||||||
315,
|
|
||||||
106
|
|
||||||
],
|
|
||||||
flags: {},
|
|
||||||
order: 0,
|
|
||||||
mode: 0,
|
|
||||||
inputs: [],
|
|
||||||
outputs: [
|
|
||||||
{
|
|
||||||
name: "LATENT",
|
|
||||||
type: "LATENT",
|
|
||||||
links: [
|
|
||||||
2
|
|
||||||
],
|
|
||||||
slot_index: 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
title: "EmptyLatentImage",
|
|
||||||
properties: {},
|
|
||||||
widgets_values: [
|
|
||||||
512,
|
|
||||||
512,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
type: "KSampler",
|
|
||||||
pos: [
|
|
||||||
863,
|
|
||||||
186
|
|
||||||
],
|
|
||||||
size: [
|
|
||||||
315,
|
|
||||||
262
|
|
||||||
],
|
|
||||||
flags: {},
|
|
||||||
order: 4,
|
|
||||||
mode: 0,
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
name: "model",
|
|
||||||
type: "MODEL",
|
|
||||||
link: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "positive",
|
|
||||||
type: "CONDITIONING",
|
|
||||||
link: 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "negative",
|
|
||||||
type: "CONDITIONING",
|
|
||||||
link: 6
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "latent_image",
|
|
||||||
type: "LATENT",
|
|
||||||
link: 2
|
|
||||||
}
|
|
||||||
],
|
|
||||||
outputs: [
|
|
||||||
{
|
|
||||||
name: "LATENT",
|
|
||||||
type: "LATENT",
|
|
||||||
links: [
|
|
||||||
7
|
|
||||||
],
|
|
||||||
slot_index: 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
title: "KSampler",
|
|
||||||
properties: {},
|
|
||||||
widgets_values: [
|
|
||||||
8566257,
|
|
||||||
"randomize",
|
|
||||||
8,
|
|
||||||
8,
|
|
||||||
"euler",
|
|
||||||
"normal",
|
|
||||||
1
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 8,
|
|
||||||
type: "VAEDecode",
|
|
||||||
pos: [
|
|
||||||
1209,
|
|
||||||
188
|
|
||||||
],
|
|
||||||
size: [
|
|
||||||
210,
|
|
||||||
46
|
|
||||||
],
|
|
||||||
flags: {},
|
|
||||||
order: 5,
|
|
||||||
mode: 0,
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
name: "samples",
|
|
||||||
type: "LATENT",
|
|
||||||
link: 7
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "vae",
|
|
||||||
type: "VAE",
|
|
||||||
link: 8
|
|
||||||
}
|
|
||||||
],
|
|
||||||
outputs: [
|
|
||||||
{
|
|
||||||
name: "IMAGE",
|
|
||||||
type: "IMAGE",
|
|
||||||
links: [
|
|
||||||
9
|
|
||||||
],
|
|
||||||
slot_index: 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
title: "VAEDecode",
|
|
||||||
properties: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 9,
|
|
||||||
type: "SaveImage",
|
|
||||||
pos: [
|
|
||||||
1451,
|
|
||||||
189
|
|
||||||
],
|
|
||||||
size: [
|
|
||||||
210,
|
|
||||||
82
|
|
||||||
],
|
|
||||||
flags: {},
|
|
||||||
order: 6,
|
|
||||||
mode: 0,
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
name: "images",
|
|
||||||
type: "IMAGE",
|
|
||||||
link: 9
|
|
||||||
}
|
|
||||||
],
|
|
||||||
outputs: [],
|
|
||||||
title: "SaveImage",
|
|
||||||
properties: {},
|
|
||||||
widgets_values: [
|
|
||||||
[],
|
|
||||||
"ComfyUI"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
type: "CheckpointLoaderSimple",
|
|
||||||
pos: [
|
|
||||||
26,
|
|
||||||
474
|
|
||||||
],
|
|
||||||
size: [
|
|
||||||
315,
|
|
||||||
98
|
|
||||||
],
|
|
||||||
flags: {},
|
|
||||||
order: 1,
|
|
||||||
mode: 0,
|
|
||||||
inputs: [],
|
|
||||||
outputs: [
|
|
||||||
{
|
|
||||||
name: "MODEL",
|
|
||||||
type: "MODEL",
|
|
||||||
links: [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
slot_index: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "CLIP",
|
|
||||||
type: "CLIP",
|
|
||||||
links: [
|
|
||||||
3,
|
|
||||||
5
|
|
||||||
],
|
|
||||||
slot_index: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "VAE",
|
|
||||||
type: "VAE",
|
|
||||||
links: [
|
|
||||||
8
|
|
||||||
],
|
|
||||||
slot_index: 2
|
|
||||||
}
|
|
||||||
],
|
|
||||||
title: "CheckpointLoaderSimple",
|
|
||||||
properties: {},
|
|
||||||
widgets_values: [
|
|
||||||
"v1-5-pruned-emaonly.ckpt"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
links: [
|
|
||||||
[
|
|
||||||
1,
|
|
||||||
4,
|
|
||||||
0,
|
|
||||||
3,
|
|
||||||
0,
|
|
||||||
"MODEL"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2,
|
|
||||||
5,
|
|
||||||
0,
|
|
||||||
3,
|
|
||||||
3,
|
|
||||||
"LATENT"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
1,
|
|
||||||
6,
|
|
||||||
0,
|
|
||||||
"CLIP"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4,
|
|
||||||
6,
|
|
||||||
0,
|
|
||||||
3,
|
|
||||||
1,
|
|
||||||
"CONDITIONING"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
5,
|
|
||||||
4,
|
|
||||||
1,
|
|
||||||
7,
|
|
||||||
0,
|
|
||||||
"CLIP"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
6,
|
|
||||||
7,
|
|
||||||
0,
|
|
||||||
3,
|
|
||||||
2,
|
|
||||||
"CONDITIONING"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
7,
|
|
||||||
3,
|
|
||||||
0,
|
|
||||||
8,
|
|
||||||
0,
|
|
||||||
"LATENT"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
8,
|
|
||||||
4,
|
|
||||||
2,
|
|
||||||
8,
|
|
||||||
1,
|
|
||||||
"VAE"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
9,
|
|
||||||
8,
|
|
||||||
0,
|
|
||||||
9,
|
|
||||||
0,
|
|
||||||
"IMAGE"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
groups: [],
|
groups: [],
|
||||||
config: {},
|
config: {},
|
||||||
extra: {},
|
extra: {},
|
||||||
version: 10
|
version: 0
|
||||||
},
|
},
|
||||||
panes: {
|
panes: {}
|
||||||
panels: [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
nodeId: 7
|
|
||||||
},
|
|
||||||
{
|
|
||||||
nodeId: 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
nodeId: 6
|
|
||||||
},
|
|
||||||
{
|
|
||||||
nodeId: 9
|
|
||||||
},
|
|
||||||
{
|
|
||||||
nodeId: 4
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
nodeId: 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defaultGraph;
|
export default defaultGraph;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export interface ComfyWidgetProperties extends Record<string, any> {
|
|||||||
* widget is changed, the value of the first output in the node is updated
|
* widget is changed, the value of the first output in the node is updated
|
||||||
* in the litegraph instance.
|
* in the litegraph instance.
|
||||||
*/
|
*/
|
||||||
export abstract class ComfyWidgetNode<T> extends ComfyGraphNode {
|
export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
|
||||||
abstract properties: ComfyWidgetProperties;
|
abstract properties: ComfyWidgetProperties;
|
||||||
|
|
||||||
value: Writable<T>
|
value: Writable<T>
|
||||||
@@ -134,7 +134,7 @@ export class ComfySliderNode extends ComfyWidgetNode<number> {
|
|||||||
this.setProperty("min", Math.max(this.properties.min, input.config.min))
|
this.setProperty("min", Math.max(this.properties.min, input.config.min))
|
||||||
this.setProperty("max", Math.max(this.properties.max, input.config.max))
|
this.setProperty("max", Math.max(this.properties.max, input.config.max))
|
||||||
this.setProperty("step", Math.max(this.properties.step, input.config.step))
|
this.setProperty("step", Math.max(this.properties.step, input.config.step))
|
||||||
this.setProperty("value", Math.max(Math.min(this.properties.value, this.properties.max), this.properties.min))
|
this.setValue(Math.max(Math.min(this.properties.value, this.properties.max), this.properties.min))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ export interface ComfyComboProperties extends ComfyWidgetProperties {
|
|||||||
|
|
||||||
export class ComfyComboNode extends ComfyWidgetNode<string> {
|
export class ComfyComboNode extends ComfyWidgetNode<string> {
|
||||||
override properties: ComfyComboProperties = {
|
override properties: ComfyComboProperties = {
|
||||||
options: ["*"]
|
options: ["A", "B", "C", "D"]
|
||||||
}
|
}
|
||||||
|
|
||||||
static slotLayout: SlotLayout = {
|
static slotLayout: SlotLayout = {
|
||||||
@@ -164,7 +164,7 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
|
|||||||
override inputWidgetTypes = ["combo", "enum"]
|
override inputWidgetTypes = ["combo", "enum"]
|
||||||
|
|
||||||
constructor(name?: string) {
|
constructor(name?: string) {
|
||||||
super(name, "*")
|
super(name, "A")
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnectOutput(
|
onConnectOutput(
|
||||||
@@ -194,9 +194,9 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
|
|||||||
override clampOneConfig(input: IComfyInputSlot) {
|
override clampOneConfig(input: IComfyInputSlot) {
|
||||||
if (!(this.properties.value in input.config.values)) {
|
if (!(this.properties.value in input.config.values)) {
|
||||||
if (input.config.values.length === 0)
|
if (input.config.values.length === 0)
|
||||||
this.setProperty("value", "")
|
this.setValue("")
|
||||||
else
|
else
|
||||||
this.setProperty("value", input.config.values[0])
|
this.setValue(input.config.values[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { get, writable } from 'svelte/store';
|
|||||||
import type { Readable, Writable } from 'svelte/store';
|
import type { Readable, Writable } from 'svelte/store';
|
||||||
import type ComfyApp from "$lib/components/ComfyApp"
|
import type ComfyApp from "$lib/components/ComfyApp"
|
||||||
import type { LGraphNode, IWidget, LGraph } from "@litegraph-ts/core"
|
import type { LGraphNode, IWidget, LGraph } from "@litegraph-ts/core"
|
||||||
import nodeState from "$lib/state/nodeState";
|
|
||||||
import type { NodeStateStore } from './nodeState';
|
|
||||||
import { dndzone, SHADOW_PLACEHOLDER_ITEM_ID } from 'svelte-dnd-action';
|
import { dndzone, SHADOW_PLACEHOLDER_ITEM_ID } from 'svelte-dnd-action';
|
||||||
import type { ComfyWidgetNode } from '$lib/nodes';
|
import type { ComfyWidgetNode } from '$lib/nodes';
|
||||||
|
|
||||||
@@ -90,8 +88,8 @@ export interface WidgetLayout extends IDragItem {
|
|||||||
type DragItemID = string;
|
type DragItemID = string;
|
||||||
|
|
||||||
type LayoutStateOps = {
|
type LayoutStateOps = {
|
||||||
addContainer: (parentId: DragItemID, attrs: Partial<Attributes>, index: number) => ContainerLayout,
|
addContainer: (parent: ContainerLayout | null, attrs: Partial<Attributes>, index: number) => ContainerLayout,
|
||||||
addWidget: (parentId: DragItemID, node: LGraphNode, widget: IWidget<any, any>, attrs: Partial<Attributes>, index: number) => WidgetLayout,
|
addWidget: (parent: ContainerLayout, node: ComfyWidgetNode, attrs: Partial<Attributes>, index: number) => WidgetLayout,
|
||||||
findDefaultContainerForInsertion: () => ContainerLayout | null,
|
findDefaultContainerForInsertion: () => ContainerLayout | null,
|
||||||
updateChildren: (parent: IDragItem, children: IDragItem[]) => IDragItem[],
|
updateChildren: (parent: IDragItem, children: IDragItem[]) => IDragItem[],
|
||||||
nodeAdded: (node: LGraphNode) => void,
|
nodeAdded: (node: LGraphNode) => void,
|
||||||
@@ -100,7 +98,7 @@ type LayoutStateOps = {
|
|||||||
groupItems: (dragItems: IDragItem[]) => ContainerLayout,
|
groupItems: (dragItems: IDragItem[]) => ContainerLayout,
|
||||||
ungroup: (container: ContainerLayout) => void,
|
ungroup: (container: ContainerLayout) => void,
|
||||||
getCurrentSelection: () => IDragItem[],
|
getCurrentSelection: () => IDragItem[],
|
||||||
clear: () => void,
|
clear: (state?: Partial<LayoutState>) => void,
|
||||||
resetLayout: () => void,
|
resetLayout: () => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +133,7 @@ function findDefaultContainerForInsertion(): ContainerLayout | null {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function addContainer(parentId: DragItemID | null, attrs: Partial<Attributes> = {}, index: number = -1): ContainerLayout {
|
function addContainer(parent: ContainerLayout | null, attrs: Partial<Attributes> = {}, index: number = -1): ContainerLayout {
|
||||||
const state = get(store);
|
const state = get(store);
|
||||||
const dragItem: ContainerLayout = {
|
const dragItem: ContainerLayout = {
|
||||||
type: "container",
|
type: "container",
|
||||||
@@ -148,15 +146,10 @@ function addContainer(parentId: DragItemID | null, attrs: Partial<Attributes> =
|
|||||||
...attrs
|
...attrs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const parent = parentId ? state.allItems[parentId] : null;
|
const entry: DragItemEntry = { dragItem, children: [], parent: null };
|
||||||
const entry: DragItemEntry = { dragItem, children: [], parent: parent?.dragItem };
|
|
||||||
state.allItems[dragItem.id] = entry;
|
state.allItems[dragItem.id] = entry;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
parent.children ||= []
|
moveItem(dragItem, parent)
|
||||||
if (index)
|
|
||||||
parent.children.splice(index, 0, dragItem)
|
|
||||||
else
|
|
||||||
parent.children.push(dragItem)
|
|
||||||
}
|
}
|
||||||
store.set(state)
|
store.set(state)
|
||||||
return dragItem;
|
return dragItem;
|
||||||
@@ -178,7 +171,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: parentEntry.dragItem };
|
const entry: DragItemEntry = { dragItem, children: [], parent: null };
|
||||||
state.allItems[dragItem.id] = entry;
|
state.allItems[dragItem.id] = entry;
|
||||||
moveItem(dragItem, parent)
|
moveItem(dragItem, parent)
|
||||||
return dragItem;
|
return dragItem;
|
||||||
@@ -209,6 +202,7 @@ function nodeAdded(node: LGraphNode) {
|
|||||||
// 2. User adds a node with inputs that can be filled by frontend widgets.
|
// 2. User adds a node with inputs that can be filled by frontend widgets.
|
||||||
// Depending on config, this means we should instantiate default UI nodes connected to those inputs.
|
// Depending on config, this means we should instantiate default UI nodes connected to those inputs.
|
||||||
|
|
||||||
|
console.debug(node)
|
||||||
if ("svelteComponentType" in node) {
|
if ("svelteComponentType" in node) {
|
||||||
addWidget(parent, node as ComfyWidgetNode);
|
addWidget(parent, node as ComfyWidgetNode);
|
||||||
}
|
}
|
||||||
@@ -254,21 +248,25 @@ function nodeRemoved(node: LGraphNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function configureFinished(graph: LGraph) {
|
function configureFinished(graph: LGraph) {
|
||||||
const state = get(store)
|
|
||||||
const id = 0;
|
const id = 0;
|
||||||
|
|
||||||
state.isConfiguring = false;
|
clear({isConfiguring: false})
|
||||||
|
|
||||||
state.root = addContainer(null, { direction: "horizontal", showTitle: false });
|
const root = addContainer(null, { direction: "horizontal", showTitle: false });
|
||||||
const left = addContainer(state.root.id, { direction: "vertical", showTitle: false });
|
const left = addContainer(root, { direction: "vertical", showTitle: false });
|
||||||
const right = addContainer(state.root.id, { direction: "vertical", showTitle: false });
|
const right = addContainer(root, { direction: "vertical", showTitle: false });
|
||||||
|
|
||||||
for (const node of graph._nodes_in_order) {
|
const state = get(store)
|
||||||
|
state.root = root;
|
||||||
|
store.set(state)
|
||||||
|
|
||||||
|
console.debug("[layoutState] configure begin", state, graph)
|
||||||
|
|
||||||
|
for (const node of graph._nodes) {
|
||||||
nodeAdded(node)
|
nodeAdded(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug("[layoutState] configureFinished", state)
|
console.debug("[layoutState] configureFinished", state)
|
||||||
store.set(state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveItem(target: IDragItem, to: ContainerLayout, index: number = -1) {
|
function moveItem(target: IDragItem, to: ContainerLayout, index: number = -1) {
|
||||||
@@ -324,7 +322,7 @@ function groupItems(dragItems: IDragItem[]): ContainerLayout {
|
|||||||
index = indexFound
|
index = indexFound
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = addContainer(parent.id, { title: "Group" }, index)
|
const container = addContainer(parent as ContainerLayout, { title: "Group" }, index)
|
||||||
|
|
||||||
for (const item of dragItems) {
|
for (const item of dragItems) {
|
||||||
moveItem(item, container)
|
moveItem(item, container)
|
||||||
@@ -364,7 +362,7 @@ function ungroup(container: ContainerLayout) {
|
|||||||
store.set(state)
|
store.set(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear(state: Partial<LayoutState> = {}) {
|
||||||
store.set({
|
store.set({
|
||||||
root: null,
|
root: null,
|
||||||
allItems: {},
|
allItems: {},
|
||||||
@@ -372,8 +370,8 @@ function clear() {
|
|||||||
currentSelection: [],
|
currentSelection: [],
|
||||||
isMenuOpen: false,
|
isMenuOpen: false,
|
||||||
isConfiguring: true,
|
isConfiguring: true,
|
||||||
|
...state
|
||||||
})
|
})
|
||||||
addContainer(null, { direction: "horizontal", showTitle: false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetLayout() {
|
function resetLayout() {
|
||||||
|
|||||||
@@ -1,154 +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 node's state */
|
|
||||||
export type NodeUIStateStore = Writable<any>
|
|
||||||
|
|
||||||
export type NodeUIState = {
|
|
||||||
name: string,
|
|
||||||
node: LGraphNode,
|
|
||||||
widgetStates: WidgetUIState[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WidgetDrawState = {
|
|
||||||
isNodeExecuting: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/** store for one widget's state */
|
|
||||||
export type WidgetUIStateStore = Writable<any>
|
|
||||||
|
|
||||||
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<any, any>) => void,
|
|
||||||
findWidgetByName: (nodeId: number, name: string) => WidgetUIState | null,
|
|
||||||
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)
|
|
||||||
|
|
||||||
const widgets = [];
|
|
||||||
|
|
||||||
// if (node.widgets) {
|
|
||||||
// for (const [index, widget] of node.widgets.entries()) {
|
|
||||||
// let isVirtual = false;
|
|
||||||
// if ("isVirtual" in widget)
|
|
||||||
// isVirtual = (widget as ComfyWidget<any, any>).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) {
|
|
||||||
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.title
|
|
||||||
store.set(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureFinished(graph: LGraph) {
|
|
||||||
let state = get(store);
|
|
||||||
|
|
||||||
for (const node of graph.computeExecutionOrder(false, null)) {
|
|
||||||
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<any, any>) {
|
|
||||||
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,
|
|
||||||
nodeAdded,
|
|
||||||
nodeRemoved,
|
|
||||||
nodeStateChanged,
|
|
||||||
configureFinished,
|
|
||||||
widgetStateChanged,
|
|
||||||
findWidgetByName,
|
|
||||||
clear
|
|
||||||
}
|
|
||||||
export default nodeStateStore;
|
|
||||||
@@ -14,7 +14,8 @@ export type UIState = {
|
|||||||
export type WritableUIStateStore = Writable<UIState>;
|
export type WritableUIStateStore = Writable<UIState>;
|
||||||
const store: WritableUIStateStore = writable(
|
const store: WritableUIStateStore = writable(
|
||||||
{
|
{
|
||||||
graphLocked: true,
|
app: null,
|
||||||
|
graphLocked: false,
|
||||||
nodesLocked: false,
|
nodesLocked: false,
|
||||||
uiEditMode: "disabled",
|
uiEditMode: "disabled",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import ComfyApp from "./components/ComfyApp";
|
|||||||
import ComboWidget from "$lib/widgets/ComboWidget.svelte";
|
import ComboWidget from "$lib/widgets/ComboWidget.svelte";
|
||||||
import RangeWidget from "$lib/widgets/RangeWidget.svelte";
|
import RangeWidget from "$lib/widgets/RangeWidget.svelte";
|
||||||
import TextWidget from "$lib/widgets/TextWidget.svelte";
|
import TextWidget from "$lib/widgets/TextWidget.svelte";
|
||||||
import { type WidgetUIState } from "$lib/stores/nodeState";
|
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import layoutState from "$lib/stores/layoutState"
|
import layoutState from "$lib/stores/layoutState"
|
||||||
import type { SvelteComponentDev } from "svelte/internal";
|
import type { SvelteComponentDev } from "svelte/internal";
|
||||||
@@ -21,26 +20,6 @@ export function download(filename: string, text: string, type: string = "text/pl
|
|||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getComponentForWidgetState(item: WidgetUIState): typeof SvelteComponentDev {
|
|
||||||
// custom widgets with TypeScript sources
|
|
||||||
let override = ComfyApp.widget_type_overrides[item.widget.type]
|
|
||||||
if (override) {
|
|
||||||
return override
|
|
||||||
}
|
|
||||||
|
|
||||||
// litegraph.ts built-in widgets
|
|
||||||
switch (item.widget.type) {
|
|
||||||
case "combo":
|
|
||||||
return ComboWidget;
|
|
||||||
case "number":
|
|
||||||
return RangeWidget;
|
|
||||||
case "text":
|
|
||||||
return TextWidget;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function startDrag(evt: MouseEvent) {
|
export function startDrag(evt: MouseEvent) {
|
||||||
const dragItemId: string = evt.target.dataset["dragItemId"];
|
const dragItemId: string = evt.target.dataset["dragItemId"];
|
||||||
const ls = get(layoutState)
|
const ls = get(layoutState)
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { WidgetDrawState, WidgetUIState, WidgetUIStateStore } from "$lib/stores/nodeState";
|
|
||||||
import { BlockTitle } from "@gradio/atoms";
|
import { BlockTitle } from "@gradio/atoms";
|
||||||
import { Dropdown } from "@gradio/form";
|
|
||||||
import Select from 'svelte-select';
|
import Select from 'svelte-select';
|
||||||
import type { ComfyComboNode } from "$lib/nodes/index";
|
import type { ComfyComboNode } from "$lib/nodes/index";
|
||||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||||
@@ -9,32 +7,35 @@
|
|||||||
export let widget: WidgetLayout | null = null;
|
export let widget: WidgetLayout | null = null;
|
||||||
let node: ComfyComboNode | null = null;
|
let node: ComfyComboNode | null = null;
|
||||||
let nodeValue: Writable<string> | null = null;
|
let nodeValue: Writable<string> | null = null;
|
||||||
let itemValue: WidgetUIStateStore | null = null;
|
let option: any
|
||||||
let option: any;
|
|
||||||
|
|
||||||
$: if(widget) {
|
$: widget && setNodeValue(widget);
|
||||||
node = widget.node as ComfyComboNode
|
|
||||||
nodeValue = node.value;
|
|
||||||
updateOption(); // don't react on option
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateOption() {
|
function setNodeValue(widget: WidgetLayout) {
|
||||||
option = get(nodeValue);
|
if(widget && !node) {
|
||||||
|
node = widget.node as ComfyComboNode
|
||||||
|
nodeValue = node.value;
|
||||||
|
setOption($nodeValue) // don't react on option
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (option && itemValue) {
|
function setOption(value: any) {
|
||||||
$itemValue = option.value
|
option = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (nodeValue && option) {
|
||||||
|
$nodeValue = option.value;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="wrapper gr-combo">
|
<div class="wrapper gr-combo">
|
||||||
{#if node !== null && option !== undefined}
|
{#if node !== null && nodeValue !== null}
|
||||||
<label>
|
<label>
|
||||||
<BlockTitle show_label={true}>{widget.attrs.title}</BlockTitle>
|
<BlockTitle show_label={true}>{widget.attrs.title}</BlockTitle>
|
||||||
<Select
|
<Select
|
||||||
bind:value={option}
|
bind:value={option}
|
||||||
bind:items={node.properties.values}
|
bind:items={node.properties.options}
|
||||||
disabled={node.properties.values.length === 0}
|
disabled={node.properties.options.length === 0}
|
||||||
clearable={false}
|
clearable={false}
|
||||||
on:change
|
on:change
|
||||||
on:select
|
on:select
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { ImageViewer } from "$lib/ImageViewer";
|
import { ImageViewer } from "$lib/ImageViewer";
|
||||||
import type { WidgetUIState, WidgetUIStateStore } from "$lib/stores/nodeState";
|
|
||||||
import { Block } from "@gradio/atoms";
|
import { Block } from "@gradio/atoms";
|
||||||
import { Gallery } from "@gradio/gallery";
|
import { Gallery } from "@gradio/gallery";
|
||||||
import type { Styles } from "@gradio/utils";
|
import type { Styles } from "@gradio/utils";
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type { IEnumWidget, IEnumWidgetOptions, INumberWidget, LGraphNode, Widget
|
|||||||
import ComfyWidget from "./ComfyWidget";
|
import ComfyWidget from "./ComfyWidget";
|
||||||
import type { ComfyImageResult } from "$lib/nodes/ComfySaveImageNode";
|
import type { ComfyImageResult } from "$lib/nodes/ComfySaveImageNode";
|
||||||
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
|
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
|
||||||
import nodeState from "$lib/stores/nodeState"
|
|
||||||
|
|
||||||
export interface ComfyValueControlWidgetOptions extends IEnumWidgetOptions {
|
export interface ComfyValueControlWidgetOptions extends IEnumWidgetOptions {
|
||||||
}
|
}
|
||||||
@@ -50,6 +49,6 @@ export default class ComfyValueControlWidget extends ComfyWidget<ComfyValueContr
|
|||||||
if (this.targetWidget.value > max)
|
if (this.targetWidget.value > max)
|
||||||
this.targetWidget.value = max;
|
this.targetWidget.value = max;
|
||||||
|
|
||||||
nodeState.widgetStateChanged(this.node.id, this.targetWidget);
|
// nodeState.widgetStateChanged(this.node.id, this.targetWidget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
|
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
|
||||||
import type { IWidget, LGraphNode, SerializedLGraphNode, Vector2, WidgetCallback, WidgetTypes } from "@litegraph-ts/core";
|
import type { IWidget, LGraphNode, SerializedLGraphNode, Vector2, WidgetCallback, WidgetTypes } from "@litegraph-ts/core";
|
||||||
import nodeState from "$lib/stores/nodeState";
|
|
||||||
|
|
||||||
export default abstract class ComfyWidget<T = any, V = any> implements IWidget<T, V> {
|
export default abstract class ComfyWidget<T = any, V = any> implements IWidget<T, V> {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -27,7 +26,6 @@ export default abstract class ComfyWidget<T = any, V = any> implements IWidget<T
|
|||||||
|
|
||||||
setValue(value: V) {
|
setValue(value: V) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
nodeState.widgetStateChanged(this.node.id, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw?(ctx: CanvasRenderingContext2D, node: LGraphNode, width: number, posY: number, height: number): void;
|
draw?(ctx: CanvasRenderingContext2D, node: LGraphNode, width: number, posY: number, height: number): void;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { WidgetUIStateStore } from "$lib/stores/nodeState";
|
|
||||||
import { TextBox } from "@gradio/form";
|
import { TextBox } from "@gradio/form";
|
||||||
import type { ComfyComboNode } from "$lib/nodes/index";
|
import type { ComfyComboNode } from "$lib/nodes/index";
|
||||||
import { type WidgetLayout } from "$lib/stores/layoutState";
|
import { type WidgetLayout } from "$lib/stores/layoutState";
|
||||||
@@ -18,7 +17,7 @@
|
|||||||
<div class="wrapper gr-textbox">
|
<div class="wrapper gr-textbox">
|
||||||
{#if node !== null && nodeValue !== null}
|
{#if node !== null && nodeValue !== null}
|
||||||
<TextBox
|
<TextBox
|
||||||
bind:value={$itemValue}
|
bind:value={$nodeValue}
|
||||||
label={widget.attrs.title}
|
label={widget.attrs.title}
|
||||||
lines={node.properties.multiline ? 5 : 1}
|
lines={node.properties.multiline ? 5 : 1}
|
||||||
max_lines={node.properties.multiline ? 5 : 1}
|
max_lines={node.properties.multiline ? 5 : 1}
|
||||||
|
|||||||
@@ -1,20 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
|
||||||
import { get } from "svelte/store";
|
|
||||||
import { Pane, Splitpanes } from 'svelte-splitpanes';
|
|
||||||
import { Button } from "@gradio/button";
|
|
||||||
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
||||||
import { Checkbox } from "@gradio/form"
|
|
||||||
import nodeState from "$lib/stores/nodeState";
|
|
||||||
import uiState from "$lib/stores/uiState";
|
import uiState from "$lib/stores/uiState";
|
||||||
import { ImageViewer } from "$lib/ImageViewer";
|
|
||||||
import { download } from "$lib/utils"
|
|
||||||
|
|
||||||
import { LGraph, LGraphNode } from "@litegraph-ts/core";
|
import { Link, Toolbar } from "framework7-svelte"
|
||||||
import type { ComfyAPIStatus } from "$lib/api";
|
|
||||||
import queueState from "$lib/stores/queueState";
|
|
||||||
import { Page, Navbar, Link, BlockTitle, Block, List, ListItem, Toolbar } from "framework7-svelte"
|
|
||||||
import { getComponentForWidgetState } from "$lib/utils"
|
|
||||||
import { f7 } from "framework7-svelte"
|
import { f7 } from "framework7-svelte"
|
||||||
|
|
||||||
export let subworkflowID: number = -1;
|
export let subworkflowID: number = -1;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
import { Button } from "@gradio/button";
|
import { Button } from "@gradio/button";
|
||||||
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
||||||
import { Checkbox } from "@gradio/form"
|
import { Checkbox } from "@gradio/form"
|
||||||
import nodeState from "$lib/stores/nodeState";
|
|
||||||
import uiState from "$lib/stores/uiState";
|
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"
|
||||||
@@ -46,11 +45,6 @@
|
|||||||
return
|
return
|
||||||
app = $uiState.app = new ComfyApp();
|
app = $uiState.app = new ComfyApp();
|
||||||
|
|
||||||
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("autosave", doAutosave);
|
app.eventBus.on("autosave", doAutosave);
|
||||||
app.eventBus.on("restored", doRestore);
|
app.eventBus.on("restored", doRestore);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
import { Button } from "@gradio/button";
|
import { Button } from "@gradio/button";
|
||||||
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
||||||
import { Checkbox } from "@gradio/form"
|
import { Checkbox } from "@gradio/form"
|
||||||
import nodeState from "$lib/stores/nodeState";
|
|
||||||
import uiState from "$lib/stores/uiState";
|
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"
|
||||||
|
|||||||
@@ -1,21 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
|
||||||
import { get } from "svelte/store";
|
|
||||||
import { Pane, Splitpanes } from 'svelte-splitpanes';
|
|
||||||
import { Button } from "@gradio/button";
|
|
||||||
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
import ComfyApp, { type SerializedAppState } from "$lib/components/ComfyApp";
|
||||||
import { Checkbox } from "@gradio/form"
|
|
||||||
import nodeState from "$lib/stores/nodeState";
|
|
||||||
import uiState from "$lib/stores/uiState";
|
import uiState from "$lib/stores/uiState";
|
||||||
import { ImageViewer } from "$lib/ImageViewer";
|
|
||||||
import { download } from "$lib/utils"
|
|
||||||
|
|
||||||
import { LGraph, LGraphNode } from "@litegraph-ts/core";
|
|
||||||
import type { ComfyAPIStatus } from "$lib/api";
|
|
||||||
import queueState from "$lib/stores/queueState";
|
import queueState from "$lib/stores/queueState";
|
||||||
import { Page, Navbar, Link, BlockTitle, Block, List, ListItem, Toolbar } from "framework7-svelte"
|
import { Page, Navbar, Link, BlockTitle, Block, List, ListItem, Toolbar } from "framework7-svelte"
|
||||||
import { getComponentForWidgetState } from "$lib/utils"
|
|
||||||
import { f7 } from "framework7-svelte"
|
|
||||||
|
|
||||||
export let subworkflowID: number = -1;
|
export let subworkflowID: number = -1;
|
||||||
let app: ComfyApp = undefined;
|
let app: ComfyApp = undefined;
|
||||||
@@ -28,21 +16,7 @@
|
|||||||
<Page name="subworkflow">
|
<Page name="subworkflow">
|
||||||
<Navbar title="Workflow {subworkflowID}" backLink="Back" />
|
<Navbar title="Workflow {subworkflowID}" backLink="Back" />
|
||||||
|
|
||||||
{#each Object.entries($nodeState) as [id, ws]}
|
<div>Workflow!</div>
|
||||||
{@const node = app.lGraph.getNodeById(id)}
|
|
||||||
<div class:is-executing={$queueState.runningNodeId === node.id}>
|
|
||||||
<Block>
|
|
||||||
<label for={String(id)}>
|
|
||||||
<BlockTitle>
|
|
||||||
{node.title}
|
|
||||||
</BlockTitle>
|
|
||||||
</label>
|
|
||||||
{#each $nodeState[id].widgetStates as item}
|
|
||||||
<svelte:component this={getComponentForWidgetState(item)} {item} />
|
|
||||||
{/each}
|
|
||||||
</Block>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</Page>
|
</Page>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user