Error handling modals

This commit is contained in:
space-nuko
2023-05-21 16:55:18 -05:00
parent 02afbae406
commit a3c10d5be9
12 changed files with 241 additions and 61 deletions

View File

@@ -4,7 +4,10 @@ import ComfyAPI, { type ComfyAPIStatusResponse, type ComfyBoxPromptExtraData, ty
import { importA1111, parsePNGMetadata } from "$lib/pnginfo";
import EventEmitter from "events";
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
@@ -149,6 +152,11 @@ export type WorkflowLoadError = {
error: Error
}
export type VanillaWorkflowConvertResult = {
comfyBoxWorkflow: SerializedAppState,
missingNodeTypes: Set<string>
}
function isComfyBoxWorkflow(data: any): data is SerializedAppState {
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() {
try {
uiState.update(s => { s.isSavingToLocalStorage = true; return s; })
@@ -320,9 +312,12 @@ export default class ComfyApp {
return false;
const workflows = state.workflows as SerializedAppState[];
for (const workflow of workflows) {
await this.openWorkflow(workflow, defs)
}
await Promise.all(workflows.map(w => {
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") {
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> {
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();
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
this.lCanvas.deserialize(data.canvas)
@@ -586,10 +605,53 @@ export default class ComfyApp {
}
async openVanillaWorkflow(data: SerializedLGraph, filename: string) {
const converted = this.convertVanillaWorkflow(data, basename(filename))
console.info("WORKFLWO", converted)
notify("Converted ComfyUI workflow to ComfyBox format.", { type: "info" })
await this.openWorkflow(converted);
const title = basename(filename)
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" })
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) {
@@ -840,11 +902,10 @@ export default class ComfyApp {
}
modalState.pushModal({
title: "A1111 Prompt Details",
svelteComponent: A1111PromptDisplay,
svelteComponent: A1111PromptModal,
svelteProps: {
prompt: a1111Info
},
showCloseButton: true
})
}
else {

View File

@@ -137,12 +137,12 @@
if (!fileInput)
return;
fileInput.value = null;
fileInput.click();
}
function loadWorkflow(): void {
app.handleFile(fileInput.files[0]);
fileInput.files = null;
}
function doSaveLocal(): void {
@@ -327,7 +327,7 @@
Error loading app
</div>
<div>{error}</div>
{#if error.stack}
{#if error != null && error.stack}
{@const lines = error.stack.split("\n")}
{#each lines as line}
<div style:font-size="16px">{line}</div>

View File

@@ -1,5 +1,5 @@
<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 Modal from "./Modal.svelte";
@@ -12,12 +12,20 @@
modalState.closeModal(modal.id)
}
function onButtonClicked(button: ModalButton, closeDialog: Function) {
button.onClick();
if (button.closeOnClick !== false) {
closeDialog()
}
}
</script>
{#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">
{#if modal != null}
{#if modal != null && modal.title != null}
<h1 style="padding-bottom: 1rem;">{modal.title}</h1>
{/if}
</div>
@@ -26,10 +34,10 @@
<svelte:component this={modal.svelteComponent} {...modal.svelteProps}/>
{/if}
</svelte:fragment>
<div slot="buttons" let:closeDialog>
<div slot="buttons" class="buttons" let:closeDialog>
{#if modal != null && modal.buttons?.length > 0}
{#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>
{/each}
@@ -42,3 +50,9 @@
</div>
</Modal>
{/each}
<style lang="scss">
.buttons {
gap: var(--spacing-sm);
}
</style>

View File

@@ -50,7 +50,7 @@
</div>
</dialog>
<style>
<style lang="scss">
dialog {
max-width: 75vw;
border-radius: 0.2em;
@@ -93,7 +93,10 @@
.button-row {
display: flex;
flex-direction: row;
gap: var(--spacing-sm);
padding-top: 0.5em;
}
.button-row, .buttons {
gap: var(--spacing-sm);
}
</style>

View File

@@ -1,11 +1,10 @@
<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 { TextBox } from "@gradio/form";
import { JsonView } from '@zerodevx/svelte-json-view'
import type { A1111PromptAndInfo } from "./ComfyApp";
import { StaticImage } from "./gradio/image";
import type { A1111PromptAndInfo } from "$lib/components/ComfyApp";
import { StaticImage } from "$lib/components/gradio/image";
export let prompt: A1111PromptAndInfo | null = null;

View 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>
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>

View 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>

View 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>