From 220be38a0b662eb2428623466c7e08d776bce4f6 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Thu, 25 May 2023 17:38:30 -0500 Subject: [PATCH] Config default templates --- public/config.json | 12 +++--- src/lib/ComfyBoxTemplate.ts | 33 ++++++---------- src/lib/components/ComfyApp.ts | 57 +++++++++++++++++++++++---- src/lib/components/ImageUpload.svelte | 2 +- src/lib/stores/configState.ts | 10 ++++- src/lib/utils.ts | 10 +++++ 6 files changed, 89 insertions(+), 35 deletions(-) diff --git a/public/config.json b/public/config.json index dfe2fce..744ff2d 100644 --- a/public/config.json +++ b/public/config.json @@ -1,7 +1,9 @@ { - "comfyUIHostname": "localhost", - "comfyUIPort": 8188, - "alwaysStripUserState": false, - "promptForWorkflowName": false, - "confirmWhenUnloadingUnsavedChanges": true + "comfyUIHostname": "localhost", + "comfyUIPort": 8188, + "alwaysStripUserState": false, + "promptForWorkflowName": false, + "confirmWhenUnloadingUnsavedChanges": true, + "builtInTemplates": ["ControlNet", "LoRA x5", "Model Loader", "Positive_Negative", "Seed Randomizer"], + "cacheBuiltInResources": true } diff --git a/src/lib/ComfyBoxTemplate.ts b/src/lib/ComfyBoxTemplate.ts index 79c899a..b6a011e 100644 --- a/src/lib/ComfyBoxTemplate.ts +++ b/src/lib/ComfyBoxTemplate.ts @@ -338,7 +338,6 @@ export function serializeTemplate(canvas: ComfyGraphCanvas, template: ComfyBoxTe uiState.update(s => { s.forceSaveUserState = null; return s; }); nodes = relocateNodes(nodes); - nodes = removeTags(nodes); [nodes, links] = pruneDetachedLinks(nodes, links); const svg = renderSvg(canvas, graph, TEMPLATE_SVG_PADDING); @@ -378,29 +377,21 @@ export function extractTemplateJSONFromSVG(svg: string): string | null { /* * Credit goes to pythongosssss for this format */ -export function deserializeTemplateFromSVG(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = async () => { - const svg = reader.result as string; - let template = null; - let templateJSON = extractTemplateJSONFromSVG(svg); - if (templateJSON) - template = JSON.parse(templateJSON); +export function deserializeTemplateFromSVG(svg: string): SerializedComfyBoxTemplate | null { + let template = null; + let templateJSON = extractTemplateJSONFromSVG(svg); + if (templateJSON) + template = JSON.parse(templateJSON); - if (!isSerializedComfyBoxTemplate(template)) { - reject("Invalid template format!") - } - else { - template.svg = svg; - resolve(template) - } - }; - reader.readAsText(file); - }); + if (!isSerializedComfyBoxTemplate(template)) { + return null; + } + else { + template.svg = svg; + return template; + } } - export function createTemplate(nodes: LGraphNode[]): ComfyBoxTemplateResult { if (nodes.length === 0) { return { diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index 1182d93..20465a4 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -29,7 +29,7 @@ import queueState from "$lib/stores/queueState"; import selectionState from "$lib/stores/selectionState"; import uiState from "$lib/stores/uiState"; import workflowState, { ComfyBoxWorkflow, type WorkflowAttributes, type WorkflowInstID } from "$lib/stores/workflowState"; -import type { SerializedPromptOutput } from "$lib/utils"; +import { readFileToText, type SerializedPromptOutput } from "$lib/utils"; import { basename, capitalize, download, graphToGraphVis, jsonToJsObject, promptToGraphVis, range } from "$lib/utils"; import { tick } from "svelte"; import { type SvelteComponentDev } from "svelte/internal"; @@ -264,9 +264,9 @@ export default class ComfyApp { */ async loadConfig() { try { - const config = await fetch(`/config.json`); - const state = await config.json() as ConfigState; - configState.set(state); + const config = await fetch(`/config.json`, { cache: "no-store" }); + const newConfig = await config.json() as ConfigState; + configState.set({ ...get(configState), ...newConfig }); } catch (error) { console.error(`Failed to load config`, error) @@ -274,7 +274,44 @@ export default class ComfyApp { } async loadBuiltInTemplates(): Promise { - return [] + const builtInTemplates = get(configState).builtInTemplates + const options: RequestInit = get(configState).cacheBuiltInResources ? {} : { cache: "no-store" } + const promises = builtInTemplates.map(basename => { + return fetch(`/templates/${basename}.svg`, options) + .then(res => res.text()) + .catch(error => error) + }) + + const [templates, error] = await Promise.all(promises).then((results) => { + const templates: SerializedComfyBoxTemplate[] = [] + const errors: string[] = [] + + for (const r of results) { + if (r instanceof Error) { + errors.push(r.toString()) + } + else { + // bare filename of image + const svg = r as string; + const templateAndSvg = deserializeTemplateFromSVG(svg) + if (templateAndSvg == null) { + errors.push("Invalid SVG template format") + } + templates.push(templateAndSvg) + } + } + + let error = null; + if (errors && errors.length > 0) + error = "Error(s) loading builtin templates:\n" + errors.join("\n"); + + return [templates, error] + }) + + if (error) + notify(error, { type: "error" }) + + return templates; } resizeCanvas() { @@ -786,7 +823,8 @@ export default class ComfyApp { async initDefaultWorkflow(name: string = "defaultWorkflow", options?: OpenWorkflowOptions) { let state = null; try { - const graphResponse = await fetch(`/workflows/${name}.json`); + const options: RequestInit = get(configState).cacheBuiltInResources ? {} : { cache: "no-store" } + const graphResponse = await fetch(`/workflows/${name}.json`, options); state = await graphResponse.json() as SerializedAppState; } catch (error) { @@ -1061,7 +1099,12 @@ export default class ComfyApp { }; reader.readAsText(file); } else if (file.type === "image/svg+xml" || file.name.endsWith(".svg")) { - const templateAndSvg = await deserializeTemplateFromSVG(file); + const svg = await readFileToText(file); + const templateAndSvg = deserializeTemplateFromSVG(svg); + if (templateAndSvg == null) { + notify("Invalid SVG template format!", { type: "error" }) + return; + } const importTemplate = () => { try { diff --git a/src/lib/components/ImageUpload.svelte b/src/lib/components/ImageUpload.svelte index 3ee9dc3..7934b08 100644 --- a/src/lib/components/ImageUpload.svelte +++ b/src/lib/components/ImageUpload.svelte @@ -89,7 +89,7 @@ for (const r of results) { if (r instanceof Error) { - errors.push(r.cause) + errors.push(r.toString()) } else { // bare filename of image diff --git a/src/lib/stores/configState.ts b/src/lib/stores/configState.ts index 22c3e32..347bd78 100644 --- a/src/lib/stores/configState.ts +++ b/src/lib/stores/configState.ts @@ -17,6 +17,12 @@ export type ConfigState = { /** When closing the tab, open the confirmation window if there's unsaved changes */ confirmWhenUnloadingUnsavedChanges: boolean, + + /** Basenames of templates that can be loaded from public/templates. Saves LocalStorage space. */ + builtInTemplates: string[], + + /** Cache loading of built-in resources to save network use */ + cacheBuiltInResources: boolean } type ConfigStateOps = { @@ -30,7 +36,9 @@ const store: Writable = writable( comfyUIPort: 8188, alwaysStripUserState: false, promptForWorkflowName: false, - confirmWhenUnloadingUnsavedChanges: true + confirmWhenUnloadingUnsavedChanges: true, + builtInTemplates: [], + cacheBuiltInResources: true, }) function getBackendURL(): string { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 065e2fb..47a737a 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -598,3 +598,13 @@ export function calcNodesBoundingBox(nodes: SerializedLGraphNode[]): Vector4 { return [min_x, min_y, max_x, max_y]; } + +export async function readFileToText(file: File): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = async () => { + resolve(reader.result as string); + }; + reader.readAsText(file); + }) +}