Config default templates

This commit is contained in:
space-nuko
2023-05-25 17:38:30 -05:00
parent 2a0662592a
commit 220be38a0b
6 changed files with 89 additions and 35 deletions

View File

@@ -1,7 +1,9 @@
{ {
"comfyUIHostname": "localhost", "comfyUIHostname": "localhost",
"comfyUIPort": 8188, "comfyUIPort": 8188,
"alwaysStripUserState": false, "alwaysStripUserState": false,
"promptForWorkflowName": false, "promptForWorkflowName": false,
"confirmWhenUnloadingUnsavedChanges": true "confirmWhenUnloadingUnsavedChanges": true,
"builtInTemplates": ["ControlNet", "LoRA x5", "Model Loader", "Positive_Negative", "Seed Randomizer"],
"cacheBuiltInResources": true
} }

View File

@@ -338,7 +338,6 @@ export function serializeTemplate(canvas: ComfyGraphCanvas, template: ComfyBoxTe
uiState.update(s => { s.forceSaveUserState = null; return s; }); uiState.update(s => { s.forceSaveUserState = null; return s; });
nodes = relocateNodes(nodes); nodes = relocateNodes(nodes);
nodes = removeTags(nodes);
[nodes, links] = pruneDetachedLinks(nodes, links); [nodes, links] = pruneDetachedLinks(nodes, links);
const svg = renderSvg(canvas, graph, TEMPLATE_SVG_PADDING); 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 * Credit goes to pythongosssss for this format
*/ */
export function deserializeTemplateFromSVG(file: File): Promise<SerializedComfyBoxTemplate> { export function deserializeTemplateFromSVG(svg: string): SerializedComfyBoxTemplate | null {
return new Promise((resolve, reject) => { let template = null;
const reader = new FileReader(); let templateJSON = extractTemplateJSONFromSVG(svg);
reader.onload = async () => { if (templateJSON)
const svg = reader.result as string; template = JSON.parse(templateJSON);
let template = null;
let templateJSON = extractTemplateJSONFromSVG(svg);
if (templateJSON)
template = JSON.parse(templateJSON);
if (!isSerializedComfyBoxTemplate(template)) { if (!isSerializedComfyBoxTemplate(template)) {
reject("Invalid template format!") return null;
} }
else { else {
template.svg = svg; template.svg = svg;
resolve(template) return template;
} }
};
reader.readAsText(file);
});
} }
export function createTemplate(nodes: LGraphNode[]): ComfyBoxTemplateResult { export function createTemplate(nodes: LGraphNode[]): ComfyBoxTemplateResult {
if (nodes.length === 0) { if (nodes.length === 0) {
return { return {

View File

@@ -29,7 +29,7 @@ import queueState from "$lib/stores/queueState";
import selectionState from "$lib/stores/selectionState"; import selectionState from "$lib/stores/selectionState";
import uiState from "$lib/stores/uiState"; import uiState from "$lib/stores/uiState";
import workflowState, { ComfyBoxWorkflow, type WorkflowAttributes, type WorkflowInstID } from "$lib/stores/workflowState"; 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 { basename, capitalize, download, graphToGraphVis, jsonToJsObject, promptToGraphVis, range } from "$lib/utils";
import { tick } from "svelte"; import { tick } from "svelte";
import { type SvelteComponentDev } from "svelte/internal"; import { type SvelteComponentDev } from "svelte/internal";
@@ -264,9 +264,9 @@ export default class ComfyApp {
*/ */
async loadConfig() { async loadConfig() {
try { try {
const config = await fetch(`/config.json`); const config = await fetch(`/config.json`, { cache: "no-store" });
const state = await config.json() as ConfigState; const newConfig = await config.json() as ConfigState;
configState.set(state); configState.set({ ...get(configState), ...newConfig });
} }
catch (error) { catch (error) {
console.error(`Failed to load config`, error) console.error(`Failed to load config`, error)
@@ -274,7 +274,44 @@ export default class ComfyApp {
} }
async loadBuiltInTemplates(): Promise<SerializedComfyBoxTemplate[]> { async loadBuiltInTemplates(): Promise<SerializedComfyBoxTemplate[]> {
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() { resizeCanvas() {
@@ -786,7 +823,8 @@ export default class ComfyApp {
async initDefaultWorkflow(name: string = "defaultWorkflow", options?: OpenWorkflowOptions) { async initDefaultWorkflow(name: string = "defaultWorkflow", options?: OpenWorkflowOptions) {
let state = null; let state = null;
try { 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; state = await graphResponse.json() as SerializedAppState;
} }
catch (error) { catch (error) {
@@ -1061,7 +1099,12 @@ export default class ComfyApp {
}; };
reader.readAsText(file); reader.readAsText(file);
} else if (file.type === "image/svg+xml" || file.name.endsWith(".svg")) { } 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 = () => { const importTemplate = () => {
try { try {

View File

@@ -89,7 +89,7 @@
for (const r of results) { for (const r of results) {
if (r instanceof Error) { if (r instanceof Error) {
errors.push(r.cause) errors.push(r.toString())
} }
else { else {
// bare filename of image // bare filename of image

View File

@@ -17,6 +17,12 @@ export type ConfigState = {
/** When closing the tab, open the confirmation window if there's unsaved changes */ /** When closing the tab, open the confirmation window if there's unsaved changes */
confirmWhenUnloadingUnsavedChanges: boolean, 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 = { type ConfigStateOps = {
@@ -30,7 +36,9 @@ const store: Writable<ConfigState> = writable(
comfyUIPort: 8188, comfyUIPort: 8188,
alwaysStripUserState: false, alwaysStripUserState: false,
promptForWorkflowName: false, promptForWorkflowName: false,
confirmWhenUnloadingUnsavedChanges: true confirmWhenUnloadingUnsavedChanges: true,
builtInTemplates: [],
cacheBuiltInResources: true,
}) })
function getBackendURL(): string { function getBackendURL(): string {

View File

@@ -598,3 +598,13 @@ export function calcNodesBoundingBox(nodes: SerializedLGraphNode[]): Vector4 {
return [min_x, min_y, max_x, max_y]; return [min_x, min_y, max_x, max_y];
} }
export async function readFileToText(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = async () => {
resolve(reader.result as string);
};
reader.readAsText(file);
})
}