Better bottom bar

This commit is contained in:
space-nuko
2023-05-06 11:04:06 -05:00
parent 89b829f36d
commit ef4723b572
8 changed files with 298 additions and 178 deletions

View File

@@ -41,17 +41,17 @@
</script> </script>
{#if container && children} {#if container && children}
{@const edit = $uiState.uiEditMode === "widgets" && zIndex > 1} {@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets" && zIndex > 1}
{#key $attrsChanged} {#key $attrsChanged}
<div class="container {container.attrs.direction} {container.attrs.classes} {classes.join(' ')} z-index{zIndex}" <div class="container {container.attrs.direction} {container.attrs.classes} {classes.join(' ')} z-index{zIndex}"
class:hide-block={container.attrs.blockVariant === "hidden"} class:hide-block={container.attrs.blockVariant === "hidden"}
class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(container.id)} class:selected={$uiState.uiUnlocked && $layoutState.currentSelection.includes(container.id)}
class:root-container={zIndex === 0} class:root-container={zIndex === 0}
class:is-executing={container.isNodeExecuting} class:is-executing={container.isNodeExecuting}
class:edit={edit}> class:edit={edit}>
<Block> <Block>
{#if container.attrs.title !== ""} {#if container.attrs.title !== ""}
<label for={String(container.id)} class={$uiState.uiEditMode === "widgets" ? "edit-title-label" : ""}> <label for={String(container.id)} class={($uiState.uiUnlocked && $uiState.uiEditMode === "widgets") ? "edit-title-label" : ""}>
<BlockTitle>{container.attrs.title}</BlockTitle> <BlockTitle>{container.attrs.title}</BlockTitle>
</label> </label>
{/if} {/if}
@@ -64,7 +64,7 @@
centreDraggedOnCursor: true, centreDraggedOnCursor: true,
morphDisabled: true, morphDisabled: true,
dropFromOthersDisabled: zIndex === 0, dropFromOthersDisabled: zIndex === 0,
dragDisabled: zIndex === 0 || $layoutState.currentSelection.length > 2 || $uiState.uiEditMode === "disabled" dragDisabled: zIndex === 0 || $layoutState.currentSelection.length > 2 || !$uiState.uiUnlocked
}}" }}"
on:consider="{handleConsider}" on:consider="{handleConsider}"
on:finalize="{handleFinalize}" on:finalize="{handleFinalize}"

View File

@@ -19,6 +19,7 @@
import ComfyQueue from "./ComfyQueue.svelte"; import ComfyQueue from "./ComfyQueue.svelte";
import ComfyProperties from "./ComfyProperties.svelte"; import ComfyProperties from "./ComfyProperties.svelte";
import queueState from "$lib/stores/queueState"; import queueState from "$lib/stores/queueState";
import ComfyUnlockUIButton from "./ComfyUnlockUIButton.svelte";
export let app: ComfyApp = undefined; export let app: ComfyApp = undefined;
let imageViewer: ImageViewer; let imageViewer: ImageViewer;
@@ -29,7 +30,7 @@
let containerElem: HTMLDivElement; let containerElem: HTMLDivElement;
let resizeTimeout: NodeJS.Timeout | null; let resizeTimeout: NodeJS.Timeout | null;
let hasShownUIHelpToast: boolean = false; let hasShownUIHelpToast: boolean = false;
let uiTheme: string = "anapnoe"; let uiTheme: string = "";
let debugLayout: boolean = false; let debugLayout: boolean = false;
@@ -69,7 +70,7 @@
} }
} }
let propsSidebarSize = 0; //15; let propsSidebarSize = 15; //15;
function toggleProps() { function toggleProps() {
if (propsSidebarSize == 0) { if (propsSidebarSize == 0) {
@@ -124,7 +125,7 @@
app.lCanvas.recenter(); app.lCanvas.recenter();
} }
$: if ($uiState.uiEditMode !== "disabled" && !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.")
} }
@@ -158,7 +159,7 @@
(window as any).app = app; (window as any).app = app;
(window as any).appPane = uiPane; (window as any).appPane = uiPane;
await import('../../scss/ux.scss'); // await import('../../scss/ux.scss');
refreshView(); refreshView();
}) })
@@ -203,50 +204,56 @@
</Splitpanes> </Splitpanes>
</div> </div>
<div id="bottombar"> <div id="bottombar">
<Button variant="primary" on:click={queuePrompt}> <div class="left">
Queue Prompt <Button variant="primary" on:click={queuePrompt}>
</Button> Queue Prompt
<Button variant="secondary" on:click={toggleGraph}> </Button>
Toggle Graph <Button variant="secondary" on:click={toggleGraph}>
</Button> Toggle Graph
<Button variant="secondary" on:click={toggleProps}> </Button>
Toggle Props <Button variant="secondary" on:click={toggleProps}>
</Button> Toggle Props
<Button variant="secondary" on:click={toggleQueue}> </Button>
Toggle Queue <Button variant="secondary" on:click={toggleQueue}>
</Button> Toggle Queue
<Button variant="secondary" on:click={doSave}> </Button>
Save <Button variant="secondary" on:click={doSave}>
</Button> Save
<Button variant="secondary" on:click={doReset}> </Button>
Reset <Button variant="secondary" on:click={doReset}>
</Button> Reset
<Button variant="secondary" on:click={doLoadDefault}> </Button>
Load Default <Button variant="secondary" on:click={doLoadDefault}>
</Button> Load Default
<Button variant="secondary" on:click={doRecenter}> </Button>
Recenter <Button variant="secondary" on:click={doRecenter}>
</Button> Recenter
<Button variant="secondary" on:click={doRefreshCombos}> </Button>
🔄 <Button variant="secondary" on:click={doRefreshCombos}>
</Button> 🔄
<!-- <Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/> </Button>
<Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/> --> <!-- <Checkbox label="Lock Nodes" bind:value={$uiState.nodesLocked}/>
<Checkbox label="Auto-Add UI" bind:value={$uiState.autoAddUI}/> <Checkbox label="Disable Interaction" bind:value={$uiState.graphLocked}/> -->
<label class="label" for="enable-ui-editing"> <span style="display: inline-flex !important">
<BlockTitle>Enable UI Editing</BlockTitle> <Checkbox label="Auto-Add UI" bind:value={$uiState.autoAddUI}/>
<select id="enable-ui-editing" name="enable-ui-editing" bind:value={$uiState.uiEditMode}> </span>
<option value="disabled">Disabled</option> <span class="label" for="ui-edit-mode">
<option value="widgets">Widgets</option> <BlockTitle>UI Edit mode</BlockTitle>
</select> <select id="ui-edit-mode" name="ui-edit-mode" bind:value={$uiState.uiEditMode}>
</label> <option value="widgets">Widgets</option>
<label class="label" for="ui-theme"> </select>
<BlockTitle>Theme</BlockTitle> </span>
<select id="ui-theme" name="ui-theme" bind:value={uiTheme}> <span class="label" for="ui-theme">
<option value="">None</option> <BlockTitle>Theme</BlockTitle>
<option value="anapnoe">Anapnoe</option> <select id="ui-theme" name="ui-theme" bind:value={uiTheme}>
</select> <option value="">None</option>
</label> <option value="anapnoe">Anapnoe</option>
</select>
</span>
</div>
<div class="right">
<ComfyUnlockUIButton bind:toggled={$uiState.uiUnlocked} />
</div>
</div> </div>
<LightboxModal /> <LightboxModal />
</div> </div>
@@ -276,10 +283,23 @@
} }
#bottombar { #bottombar {
padding-top: 0.5em;
display: flex; display: flex;
flex-wrap: wrap; align-items: center;
width: 100%;
gap: var(--layout-gap); gap: var(--layout-gap);
margin: 10px; padding-left: 1em;
padding-right: 1em;
margin-top: auto;
overflow-x: auto;
> .left {
flex-shrink: 0;
}
> .right {
margin-left: auto
}
} }
.canvas-wrapper { .canvas-wrapper {
@@ -350,4 +370,8 @@
label.label > :global(span) { label.label > :global(span) {
top: 20%; top: 20%;
} }
span.left {
right: 0px;
}
</style> </style>

View File

@@ -3,6 +3,7 @@
import { TextBox, Checkbox } from "@gradio/form"; import { TextBox, Checkbox } from "@gradio/form";
import { LGraphNode } from "@litegraph-ts/core" import { LGraphNode } from "@litegraph-ts/core"
import layoutState, { type IDragItem, type WidgetLayout, ALL_ATTRIBUTES, type AttributesSpec } from "$lib/stores/layoutState" import layoutState, { type IDragItem, type WidgetLayout, ALL_ATTRIBUTES, type AttributesSpec } from "$lib/stores/layoutState"
import uiState from "$lib/stores/uiState"
import { get } from "svelte/store" import { get } from "svelte/store"
import type { ComfyWidgetNode } from "$lib/nodes"; import type { ComfyWidgetNode } from "$lib/nodes";
import ComfyNumberProperty from "./ComfyNumberProperty.svelte"; import ComfyNumberProperty from "./ComfyNumberProperty.svelte";
@@ -41,74 +42,92 @@
targetType = "" targetType = ""
} }
function validNodeProperty(spec: AttributesSpec, node: LGraphNode): boolean { function validNodeProperty(spec: AttributesSpec, node: LGraphNode | null): boolean {
if (node == null)
return false;
if (spec.canShow && !spec.canShow(node))
return false;
if (spec.validNodeTypes) { if (spec.validNodeTypes) {
return spec.validNodeTypes.indexOf(node.type) !== -1; return spec.validNodeTypes.indexOf(node.type) !== -1;
} }
return spec.name in node.properties return spec.name in node.properties
} }
function updateAttribute(entry: AttributesSpec, target: IDragItem, value: any) { function validWidgetAttribute(spec: AttributesSpec, widget: IDragItem | null): boolean {
if (target) { if (widget == null)
const name = entry.name return false;
console.warn("updateAttribute", name, value) if (spec.canShow)
return spec.canShow(widget);
target.attrs[name] = value return spec.name in widget.attrs
target.attrsChanged.set(!get(target.attrsChanged)) }
if (node && "propsChanged" in node) { function updateAttribute(entry: AttributesSpec, target: IDragItem | null, value: any) {
const comfyNode = node as ComfyWidgetNode if (target == null)
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1) return;
}
const name = entry.name
console.warn("updateAttribute", name, value)
target.attrs[name] = value
target.attrsChanged.set(!get(target.attrsChanged))
if (node && "propsChanged" in node) {
const comfyNode = node as ComfyWidgetNode
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
} }
} }
function updateProperty(entry: AttributesSpec, value: any) { function updateProperty(entry: AttributesSpec, value: any) {
if (node) { if (node == null)
const name = entry.name return
console.warn("updateProperty", name, value)
node.properties[name] = value; const name = entry.name
console.warn("updateProperty", name, value)
if ("propsChanged" in node) { node.properties[name] = value;
const comfyNode = node as ComfyWidgetNode
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1) if ("propsChanged" in node) {
} const comfyNode = node as ComfyWidgetNode
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
} }
} }
function getVar(node: LGraphNode, entry: AttributesSpec) { function getVar(node: LGraphNode, spec: AttributesSpec) {
let value = node[entry.name] let value = node[spec.name] || spec.defaultValue
if (entry.serialize) if (spec.serialize)
value = entry.serialize(value) value = spec.serialize(value)
console.debug("[ComfyProperties] getVar", entry, value, node) console.debug("[ComfyProperties] getVar", spec, value, node)
return value return value
} }
function updateVar(entry: any, value: any) { function updateVar(entry: any, value: any) {
if (node) { if (node == null)
const name = entry.name return;
console.warn("updateProperty", name, value)
if (entry.deserialize) const name = entry.name
value = entry.deserialize(value) console.warn("updateVar", name, value)
console.debug("[ComfyProperties] updateVar", entry, value, name, node) if (entry.deserialize)
node[name] = value; value = entry.deserialize(value)
if ("propsChanged" in node) { console.debug("[ComfyProperties] updateVar", entry, value, name, node)
const comfyNode = node as ComfyWidgetNode node[name] = value;
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
} if ("propsChanged" in node) {
const comfyNode = node as ComfyWidgetNode
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
} }
} }
function updateWorkflowAttribute(entry: AttributesSpec, value: any) { function updateWorkflowAttribute(entry: AttributesSpec, value: any) {
const name = entry.name const name = entry.name
console.warn("updateWorkflowAttribute", name, value) console.warn("updateWorkflowAttribute", name, value)
$layoutState.attrs[name] = value $layoutState.attrs[name] = value
$layoutState = $layoutState $layoutState = $layoutState
} }
</script> </script>
@@ -117,9 +136,10 @@
<div class="target-name"> <div class="target-name">
<span> <span>
<span class="title">{target?.attrs?.title || node?.title || "Workflow"}<span> <span class="title">{target?.attrs?.title || node?.title || "Workflow"}<span>
{#if targetType !== ""} {#if targetType !== ""}
<span class="type">({targetType})</span> <span class="type">({targetType})</span>
{/if} {/if}
</span>
</span> </span>
</div> </div>
</div> </div>
@@ -130,37 +150,37 @@
<span class="title">{category.categoryName}</span> <span class="title">{category.categoryName}</span>
</span> </span>
</div> </div>
{#each category.specs as spec(spec.name)} {#each category.specs as spec(spec.id)}
{#if spec.location === "widget" && target && spec.name in target.attrs} {#if spec.location === "widget" && validWidgetAttribute(spec, target)}
<div class="props-entry"> <div class="props-entry">
{#if spec.type === "string"} {#if spec.type === "string"}
<TextBox <TextBox
value={target.attrs[spec.name]} value={target.attrs[spec.name] || spec.defaultValue}
on:change={(e) => updateAttribute(spec, target, e.detail)} on:change={(e) => updateAttribute(spec, target, e.detail)}
on:input={(e) => updateAttribute(spec, target, e.detail)} on:input={(e) => updateAttribute(spec, target, e.detail)}
label={spec.name} label={spec.name}
max_lines={1} max_lines={1}
/> />
{:else if spec.type === "boolean"} {:else if spec.type === "boolean"}
<Checkbox <Checkbox
value={target.attrs[spec.name]} value={target.attrs[spec.name] || spec.defaultValue}
on:change={(e) => updateAttribute(spec, target, e.detail)} on:change={(e) => updateAttribute(spec, target, e.detail)}
label={spec.name} label={spec.name}
/> />
{:else if spec.type === "number"} {:else if spec.type === "number"}
<ComfyNumberProperty <ComfyNumberProperty
name={spec.name} name={spec.name}
value={target.attrs[spec.name]} value={target.attrs[spec.name] || spec.defaultValue}
step={1} step={1}
on:change={(e) => updateAttribute(spec, target, e.detail)} on:change={(e) => updateAttribute(spec, target, e.detail)}
/> />
{:else if spec.type === "enum"} {:else if spec.type === "enum"}
<ComfyComboProperty <ComfyComboProperty
name={spec.name} name={spec.name}
value={target.attrs[spec.name]} value={target.attrs[spec.name] || spec.defaultValue}
values={spec.values} values={spec.values}
on:change={(e) => updateAttribute(spec, target, e.detail)} on:change={(e) => updateAttribute(spec, target, e.detail)}
/> />
{/if} {/if}
</div> </div>
{:else if node} {:else if node}
@@ -168,32 +188,32 @@
<div class="props-entry"> <div class="props-entry">
{#if spec.type === "string"} {#if spec.type === "string"}
<TextBox <TextBox
value={node.properties[spec.name]} value={node.properties[spec.name] || spec.defaultValue}
on:change={(e) => updateProperty(spec, e.detail)} on:change={(e) => updateProperty(spec, e.detail)}
on:input={(e) => updateProperty(spec, e.detail)} on:input={(e) => updateProperty(spec, e.detail)}
label={spec.name} label={spec.name}
max_lines={1} max_lines={1}
/> />
{:else if spec.type === "boolean"} {:else if spec.type === "boolean"}
<Checkbox <Checkbox
value={node.properties[spec.name]} value={node.properties[spec.name] || spec.defaultValue}
label={spec.name} label={spec.name}
on:change={(e) => updateProperty(spec, e.detail)} on:change={(e) => updateProperty(spec, e.detail)}
/> />
{:else if spec.type === "number"} {:else if spec.type === "number"}
<ComfyNumberProperty <ComfyNumberProperty
name={spec.name} name={spec.name}
value={node.properties[spec.name]} value={node.properties[spec.name] || spec.defaultValue}
step={1} step={1}
on:change={(e) => updateProperty(spec, e.detail)} on:change={(e) => updateProperty(spec, e.detail)}
/> />
{:else if spec.type === "enum"} {:else if spec.type === "enum"}
<ComfyComboProperty <ComfyComboProperty
name={spec.name} name={spec.name}
value={node.properties[spec.name]} value={node.properties[spec.name] || spec.defaultValue}
values={spec.values} values={spec.values}
on:change={(e) => updateProperty(spec, e.detail)} on:change={(e) => updateProperty(spec, e.detail)}
/> />
{/if} {/if}
</div> </div>
{:else if spec.location === "nodeVars" && spec.name in node} {:else if spec.location === "nodeVars" && spec.name in node}
@@ -207,25 +227,25 @@
max_lines={1} max_lines={1}
/> />
{:else if spec.type === "boolean"} {:else if spec.type === "boolean"}
<Checkbox <Checkbox
value={getVar(node, spec)} value={getVar(node, spec)}
on:change={(e) => updateVar(spec, e.detail)} on:change={(e) => updateVar(spec, e.detail)}
label={spec.name} label={spec.name}
/> />
{:else if spec.type === "number"} {:else if spec.type === "number"}
<ComfyNumberProperty <ComfyNumberProperty
name={spec.name} name={spec.name}
value={getVar(node, spec)} value={getVar(node, spec)}
step={1} step={1}
on:change={(e) => updateVar(spec, e.detail)} on:change={(e) => updateVar(spec, e.detail)}
/> />
{:else if spec.type === "enum"} {:else if spec.type === "enum"}
<ComfyComboProperty <ComfyComboProperty
name={spec.name} name={spec.name}
value={getVar(node, spec)} value={getVar(node, spec)}
values={spec.values} values={spec.values}
on:change={(e) => updateVar(spec, e.detail)} on:change={(e) => updateVar(spec, e.detail)}
/> />
{/if} {/if}
</div> </div>
{/if} {/if}
@@ -233,32 +253,32 @@
<div class="props-entry"> <div class="props-entry">
{#if spec.type === "string"} {#if spec.type === "string"}
<TextBox <TextBox
value={$layoutState.attrs[spec.name]} value={$layoutState.attrs[spec.name] || spec.defaultValue}
on:change={(e) => updateWorkflowAttribute(spec, e.detail)} on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
on:input={(e) => updateWorkflowAttribute(spec, e.detail)} on:input={(e) => updateWorkflowAttribute(spec, e.detail)}
label={spec.name} label={spec.name}
max_lines={1} max_lines={1}
/> />
{:else if spec.type === "boolean"} {:else if spec.type === "boolean"}
<Checkbox <Checkbox
value={$layoutState.attrs[spec.name]} value={$layoutState.attrs[spec.name] || spec.defaultValue}
on:change={(e) => updateWorkflowAttribute(spec, e.detail)} on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
label={spec.name} label={spec.name}
/> />
{:else if spec.type === "number"} {:else if spec.type === "number"}
<ComfyNumberProperty <ComfyNumberProperty
name={spec.name} name={spec.name}
value={$layoutState.attrs[spec.name]} value={$layoutState.attrs[spec.name] || spec.defaultValue}
step={1} step={1}
on:change={(e) => updateWorkflowAttribute(spec, e.detail)} on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
/> />
{:else if spec.type === "enum"} {:else if spec.type === "enum"}
<ComfyComboProperty <ComfyComboProperty
name={spec.name} name={spec.name}
value={$layoutState.attrs[spec.name]} value={$layoutState.attrs[spec.name] || spec.defaultValue}
values={spec.values} values={spec.values}
on:change={(e) => updateWorkflowAttribute(spec, e.detail)} on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
/> />
{/if} {/if}
</div> </div>
{/if} {/if}

View File

@@ -69,7 +69,7 @@
} }
async function onRightClick(e) { async function onRightClick(e) {
if ($uiState.uiEditMode === "disabled") if (!$uiState.uiUnlocked)
return; return;
e.preventDefault(); e.preventDefault();

View File

@@ -0,0 +1,42 @@
<script lang="ts">
import { Button } from "@gradio/button"
import { LockOpen2, LockClosed } from "radix-icons-svelte"
export let toggled: boolean = false;
function toggle() {
toggled = !toggled;
}
</script>
<div class="wrapper button lg" class:toggled>
<Button on:click={toggle} variant={toggled ? "primary" : "secondary"}>
{#if toggled}
<LockOpen2 />
{:else}
<LockClosed />
{/if}
</Button>
</div>
<style lang="scss">
.wrapper {
display: inline-flex;
width: var(--size-12);
height: var(--size-12);
> :global(.lg) {
border: var(--button-border-width) solid var(--neutral-400);
}
&.toggled {
:global(svg) {
color: var(--button-primary-text-color);
}
}
:global(svg) {
color: var(--button-secondary-text-color);
}
}
</style>

View File

@@ -40,7 +40,8 @@
propsChanged = null; propsChanged = null;
} }
$: showHandles = $uiState.uiEditMode === "widgets" // TODO $: showHandles = $uiState.uiUnlocked
&& $uiState.uiEditMode === "widgets" // TODO
&& zIndex > 1 && zIndex > 1
&& !$layoutState.isMenuOpen && !$layoutState.isMenuOpen
@@ -61,12 +62,12 @@
<BlockContainer {container} {classes} {zIndex} {showHandles} /> <BlockContainer {container} {classes} {zIndex} {showHandles} />
{/key} {/key}
{:else if widget && widget.node} {:else if widget && widget.node}
{@const edit = $uiState.uiEditMode === "widgets" && zIndex > 1} {@const edit = $uiState.uiUnlocked && $uiState.uiEditMode === "widgets" && zIndex > 1}
{#key $attrsChanged} {#key $attrsChanged}
{#key $propsChanged} {#key $propsChanged}
<div class="widget {widget.attrs.classes} {getWidgetClass()}" <div class="widget {widget.attrs.classes} {getWidgetClass()}"
class:edit={edit} class:edit={edit}
class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(widget.id)} class:selected={$uiState.uiUnlocked && $layoutState.currentSelection.includes(widget.id)}
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.id} class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.id}
class:hidden={widget.attrs.hidden} class:hidden={widget.attrs.hidden}
> >

View File

@@ -39,15 +39,18 @@ export type Attributes = {
} }
export type AttributesSpec = { export type AttributesSpec = {
id?: number, // for svelte keyed each
name: string, name: string,
type: string, type: string,
location: "widget" | "nodeProps" | "nodeVars" | "workflow" location: "widget" | "nodeProps" | "nodeVars" | "workflow"
editable: boolean, editable: boolean,
defaultValue?: any,
values?: string[], values?: string[],
hidden?: boolean, hidden?: boolean,
validNodeTypes?: string[], validNodeTypes?: string[],
canShow?: (arg: IDragItem | LGraphNode) => boolean,
serialize?: (arg: any) => string, serialize?: (arg: any) => string,
deserialize?: (arg: string) => any, deserialize?: (arg: string) => any,
} }
@@ -86,18 +89,22 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
type: "enum", type: "enum",
location: "widget", location: "widget",
editable: true, editable: true,
values: ["horizontal", "vertical"] values: ["horizontal", "vertical"],
defaultValue: "vertical",
canShow: (di: IDragItem) => di.type === "container"
}, },
{ {
name: "flexGrow", name: "flexGrow",
type: "number", type: "number",
location: "widget", location: "widget",
defaultValue: 100,
editable: true editable: true
}, },
{ {
name: "classes", name: "classes",
type: "string", type: "string",
location: "widget", location: "widget",
defaultValue: "",
editable: true, editable: true,
}, },
{ {
@@ -105,7 +112,20 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
type: "enum", type: "enum",
location: "widget", location: "widget",
editable: true, editable: true,
values: ["block", "hidden"] values: ["block", "hidden"],
defaultValue: "block",
canShow: (di: IDragItem) => di.type === "container"
},
// Container variants
{
name: "variant",
type: "enum",
location: "widget",
editable: true,
values: ["block", "accordion", "tabs"],
defaultValue: "block",
canShow: (di: IDragItem) => di.type === "container"
}, },
] ]
}, },
@@ -156,6 +176,7 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
location: "nodeProps", location: "nodeProps",
editable: true, editable: true,
validNodeTypes: ["ui/button"], validNodeTypes: ["ui/button"],
defaultValue: "bang"
}, },
// Workflow // Workflow
@@ -163,11 +184,21 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
name: "defaultSubgraph", name: "defaultSubgraph",
type: "string", type: "string",
location: "workflow", location: "workflow",
editable: true editable: true,
defaultValue: ""
} }
] ]
} }
]; ];
let i = 0;
for (const cat of Object.values(ALL_ATTRIBUTES)) {
for (const val of Object.values(cat.specs)) {
val.id = i;
i += 1;
}
}
export { ALL_ATTRIBUTES }; export { ALL_ATTRIBUTES };
export interface IDragItem { export interface IDragItem {

View File

@@ -2,13 +2,14 @@ import { writable } from 'svelte/store';
import type { Readable, Writable } from 'svelte/store'; import type { Readable, Writable } from 'svelte/store';
import type ComfyApp from "$lib/components/ComfyApp" import type ComfyApp from "$lib/components/ComfyApp"
export type UIEditMode = "disabled" | "widgets" | "containers" | "layout"; export type UIEditMode = "widgets" | "containers" | "layout";
export type UIState = { export type UIState = {
app: ComfyApp, app: ComfyApp,
nodesLocked: boolean, nodesLocked: boolean,
graphLocked: boolean, graphLocked: boolean,
autoAddUI: boolean, autoAddUI: boolean,
uiUnlocked: boolean,
uiEditMode: UIEditMode, uiEditMode: UIEditMode,
} }
@@ -19,7 +20,8 @@ const store: WritableUIStateStore = writable(
graphLocked: false, graphLocked: false,
nodesLocked: false, nodesLocked: false,
autoAddUI: true, autoAddUI: true,
uiEditMode: "disabled", uiUnlocked: false,
uiEditMode: "widgets"
}) })
const uiStateStore: WritableUIStateStore = const uiStateStore: WritableUIStateStore =