Fix mobile
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -22,12 +22,14 @@
|
||||
let queueCompleted: Writable<CompletedQueueEntry[]> | 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; */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -181,6 +181,19 @@ function findEntryInPending(promptID: PromptID): [number, QueueEntry | null, Wri
|
||||
return [-1, null, null]
|
||||
}
|
||||
|
||||
function moveToRunning(index: number, queue: Writable<QueueEntry[]>) {
|
||||
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<QueueEntry[]>, 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
|
||||
})
|
||||
|
||||
@@ -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;
|
||||
</script>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user