Error handling modals
This commit is contained in:
@@ -4,7 +4,10 @@ import ComfyAPI, { type ComfyAPIStatusResponse, type ComfyBoxPromptExtraData, ty
|
|||||||
import { importA1111, parsePNGMetadata } from "$lib/pnginfo";
|
import { importA1111, parsePNGMetadata } from "$lib/pnginfo";
|
||||||
import EventEmitter from "events";
|
import EventEmitter from "events";
|
||||||
import type TypedEmitter from "typed-emitter";
|
import type TypedEmitter from "typed-emitter";
|
||||||
import A1111PromptDisplay from "./A1111PromptDisplay.svelte";
|
import A1111PromptModal from "./modal/A1111PromptModal.svelte";
|
||||||
|
import MissingNodeTypesModal from "./modal/MissingNodeTypesModal.svelte";
|
||||||
|
import WorkflowLoadErrorModal from "./modal/WorkflowLoadErrorModal.svelte";
|
||||||
|
import ConfirmConvertWithMissingNodeTypesModal from "./modal/ConfirmConvertWithMissingNodeTypesModal.svelte";
|
||||||
|
|
||||||
|
|
||||||
// Import nodes
|
// Import nodes
|
||||||
@@ -149,6 +152,11 @@ export type WorkflowLoadError = {
|
|||||||
error: Error
|
error: Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VanillaWorkflowConvertResult = {
|
||||||
|
comfyBoxWorkflow: SerializedAppState,
|
||||||
|
missingNodeTypes: Set<string>
|
||||||
|
}
|
||||||
|
|
||||||
function isComfyBoxWorkflow(data: any): data is SerializedAppState {
|
function isComfyBoxWorkflow(data: any): data is SerializedAppState {
|
||||||
return data != null && (typeof data === "object") && data.comfyBoxWorkflow;
|
return data != null && (typeof data === "object") && data.comfyBoxWorkflow;
|
||||||
}
|
}
|
||||||
@@ -271,22 +279,6 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
convertVanillaWorkflow(workflow: ComfyVanillaWorkflow, title: string): SerializedAppState {
|
|
||||||
const attrs: WorkflowAttributes = {
|
|
||||||
...defaultWorkflowAttributes,
|
|
||||||
title
|
|
||||||
}
|
|
||||||
|
|
||||||
const canvas: SerializedGraphCanvasState = {
|
|
||||||
offset: [0, 0],
|
|
||||||
scale: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
const comfyBoxWorkflow = convertVanillaWorkflow(workflow, attrs);
|
|
||||||
return this.serialize(comfyBoxWorkflow, canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
saveStateToLocalStorage() {
|
saveStateToLocalStorage() {
|
||||||
try {
|
try {
|
||||||
uiState.update(s => { s.isSavingToLocalStorage = true; return s; })
|
uiState.update(s => { s.isSavingToLocalStorage = true; return s; })
|
||||||
@@ -320,9 +312,12 @@ export default class ComfyApp {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const workflows = state.workflows as SerializedAppState[];
|
const workflows = state.workflows as SerializedAppState[];
|
||||||
for (const workflow of workflows) {
|
await Promise.all(workflows.map(w => {
|
||||||
await this.openWorkflow(workflow, defs)
|
return this.openWorkflow(w, defs).catch(error => {
|
||||||
}
|
console.error("Failed restoring previous workflow", error)
|
||||||
|
notify(`Failed restoring previous workflow: ${error}`, { type: "error" })
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
if (typeof state.activeWorkflowIndex === "number") {
|
if (typeof state.activeWorkflowIndex === "number") {
|
||||||
workflowState.setActiveWorkflow(this.lCanvas, state.activeWorkflowIndex);
|
workflowState.setActiveWorkflow(this.lCanvas, state.activeWorkflowIndex);
|
||||||
@@ -566,11 +561,35 @@ export default class ComfyApp {
|
|||||||
|
|
||||||
async openWorkflow(data: SerializedAppState, refreshCombos: boolean | Record<string, ComfyNodeDef> = true): Promise<ComfyWorkflow> {
|
async openWorkflow(data: SerializedAppState, refreshCombos: boolean | Record<string, ComfyNodeDef> = true): Promise<ComfyWorkflow> {
|
||||||
if (data.version !== COMFYBOX_SERIAL_VERSION) {
|
if (data.version !== COMFYBOX_SERIAL_VERSION) {
|
||||||
throw `Invalid ComfyBox saved data format: ${data.version}`
|
const mes = `Invalid ComfyBox saved data format: ${data.version}`
|
||||||
|
notify(mes, { type: "error" })
|
||||||
|
return Promise.reject(mes);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clean();
|
this.clean();
|
||||||
|
|
||||||
const workflow = workflowState.openWorkflow(this.lCanvas, data);
|
let workflow: ComfyWorkflow;
|
||||||
|
try {
|
||||||
|
workflow = workflowState.openWorkflow(this.lCanvas, data);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
modalState.pushModal({
|
||||||
|
svelteComponent: WorkflowLoadErrorModal,
|
||||||
|
svelteProps: {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workflow.missingNodeTypes.size > 0) {
|
||||||
|
modalState.pushModal({
|
||||||
|
svelteComponent: MissingNodeTypesModal,
|
||||||
|
svelteProps: {
|
||||||
|
missingNodeTypes: workflow.missingNodeTypes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Restore canvas offset/zoom
|
// Restore canvas offset/zoom
|
||||||
this.lCanvas.deserialize(data.canvas)
|
this.lCanvas.deserialize(data.canvas)
|
||||||
@@ -586,10 +605,53 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async openVanillaWorkflow(data: SerializedLGraph, filename: string) {
|
async openVanillaWorkflow(data: SerializedLGraph, filename: string) {
|
||||||
const converted = this.convertVanillaWorkflow(data, basename(filename))
|
const title = basename(filename)
|
||||||
console.info("WORKFLWO", converted)
|
|
||||||
|
const attrs: WorkflowAttributes = {
|
||||||
|
...defaultWorkflowAttributes,
|
||||||
|
title
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvas: SerializedGraphCanvasState = {
|
||||||
|
offset: [0, 0],
|
||||||
|
scale: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const [comfyBoxWorkflow, layoutState] = convertVanillaWorkflow(data, attrs);
|
||||||
|
|
||||||
|
const addWorkflow = () => {
|
||||||
notify("Converted ComfyUI workflow to ComfyBox format.", { type: "info" })
|
notify("Converted ComfyUI workflow to ComfyBox format.", { type: "info" })
|
||||||
await this.openWorkflow(converted);
|
workflowState.addWorkflow(this.lCanvas, comfyBoxWorkflow)
|
||||||
|
this.lCanvas.deserialize(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comfyBoxWorkflow.missingNodeTypes.size > 0) {
|
||||||
|
modalState.pushModal({
|
||||||
|
svelteComponent: ConfirmConvertWithMissingNodeTypesModal,
|
||||||
|
svelteProps: {
|
||||||
|
missingNodeTypes: comfyBoxWorkflow.missingNodeTypes
|
||||||
|
},
|
||||||
|
closeOnClick: false,
|
||||||
|
showCloseButton: false,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
name: "Cancel",
|
||||||
|
variant: "secondary",
|
||||||
|
onClick: () => {
|
||||||
|
layoutStates.remove(comfyBoxWorkflow.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Convert",
|
||||||
|
variant: "primary",
|
||||||
|
onClick: addWorkflow
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addWorkflow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setActiveWorkflow(id: WorkflowInstID) {
|
setActiveWorkflow(id: WorkflowInstID) {
|
||||||
@@ -840,11 +902,10 @@ export default class ComfyApp {
|
|||||||
}
|
}
|
||||||
modalState.pushModal({
|
modalState.pushModal({
|
||||||
title: "A1111 Prompt Details",
|
title: "A1111 Prompt Details",
|
||||||
svelteComponent: A1111PromptDisplay,
|
svelteComponent: A1111PromptModal,
|
||||||
svelteProps: {
|
svelteProps: {
|
||||||
prompt: a1111Info
|
prompt: a1111Info
|
||||||
},
|
},
|
||||||
showCloseButton: true
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -137,12 +137,12 @@
|
|||||||
if (!fileInput)
|
if (!fileInput)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
fileInput.value = null;
|
||||||
fileInput.click();
|
fileInput.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadWorkflow(): void {
|
function loadWorkflow(): void {
|
||||||
app.handleFile(fileInput.files[0]);
|
app.handleFile(fileInput.files[0]);
|
||||||
fileInput.files = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function doSaveLocal(): void {
|
function doSaveLocal(): void {
|
||||||
@@ -327,7 +327,7 @@
|
|||||||
Error loading app
|
Error loading app
|
||||||
</div>
|
</div>
|
||||||
<div>{error}</div>
|
<div>{error}</div>
|
||||||
{#if error.stack}
|
{#if error != null && error.stack}
|
||||||
{@const lines = error.stack.split("\n")}
|
{@const lines = error.stack.split("\n")}
|
||||||
{#each lines as line}
|
{#each lines as line}
|
||||||
<div style:font-size="16px">{line}</div>
|
<div style:font-size="16px">{line}</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import modalState, { type ModalData } from "$lib/stores/modalState";
|
import modalState, { type ModalButton, type ModalData } from "$lib/stores/modalState";
|
||||||
import { Button } from "@gradio/button";
|
import { Button } from "@gradio/button";
|
||||||
import Modal from "./Modal.svelte";
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
@@ -12,12 +12,20 @@
|
|||||||
|
|
||||||
modalState.closeModal(modal.id)
|
modalState.closeModal(modal.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onButtonClicked(button: ModalButton, closeDialog: Function) {
|
||||||
|
button.onClick();
|
||||||
|
|
||||||
|
if (button.closeOnClick !== false) {
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each $modalState.activeModals as modal(modal.id)}
|
{#each $modalState.activeModals as modal(modal.id)}
|
||||||
<Modal showModal={true} on:close={() => onClose(modal)}>
|
<Modal showModal={true} closeOnClick={modal.closeOnClick} on:close={() => onClose(modal)}>
|
||||||
<div slot="header" class="modal-header">
|
<div slot="header" class="modal-header">
|
||||||
{#if modal != null}
|
{#if modal != null && modal.title != null}
|
||||||
<h1 style="padding-bottom: 1rem;">{modal.title}</h1>
|
<h1 style="padding-bottom: 1rem;">{modal.title}</h1>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -26,10 +34,10 @@
|
|||||||
<svelte:component this={modal.svelteComponent} {...modal.svelteProps}/>
|
<svelte:component this={modal.svelteComponent} {...modal.svelteProps}/>
|
||||||
{/if}
|
{/if}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<div slot="buttons" let:closeDialog>
|
<div slot="buttons" class="buttons" let:closeDialog>
|
||||||
{#if modal != null && modal.buttons?.length > 0}
|
{#if modal != null && modal.buttons?.length > 0}
|
||||||
{#each modal.buttons as button}
|
{#each modal.buttons as button}
|
||||||
<Button variant={button.variant} on:click={button.onClick}>
|
<Button variant={button.variant} on:click={() => onButtonClicked(button, closeDialog)}>
|
||||||
{button.name}
|
{button.name}
|
||||||
</Button>
|
</Button>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -42,3 +50,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.buttons {
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<style>
|
<style lang="scss">
|
||||||
dialog {
|
dialog {
|
||||||
max-width: 75vw;
|
max-width: 75vw;
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
@@ -93,7 +93,10 @@
|
|||||||
.button-row {
|
.button-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: var(--spacing-sm);
|
|
||||||
padding-top: 0.5em;
|
padding-top: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-row, .buttons {
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ComfyBoxStdPrompt from "$lib/ComfyBoxStdPrompt";
|
|
||||||
import type { A1111ParsedInfotext } from "$lib/parseA1111";
|
import type { A1111ParsedInfotext } from "$lib/parseA1111";
|
||||||
import { Block, BlockTitle } from "@gradio/atoms";
|
import { Block, BlockTitle } from "@gradio/atoms";
|
||||||
import { TextBox } from "@gradio/form";
|
import { TextBox } from "@gradio/form";
|
||||||
import { JsonView } from '@zerodevx/svelte-json-view'
|
import { JsonView } from '@zerodevx/svelte-json-view'
|
||||||
import type { A1111PromptAndInfo } from "./ComfyApp";
|
import type { A1111PromptAndInfo } from "$lib/components/ComfyApp";
|
||||||
import { StaticImage } from "./gradio/image";
|
import { StaticImage } from "$lib/components/gradio/image";
|
||||||
|
|
||||||
export let prompt: A1111PromptAndInfo | null = null;
|
export let prompt: A1111PromptAndInfo | null = null;
|
||||||
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let missingNodeTypes: Set<string> = new Set();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
When loading the graph, the following node types were not found:
|
||||||
|
<ul>
|
||||||
|
{#each Array.from(missingNodeTypes) as type}
|
||||||
|
<li>{type}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
Do you want to convert the workflow into ComfyBox format anyway? (You may lose some workflow state)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
div {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
padding-left: 2rem;
|
||||||
|
|
||||||
|
> li {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
26
src/lib/components/modal/MissingNodeTypesModal.svelte
Normal file
26
src/lib/components/modal/MissingNodeTypesModal.svelte
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let missingNodeTypes: Set<string> = new Set();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
When loading the graph, the following node types were not found:
|
||||||
|
<ul>
|
||||||
|
{#each Array.from(missingNodeTypes) as type}
|
||||||
|
<li>{type}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
Nodes that have failed to load will show as red on the graph.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
div {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
padding-left: 2rem;
|
||||||
|
|
||||||
|
> li {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
38
src/lib/components/modal/WorkflowLoadErrorModal.svelte
Normal file
38
src/lib/components/modal/WorkflowLoadErrorModal.svelte
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let error: Error;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
Loading aborted due to error reloading workflow data:
|
||||||
|
<div class="error-block">
|
||||||
|
<pre class="error">
|
||||||
|
{error.toString()}
|
||||||
|
</pre>
|
||||||
|
<pre class="stack">
|
||||||
|
{error?.stack || "(No stacktrace available)"}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
div {
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
> .error-block {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 1rem 0;
|
||||||
|
background-color: rgba(255, 0, 0, 0.2);
|
||||||
|
&.stack {
|
||||||
|
font-size: 1.0rem;
|
||||||
|
color: #ccc;
|
||||||
|
max-height: 50vh;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: rgb(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { LGraph, type INodeInputSlot, type SerializedLGraph, type LinkID, type UUID, type NodeID, LiteGraph, BuiltInSlotType, type SerializedLGraphNode, type Vector2, BuiltInSlotShape, type INodeOutputSlot } from "@litegraph-ts/core";
|
import { LGraph, type INodeInputSlot, type SerializedLGraph, type LinkID, type UUID, type NodeID, LiteGraph, BuiltInSlotType, type SerializedLGraphNode, type Vector2, BuiltInSlotShape, type INodeOutputSlot } from "@litegraph-ts/core";
|
||||||
import type { SerializedAppState } from "./components/ComfyApp";
|
import type { SerializedAppState } from "./components/ComfyApp";
|
||||||
import layoutStates, { defaultWorkflowAttributes, type ContainerLayout, type DragItemID, type SerializedDragEntry, type SerializedLayoutState } from "./stores/layoutStates";
|
import layoutStates, { defaultWorkflowAttributes, type ContainerLayout, type DragItemID, type SerializedDragEntry, type SerializedLayoutState, type WritableLayoutStateStore } from "./stores/layoutStates";
|
||||||
import { ComfyWorkflow, type WorkflowAttributes } from "./stores/workflowState";
|
import { ComfyWorkflow, type WorkflowAttributes } from "./stores/workflowState";
|
||||||
import type { SerializedGraphCanvasState } from "./ComfyGraphCanvas";
|
import type { SerializedGraphCanvasState } from "./ComfyGraphCanvas";
|
||||||
import ComfyApp from "./components/ComfyApp";
|
import ComfyApp from "./components/ComfyApp";
|
||||||
@@ -158,7 +158,7 @@ function rewriteIDsInGraph(vanillaWorkflow: ComfyVanillaWorkflow) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function convertVanillaWorkflow(vanillaWorkflow: ComfyVanillaWorkflow, attrs: WorkflowAttributes): ComfyWorkflow {
|
export default function convertVanillaWorkflow(vanillaWorkflow: ComfyVanillaWorkflow, attrs: WorkflowAttributes): [ComfyWorkflow, WritableLayoutStateStore] {
|
||||||
const [comfyBoxWorkflow, layoutState] = ComfyWorkflow.create();
|
const [comfyBoxWorkflow, layoutState] = ComfyWorkflow.create();
|
||||||
const { root, left, right } = layoutState.initDefaultLayout();
|
const { root, left, right } = layoutState.initDefaultLayout();
|
||||||
|
|
||||||
@@ -335,11 +335,5 @@ export default function convertVanillaWorkflow(vanillaWorkflow: ComfyVanillaWork
|
|||||||
const layout = layoutState.serialize();
|
const layout = layoutState.serialize();
|
||||||
comfyBoxWorkflow.deserialize(layoutState, { graph: vanillaWorkflow, attrs, layout })
|
comfyBoxWorkflow.deserialize(layoutState, { graph: vanillaWorkflow, attrs, layout })
|
||||||
|
|
||||||
for (const node of comfyBoxWorkflow.graph.iterateNodesInOrder()) {
|
return [comfyBoxWorkflow, layoutState]
|
||||||
if ((node as any).isBackendNode) {
|
|
||||||
console.warn("BENDNODE", node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return comfyBoxWorkflow
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,18 @@ import { v4 as uuidv4 } from "uuid";
|
|||||||
export type ModalButton = {
|
export type ModalButton = {
|
||||||
name: string,
|
name: string,
|
||||||
variant: "primary" | "secondary",
|
variant: "primary" | "secondary",
|
||||||
onClick: () => void
|
onClick: () => void,
|
||||||
|
closeOnClick?: boolean
|
||||||
}
|
}
|
||||||
export interface ModalData {
|
export interface ModalData {
|
||||||
id: string,
|
id: string,
|
||||||
title: string,
|
title?: string,
|
||||||
onClose?: () => void,
|
onClose?: () => void,
|
||||||
svelteComponent?: typeof SvelteComponentDev,
|
svelteComponent?: typeof SvelteComponentDev,
|
||||||
svelteProps?: Record<string, any>,
|
svelteProps: Record<string, any>,
|
||||||
buttons?: ModalButton[],
|
buttons: ModalButton[],
|
||||||
showCloseButton?: boolean
|
showCloseButton: boolean,
|
||||||
|
closeOnClick: boolean
|
||||||
}
|
}
|
||||||
export interface ModalState {
|
export interface ModalState {
|
||||||
activeModals: ModalData[]
|
activeModals: ModalData[]
|
||||||
@@ -34,7 +36,10 @@ const store: Writable<ModalState> = writable(
|
|||||||
|
|
||||||
function pushModal(data: Partial<ModalData>) {
|
function pushModal(data: Partial<ModalData>) {
|
||||||
const modal: ModalData = {
|
const modal: ModalData = {
|
||||||
title: "Modal",
|
showCloseButton: true,
|
||||||
|
closeOnClick: true,
|
||||||
|
buttons: [],
|
||||||
|
svelteProps: {},
|
||||||
...data,
|
...data,
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export class ComfyWorkflow {
|
|||||||
/*
|
/*
|
||||||
* Missing node types encountered when deserializing the graph
|
* Missing node types encountered when deserializing the graph
|
||||||
*/
|
*/
|
||||||
missingNodeTypes: string[];
|
missingNodeTypes: Set<string> = new Set();
|
||||||
|
|
||||||
get layout(): WritableLayoutStateStore | null {
|
get layout(): WritableLayoutStateStore | null {
|
||||||
return layoutStates.getLayout(this.id)
|
return layoutStates.getLayout(this.id)
|
||||||
@@ -168,6 +168,13 @@ export class ComfyWorkflow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a workflow and layout.
|
||||||
|
*
|
||||||
|
* NOTE: The layout will be attached to the global store, but the workflow
|
||||||
|
* will not. If you change your mind later be sure to call
|
||||||
|
* layoutStates.remove(workflow.id)!
|
||||||
|
*/
|
||||||
static create(title: string = "New Workflow"): [ComfyWorkflow, WritableLayoutStateStore] {
|
static create(title: string = "New Workflow"): [ComfyWorkflow, WritableLayoutStateStore] {
|
||||||
const workflow = new ComfyWorkflow(title);
|
const workflow = new ComfyWorkflow(title);
|
||||||
const layoutState = layoutStates.create(workflow);
|
const layoutState = layoutStates.create(workflow);
|
||||||
@@ -175,7 +182,7 @@ export class ComfyWorkflow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deserialize(layoutState: WritableLayoutStateStore, data: SerializedWorkflowState) {
|
deserialize(layoutState: WritableLayoutStateStore, data: SerializedWorkflowState) {
|
||||||
this.missingNodeTypes = []
|
this.missingNodeTypes.clear();
|
||||||
|
|
||||||
for (let n of data.graph.nodes) {
|
for (let n of data.graph.nodes) {
|
||||||
// Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
|
// Patch T2IAdapterLoader to ControlNetLoader since they are the same node now
|
||||||
@@ -183,7 +190,7 @@ export class ComfyWorkflow {
|
|||||||
|
|
||||||
// Find missing node types
|
// Find missing node types
|
||||||
if (!(n.type in LiteGraph.registered_node_types)) {
|
if (!(n.type in LiteGraph.registered_node_types)) {
|
||||||
this.missingNodeTypes.push(n.type);
|
this.missingNodeTypes.add(n.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,6 +240,7 @@ type WorkflowStateOps = {
|
|||||||
getActiveWorkflow: () => ComfyWorkflow | null
|
getActiveWorkflow: () => ComfyWorkflow | null
|
||||||
createNewWorkflow: (canvas: ComfyGraphCanvas, title?: string, setActive?: boolean) => ComfyWorkflow,
|
createNewWorkflow: (canvas: ComfyGraphCanvas, title?: string, setActive?: boolean) => ComfyWorkflow,
|
||||||
openWorkflow: (canvas: ComfyGraphCanvas, data: SerializedAppState) => ComfyWorkflow,
|
openWorkflow: (canvas: ComfyGraphCanvas, data: SerializedAppState) => ComfyWorkflow,
|
||||||
|
addWorkflow: (canvas: ComfyGraphCanvas, data: ComfyWorkflow) => void,
|
||||||
closeWorkflow: (canvas: ComfyGraphCanvas, index: number) => void,
|
closeWorkflow: (canvas: ComfyGraphCanvas, index: number) => void,
|
||||||
closeAllWorkflows: (canvas: ComfyGraphCanvas) => void,
|
closeAllWorkflows: (canvas: ComfyGraphCanvas) => void,
|
||||||
setActiveWorkflow: (canvas: ComfyGraphCanvas, index: number) => ComfyWorkflow | null
|
setActiveWorkflow: (canvas: ComfyGraphCanvas, index: number) => ComfyWorkflow | null
|
||||||
@@ -296,6 +304,12 @@ function openWorkflow(canvas: ComfyGraphCanvas, data: SerializedAppState): Comfy
|
|||||||
const [workflow, layoutState] = ComfyWorkflow.create("Workflow")
|
const [workflow, layoutState] = ComfyWorkflow.create("Workflow")
|
||||||
workflow.deserialize(layoutState, { graph: data.workflow, layout: data.layout, attrs: data.attrs })
|
workflow.deserialize(layoutState, { graph: data.workflow, layout: data.layout, attrs: data.attrs })
|
||||||
|
|
||||||
|
addWorkflow(canvas, workflow);
|
||||||
|
|
||||||
|
return workflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addWorkflow(canvas: ComfyGraphCanvas, workflow: ComfyWorkflow) {
|
||||||
const state = get(store);
|
const state = get(store);
|
||||||
state.openedWorkflows.push(workflow);
|
state.openedWorkflows.push(workflow);
|
||||||
state.openedWorkflowsByID[workflow.id] = workflow;
|
state.openedWorkflowsByID[workflow.id] = workflow;
|
||||||
@@ -317,7 +331,6 @@ function closeWorkflow(canvas: ComfyGraphCanvas, index: number) {
|
|||||||
|
|
||||||
layoutStates.remove(workflow.id)
|
layoutStates.remove(workflow.id)
|
||||||
|
|
||||||
|
|
||||||
state.openedWorkflows.splice(index, 1)
|
state.openedWorkflows.splice(index, 1)
|
||||||
delete state.openedWorkflowsByID[workflow.id]
|
delete state.openedWorkflowsByID[workflow.id]
|
||||||
let newIndex = clamp(index, 0, state.openedWorkflows.length - 1)
|
let newIndex = clamp(index, 0, state.openedWorkflows.length - 1)
|
||||||
@@ -372,6 +385,7 @@ const workflowStateStore: WritableWorkflowStateStore =
|
|||||||
getActiveWorkflow,
|
getActiveWorkflow,
|
||||||
createNewWorkflow,
|
createNewWorkflow,
|
||||||
openWorkflow,
|
openWorkflow,
|
||||||
|
addWorkflow,
|
||||||
closeWorkflow,
|
closeWorkflow,
|
||||||
closeAllWorkflows,
|
closeAllWorkflows,
|
||||||
setActiveWorkflow,
|
setActiveWorkflow,
|
||||||
|
|||||||
@@ -41,12 +41,12 @@
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
navigator.vibrate(20)
|
navigator.vibrate(20)
|
||||||
|
fileInput.value = null;
|
||||||
fileInput.click();
|
fileInput.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadWorkflow(): void {
|
function loadWorkflow(): void {
|
||||||
app.handleFile(fileInput.files[0]);
|
app.handleFile(fileInput.files[0]);
|
||||||
fileInput.files = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function doSaveLocal(): void {
|
function doSaveLocal(): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user