Workflow saving/loading
This commit is contained in:
Submodule litegraph updated: 39b040a0b1...fd575bf9a2
@@ -21,6 +21,7 @@
|
|||||||
import queueState from "$lib/stores/queueState";
|
import queueState from "$lib/stores/queueState";
|
||||||
import ComfyUnlockUIButton from "./ComfyUnlockUIButton.svelte";
|
import ComfyUnlockUIButton from "./ComfyUnlockUIButton.svelte";
|
||||||
import ComfyGraphView from "./ComfyGraphView.svelte";
|
import ComfyGraphView from "./ComfyGraphView.svelte";
|
||||||
|
import { download } from "$lib/utils";
|
||||||
|
|
||||||
export let app: ComfyApp = undefined;
|
export let app: ComfyApp = undefined;
|
||||||
let imageViewer: ImageViewer;
|
let imageViewer: ImageViewer;
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
let resizeTimeout: NodeJS.Timeout | null;
|
let resizeTimeout: NodeJS.Timeout | null;
|
||||||
let hasShownUIHelpToast: boolean = false;
|
let hasShownUIHelpToast: boolean = false;
|
||||||
let uiTheme: string = "";
|
let uiTheme: string = "";
|
||||||
|
let fileInput: HTMLInputElement = undefined;
|
||||||
|
|
||||||
let debugLayout: boolean = false;
|
let debugLayout: boolean = false;
|
||||||
|
|
||||||
@@ -100,6 +102,43 @@
|
|||||||
if (!app?.lGraph)
|
if (!app?.lGraph)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const promptFilename = false; // TODO
|
||||||
|
|
||||||
|
let filename = "workflow.json";
|
||||||
|
if (promptFilename) {
|
||||||
|
filename = prompt("Save workflow as:", filename);
|
||||||
|
if (!filename) return;
|
||||||
|
if (!filename.toLowerCase().endsWith(".json")) {
|
||||||
|
filename += ".json";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const date = new Date();
|
||||||
|
const formattedDate = date.toISOString().replace(/:/g, '-').replace(/\.\d{3}/g, '').replace('T', '_').replace("Z", "");
|
||||||
|
filename = `workflow-${formattedDate}.json`
|
||||||
|
}
|
||||||
|
|
||||||
|
const indent = 2
|
||||||
|
const json = JSON.stringify(app.serialize(), null, indent)
|
||||||
|
|
||||||
|
download(filename, json, "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
function doLoad(): void {
|
||||||
|
if (!app?.lGraph || !fileInput)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fileInput.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadWorkflow(): void {
|
||||||
|
app.handleFile(fileInput.files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function doSaveLocal(): void {
|
||||||
|
if (!app?.lGraph)
|
||||||
|
return;
|
||||||
|
|
||||||
app.saveStateToLocalStorage();
|
app.saveStateToLocalStorage();
|
||||||
toast.push("Saved to local storage.")
|
toast.push("Saved to local storage.")
|
||||||
//
|
//
|
||||||
@@ -109,13 +148,6 @@
|
|||||||
// download(`workflow-${formattedDate}.json`, JSON.stringify(app.serialize()), "application/json")
|
// download(`workflow-${formattedDate}.json`, JSON.stringify(app.serialize()), "application/json")
|
||||||
}
|
}
|
||||||
|
|
||||||
function doReset(): void {
|
|
||||||
var confirmed = confirm("Are you sure you want to clear the current workflow?");
|
|
||||||
if (confirmed) {
|
|
||||||
app.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doLoadDefault(): void {
|
async function doLoadDefault(): void {
|
||||||
var confirmed = confirm("Are you sure you want to clear the current workflow and load the default graph?");
|
var confirmed = confirm("Are you sure you want to clear the current workflow and load the default graph?");
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
@@ -123,6 +155,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function doClear(): void {
|
||||||
|
var confirmed = confirm("Are you sure you want to clear the current workflow?");
|
||||||
|
if (confirmed) {
|
||||||
|
app.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$: if ($uiState.uiUnlocked && !hasShownUIHelpToast) {
|
$: if ($uiState.uiUnlocked && !hasShownUIHelpToast) {
|
||||||
hasShownUIHelpToast = true;
|
hasShownUIHelpToast = true;
|
||||||
toast.push("Right-click to open context menu.")
|
toast.push("Right-click to open context menu.")
|
||||||
@@ -222,8 +261,14 @@
|
|||||||
<Button variant="secondary" on:click={doSave}>
|
<Button variant="secondary" on:click={doSave}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="secondary" on:click={doReset}>
|
<Button variant="secondary" on:click={doSaveLocal}>
|
||||||
Reset
|
Save Local
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" on:click={doLoad}>
|
||||||
|
Load
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" on:click={doClear}>
|
||||||
|
Clear
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="secondary" on:click={doLoadDefault}>
|
<Button variant="secondary" on:click={doLoadDefault}>
|
||||||
Load Default
|
Load Default
|
||||||
@@ -255,6 +300,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<LightboxModal />
|
<LightboxModal />
|
||||||
|
<input bind:this={fileInput} id="comfy-file-input" type="file" accept=".json" on:change={loadWorkflow} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SvelteToast options={toastOptions} />
|
<SvelteToast options={toastOptions} />
|
||||||
@@ -358,4 +404,8 @@
|
|||||||
span.left {
|
span.left {
|
||||||
right: 0px;
|
right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#comfy-file-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
clear() {
|
||||||
this.clean();
|
this.clean();
|
||||||
|
|
||||||
const blankGraph: SerializedLGraph = {
|
const blankGraph: SerializedLGraph = {
|
||||||
@@ -634,16 +634,21 @@ export default class ComfyApp {
|
|||||||
if (file.type === "image/png") {
|
if (file.type === "image/png") {
|
||||||
const pngInfo = await getPngMetadata(file);
|
const pngInfo = await getPngMetadata(file);
|
||||||
if (pngInfo) {
|
if (pngInfo) {
|
||||||
if (pngInfo.workflow) {
|
if (pngInfo.comfyBoxConfig) {
|
||||||
this.loadGraphData(JSON.parse(pngInfo.workflow));
|
this.deserialize(JSON.parse(pngInfo.comfyBoxConfig));
|
||||||
} else if (pngInfo.parameters) {
|
} else if (pngInfo.parameters) {
|
||||||
importA1111(this.lGraph, pngInfo.parameters, this.api);
|
throw "TODO import A111 import!"
|
||||||
|
// importA1111(this.lGraph, pngInfo.parameters, this.api);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error("No metadata found in image file.", pngInfo)
|
||||||
|
toast.push("No metadata found in image file.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (file.type === "application/json" || file.name.endsWith(".json")) {
|
} else if (file.type === "application/json" || file.name.endsWith(".json")) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = () => {
|
reader.onload = () => {
|
||||||
this.loadGraphData(JSON.parse(reader.result as string));
|
this.deserialize(JSON.parse(reader.result as string));
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ export default class ComfyValueControl extends ComfyGraphNode {
|
|||||||
{ name: "step", type: "number" }
|
{ name: "step", type: "number" }
|
||||||
],
|
],
|
||||||
outputs: [
|
outputs: [
|
||||||
{ name: "value", type: "*" }
|
{ name: "value", type: "*" },
|
||||||
|
{ name: "changed", type: BuiltInSlotType.EVENT }
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +94,7 @@ export default class ComfyValueControl extends ComfyGraphNode {
|
|||||||
|
|
||||||
v = clamp(v, min, max)
|
v = clamp(v, min, max)
|
||||||
this.setProperty("value", v)
|
this.setProperty("value", v)
|
||||||
|
this.triggerSlot(1, v)
|
||||||
this.setOutputData(0, v)
|
this.setOutputData(0, v)
|
||||||
|
|
||||||
console.debug("ValueControl", v, this.properties)
|
console.debug("ValueControl", v, this.properties)
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
|
|||||||
if (this.changedIndex !== null && this.outputs.length >= this.changedIndex) {
|
if (this.changedIndex !== null && this.outputs.length >= this.changedIndex) {
|
||||||
const changedOutput = this.outputs[this.changedIndex]
|
const changedOutput = this.outputs[this.changedIndex]
|
||||||
if (changedOutput.type === BuiltInSlotType.EVENT)
|
if (changedOutput.type === BuiltInSlotType.EVENT)
|
||||||
this.triggerSlot(this.changedIndex, "changed")
|
this.triggerSlot(this.changedIndex, get(this.value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -790,7 +790,10 @@ function initDefaultLayout() {
|
|||||||
currentSelection: [],
|
currentSelection: [],
|
||||||
currentSelectionNodes: [],
|
currentSelectionNodes: [],
|
||||||
isMenuOpen: false,
|
isMenuOpen: false,
|
||||||
isConfiguring: false
|
isConfiguring: false,
|
||||||
|
attrs: {
|
||||||
|
defaultSubgraph: ""
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const root = addContainer(null, { direction: "horizontal", title: "" });
|
const root = addContainer(null, { direction: "horizontal", title: "" });
|
||||||
|
|||||||
Reference in New Issue
Block a user