diff --git a/src/lib/ComfyGraphCanvas.ts b/src/lib/ComfyGraphCanvas.ts index bb92061..7e5be29 100644 --- a/src/lib/ComfyGraphCanvas.ts +++ b/src/lib/ComfyGraphCanvas.ts @@ -1,5 +1,7 @@ import { BuiltInSlotShape, LGraph, LGraphCanvas, LGraphNode, LiteGraph, NodeMode, type MouseEventExt, type Vector2, type Vector4 } from "@litegraph-ts/core"; import type ComfyApp from "./components/ComfyApp"; +import queueState from "./stores/queueState"; +import { get } from "svelte/store"; export default class ComfyGraphCanvas extends LGraphCanvas { app: ComfyApp @@ -30,8 +32,10 @@ export default class ComfyGraphCanvas extends LGraphCanvas { ): void { super.drawNodeShape(node, ctx, size, fgColor, bgColor, selected, mouseOver); + let state = get(queueState); + let color = null; - if (node.id === +this.app.runningNodeId) { + if (node.id === +state.runningNodeId) { color = "#0f0"; } else if (this.app.dragOverNode && node.id === this.app.dragOverNode.id) { color = "dodgerblue"; @@ -68,9 +72,9 @@ export default class ComfyGraphCanvas extends LGraphCanvas { ctx.strokeStyle = fgColor; ctx.globalAlpha = 1; - if (this.app.progress) { + if (state.progress) { ctx.fillStyle = "green"; - ctx.fillRect(0, 0, size[0] * (this.app.progress.value / this.app.progress.max), 6); + ctx.fillRect(0, 0, size[0] * (state.progress.value / state.progress.max), 6); ctx.fillStyle = bgColor; } } diff --git a/src/lib/api.ts b/src/lib/api.ts index 519db5d..23fe14b 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -8,6 +8,12 @@ type PromptRequestBody = { export type QueueItemType = "queue" | "history"; +export type ComfyAPIQueueStatus = { + exec_info: { + queue_remaining: number | "X"; + } +} + export default class ComfyAPI extends EventTarget { private registered: Set = new Set(); diff --git a/src/lib/components/ComfyApp.svelte b/src/lib/components/ComfyApp.svelte index 8fd7c99..f216d9b 100644 --- a/src/lib/components/ComfyApp.svelte +++ b/src/lib/components/ComfyApp.svelte @@ -13,10 +13,14 @@ import { LGraph, LGraphNode } from "@litegraph-ts/core"; import LightboxModal from "./LightboxModal.svelte"; import { Block } from "@gradio/atoms"; + import ComfyQueue from "./ComfyQueue.svelte"; + import type { ComfyAPIStatus } from "$lib/api"; + import queueState from "$lib/stores/queueState"; let app: ComfyApp = undefined; let imageViewer: ImageViewer; let uiPane: ComfyUIPane = undefined; + let queue: ComfyQueue = undefined; let mainElem: HTMLDivElement; let containerElem: HTMLDivElement; let nodesLocked: boolean = false; @@ -124,6 +128,10 @@ app.eventBus.on("autosave", doAutosave); app.eventBus.on("restored", doRestore); + app.api.addEventListener("status", (ev: CustomEvent) => { + queueState.statusUpdated(ev.detail as ComfyAPIStatus); + }); + await app.setup(); refreshView(); @@ -160,7 +168,7 @@ diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index d6298aa..6fd816f 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -15,6 +15,7 @@ import type ComfyGraphNode from "$lib/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"; LiteGraph.catch_exceptions = false; @@ -70,9 +71,7 @@ export default class ComfyApp { nodeOutputs: Record = {}; eventBus: TypedEmitter = new EventEmitter() as TypedEmitter; - runningNodeId: number | null = null; dragOverNode: LGraphNode | null = null; - progress: Progress | null = null; shiftDown: boolean = false; selectedGroupMoving: boolean = false; @@ -366,7 +365,7 @@ export default class ComfyApp { * Handles updates from the API socket */ private addApiUpdateHandlers() { - this.api.addEventListener("status", ({ detail }: CustomEvent) => { + this.api.addEventListener("status", ({ detail: ComfyAPIStatus }: CustomEvent) => { // this.ui.setStatus(detail); }); @@ -379,13 +378,12 @@ export default class ComfyApp { }); this.api.addEventListener("progress", ({ detail }: CustomEvent) => { - this.progress = detail; + queueState.progressUpdated(detail); this.lGraph.setDirtyCanvas(true, false); }); this.api.addEventListener("executing", ({ detail }: CustomEvent) => { - this.progress = null; - this.runningNodeId = detail; + queueState.executingUpdated(detail); this.lGraph.setDirtyCanvas(true, false); }); diff --git a/src/lib/components/ComfyPane.svelte b/src/lib/components/ComfyPane.svelte index cc45d95..56d4b6e 100644 --- a/src/lib/components/ComfyPane.svelte +++ b/src/lib/components/ComfyPane.svelte @@ -1,11 +1,13 @@ + +
+
+ {#if $queueState.runningNodeId || $queueState.progress} +
+ Node: {getNodeInfo($queueState.runningNodeId)} +
+
+ +
+ {/if} + {#if typeof $queueState.queueRemaining === "number" && $queueState.queueRemaining > 0} +
+
+ Queued prompts: {$queueState.queueRemaining}. +
+
+ {:else} +
+
+ Nothing queued. +
+
+ {/if} +
+
+ + diff --git a/src/lib/components/ComfyUIPane.svelte b/src/lib/components/ComfyUIPane.svelte index 500a699..5304c3a 100644 --- a/src/lib/components/ComfyUIPane.svelte +++ b/src/lib/components/ComfyUIPane.svelte @@ -6,11 +6,7 @@ import type { SerializedPanes } from "./ComfyApp" import ComfyPane from "./ComfyPane.svelte"; import widgetState, { type WidgetUIState } from "$lib/stores/widgetState"; - - type DragItem = { - id: number, - node: LGraphNode - } + import type { DragItem } from "./ComfyUIPane"; export let app: ComfyApp; let dragConfigured: boolean = false; @@ -84,7 +80,7 @@ // Put everything left over into other columns if (Object.keys(nodeIdToDragItem).length > 0) { - console.warn("Extra panels without ordering found", nodeIdToDragItem) + console.warn("Extra panels without ordering found", nodeIdToDragItem, panels) for (const nodeId in nodeIdToDragItem) { const dragItem = nodeIdToDragItem[nodeId]; const paneIndex = findLeastPopulatedPaneIndex(); diff --git a/src/lib/components/ComfyUIPane.ts b/src/lib/components/ComfyUIPane.ts new file mode 100644 index 0000000..c555e03 --- /dev/null +++ b/src/lib/components/ComfyUIPane.ts @@ -0,0 +1,7 @@ +import type { LGraphNode } from "@litegraph-ts/core" + +export type DragItem = { + id: number, + node: LGraphNode, + isNodeExecuting?: boolean +} diff --git a/src/lib/components/ProgressBar.svelte b/src/lib/components/ProgressBar.svelte new file mode 100644 index 0000000..5bb2b6f --- /dev/null +++ b/src/lib/components/ProgressBar.svelte @@ -0,0 +1,46 @@ + + +
+
+ {text} +
+
+ + diff --git a/src/lib/stores/queueState.ts b/src/lib/stores/queueState.ts new file mode 100644 index 0000000..1b714a0 --- /dev/null +++ b/src/lib/stores/queueState.ts @@ -0,0 +1,53 @@ +import type { ComfyAPIQueueStatus } from "$lib/api"; +import type { Progress } from "$lib/components/ComfyApp"; +import { writable, type Writable } from "svelte/store"; + +export type QueueItem = { + name: string +} + +type QueueStateOps = { + statusUpdated: (status: ComfyAPIQueueStatus) => void, + executingUpdated: (runningNodeId: string | null) => void, + progressUpdated: (progress: Progress | null) => void +} + +export type QueueState = { + queueRemaining: number | "X" | null; + runningNodeId: number | null; + progress: Progress | null +} +type WritableQueueStateStore = Writable & QueueStateOps; + +const store: Writable = writable({ queueRemaining: null, runningNodeId: null, progress: null }) + +function statusUpdated(status: ComfyAPIQueueStatus) { + store.update((s) => { + s.queueRemaining = status.exec_info.queue_remaining; + return s + }) +} + +function executingUpdated(runningNodeId: string | null) { + store.update((s) => { + s.progress = null; + s.runningNodeId = parseInt(runningNodeId); + return s + }) +} + +function progressUpdated(progress: Progress | null) { + store.update((s) => { + s.progress = progress; + return s + }) +} + +const queueStateStore: WritableQueueStateStore = +{ + ...store, + statusUpdated, + executingUpdated, + progressUpdated +} +export default queueStateStore; diff --git a/src/lib/stores/widgetState.ts b/src/lib/stores/widgetState.ts index 7a27bb1..ab984db 100644 --- a/src/lib/stores/widgetState.ts +++ b/src/lib/stores/widgetState.ts @@ -11,6 +11,10 @@ export type WidgetUIState = { isVirtual: boolean } +export type WidgetDrawState = { + isNodeExecuting: boolean +} + type NodeID = number; type WidgetStateOps = { @@ -24,7 +28,7 @@ type WidgetStateOps = { export type WidgetStateStore = Record; type WritableWidgetStateStore = Writable & WidgetStateOps; -const store: Writable> = writable({}) +const store: Writable = writable({}) function clear() { store.set({}) diff --git a/src/lib/widgets/ComboWidget.svelte b/src/lib/widgets/ComboWidget.svelte index d75127c..ed9df2c 100644 --- a/src/lib/widgets/ComboWidget.svelte +++ b/src/lib/widgets/ComboWidget.svelte @@ -1,5 +1,5 @@