Template raw JSON view
This commit is contained in:
@@ -73,7 +73,6 @@
|
|||||||
"@sveltejs/vite-plugin-svelte": "^2.1.1",
|
"@sveltejs/vite-plugin-svelte": "^2.1.1",
|
||||||
"@tsconfig/svelte": "^4.0.1",
|
"@tsconfig/svelte": "^4.0.1",
|
||||||
"@types/dompurify": "^3.0.2",
|
"@types/dompurify": "^3.0.2",
|
||||||
"@zerodevx/svelte-json-view": "^1.0.5",
|
|
||||||
"canvas-to-svg": "^1.0.3",
|
"canvas-to-svg": "^1.0.3",
|
||||||
"cm6-theme-basic-dark": "^0.2.0",
|
"cm6-theme-basic-dark": "^0.2.0",
|
||||||
"cm6-theme-basic-light": "^0.2.0",
|
"cm6-theme-basic-light": "^0.2.0",
|
||||||
|
|||||||
@@ -707,7 +707,7 @@
|
|||||||
"buttonVariant": "primary",
|
"buttonVariant": "primary",
|
||||||
"buttonSize": "large",
|
"buttonSize": "large",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"destroyChildOnCLose": false
|
"destroyChildOnClose": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
@@ -733,7 +733,7 @@
|
|||||||
"buttonVariant": "primary",
|
"buttonVariant": "primary",
|
||||||
"buttonSize": "large",
|
"buttonSize": "large",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"destroyChildOnCLose": false
|
"destroyChildOnClose": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
@@ -760,7 +760,7 @@
|
|||||||
"buttonVariant": "primary",
|
"buttonVariant": "primary",
|
||||||
"buttonSize": "large",
|
"buttonSize": "large",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"destroyChildOnCLose": false
|
"destroyChildOnClose": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
@@ -788,7 +788,7 @@
|
|||||||
"buttonVariant": "primary",
|
"buttonVariant": "primary",
|
||||||
"buttonSize": "large",
|
"buttonSize": "large",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"destroyChildOnCLose": false
|
"destroyChildOnClose": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
@@ -813,7 +813,7 @@
|
|||||||
"buttonVariant": "primary",
|
"buttonVariant": "primary",
|
||||||
"buttonSize": "large",
|
"buttonSize": "large",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"destroyChildOnCLose": false
|
"destroyChildOnClose": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
@@ -837,7 +837,7 @@
|
|||||||
"buttonVariant": "primary",
|
"buttonVariant": "primary",
|
||||||
"buttonSize": "large",
|
"buttonSize": "large",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"destroyChildOnCLose": false
|
"destroyChildOnClose": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
@@ -864,7 +864,7 @@
|
|||||||
"buttonVariant": "primary",
|
"buttonVariant": "primary",
|
||||||
"buttonSize": "large",
|
"buttonSize": "large",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"destroyChildOnCLose": false
|
"destroyChildOnClose": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
@@ -888,7 +888,7 @@
|
|||||||
"buttonVariant": "primary",
|
"buttonVariant": "primary",
|
||||||
"buttonSize": "large",
|
"buttonSize": "large",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"destroyChildOnCLose": false
|
"destroyChildOnClose": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
@@ -915,7 +915,7 @@
|
|||||||
"buttonVariant": "primary",
|
"buttonVariant": "primary",
|
||||||
"buttonSize": "large",
|
"buttonSize": "large",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"destroyChildOnCLose": false
|
"destroyChildOnClose": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
@@ -930,4 +930,4 @@
|
|||||||
],
|
],
|
||||||
"scale": 1
|
"scale": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ 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);
|
||||||
@@ -357,22 +358,35 @@ export function serializeTemplate(canvas: ComfyGraphCanvas, template: ComfyBoxTe
|
|||||||
return serTemplate;
|
return serTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract embedded workflow from desc tags
|
||||||
|
*/
|
||||||
|
export function extractTemplateJSONFromSVG(svg: string): string | null {
|
||||||
|
const descEnd = svg.lastIndexOf("</desc>");
|
||||||
|
if (descEnd !== -1) {
|
||||||
|
const descStart = svg.lastIndexOf("<desc>", 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<SerializedComfyBoxTemplate> {
|
export function deserializeTemplateFromSVG(file: File): Promise<SerializedComfyBoxTemplate> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = async () => {
|
reader.onload = async () => {
|
||||||
const svg = reader.result as string;
|
const svg = reader.result as string;
|
||||||
let template = null;
|
let template = null;
|
||||||
|
let templateJSON = extractTemplateJSONFromSVG(svg);
|
||||||
// Extract embedded workflow from desc tags
|
if (templateJSON)
|
||||||
const descEnd = svg.lastIndexOf("</desc>");
|
template = JSON.parse(templateJSON);
|
||||||
if (descEnd !== -1) {
|
|
||||||
const descStart = svg.lastIndexOf("<desc>", descEnd);
|
|
||||||
if (descStart !== -1) {
|
|
||||||
const json = svg.substring(descStart + 6, descEnd);
|
|
||||||
template = JSON.parse(unescapeXml(json));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isSerializedComfyBoxTemplate(template)) {
|
if (!isSerializedComfyBoxTemplate(template)) {
|
||||||
reject("Invalid template format!")
|
reject("Invalid template format!")
|
||||||
|
|||||||
@@ -143,7 +143,8 @@
|
|||||||
{
|
{
|
||||||
name: "Delete",
|
name: "Delete",
|
||||||
variant: "secondary",
|
variant: "secondary",
|
||||||
onClick: deleteTemplate
|
onClick: deleteTemplate,
|
||||||
|
disabled: layout.template.isBuiltIn
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Close",
|
name: "Close",
|
||||||
|
|||||||
@@ -13,6 +13,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onButtonClicked(modal: ModalData, button: ModalButton, closeDialog: Function) {
|
function onButtonClicked(modal: ModalData, button: ModalButton, closeDialog: Function) {
|
||||||
|
if (button.disabled)
|
||||||
|
return;
|
||||||
|
|
||||||
if (button.onClick(modal) === false)
|
if (button.onClick(modal) === false)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -39,7 +42,7 @@
|
|||||||
<div slot="buttons" class="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={() => onButtonClicked(modal, button, closeDialog)}>
|
<Button variant={button.variant} disabled={button.disabled} on:click={() => onButtonClicked(modal, button, closeDialog)}>
|
||||||
{button.name}
|
{button.name}
|
||||||
</Button>
|
</Button>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
141
src/lib/components/JsonView.svelte
Normal file
141
src/lib/components/JsonView.svelte
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
/*
|
||||||
|
* Modified from @zerodevx/svelte-json-view
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {*} - object or array to display */
|
||||||
|
export let json: any
|
||||||
|
/** @type {number} - initial expansion depth */
|
||||||
|
export let depth: number = Infinity
|
||||||
|
export let collapseByDefault: boolean | ((v: any) => boolean) | null = false;
|
||||||
|
export let _cur: number = 0
|
||||||
|
export let _last: boolean = true
|
||||||
|
|
||||||
|
/** @type {*[]} */
|
||||||
|
let items: any[]
|
||||||
|
let isArray: boolean = false
|
||||||
|
let brackets: [string, string] = ['', '']
|
||||||
|
export let collapsed: boolean | null = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} i
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getType(i: any): string {
|
||||||
|
if (i === null) return 'null'
|
||||||
|
return typeof i
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} i
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function format(i: any): string {
|
||||||
|
const t = getType(i)
|
||||||
|
if (t === 'string') return `"${i}"`
|
||||||
|
if (t === 'function') return 'f () {...}'
|
||||||
|
if (t === 'symbol') return i.toString()
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
function clicked() {
|
||||||
|
collapsed = !collapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
function pressed(e) {
|
||||||
|
if (e instanceof KeyboardEvent && ['Enter', ' '].includes(e.key)) clicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
items = getType(json) === 'object' ? Object.keys(json) : []
|
||||||
|
isArray = Array.isArray(json)
|
||||||
|
brackets = isArray ? ['[', ']'] : ['{', '}']
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (collapsed === null)
|
||||||
|
collapsed = depth < _cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcCollapsed(json: any): boolean | null {
|
||||||
|
if (typeof collapseByDefault === "function")
|
||||||
|
return collapseByDefault(json)
|
||||||
|
else if (typeof collapseByDefault === "boolean")
|
||||||
|
return collapseByDefault
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if !items.length}
|
||||||
|
<span class="_jsonBkt empty">{brackets[0]}{brackets[1]}</span>{#if !_last}<span class="_jsonSep"
|
||||||
|
>,</span
|
||||||
|
>{/if}
|
||||||
|
{:else if collapsed}
|
||||||
|
<span class="_jsonBkt _jsonCollapsed" role="button" tabindex="0" on:click={clicked} on:keydown={pressed}
|
||||||
|
>{brackets[0]}...{brackets[1]}</span
|
||||||
|
>{#if !_last && collapsed}<span class="_jsonSep">,</span>{/if}
|
||||||
|
{:else}
|
||||||
|
<span class="_jsonBkt" role="button" tabindex="0" on:click={clicked} on:keydown={pressed}
|
||||||
|
>{brackets[0]}</span
|
||||||
|
>
|
||||||
|
<ul class="_jsonList">
|
||||||
|
{#each items as i, idx}
|
||||||
|
<li>
|
||||||
|
{#if !isArray}
|
||||||
|
<span class="_jsonKey">"{i}"</span><span class="_jsonSep">:</span>
|
||||||
|
{/if}
|
||||||
|
{#if getType(json[i]) === 'object'}
|
||||||
|
<svelte:self json={json[i]} {collapseByDefault} collapsed={calcCollapsed(json[i])} {depth} _cur={_cur + 1} _last={idx === items.length - 1} />
|
||||||
|
{:else}
|
||||||
|
<span class="_jsonVal {getType(json[i])}">{format(json[i])}</span
|
||||||
|
>{#if idx < items.length - 1}<span class="_jsonSep">,</span>{/if}
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
<span class="_jsonBkt" role="button" tabindex="0" on:click={clicked} on:keydown={pressed}
|
||||||
|
>{brackets[1]}</span
|
||||||
|
>{#if !_last}<span class="_jsonSep">,</span>{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
._jsonList {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-left: var(--jsonPaddingLeft, 1rem);
|
||||||
|
border-left: var(--jsonBorderLeft, 1px dotted);
|
||||||
|
}
|
||||||
|
._jsonBkt {
|
||||||
|
color: var(--jsonBracketColor, currentcolor);
|
||||||
|
}
|
||||||
|
._jsonBkt:not(.empty):hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--jsonBracketHoverBackground, #e5e7eb);
|
||||||
|
}
|
||||||
|
._jsonSep {
|
||||||
|
color: var(--jsonSeparatorColor, currentcolor);
|
||||||
|
}
|
||||||
|
._jsonKey {
|
||||||
|
color: var(--jsonKeyColor, currentcolor);
|
||||||
|
}
|
||||||
|
._jsonVal {
|
||||||
|
color: var(--jsonValColor, #9ca3af);
|
||||||
|
}
|
||||||
|
._jsonVal.string {
|
||||||
|
color: var(--jsonValStringColor, #059669);
|
||||||
|
}
|
||||||
|
._jsonVal.number {
|
||||||
|
color: var(--jsonValNumberColor, #d97706);
|
||||||
|
}
|
||||||
|
._jsonVal.boolean {
|
||||||
|
color: var(--jsonValBooleanColor, #2563eb);
|
||||||
|
}
|
||||||
|
._jsonCollapsed {
|
||||||
|
color: var(--jsonCollapsedColor, currentcolor);
|
||||||
|
background: var(--jsonCollapsedBackground, currentcolor);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
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 '$lib/components/JsonView.svelte'
|
||||||
import type { A1111PromptAndInfo } from "$lib/components/ComfyApp";
|
import type { A1111PromptAndInfo } from "$lib/components/ComfyApp";
|
||||||
import { StaticImage } from "$lib/components/gradio/image";
|
import { StaticImage } from "$lib/components/gradio/image";
|
||||||
|
|
||||||
@@ -65,17 +65,6 @@
|
|||||||
height: 70vh;
|
height: 70vh;
|
||||||
color: none;
|
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;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
overflow-y: none;
|
overflow-y: none;
|
||||||
@@ -87,7 +76,7 @@
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.json {
|
.json {
|
||||||
font-family: monospace;
|
@include json-view;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-container {
|
.scroll-container {
|
||||||
|
|||||||
@@ -2,14 +2,18 @@
|
|||||||
import type { ComfyBoxTemplate, SerializedComfyBoxTemplate } from "$lib/ComfyBoxTemplate";
|
import type { ComfyBoxTemplate, SerializedComfyBoxTemplate } from "$lib/ComfyBoxTemplate";
|
||||||
import type { SerializedDragEntry, SerializedLayoutState } from "$lib/stores/layoutStates";
|
import type { SerializedDragEntry, SerializedLayoutState } from "$lib/stores/layoutStates";
|
||||||
import { Block, BlockTitle } from "@gradio/atoms";
|
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 SerializedLayoutPreviewNode from "./SerializedLayoutPreviewNode.svelte";
|
||||||
import Row from "../gradio/app/Row.svelte";
|
import Row from "../gradio/app/Row.svelte";
|
||||||
import createDOMPurify from "dompurify"
|
import createDOMPurify from "dompurify"
|
||||||
import Column from "../gradio/app/Column.svelte";
|
import Column from "../gradio/app/Column.svelte";
|
||||||
import Accordion from "../gradio/app/Accordion.svelte";
|
import Accordion from "../gradio/app/Accordion.svelte";
|
||||||
import Textbox from "@gradio/form/src/Textbox.svelte";
|
import Textbox from "@gradio/form/src/Textbox.svelte";
|
||||||
import type { ModalData } from "$lib/stores/modalState";
|
import type { ModalData } from "$lib/stores/modalState";
|
||||||
import { writable, type Writable } from "svelte/store";
|
import { writable, type Writable } from "svelte/store";
|
||||||
|
import { negmod } from "$lib/utils";
|
||||||
const DOMPurify = createDOMPurify(window);
|
const DOMPurify = createDOMPurify(window);
|
||||||
|
|
||||||
export let templateAndSvg: SerializedComfyBoxTemplate;
|
export let templateAndSvg: SerializedComfyBoxTemplate;
|
||||||
@@ -18,6 +22,45 @@
|
|||||||
let layout: SerializedLayoutState | null
|
let layout: SerializedLayoutState | null
|
||||||
let root: SerializedDragEntry | null
|
let root: SerializedDragEntry | null
|
||||||
let state: Writable<any> = writable({})
|
let state: Writable<any> = 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;
|
state = _modal.state;
|
||||||
@@ -31,9 +74,9 @@
|
|||||||
let saneSvg: string = "";
|
let saneSvg: string = "";
|
||||||
|
|
||||||
$: saneSvg = templateAndSvg
|
$: saneSvg = templateAndSvg
|
||||||
? DOMPurify.sanitize(templateAndSvg.svg, { USE_PROFILES: { svg: true, svgFilters: true } })
|
? DOMPurify.sanitize(templateAndSvg.svg, { USE_PROFILES: { svg: true, svgFilters: true } })
|
||||||
.replace("<svg", "<svg style='background: url(\"image/graph-bg.png\")'")
|
.replace("<svg", "<svg style='background: url(\"image/graph-bg.png\")'")
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
$: if (templateAndSvg) {
|
$: if (templateAndSvg) {
|
||||||
layout = templateAndSvg.layout;
|
layout = templateAndSvg.layout;
|
||||||
@@ -58,6 +101,7 @@
|
|||||||
<BlockTitle>Metadata</BlockTitle>
|
<BlockTitle>Metadata</BlockTitle>
|
||||||
<div>
|
<div>
|
||||||
<Textbox label="Name" disabled={!editable} bind:value={$state.name} lines={1} max_lines={1} />
|
<Textbox label="Name" disabled={!editable} bind:value={$state.name} lines={1} max_lines={1} />
|
||||||
|
<Textbox label="Created At" disabled={true} bind:value={createdAt} lines={1} max_lines={1} />
|
||||||
<Textbox label="Author" disabled={!editable} bind:value={$state.author} lines={1} max_lines={1} />
|
<Textbox label="Author" disabled={!editable} bind:value={$state.author} lines={1} max_lines={1} />
|
||||||
<Textbox label="Description" disabled={!editable} bind:value={$state.description} lines={5} max_lines={5} />
|
<Textbox label="Description" disabled={!editable} bind:value={$state.description} lines={5} max_lines={5} />
|
||||||
</div>
|
</div>
|
||||||
@@ -76,15 +120,31 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</Row>
|
</Row>
|
||||||
<div class="template-graph-preview">
|
<div class="template-graph-preview">
|
||||||
<Block>
|
<Tabs selected="graph">
|
||||||
<Accordion label="Graph">
|
<TabItem name="Graph" id="graph">
|
||||||
<Block>
|
<Block>
|
||||||
<div class="template-graph-wrapper">
|
<Accordion label="Graph">
|
||||||
{@html saneSvg}
|
<Block>
|
||||||
</div>
|
<div class="template-graph-wrapper">
|
||||||
|
{@html saneSvg}
|
||||||
|
</div>
|
||||||
|
</Block>
|
||||||
|
</Accordion>
|
||||||
</Block>
|
</Block>
|
||||||
</Accordion>
|
</TabItem>
|
||||||
</Block>
|
<TabItem name="Raw JSON" id="json" on:select={() => (showJSON = true)}>
|
||||||
|
{#key showAllJSON}
|
||||||
|
{#if showJSON}
|
||||||
|
<button class="json-button" on:click={expandJSON}>
|
||||||
|
<JSONIcon />
|
||||||
|
</button>
|
||||||
|
<div class="json">
|
||||||
|
<JsonView json={rawTemplate} {collapseByDefault} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/key}
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -120,10 +180,34 @@
|
|||||||
:global(> .block) {
|
:global(> .block) {
|
||||||
background: var(--panel-background-fill);
|
background: var(--panel-background-fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.json {
|
||||||
|
@include json-view;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.template-graph-wrapper {
|
.template-graph-wrapper {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
margin: 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);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export type ModalButton = {
|
|||||||
name: string,
|
name: string,
|
||||||
variant: "primary" | "secondary",
|
variant: "primary" | "secondary",
|
||||||
onClick: (state: ModalData) => boolean | void,
|
onClick: (state: ModalData) => boolean | void,
|
||||||
|
disabled?: boolean,
|
||||||
closeOnClick?: boolean
|
closeOnClick?: boolean
|
||||||
}
|
}
|
||||||
export interface ModalData {
|
export interface ModalData {
|
||||||
|
|||||||
@@ -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 {
|
hr {
|
||||||
color: var(--panel-border-color);
|
color: var(--panel-border-color);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user