Config default templates
This commit is contained in:
@@ -3,5 +3,7 @@
|
|||||||
"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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) => {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = async () => {
|
|
||||||
const svg = reader.result as string;
|
|
||||||
let template = null;
|
let template = null;
|
||||||
let templateJSON = extractTemplateJSONFromSVG(svg);
|
let templateJSON = extractTemplateJSONFromSVG(svg);
|
||||||
if (templateJSON)
|
if (templateJSON)
|
||||||
template = JSON.parse(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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user