From f793630064ae0e904f88b2b2ee29dfa480f4e736 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Wed, 24 May 2023 14:35:58 -0500 Subject: [PATCH] Parse template, refactor layout panes --- package.json | 3 +- pnpm-lock.yaml | 31 +- src/lib/ComfyBoxTemplate.ts | 101 +- src/lib/ComfyGraph.ts | 3 +- src/lib/ComfyGraphCanvas.ts | 99 +- src/lib/DanbooruTags.ts | 1 - src/lib/components/AccordionContainer.svelte | 3 +- src/lib/components/BlockContainer.svelte | 2 + src/lib/components/ComfyApp.ts | 20 + .../components/ComfyBoxWorkflowView.svelte | 99 +- .../components/ComfyBoxWorkflowsView.svelte | 1085 ++++++++--------- src/lib/components/ComfyGraphView.svelte | 6 +- src/lib/components/ComfyPaneView.svelte | 43 + src/lib/components/ComfyQueue.svelte | 4 +- src/lib/components/ImageUpload.svelte | 2 - src/lib/components/TabsContainer.svelte | 3 +- .../components/modal/EditTemplateModal.svelte | 117 ++ .../modal/SerializedLayoutPreviewNode.svelte | 48 + src/lib/stores/interfaceState.ts | 4 + src/lib/stores/uiState.ts | 2 +- src/scss/global.scss | 7 +- tsconfig.json | 1 + 22 files changed, 1028 insertions(+), 656 deletions(-) create mode 100644 src/lib/components/ComfyPaneView.svelte create mode 100644 src/lib/components/modal/EditTemplateModal.svelte create mode 100644 src/lib/components/modal/SerializedLayoutPreviewNode.svelte diff --git a/package.json b/package.json index a666beb..4df6182 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "@litegraph-ts/tsconfig": "workspace:*", "@sveltejs/vite-plugin-svelte": "^2.1.1", "@tsconfig/svelte": "^4.0.1", + "@types/dompurify": "^3.0.2", "@zerodevx/svelte-json-view": "^1.0.5", "canvas-to-svg": "^1.0.3", "cm6-theme-basic-dark": "^0.2.0", @@ -79,6 +80,7 @@ "codemirror": "^6.0.1", "csv": "^6.3.0", "csv-parse": "^5.3.10", + "dompurify": "^3.0.3", "events": "^3.3.0", "framework7": "^8.0.3", "framework7-svelte": "^8.0.3", @@ -88,7 +90,6 @@ "radix-icons-svelte": "^1.2.1", "style-mod": "^4.0.3", "svelte-bootstrap-icons": "^2.3.1", - "svelte-codemirror-editor": "^1.1.0", "svelte-feather-icons": "^4.0.0", "svelte-preprocess": "^5.0.3", "svelte-select": "^5.5.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77eda1e..b7cfc57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,6 +94,9 @@ importers: '@tsconfig/svelte': specifier: ^4.0.1 version: 4.0.1 + '@types/dompurify': + specifier: ^3.0.2 + version: 3.0.2 '@zerodevx/svelte-json-view': specifier: ^1.0.5 version: 1.0.5(svelte@3.58.0) @@ -115,6 +118,9 @@ importers: csv-parse: specifier: ^5.3.10 version: 5.3.10 + dompurify: + specifier: ^3.0.3 + version: 3.0.3 events: specifier: ^3.3.0 version: 3.3.0 @@ -142,9 +148,6 @@ importers: svelte-bootstrap-icons: specifier: ^2.3.1 version: 2.3.1 - svelte-codemirror-editor: - specifier: ^1.1.0 - version: 1.1.0(codemirror@6.0.1) svelte-feather-icons: specifier: ^4.0.0 version: 4.0.0 @@ -3235,6 +3238,12 @@ packages: resolution: {integrity: sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==} dev: true + /@types/dompurify@3.0.2: + resolution: {integrity: sha512-YBL4ziFebbbfQfH5mlC+QTJsvh0oJUrWbmxKMyEdL7emlHJqGR2Qb34TEFKj+VCayBvjKy3xczMFNhugThUsfQ==} + dependencies: + '@types/trusted-types': 2.0.3 + dev: false + /@types/estree@1.0.1: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} @@ -3318,6 +3327,10 @@ packages: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true + /@types/trusted-types@2.0.3: + resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} + dev: false + /@types/uuid@9.0.1: resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==} dev: false @@ -4562,6 +4575,10 @@ packages: domelementtype: 2.3.0 dev: false + /dompurify@3.0.3: + resolution: {integrity: sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ==} + dev: false + /domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} dependencies: @@ -8097,14 +8114,6 @@ packages: - sugarss dev: true - /svelte-codemirror-editor@1.1.0(codemirror@6.0.1): - resolution: {integrity: sha512-wFdMIsZds5qzn3x2NbFUxDVU6Cn3rwFdq0035ypaFVgzTjJ90bnPm6IbrFA4OJz1ngIyfbIuPAPDjm7rJIr0gg==} - peerDependencies: - codemirror: ^6.0.0 - dependencies: - codemirror: 6.0.1 - dev: false - /svelte-dnd-action@0.9.22(svelte@3.58.0): resolution: {integrity: sha512-lOQJsNLM1QWv5mdxIkCVtk6k4lHCtLgfE59y8rs7iOM6erchbLC9hMEFYSveZz7biJV0mpg7yDSs4bj/RT/YkA==} peerDependencies: diff --git a/src/lib/ComfyBoxTemplate.ts b/src/lib/ComfyBoxTemplate.ts index 9b6e2fd..07e52ea 100644 --- a/src/lib/ComfyBoxTemplate.ts +++ b/src/lib/ComfyBoxTemplate.ts @@ -1,4 +1,4 @@ -import { Subgraph, type LGraphNode, type LLink, type SerializedLGraphNode, type SerializedLLink, LGraph } from "@litegraph-ts/core" +import { Subgraph, type LGraphNode, type LLink, type SerializedLGraphNode, type SerializedLLink, LGraph, type NodeID } from "@litegraph-ts/core" import layoutStates, { isComfyWidgetNode, type ContainerLayout, type SerializedDragEntry, type WidgetLayout, type DragItemID, type WritableLayoutStateStore, type DragItemEntry, type SerializedLayoutState } from "./stores/layoutStates" import type { ComfyWidgetNode } from "./nodes/widgets" import type ComfyGraphCanvas from "./ComfyGraphCanvas" @@ -11,18 +11,27 @@ import { download } from "./utils"; */ export type ComfyBoxTemplate = { version: 1, + metadata: ComfyBoxTemplateMetadata, nodes: LGraphNode[], links: LLink[], container?: DragItemEntry } +export type SerializedTemplateLink = [NodeID, number, NodeID, number]; + /* * In ComfyBox a template contains a subset of nodes in the graph and the set of * components they represent in the UI. */ export type SerializedComfyBoxTemplate = { + isComfyBoxTemplate: true, version: 1, + /* + * Serialized metadata + */ + metadata: ComfyBoxTemplateMetadata, + /* * Serialized nodes */ @@ -31,7 +40,7 @@ export type SerializedComfyBoxTemplate = { /* * Serialized inner links */ - links: SerializedLLink[], + links: SerializedTemplateLink[], /* * Serialized container type drag item @@ -39,8 +48,27 @@ export type SerializedComfyBoxTemplate = { layout?: SerializedLayoutState } -export type SerializedComfyBoxTemplateData = { - comfyBoxTemplate: SerializedComfyBoxTemplate +function isSerializedComfyBoxTemplate(param: any): param is SerializedComfyBoxTemplate { + return param && param.isComfyBoxTemplate; +} + +export type SerializedComfyBoxTemplateAndSVG = { + template: SerializedComfyBoxTemplate, + svg: string, +} + +export type ComfyBoxTemplateMetadata = { + title: string, + author: string, + tags: string[], + + // TODO required/optional python extensions +} + +const DEFAULT_TEMPLATE_METADATA = { + title: "New Template", + author: "Anonymous", + tags: [] } export type ComfyBoxTemplateError = { @@ -117,7 +145,7 @@ function unescapeXml(safe) { const TEMPLATE_SVG_PADDING: number = 50; -function renderSvg(canvas: ComfyGraphCanvas, graph: LGraph, extraData: any, padding: number): string { +function renderSvg(canvas: ComfyGraphCanvas, graph: LGraph, extraData: SerializedComfyBoxTemplate, padding: number): string { // Calculate the min max bounds for the nodes on the graph const bounds = graph._nodes.reduce( (p, n) => { @@ -227,7 +255,7 @@ function renderSvg(canvas: ComfyGraphCanvas, graph: LGraph, extraData: any, padd return svg } -function pruneDetachedLinks(nodes: SerializedLGraphNode[], links: SerializedLLink[]): [SerializedLGraphNode[], SerializedLLink[]] { +function pruneDetachedLinks(nodes: SerializedLGraphNode[], links: SerializedTemplateLink[]): [SerializedLGraphNode[], SerializedTemplateLink[]] { const nodeIds = new Set(nodes.map(n => n.id)); for (const node of nodes) { @@ -247,13 +275,17 @@ function pruneDetachedLinks(nodes: SerializedLGraphNode[], links: SerializedLLin } links = links.filter(l => { - return nodeIds.has(l[1]) && nodeIds.has(l[3]); + return nodeIds.has(l[0]) && nodeIds.has(l[2]); }) return [nodes, links] } -export function serializeTemplate(canvas: ComfyGraphCanvas, template: ComfyBoxTemplate): SerializedComfyBoxTemplate { +function convLinkForTemplate(link: LLink): SerializedTemplateLink { + return [link.origin_id, link.origin_slot, link.target_id, link.target_slot]; +} + +export function serializeTemplate(canvas: ComfyGraphCanvas, template: ComfyBoxTemplate): SerializedComfyBoxTemplateAndSVG { let graph: LGraph if (template.nodes.length === 1 && template.nodes[0].is(Subgraph)) { graph = template.nodes[0].subgraph @@ -266,27 +298,56 @@ export function serializeTemplate(canvas: ComfyGraphCanvas, template: ComfyBoxTe if (layoutState == null) throw "Couldn't find layout for template being serialized!" + const metadata = template.metadata; let nodes = template.nodes.map(n => n.serialize()); - let links = template.links.map(l => l.serialize()); + let links = template.links.map(convLinkForTemplate); const layout = layoutState.serializeAtRoot(template.container.dragItem.id); [nodes, links] = pruneDetachedLinks(nodes, links); - let comfyBoxTemplate: SerializedComfyBoxTemplate = { + let serTemplate: SerializedComfyBoxTemplate = { + isComfyBoxTemplate: true, version: 1, - nodes: nodes, - links: links, - layout: layout + metadata, + nodes, + links, + layout, } - let templateData: SerializedComfyBoxTemplateData = { - comfyBoxTemplate - } + const svg = renderSvg(canvas, graph, serTemplate, TEMPLATE_SVG_PADDING) - const svg = renderSvg(canvas, graph, templateData, TEMPLATE_SVG_PADDING) - download("workflow.svg", svg, "image/svg+xml"); + return { svg, template: serTemplate } +} - return comfyBoxTemplate +export function deserializeTemplate(file: File): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = async () => { + const svg = reader.result as string; + let template = null; + + // Extract embedded workflow from desc tags + const descEnd = svg.lastIndexOf(""); + if (descEnd !== -1) { + const descStart = svg.lastIndexOf("", descEnd); + if (descStart !== -1) { + const json = svg.substring(descStart + 6, descEnd); + template = JSON.parse(unescapeXml(json)); + } + } + + if (!isSerializedComfyBoxTemplate(template)) { + reject("Invalid template format!") + } + else { + const result: SerializedComfyBoxTemplateAndSVG = { + svg, template + } + resolve(result) + } + }; + reader.readAsText(file); + }); } @@ -325,6 +386,7 @@ export function createTemplate(nodes: LGraphNode[]): ComfyBoxTemplateResult { return { version: 1, + metadata: { ...DEFAULT_TEMPLATE_METADATA }, nodes: nodes, links: links, container: container @@ -334,6 +396,7 @@ export function createTemplate(nodes: LGraphNode[]): ComfyBoxTemplateResult { // No UI to serialize. return { version: 1, + metadata: { ...DEFAULT_TEMPLATE_METADATA }, nodes: nodes, links: links, } diff --git a/src/lib/ComfyGraph.ts b/src/lib/ComfyGraph.ts index dcb9646..d159df6 100644 --- a/src/lib/ComfyGraph.ts +++ b/src/lib/ComfyGraph.ts @@ -1,4 +1,4 @@ -import { LConnectionKind, LGraph, LGraphNode, type INodeSlot, type SlotIndex, LiteGraph, getStaticProperty, type LGraphAddNodeOptions, LGraphCanvas, type LGraphRemoveNodeOptions, Subgraph, type LGraphAddNodeMode } from "@litegraph-ts/core"; +import { LConnectionKind, LGraph, LGraphNode, type INodeSlot, type SlotIndex, LiteGraph, getStaticProperty, type LGraphAddNodeOptions, LGraphCanvas, type LGraphRemoveNodeOptions, Subgraph, type LGraphAddNodeMode, type SerializedLGraphNode, type Vector2 } from "@litegraph-ts/core"; import GraphSync from "./GraphSync"; import EventEmitter from "events"; import type TypedEmitter from "typed-emitter"; @@ -13,6 +13,7 @@ import type { WritableLayoutStateStore } from "./stores/layoutStates"; import layoutStates from "./stores/layoutStates"; import type { ComfyBoxWorkflow, WorkflowInstID } from "./stores/workflowState"; import workflowState from "./stores/workflowState"; +import type { SerializedComfyBoxTemplate } from "./ComfyBoxTemplate"; type ComfyGraphEvents = { configured: (graph: LGraph) => void diff --git a/src/lib/ComfyGraphCanvas.ts b/src/lib/ComfyGraphCanvas.ts index f70ecc0..1bc57fc 100644 --- a/src/lib/ComfyGraphCanvas.ts +++ b/src/lib/ComfyGraphCanvas.ts @@ -1,4 +1,4 @@ -import { BuiltInSlotShape, LGraphCanvas, LGraphNode, LLink, LiteGraph, NodeMode, Subgraph, TitleMode, type ContextMenuItem, type IContextMenuItem, type NodeID, type Vector2, type Vector4, type MouseEventExt, ContextMenu } from "@litegraph-ts/core"; +import { BuiltInSlotShape, LGraphCanvas, LGraphNode, LLink, LiteGraph, NodeMode, Subgraph, TitleMode, type ContextMenuItem, type IContextMenuItem, type NodeID, type Vector2, type Vector4, type MouseEventExt, ContextMenu, type SerializedLGraphNode } from "@litegraph-ts/core"; import { get, type Unsubscriber } from "svelte/store"; import type ComfyGraph from "./ComfyGraph"; import type ComfyApp from "./components/ComfyApp"; @@ -6,14 +6,40 @@ import { ComfyReroute } from "./nodes"; import layoutStates from "./stores/layoutStates"; import queueState from "./stores/queueState"; import selectionState from "./stores/selectionState"; -import { createTemplate, type ComfyBoxTemplate, serializeTemplate } from "./ComfyBoxTemplate"; +import { createTemplate, type ComfyBoxTemplate, serializeTemplate, type SerializedComfyBoxTemplate } from "./ComfyBoxTemplate"; import notify from "./notify"; +import { v4 as uuidv4 } from "uuid" +import { download } from "./utils"; export type SerializedGraphCanvasState = { offset: Vector2, scale: number } +function getMinPos(nodes: SerializedLGraphNode[]): Vector2 { + var posMin: Vector2 = [0, 0] + var posMinIndexes: [number, number] | null = null; + + for (var i = 0; i < nodes.length; ++i) { + if (posMin) { + if (posMin[0] > nodes[i].pos[0]) { + posMin[0] = nodes[i].pos[0]; + posMinIndexes[0] = i; + } + if (posMin[1] > nodes[i].pos[1]) { + posMin[1] = nodes[i].pos[1]; + posMinIndexes[1] = i; + } + } + else { + posMin = [nodes[i].pos[0], nodes[i].pos[1]]; + posMinIndexes = [i, i]; + } + } + + return posMin; +} + export default class ComfyGraphCanvas extends LGraphCanvas { app: ComfyApp | null; private _unsubscribe: Unsubscriber; @@ -439,19 +465,49 @@ export default class ComfyGraphCanvas extends LGraphCanvas { this.graph.add(subgraph) } - override getCanvasMenuOptions(): ContextMenuItem[] { - const options = super.getCanvasMenuOptions(); + /* + * Inserts a ComfyBox template. Logic is similar to pasting from the + * clipboard in vanilla litegraph. + */ + insertTemplate(template: SerializedComfyBoxTemplate, pos: Vector2) { + const minPos = getMinPos(template.nodes); - options.push( - { - content: "Convert to Subgraph", - has_submenu: false, - disabled: Object.keys(this.selected_nodes).length === 0, - callback: this.convertToSubgraph.bind(this) - }, - ) + const templateNodeIDToNewNode: Record = {} - return options + var nodes = []; + for (var i = 0; i < template.nodes.length; ++i) { + var node_data = template.nodes[i]; + var node = LiteGraph.createNode(node_data.type); + if (node) { + const prevNodeId = node_data.id; + node_data.id = uuidv4(); + templateNodeIDToNewNode[prevNodeId] = node + + node.configure(node_data); + + node.pos[0] += pos[0] - minPos[0]; //+= 5; + node.pos[1] += pos[1] - minPos[1]; //+= 5; + + this.graph.add(node, { doProcessChange: false, addedBy: "template" as any }); + + nodes.push(node); + } + } + + //create links + for (var i = 0; i < template.links.length; ++i) { + var link_info = template.links[i]; + var origin_node = templateNodeIDToNewNode[link_info[0]]; + var target_node = templateNodeIDToNewNode[link_info[2]]; + if (origin_node && target_node) + origin_node.connect(link_info[1], target_node, link_info[3]); + else + console.error("[ComfyGraphCanvas] nodes missing on template insertion!", link_info); + } + + this.selectNodes(nodes); + + this.graph.afterChange(); } saveAsTemplate(_value: IContextMenuItem, _options, mouseEvent, prevMenu, node?: LGraphNode) { @@ -470,6 +526,23 @@ export default class ComfyGraphCanvas extends LGraphCanvas { console.warn("TEMPLATEFOUND", template) const serialized = serializeTemplate(this, template); + + download("template.svg", serialized.svg, "image/svg+xml"); + } + + override getCanvasMenuOptions(): ContextMenuItem[] { + const options = super.getCanvasMenuOptions(); + + options.push( + { + content: "Convert to Subgraph", + has_submenu: false, + disabled: Object.keys(this.selected_nodes).length === 0, + callback: this.convertToSubgraph.bind(this) + }, + ) + + return options } override getNodeMenuOptions(node: LGraphNode): ContextMenuItem[] { diff --git a/src/lib/DanbooruTags.ts b/src/lib/DanbooruTags.ts index 4fbb3dc..27ddcc7 100644 --- a/src/lib/DanbooruTags.ts +++ b/src/lib/DanbooruTags.ts @@ -116,7 +116,6 @@ export default class DanbooruTags { }) console.log(`Parsed ${this.tags.length} tags in ${time / 1000}ms.`) - console.error(this.tags[0]) } autocomplete(context: CompletionContext): CompletionResult { diff --git a/src/lib/components/AccordionContainer.svelte b/src/lib/components/AccordionContainer.svelte index cde9290..6dbda8c 100644 --- a/src/lib/components/AccordionContainer.svelte +++ b/src/lib/components/AccordionContainer.svelte @@ -219,8 +219,7 @@ .animation-wrapper { position: relative; - flex-grow: 100; - flex-basis: 0; + flex: 1 1 0%; } .handle-widget:hover { diff --git a/src/lib/components/BlockContainer.svelte b/src/lib/components/BlockContainer.svelte index 5d230b5..1fb5072 100644 --- a/src/lib/components/BlockContainer.svelte +++ b/src/lib/components/BlockContainer.svelte @@ -261,7 +261,9 @@ .animation-wrapper { position: relative; + flex-basis: 0%; flex-grow: 100; + flex-shrink: 100; } .handle-hidden { diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index 93d929a..27e5be7 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -5,6 +5,7 @@ import A1111PromptModal from "./modal/A1111PromptModal.svelte"; import ConfirmConvertWithMissingNodeTypesModal from "./modal/ConfirmConvertWithMissingNodeTypesModal.svelte"; import MissingNodeTypesModal from "./modal/MissingNodeTypesModal.svelte"; import WorkflowLoadErrorModal from "./modal/WorkflowLoadErrorModal.svelte"; +import EditTemplateModal from "./modal/EditTemplateModal.svelte"; import * as nodes from "$lib/nodes/index"; @@ -35,6 +36,7 @@ import { type SvelteComponentDev } from "svelte/internal"; import { get, writable, type Writable } from "svelte/store"; import ComfyPromptSerializer, { isActiveBackendNode, UpstreamNodeLocator } from "./ComfyPromptSerializer"; import DanbooruTags from "$lib/DanbooruTags"; +import { deserializeTemplate } from "$lib/ComfyBoxTemplate"; export const COMFYBOX_SERIAL_VERSION = 1; @@ -1001,6 +1003,24 @@ export default class ComfyApp { } }; reader.readAsText(file); + } else if (file.type === "image/svg+xml" || file.name.endsWith(".svg")) { + const templateAndSvg = await deserializeTemplate(file); + modalState.pushModal({ + title: "ComfyBox Template Preview", + svelteComponent: EditTemplateModal, + closeOnClick: false, + showCloseButton: false, + svelteProps: { + templateAndSvg + }, + buttons: [ + { + name: "Close", + variant: "secondary", + onClick: () => { } + }, + ] + }) } } diff --git a/src/lib/components/ComfyBoxWorkflowView.svelte b/src/lib/components/ComfyBoxWorkflowView.svelte index 63f3b0e..d766cd2 100644 --- a/src/lib/components/ComfyBoxWorkflowView.svelte +++ b/src/lib/components/ComfyBoxWorkflowView.svelte @@ -11,7 +11,7 @@ import type { ComfyBoxWorkflow } from "$lib/stores/workflowState"; export let app: ComfyApp; - export let workflow: ComfyBoxWorkflow; + export let workflow: ComfyBoxWorkflow | null = null; let layoutState: WritableLayoutStateStore | null; @@ -111,9 +111,9 @@ let menuPos = { x: 0, y: 0 }; let showMenu = false; - $: $layoutState.isMenuOpen = showMenu; + $: if (layoutState) $layoutState.isMenuOpen = showMenu; - $: if ($layoutState.root) { + $: if (layoutState && $layoutState.root) { root = $layoutState.root } else { root = null; @@ -138,44 +138,50 @@ } -{#if layoutState != null} -
- -
-{/if} +{#if workflow != null} + {#if layoutState != null} +
+ +
+ {/if} -{#if showMenu} - - moveUp()} - text="Move Up" /> - moveDown()} - text="Move Down" /> - sendToTop()} - text="Send to Top" /> - sendToBottom()} - text="Send to Bottom" /> - - groupWidgets(false)} - text="Group" /> - groupWidgets(true)} - text="Group Horizontally" /> - - + {#if showMenu} + + moveUp()} + text="Move Up" /> + moveDown()} + text="Move Down" /> + sendToTop()} + text="Send to Top" /> + sendToBottom()} + text="Send to Bottom" /> + + groupWidgets(false)} + text="Group" /> + groupWidgets(true)} + text="Group Horizontally" /> + + + {/if} +{:else} +
+ No workflow loaded +
{/if} diff --git a/src/lib/components/ComfyBoxWorkflowsView.svelte b/src/lib/components/ComfyBoxWorkflowsView.svelte index 1f2ac18..1e5f949 100644 --- a/src/lib/components/ComfyBoxWorkflowsView.svelte +++ b/src/lib/components/ComfyBoxWorkflowsView.svelte @@ -1,556 +1,529 @@ - - -
- - - - - - - - {#if $workflowState.activeWorkflow != null} - - {:else} - No workflow loaded - {/if} - - - - - - - - - - -
-
- {#each openedWorkflows.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)} - {@const workflow = workflowState.getWorkflow(item.id)} - - {#if workflow[SHADOW_ITEM_MARKER_PROPERTY_NAME]} -
- {/if} - - {/each} -
- -
-
-
-
- {#if workflow != null && workflow.attrs.queuePromptButtonName != ""} - - {/if} - - - - - - - - - - - - - - UI Edit mode - - - - Theme - - -
-
- -
-
-
-
- - -{#if appSetupPromise} - {#await appSetupPromise} -
- Loading... -
- {:catch error} -
-
- Error loading app -
-
{error}
- {#if error != null && error.stack} - {@const lines = error.stack.split("\n")} - {#each lines as line} -
{line}
- {/each} - {/if} -
- {/await} -{/if} - - + + +
+ + + + + + + + + + + + + + + + + + +
+
+ {#each openedWorkflows.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)} + {@const workflow = workflowState.getWorkflow(item.id)} + + {#if workflow[SHADOW_ITEM_MARKER_PROPERTY_NAME]} +
+ {/if} + + {/each} +
+ +
+
+
+
+ {#if workflow != null && workflow.attrs.queuePromptButtonName != ""} + + {/if} + + + + + + + + + + + + + + UI Edit mode + + + + Theme + + +
+
+ +
+
+
+
+ + +{#if appSetupPromise} + {#await appSetupPromise} +
+ Loading... +
+ {:catch error} +
+
+ Error loading app +
+
{error}
+ {#if error != null && error.stack} + {@const lines = error.stack.split("\n")} + {#each lines as line} +
{line}
+ {/each} + {/if} +
+ {/await} +{/if} + + diff --git a/src/lib/components/ComfyGraphView.svelte b/src/lib/components/ComfyGraphView.svelte index a71e497..44b35ac 100644 --- a/src/lib/components/ComfyGraphView.svelte +++ b/src/lib/components/ComfyGraphView.svelte @@ -1,10 +1,10 @@ + + + +
+ {#if mode === "activeWorkflow"} + + {:else if mode === "graph"} + + {:else if mode === "properties"} + + {:else if mode === "queue"} + + {:else} +
(Blank)
+ {/if} +
+ + + diff --git a/src/lib/components/ComfyQueue.svelte b/src/lib/components/ComfyQueue.svelte index 0208c74..3999f9b 100644 --- a/src/lib/components/ComfyQueue.svelte +++ b/src/lib/components/ComfyQueue.svelte @@ -211,7 +211,7 @@ await tick(); // Wait for list size to be recalculated queueList.scroll({ top: queueList.scrollHeight }) } - console.warn("[ComfyQueue] BUILDQUEUE", _entries, $queuePending, $queueRunning) + console.warn("[ComfyQueue] BUILDQUEUE", _entries.length, $queuePending.length, $queueRunning.length) } async function updateFromHistory() { @@ -219,7 +219,7 @@ if (queueList) { queueList.scrollTo(0, 0); } - console.warn("[ComfyQueue] BUILDHISTORY", _entries, $queueCompleted) + console.warn("[ComfyQueue] BUILDHISTORY", _entries.length, $queueCompleted.length) } async function interrupt() { diff --git a/src/lib/components/ImageUpload.svelte b/src/lib/components/ImageUpload.svelte index b423d9a..c5ec11f 100644 --- a/src/lib/components/ImageUpload.svelte +++ b/src/lib/components/ImageUpload.svelte @@ -174,8 +174,6 @@ uploaded = true; } - $: console.warn(imgWidth, imgHeight, "IMGSIZE!!") - function handle_clear(_e: CustomEvent) { _value = null; value = []; diff --git a/src/lib/components/TabsContainer.svelte b/src/lib/components/TabsContainer.svelte index 74d4a7d..5d71a34 100644 --- a/src/lib/components/TabsContainer.svelte +++ b/src/lib/components/TabsContainer.svelte @@ -222,8 +222,7 @@ .animation-wrapper { position: relative; - flex-grow: 100; - flex-basis: 0; + flex: 1 100 0%; } .handle-widget:hover { diff --git a/src/lib/components/modal/EditTemplateModal.svelte b/src/lib/components/modal/EditTemplateModal.svelte new file mode 100644 index 0000000..555026f --- /dev/null +++ b/src/lib/components/modal/EditTemplateModal.svelte @@ -0,0 +1,117 @@ + + +
+ + + + + {#if root} + +
+ + Layout + + +
+
+ {/if} +
+
+ + + +
+ {@html saneSvg} +
+
+
+
+
+
+ + diff --git a/src/lib/components/modal/SerializedLayoutPreviewNode.svelte b/src/lib/components/modal/SerializedLayoutPreviewNode.svelte new file mode 100644 index 0000000..3b72c17 --- /dev/null +++ b/src/lib/components/modal/SerializedLayoutPreviewNode.svelte @@ -0,0 +1,48 @@ + + +{#if entry} + {#if entry.dragItem.type === "container"} +
+ + + {#each entry.children as childID} + {@const child = layout.allItems[childID]} + + {/each} + + +
+{:else} +
+ + {entry.dragItem.attrs.title} + +
+ {/if} +{:else} + + Missing drag entry! {entryID} + +{/if} + + diff --git a/src/lib/stores/interfaceState.ts b/src/lib/stores/interfaceState.ts index f2fb569..88b1d9c 100644 --- a/src/lib/stores/interfaceState.ts +++ b/src/lib/stores/interfaceState.ts @@ -9,6 +9,8 @@ export type InterfaceState = { pointerNearLeft: boolean, showIndicator: boolean, indicatorValue: any, + + graphTransitioning: boolean } type InterfaceStateOps = { @@ -22,6 +24,8 @@ const store: Writable = writable( pointerNearLeft: false, showIndicator: false, indicatorValue: null, + + graphTransitioning: false }) const debounceDrag = debounce(() => { store.update(s => { s.showIndicator = false; return s }) }, 1000) diff --git a/src/lib/stores/uiState.ts b/src/lib/stores/uiState.ts index c827741..23ef527 100644 --- a/src/lib/stores/uiState.ts +++ b/src/lib/stores/uiState.ts @@ -29,7 +29,7 @@ const store: Writable = writable( uiEditMode: "widgets", reconnecting: false, - isSavingToLocalStorage: false + isSavingToLocalStorage: false, }) function reconnecting() { diff --git a/src/scss/global.scss b/src/scss/global.scss index f2cea71..5176081 100644 --- a/src/scss/global.scss +++ b/src/scss/global.scss @@ -6,10 +6,13 @@ body { // Disable pull to refresh overscroll-behavior-y: contain; -} -#app-root { background: var(--body-background-fill); + + width: 100%; + height: 100%; + margin: 0px; + font-family: Arial; } :root { diff --git a/tsconfig.json b/tsconfig.json index 21380bb..ac9f7d4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "checkJs": true, "strict": false, "baseUrl": "./src", + "typeRoots": ["./node_modules/@types/", "./src"], "paths": { "$lib": ["lib"], "$lib/*": ["lib/*"]