From fa16d1ee38f0b1a8c034fb754393691d11f0247a Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sun, 21 May 2023 20:43:47 -0500 Subject: [PATCH] Fix mobile --- README.md | 4 ++- src/lib/components/ComfyQueue.svelte | 46 +++++++++++++++++++--------- src/lib/convertVanillaWorkflow.ts | 10 +++--- src/lib/stores/queueState.ts | 18 ++++++++++- src/mobile/routes/subworkflow.svelte | 3 +- src/scss/global.scss | 3 +- 6 files changed, 60 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index e7c78fd..889f090 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,9 @@ ComfyBox is a frontend to Stable Diffusion that lets you create custom image gen ## Usage -You can import your existing workflows from ComfyUI into ComfyBox by simply clicking `Load` and choosing the `.json` or `.png` with embedded metadata, or dropping either file onto the graph viewer. +A preconfigured workflow is included for the most common txt2img and img2img use cases, so all it takes to start generating is clicking `Load Default` to load the default workflow and then `Queue Prompt`. + +You can import your existing workflows from ComfyUI into ComfyBox by clicking `Load` and choosing the `.json` or `.png` with embedded metadata, or dropping either file onto the graph viewer. ## NOTE diff --git a/src/lib/components/ComfyQueue.svelte b/src/lib/components/ComfyQueue.svelte index 6c4d320..50e2c0d 100644 --- a/src/lib/components/ComfyQueue.svelte +++ b/src/lib/components/ComfyQueue.svelte @@ -22,12 +22,14 @@ let queueCompleted: Writable | null = null; let queueList: HTMLDivElement | null = null; + type QueueUIEntryStatus = QueueEntryStatus | "pending" | "running"; + type QueueUIEntry = { entry: QueueEntry, message: string, submessage: string, date?: string, - status: QueueEntryStatus | "pending" | "running", + status: QueueUIEntryStatus, images?: string[], // URLs details?: string // shown in a tooltip on hover } @@ -39,21 +41,29 @@ } let mode: QueueItemType = "queue"; + let changed = true; function switchMode(newMode: QueueItemType) { - const changed = mode !== newMode + changed = mode !== newMode mode = newMode - if (changed) + if (changed) { + _queuedEntries = [] + _runningEntries = [] _entries = [] + } } + let _queuedEntries: QueueUIEntry[] = [] + let _runningEntries: QueueUIEntry[] = [] let _entries: QueueUIEntry[] = [] - $: if (mode === "queue" && $queuePending && $queuePending.length != _entries.length) { + $: if (mode === "queue" && (changed || ($queuePending && $queuePending.length != _queuedEntries.length))) { updateFromQueue(); + changed = false; } - else if (mode === "history" && $queueCompleted && $queueCompleted.length != _entries.length) { + else if (mode === "history" && (changed || ($queueCompleted && $queueCompleted.length != _entries.length))) { updateFromHistory(); + changed = false; } function formatDate(date: Date): string { @@ -62,7 +72,7 @@ return [time, day].join(", ") } - function convertEntry(entry: QueueEntry): QueueUIEntry { + function convertEntry(entry: QueueEntry, status: QueueUIEntryStatus): QueueUIEntry { let date = entry.finishedAt || entry.queuedAt; let dateStr = null; if (date) { @@ -93,13 +103,13 @@ message, submessage, date: dateStr, - status: "pending", + status, images: [] } } - function convertPendingEntry(entry: QueueEntry): QueueUIEntry { - const result = convertEntry(entry); + function convertPendingEntry(entry: QueueEntry, status: QueueUIEntryStatus): QueueUIEntry { + const result = convertEntry(entry, status); const thumbnails = entry.extraData?.thumbnails if (thumbnails) { @@ -110,8 +120,7 @@ } function convertCompletedEntry(entry: CompletedQueueEntry): QueueUIEntry { - const result = convertEntry(entry.entry); - result.status = entry.status; + const result = convertEntry(entry.entry, entry.status); const images = Object.values(entry.entry.outputs).flatMap(o => o.images) .map(convertComfyOutputToComfyURL); @@ -128,12 +137,15 @@ } async function updateFromQueue() { - _entries = $queuePending.map(convertPendingEntry).reverse(); // newest entries appear at the top + // newest entries appear at the top + _queuedEntries = $queuePending.map((e) => convertPendingEntry(e, "pending")).reverse(); + _runningEntries = $queueRunning.map((e) => convertPendingEntry(e, "running")).reverse(); + _entries = [..._queuedEntries, ..._runningEntries] if (queueList) { await tick(); // Wait for list size to be recalculated queueList.scroll({ top: queueList.scrollHeight }) } - console.warn("[ComfyQueue] BUILDQUEUE", _entries, $queuePending) + console.warn("[ComfyQueue] BUILDQUEUE", _entries, $queuePending, $queueRunning) } async function updateFromHistory() { @@ -368,6 +380,10 @@ &:hover:not(:has(img:hover)) { cursor: pointer; background: var(--block-background-fill); + + &.running { + background: var(--comfy-accent-soft); + } } &.success { @@ -382,10 +398,10 @@ color: var(--comfy-disable-textbox-text-color); } &.running { - /* background: lightblue; */ + background: var(--block-background-fill); + border: 3px dashed var(--neutral-500); } &.pending, &.unknown { - /* background: orange; */ } } diff --git a/src/lib/convertVanillaWorkflow.ts b/src/lib/convertVanillaWorkflow.ts index 6f73e7b..4b4ca7a 100644 --- a/src/lib/convertVanillaWorkflow.ts +++ b/src/lib/convertVanillaWorkflow.ts @@ -159,7 +159,6 @@ function rewriteIDsInGraph(vanillaWorkflow: ComfyVanillaWorkflow) { link[3] = getNodeID(link[3]) } - // Recurse! for (const node of vanillaWorkflow.nodes) { if (node.type === "graph/subgraph") { @@ -170,7 +169,8 @@ function rewriteIDsInGraph(vanillaWorkflow: ComfyVanillaWorkflow) { /* * Returns [nodeType, inputType, addedWidgetCount] for a config type, like "FLOAT" -> ["ui/number", "number", 1] - * For "INT:seed" it's ["ui/number", "number", 2] since that type adds a randomizer combo widget + * For "INT:seed" it's ["ui/number", "number", 2] since that type adds a randomizer combo widget, + * so there will be 2 total widgets */ function getWidgetTypesFromConfig(inputName: string, inputType: ComfyNodeDefInputType): [string, SlotType, number] | null { let widgetNodeType = null; @@ -266,6 +266,7 @@ function convertPrimitiveNode(vanillaWorkflow: ComfyVanillaWorkflow, node: Seria widgetNodeType, value); + // Set the UI node's min/max/step from the node def configureWidgetNodeProperties(serWidgetNode, widgetOpts) let foundTitle = null; @@ -276,7 +277,6 @@ function convertPrimitiveNode(vanillaWorkflow: ComfyVanillaWorkflow, node: Seria const newLinkOutputSlot = serWidgetNode.outputs.findIndex(o => o.name === comfyWidgetNode.outputSlotName) if (newLinkOutputSlot !== -1) { const newLinkOutput = serWidgetNode.outputs[newLinkOutputSlot]; - // TODO other links need pruning? for (const linkID of mainOutput.links) { const link = vanillaWorkflow.links.find(l => l[0] === linkID) if (link) { @@ -293,6 +293,8 @@ function convertPrimitiveNode(vanillaWorkflow: ComfyVanillaWorkflow, node: Seria // Make sure that the input type for the connected inputs is correct. // ComfyUI seems to set them to the input def type instead of the litegraph type. // For example a "number" input gets changed to type "INT" or "FLOAT" + // Also ensure the input is marked for serialization, else there + // will be random prompt validation errors on the backend link[5] = widgetInputType // link data type if (foundInput != null) { foundInput.type = widgetInputType; @@ -343,8 +345,6 @@ function removeSerializedNode(vanillaWorkflow: SerializedLGraph, node: Serialize /* * Converts a workflow saved with vanilla ComfyUI into a ComfyBox workflow, * adding UI nodes for each widget. - * - * TODO: test this! */ export default function convertVanillaWorkflow(vanillaWorkflow: ComfyVanillaWorkflow, attrs: WorkflowAttributes): [ComfyWorkflow, WritableLayoutStateStore] { const [comfyBoxWorkflow, layoutState] = ComfyWorkflow.create(); diff --git a/src/lib/stores/queueState.ts b/src/lib/stores/queueState.ts index 4f563c2..979d129 100644 --- a/src/lib/stores/queueState.ts +++ b/src/lib/stores/queueState.ts @@ -181,6 +181,19 @@ function findEntryInPending(promptID: PromptID): [number, QueueEntry | null, Wri return [-1, null, null] } +function moveToRunning(index: number, queue: Writable) { + const state = get(store) + + const entry = get(queue)[index]; + console.debug("[queueState] Move to running", entry.promptID, index) + // entry.startedAt = new Date() // Now + queue.update(qp => { qp.splice(index, 1); return qp }); + state.queueRunning.update(qr => { qr.push(entry); return qr }) + + state.isInterrupting = false; + store.set(state) +} + function moveToCompleted(index: number, queue: Writable, status: QueueEntryStatus, message?: string, error?: string) { const state = get(store) @@ -298,9 +311,12 @@ function executionStart(promptID: PromptID) { const [index, entry, queue] = findEntryInPending(promptID); if (entry == null) { const entry = createNewQueueEntry(promptID); - s.queuePending.update(qp => { qp.push(entry); return qp }) + s.queueRunning.update(qr => { qr.push(entry); return qr }) console.debug("[queueState] ADD PROMPT", promptID) } + else { + moveToRunning(index, queue) + } s.isInterrupting = false; return s }) diff --git a/src/mobile/routes/subworkflow.svelte b/src/mobile/routes/subworkflow.svelte index 9f52163..a05ac90 100644 --- a/src/mobile/routes/subworkflow.svelte +++ b/src/mobile/routes/subworkflow.svelte @@ -2,9 +2,9 @@ import { Page, Navbar, Link, BlockTitle, Block, List, ListItem, Toolbar } from "framework7-svelte" import WidgetContainer from "$lib/components/WidgetContainer.svelte"; import type ComfyApp from "$lib/components/ComfyApp"; - import type { ComfyWorkflow } from "$lib/components/ComfyApp"; import { writable, type Writable } from "svelte/store"; import type { WritableLayoutStateStore } from "$lib/stores/layoutStates"; + import workflowState, { type ComfyWorkflow } from "$lib/stores/workflowState"; export let subworkflowID: number = -1; export let app: ComfyApp @@ -13,6 +13,7 @@ let workflow: ComfyWorkflow | null = null let layoutState: WritableLayoutStateStore | null = null; + $: workflow = $workflowState.activeWorkflow; $: layoutState = workflow ? workflow.layout : null; diff --git a/src/scss/global.scss b/src/scss/global.scss index ae2abd8..ed2d51c 100644 --- a/src/scss/global.scss +++ b/src/scss/global.scss @@ -31,6 +31,7 @@ body { --comfy-splitpanes-background-fill: var(--secondary-100); --comfy-splitpanes-background-fill-hover: var(--secondary-300); --comfy-splitpanes-background-fill-active: var(--secondary-400); + --comfy-dropdown-list-background: white; --comfy-dropdown-item-color-hover: white; --comfy-dropdown-item-background-hover: var(--neutral-400); --comfy-dropdown-item-color-active: var(--neutral-100); @@ -107,7 +108,7 @@ hr { color: var(--panel-border-color); } -input, textarea { +input:not(input[type=radio]), textarea { border-radius: 0 !important; }