From e6b446079a16e175f670f891c6fe7a3edfbf6464 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Thu, 25 May 2023 16:43:59 -0500 Subject: [PATCH] Template raw JSON view --- package.json | 1 - public/workflows/upscaleByModel.json | 20 +-- src/lib/ComfyBoxTemplate.ts | 34 +++-- src/lib/components/ComfyTemplates.svelte | 3 +- src/lib/components/GlobalModal.svelte | 5 +- src/lib/components/JsonView.svelte | 141 ++++++++++++++++++ .../components/modal/A1111PromptModal.svelte | 15 +- .../components/modal/EditTemplateModal.svelte | 114 ++++++++++++-- src/lib/stores/modalState.ts | 1 + src/scss/global.scss | 17 +++ 10 files changed, 300 insertions(+), 51 deletions(-) create mode 100644 src/lib/components/JsonView.svelte diff --git a/package.json b/package.json index aa66494..6ea0caf 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,6 @@ "@sveltejs/vite-plugin-svelte": "^2.1.1", "@tsconfig/svelte": "^4.0.1", "@types/dompurify": "^3.0.2", - "@zerodevx/svelte-json-view": "^1.0.5", "canvas-to-svg": "^1.0.3", "cm6-theme-basic-dark": "^0.2.0", "cm6-theme-basic-light": "^0.2.0", diff --git a/public/workflows/upscaleByModel.json b/public/workflows/upscaleByModel.json index 7a839a1..5dd8788 100644 --- a/public/workflows/upscaleByModel.json +++ b/public/workflows/upscaleByModel.json @@ -707,7 +707,7 @@ "buttonVariant": "primary", "buttonSize": "large", "tags": [], - "destroyChildOnCLose": false + "destroyChildOnClose": false } }, "children": [ @@ -733,7 +733,7 @@ "buttonVariant": "primary", "buttonSize": "large", "tags": [], - "destroyChildOnCLose": false + "destroyChildOnClose": false } }, "children": [ @@ -760,7 +760,7 @@ "buttonVariant": "primary", "buttonSize": "large", "tags": [], - "destroyChildOnCLose": false + "destroyChildOnClose": false } }, "children": [ @@ -788,7 +788,7 @@ "buttonVariant": "primary", "buttonSize": "large", "tags": [], - "destroyChildOnCLose": false + "destroyChildOnClose": false } }, "children": [], @@ -813,7 +813,7 @@ "buttonVariant": "primary", "buttonSize": "large", "tags": [], - "destroyChildOnCLose": false + "destroyChildOnClose": false } }, "children": [], @@ -837,7 +837,7 @@ "buttonVariant": "primary", "buttonSize": "large", "tags": [], - "destroyChildOnCLose": false + "destroyChildOnClose": false } }, "children": [ @@ -864,7 +864,7 @@ "buttonVariant": "primary", "buttonSize": "large", "tags": [], - "destroyChildOnCLose": false + "destroyChildOnClose": false } }, "children": [], @@ -888,7 +888,7 @@ "buttonVariant": "primary", "buttonSize": "large", "tags": [], - "destroyChildOnCLose": false + "destroyChildOnClose": false } }, "children": [ @@ -915,7 +915,7 @@ "buttonVariant": "primary", "buttonSize": "large", "tags": [], - "destroyChildOnCLose": false + "destroyChildOnClose": false } }, "children": [], @@ -930,4 +930,4 @@ ], "scale": 1 } -} \ No newline at end of file +} diff --git a/src/lib/ComfyBoxTemplate.ts b/src/lib/ComfyBoxTemplate.ts index c59b040..79c899a 100644 --- a/src/lib/ComfyBoxTemplate.ts +++ b/src/lib/ComfyBoxTemplate.ts @@ -338,6 +338,7 @@ export function serializeTemplate(canvas: ComfyGraphCanvas, template: ComfyBoxTe uiState.update(s => { s.forceSaveUserState = null; return s; }); nodes = relocateNodes(nodes); + nodes = removeTags(nodes); [nodes, links] = pruneDetachedLinks(nodes, links); const svg = renderSvg(canvas, graph, TEMPLATE_SVG_PADDING); @@ -357,22 +358,35 @@ export function serializeTemplate(canvas: ComfyGraphCanvas, template: ComfyBoxTe return serTemplate; } + +/* + * Extract embedded workflow from desc tags + */ +export function extractTemplateJSONFromSVG(svg: string): string | null { + const descEnd = svg.lastIndexOf(""); + if (descEnd !== -1) { + const descStart = svg.lastIndexOf("", descEnd); + if (descStart !== -1) { + const json = svg.substring(descStart + 6, descEnd); + return unescapeXml(json); + } + } + + return null; +} + +/* + * Credit goes to pythongosssss for this format + */ export function deserializeTemplateFromSVG(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = async () => { const svg = reader.result as string; let template = null; - - // Extract embedded workflow from desc tags - const descEnd = svg.lastIndexOf(""); - if (descEnd !== -1) { - const descStart = svg.lastIndexOf("", descEnd); - if (descStart !== -1) { - const json = svg.substring(descStart + 6, descEnd); - template = JSON.parse(unescapeXml(json)); - } - } + let templateJSON = extractTemplateJSONFromSVG(svg); + if (templateJSON) + template = JSON.parse(templateJSON); if (!isSerializedComfyBoxTemplate(template)) { reject("Invalid template format!") diff --git a/src/lib/components/ComfyTemplates.svelte b/src/lib/components/ComfyTemplates.svelte index aae70a4..8609ead 100644 --- a/src/lib/components/ComfyTemplates.svelte +++ b/src/lib/components/ComfyTemplates.svelte @@ -143,7 +143,8 @@ { name: "Delete", variant: "secondary", - onClick: deleteTemplate + onClick: deleteTemplate, + disabled: layout.template.isBuiltIn }, { name: "Close", diff --git a/src/lib/components/GlobalModal.svelte b/src/lib/components/GlobalModal.svelte index 047c31d..4a0262e 100644 --- a/src/lib/components/GlobalModal.svelte +++ b/src/lib/components/GlobalModal.svelte @@ -13,6 +13,9 @@ } function onButtonClicked(modal: ModalData, button: ModalButton, closeDialog: Function) { + if (button.disabled) + return; + if (button.onClick(modal) === false) return @@ -39,7 +42,7 @@
{#if modal != null && modal.buttons?.length > 0} {#each modal.buttons as button} - {/each} diff --git a/src/lib/components/JsonView.svelte b/src/lib/components/JsonView.svelte new file mode 100644 index 0000000..e3d0173 --- /dev/null +++ b/src/lib/components/JsonView.svelte @@ -0,0 +1,141 @@ + + +{#if !items.length} + {brackets[0]}{brackets[1]}{#if !_last},{/if} +{:else if collapsed} + {brackets[0]}...{brackets[1]}{#if !_last && collapsed},{/if} +{:else} + {brackets[0]} + + {brackets[1]}{#if !_last},{/if} +{/if} + + diff --git a/src/lib/components/modal/A1111PromptModal.svelte b/src/lib/components/modal/A1111PromptModal.svelte index 8261623..f2fc1d5 100644 --- a/src/lib/components/modal/A1111PromptModal.svelte +++ b/src/lib/components/modal/A1111PromptModal.svelte @@ -2,7 +2,7 @@ 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 JsonView from '$lib/components/JsonView.svelte' import type { A1111PromptAndInfo } from "$lib/components/ComfyApp"; import { StaticImage } from "$lib/components/gradio/image"; @@ -65,17 +65,6 @@ height: 70vh; color: none; - --jsonPaddingLeft: 1rem; - --jsonBorderLeft: 1px dotted var(--neutral-600); - --jsonBracketColor: currentcolor; - --jsonBracketHoverBackground: var(--neutral-100); - --jsonSeparatorColor: currentcolor; - --jsonKeyColor: var(--body-text-color); - --jsonValColor: var(--body-text-color-subdued); - --jsonValStringColor: var(--color-green-500); - --jsonValNumberColor: var(--color-blue-500); - --jsonValBooleanColor: var(--color-red-500); - display: flex; flex-wrap: nowrap; overflow-y: none; @@ -87,7 +76,7 @@ overflow: auto; .json { - font-family: monospace; + @include json-view; } .scroll-container { diff --git a/src/lib/components/modal/EditTemplateModal.svelte b/src/lib/components/modal/EditTemplateModal.svelte index bb78c90..cd20768 100644 --- a/src/lib/components/modal/EditTemplateModal.svelte +++ b/src/lib/components/modal/EditTemplateModal.svelte @@ -2,14 +2,18 @@ import type { ComfyBoxTemplate, SerializedComfyBoxTemplate } from "$lib/ComfyBoxTemplate"; import type { SerializedDragEntry, SerializedLayoutState } from "$lib/stores/layoutStates"; import { Block, BlockTitle } from "@gradio/atoms"; + import { Tabs, TabItem } from "@gradio/tabs"; + import { JSON as JSONIcon } from "@gradio/icons"; + import JsonView from '$lib/components/JsonView.svelte' import SerializedLayoutPreviewNode from "./SerializedLayoutPreviewNode.svelte"; import Row from "../gradio/app/Row.svelte"; import createDOMPurify from "dompurify" - import Column from "../gradio/app/Column.svelte"; - import Accordion from "../gradio/app/Accordion.svelte"; - import Textbox from "@gradio/form/src/Textbox.svelte"; - import type { ModalData } from "$lib/stores/modalState"; - import { writable, type Writable } from "svelte/store"; + import Column from "../gradio/app/Column.svelte"; + import Accordion from "../gradio/app/Accordion.svelte"; + import Textbox from "@gradio/form/src/Textbox.svelte"; + import type { ModalData } from "$lib/stores/modalState"; + import { writable, type Writable } from "svelte/store"; + import { negmod } from "$lib/utils"; const DOMPurify = createDOMPurify(window); export let templateAndSvg: SerializedComfyBoxTemplate; @@ -18,6 +22,45 @@ let layout: SerializedLayoutState | null let root: SerializedDragEntry | null let state: Writable = writable({}) + let rawTemplate: SerializedComfyBoxTemplate | null + let showJSON = false; + let showAllJSON: number = 0; + let createdAt = ""; + + $: { + rawTemplate = { ...templateAndSvg }; + rawTemplate.svg = undefined; + } + + function collapseByDefault(json: any): boolean { + switch (showAllJSON) { + case 0: + return typeof json["id"] === "string"; + case 1: + return typeof json["nodes"] === "object" + case 2: + default: + return false; + } + } + + function expandJSON() { + showAllJSON = negmod(showAllJSON + 1, 3) + } + + $: { + let options: Intl.DateTimeFormatOptions = { + weekday: 'short', + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric' + }; + const date = new Date(templateAndSvg.metadata.createdAt); + createdAt = date.toLocaleString('en-US', options); + } $: { state = _modal.state; @@ -31,9 +74,9 @@ let saneSvg: string = ""; $: saneSvg = templateAndSvg - ? DOMPurify.sanitize(templateAndSvg.svg, { USE_PROFILES: { svg: true, svgFilters: true } }) - .replace("Metadata
+
@@ -76,15 +120,31 @@ {/if}
- - + + -
- {@html saneSvg} -
+ + +
+ {@html saneSvg} +
+
+
-
-
+ + (showJSON = true)}> + {#key showAllJSON} + {#if showJSON} + +
+ +
+ {/if} + {/key} +
+
@@ -120,10 +180,34 @@ :global(> .block) { background: var(--panel-background-fill); } + + .json { + @include json-view; + } } .template-graph-wrapper { overflow: auto; margin: auto; } + + + .json-button { + display: flex; + position: absolute; + top: var(--block-label-margin); + right: var(--block-label-margin); + align-items: center; + box-shadow: var(--shadow-drop); + border: 1px solid var(--border-color-primary); + border-radius: var(--block-label-right-radius); + background: var(--block-label-background-fill); + padding: 5px; + width: 30px; + height: 30px; + overflow: hidden; + color: var(--block-label-text-color); + font: var(--font); + font-size: var(--button-small-text-size); + } diff --git a/src/lib/stores/modalState.ts b/src/lib/stores/modalState.ts index 6cb89d8..116a3ce 100644 --- a/src/lib/stores/modalState.ts +++ b/src/lib/stores/modalState.ts @@ -8,6 +8,7 @@ export type ModalButton = { name: string, variant: "primary" | "secondary", onClick: (state: ModalData) => boolean | void, + disabled?: boolean, closeOnClick?: boolean } export interface ModalData { diff --git a/src/scss/global.scss b/src/scss/global.scss index 2f7c40d..f3545c4 100644 --- a/src/scss/global.scss +++ b/src/scss/global.scss @@ -146,6 +146,23 @@ body { } } +@mixin json-view { + --jsonPaddingLeft: 1rem; + --jsonBorderLeft: 1px dotted var(--neutral-600); + --jsonBracketColor: currentcolor; + --jsonBracketHoverBackground: var(--primary-200); + --jsonSeparatorColor: currentcolor; + --jsonKeyColor: var(--body-text-color); + --jsonValColor: var(--body-text-color-subdued); + --jsonValStringColor: var(--color-green-500); + --jsonValNumberColor: var(--color-blue-500); + --jsonValBooleanColor: var(--color-red-500); + --jsonCollapsedColor: var(--neutral-100); + --jsonCollapsedBackground: var(--primary-400); + + font-family: monospace; +} + hr { color: var(--panel-border-color); }