Node hidden property

This commit is contained in:
space-nuko
2023-05-05 02:11:07 -05:00
parent 7f64b743a7
commit 8fa267982e
7 changed files with 230 additions and 65 deletions

View File

@@ -43,12 +43,13 @@
{#if container && children} {#if container && children}
{#key $attrsChanged} {#key $attrsChanged}
<div class="container {container.attrs.direction} {container.attrs.classes} {classes.join(' ')}" <div class="container {container.attrs.direction} {container.attrs.classes} {classes.join(' ')}"
class:hide-block={container.attrs.blockVariant === "hidden"}
class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(container.id)} class:selected={$uiState.uiEditMode !== "disabled" && $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:container-edit-outline={$uiState.uiEditMode === "widgets" && zIndex > 1}> class:edit={$uiState.uiEditMode === "widgets" && zIndex > 1}>
<Block> <Block>
{#if container.attrs.showTitle} {#if container.attrs.title !== ""}
<label for={String(container.id)} class={$uiState.uiEditMode === "widgets" ? "edit-title-label" : ""}> <label for={String(container.id)} class={$uiState.uiEditMode === "widgets" ? "edit-title-label" : ""}>
<BlockTitle>{container.attrs.title}</BlockTitle> <BlockTitle>{container.attrs.title}</BlockTitle>
</label> </label>
@@ -68,7 +69,9 @@
on:finalize="{handleFinalize}" on:finalize="{handleFinalize}"
> >
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)} {#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
{@const hidden = item?.node?.properties?.hidden}
<div class="animation-wrapper" <div class="animation-wrapper"
class:hidden={hidden}
animate:flip={{duration:flipDurationMs}}> animate:flip={{duration:flipDurationMs}}>
<WidgetContainer dragItem={item} zIndex={zIndex+1} /> <WidgetContainer dragItem={item} zIndex={zIndex+1} />
{#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]} {#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
@@ -96,6 +99,10 @@
min-width: 200px; min-width: 200px;
} }
&:not(.edit) > .animation-wrapper.hidden {
display: none;
}
&.empty { &.empty {
border-width: 3px; border-width: 3px;
border-color: var(--color-grey-400); border-color: var(--color-grey-400);
@@ -137,6 +144,25 @@
height: fit-content; height: fit-content;
} }
.edit > :global(.block) {
border-color: var(--color-pink-500);
border-width: 2px;
border-style: dashed !important;
margin: 0.2em;
padding: 1.4em;
}
:global(.hide-block > .block) {
padding: 0.5em 0.25em;
box-shadow: unset;
border-width: 0;
border-color: unset;
border-radius: unset;
background: var(--block-background-fill);
width: 100%;
line-height: var(--line-sm);
}
&.horizontal { &.horizontal {
flex-wrap: wrap; flex-wrap: wrap;
gap: var(--layout-gap); gap: var(--layout-gap);
@@ -176,14 +202,6 @@
padding: 0.2em; padding: 0.2em;
} }
.animation-wrapper {
position: relative;
&:not(.edit) {
flex-grow: 1;
}
}
.handle { .handle {
cursor: grab; cursor: grab;
z-index: 99999; z-index: 99999;
@@ -194,6 +212,11 @@
height: 100%; height: 100%;
} }
.animation-wrapper {
position: relative;
flex-grow: 1;
}
.handle-widget:hover { .handle-widget:hover {
background-color: #add8e680; background-color: #add8e680;
} }
@@ -248,14 +271,6 @@
color: var(--input-placeholder-color); color: var(--input-placeholder-color);
} }
.container-edit-outline > :global(.block) {
border-color: var(--color-pink-500);
border-width: 2px;
border-style: dashed !important;
margin: 0.2em;
padding: 1.4em;
}
.widget-edit-outline { .widget-edit-outline {
border: 2px dashed var(--color-blue-400); border: 2px dashed var(--color-blue-400);
margin: 0.2em; margin: 0.2em;

View File

@@ -1,17 +1,20 @@
<script lang="ts"> <script lang="ts">
import { Block, BlockTitle } from "@gradio/atoms"; import { Block, BlockTitle } from "@gradio/atoms";
import { TextBox, Checkbox } from "@gradio/form"; import { TextBox, Checkbox } from "@gradio/form";
import layoutState, { ALL_ATTRIBUTES } from "$lib/stores/layoutState" import { LGraphNode } from "@litegraph-ts/core"
import layoutState, { type IDragItem, type WidgetLayout, ALL_ATTRIBUTES } from "$lib/stores/layoutState"
import { get } from "svelte/store" import { get } from "svelte/store"
import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
import type { ComfyWidgetNode } from "$lib/nodes";
let target: IDragItem | null = null; let target: IDragItem | null = null;
let node: LGraphNode | null = null; let node: LGraphNode | null = null;
$: if ($layoutState.currentSelection.length > 0) { $: if ($layoutState.currentSelection.length > 0) {
const targetId = $layoutState.currentSelection.slice(-1) const targetId = $layoutState.currentSelection.slice(-1)[0]
target = $layoutState.allItems[targetId].dragItem target = $layoutState.allItems[targetId].dragItem
if (target.type === "widget") { if (target.type === "widget") {
node = target.node node = (target as WidgetLayout).node
} }
else { else {
node = null; node = null;
@@ -22,19 +25,15 @@
node = null; node = null;
} }
const _entries = [ let targetType: string = "???"
{ name: "title" },
{ name: "showTitle" },
{ name: "direction" },
{ name: "classes" },
]
function getTargetType(): string { $: {
if (node) if (node != null)
return "Node" targetType = node.type || "Widget"
else if (target?.type === "container") else if (target)
"Group" targetType = "group"
return "???" else
targetType = "???"
} }
function updateAttribute(entry: any, value: any) { function updateAttribute(entry: any, value: any) {
@@ -45,8 +44,23 @@
target.attrs[name] = value target.attrs[name] = value
target.attrsChanged.set(!get(target.attrsChanged)) target.attrsChanged.set(!get(target.attrsChanged))
if (node) { if (node && "propsChanged" in node) {
node.propsChanged.set(!get(node.propsChanged)) const comfyNode = node as ComfyWidgetNode
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
}
}
}
function updateProperty(entry: any, value: any) {
if (node) {
const name = entry.name
console.warn("updateProperty", name, value)
node.properties[name] = value;
if ("propsChanged" in node) {
const comfyNode = node as ComfyWidgetNode
comfyNode.propsChanged.set(get(comfyNode.propsChanged) + 1)
} }
} }
} }
@@ -58,7 +72,7 @@
<div class="target-name"> <div class="target-name">
<span> <span>
<span class="title">{target.attrs.title}</span> <span class="title">{target.attrs.title}</span>
<span class="type">({getTargetType()})</span> <span class="type">({targetType})</span>
</span> </span>
</div> </div>
</div> </div>
@@ -66,12 +80,11 @@
{#each ALL_ATTRIBUTES as category(category.categoryName)} {#each ALL_ATTRIBUTES as category(category.categoryName)}
<div class="category-name"> <div class="category-name">
<span> <span>
<span class="title">{target.attrs.title}</span> <span class="title">{category.categoryName}</span>
</span> </span>
</div> </div>
{#each category.specs as spec(spec.name)} {#each category.specs as spec(spec.name)}
{@const has = spec.name in target.attrs} {#if spec.location === "widget" && spec.name in target.attrs}
{#if has}
<div class="props-entry"> <div class="props-entry">
{#if spec.type === "string"} {#if spec.type === "string"}
<TextBox <TextBox
@@ -86,6 +99,16 @@
on:change={(e) => updateAttribute(spec, e.detail)} on:change={(e) => updateAttribute(spec, e.detail)}
label={spec.name} label={spec.name}
/> />
{:else if spec.type === "number"}
<label class="number-wrapper">
<BlockTitle>{spec.name}</BlockTitle>
<input
type="number"
value={target.attrs[spec.name]}
step={1}
on:change={(e) => updateAttribute(spec, e.currentTarget.valueAsNumber)}
/>
</label>
{:else if spec.type === "enum"} {:else if spec.type === "enum"}
<label class="select-wrapper"> <label class="select-wrapper">
<BlockTitle>{spec.name}</BlockTitle> <BlockTitle>{spec.name}</BlockTitle>
@@ -103,6 +126,52 @@
</label> </label>
{/if} {/if}
</div> </div>
{:else if node}
{#if spec.location === "nodeProps" && spec.name in node.properties}
<div class="props-entry">
{#if spec.type === "string"}
<TextBox
value={node.properties[spec.name]}
on:change={(e) => updateProperty(spec, e.detail)}
label={spec.name}
max_lines={1}
/>
{:else if spec.type === "boolean"}
<Checkbox
value={node.properties[spec.name]}
on:change={(e) => updateProperty(spec, e.detail)}
label={spec.name}
/>
{:else if spec.type === "number"}
<label class="number-wrapper">
<BlockTitle>{spec.name}</BlockTitle>
<div class="number">
<input
type="number"
value={node.properties[spec.name]}
step={1}
on:change={(e) => updateProperty(spec, e.currentTarget.valueAsNumber)}
/>
</div>
</label>
{:else if spec.type === "enum"}
<label class="select-wrapper">
<BlockTitle>{spec.name}</BlockTitle>
<div class="select">
<select
value={node.properties[spec.name]}
on:change={(e) => updateProperty(spec, e.currentTarget.options[e.currentTarget.selectedIndex].value)}>
{#each spec.values as value}
<option value={value}>
{value}
</option>
{/each}
</select>
</div>
</label>
{/if}
</div>
{/if}
{/if} {/if}
{/each} {/each}
{/each} {/each}
@@ -112,7 +181,9 @@
<style lang="scss"> <style lang="scss">
.props-entry { .props-entry {
padding: 0.5rem 0.5rem 0 0.5rem; padding-bottom: 0.5rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
} }
@@ -120,6 +191,7 @@
.target-name { .target-name {
border-color: var(--neutral-400); border-color: var(--neutral-400);
background: var(--neutral-300); background: var(--neutral-300);
padding: 0.8rem 1.0rem;
.title { .title {
font-weight: bold; font-weight: bold;
@@ -127,19 +199,31 @@
} }
.category-name { .category-name {
padding: 0.4rem 1.0rem;
border-color: var(--neutral-300); border-color: var(--neutral-300);
background: var(--neutral-200); background: var(--neutral-200);
} }
.target-name, .category-name { .target-name, .category-name {
border-width: var(--block-border-width); border-width: var(--block-border-width);
padding: 0.4rem 1.0rem;
.type { .type {
color: var(--neutral-500); color: var(--neutral-500);
} }
} }
.number-wrapper {
width: 100%;
.number {
width: 100%;
input {
width: 100%
}
}
}
.select-wrapper { .select-wrapper {
width: 100%; width: 100%;

View File

@@ -6,12 +6,14 @@
import { startDrag, stopDrag } from "$lib/utils" import { startDrag, stopDrag } from "$lib/utils"
import BlockContainer from "./BlockContainer.svelte" import BlockContainer from "./BlockContainer.svelte"
import { type Writable } from "svelte/store" import { type Writable } from "svelte/store"
import type { ComfyWidgetNode } from "$lib/nodes";
export let dragItem: IDragItem | null = null; export let dragItem: IDragItem | null = null;
export let zIndex: number = 0; export let zIndex: number = 0;
export let classes: string[] = []; export let classes: string[] = [];
let container: ContainerLayout | null = null; let container: ContainerLayout | null = null;
let attrsChanged: Writable<boolean> | null = null; let attrsChanged: Writable<boolean> | null = null;
let propsChanged: Writable<number> | null = null;
let widget: WidgetLayout | null = null; let widget: WidgetLayout | null = null;
let showHandles: boolean = false; let showHandles: boolean = false;
@@ -20,16 +22,22 @@
container = null; container = null;
widget = null; widget = null;
attrsChanged = null; attrsChanged = null;
propsChanged = null;
} }
else if (dragItem.type === "container") { else if (dragItem.type === "container") {
container = dragItem as ContainerLayout; container = dragItem as ContainerLayout;
attrsChanged = container.attrsChanged; attrsChanged = container.attrsChanged;
widget = null; widget = null;
propsChanged = null;
} }
else if (dragItem.type === "widget") { else if (dragItem.type === "widget") {
widget = dragItem as WidgetLayout; widget = dragItem as WidgetLayout;
attrsChanged = widget.attrsChanged; attrsChanged = widget.attrsChanged;
container = null; container = null;
if (widget.node && "propsChanged" in widget.node)
propsChanged = (widget.node as ComfyWidgetNode).propsChanged
else
propsChanged = null;
} }
$: showHandles = $uiState.uiEditMode === "widgets" // TODO $: showHandles = $uiState.uiEditMode === "widgets" // TODO
@@ -53,17 +61,24 @@
<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}
{#key $attrsChanged} {#key $attrsChanged}
<div class="widget {widget.attrs.classes} {getWidgetClass()}" {#key $propsChanged}
class:widget-edit-outline={$uiState.uiEditMode === "widgets" && zIndex > 1} <div class="widget {widget.attrs.classes} {getWidgetClass()}"
class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(widget.id)} class:edit={edit}
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.id} class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(widget.id)}
> class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.id}
<svelte:component this={widget.node.svelteComponentType} {widget} /> class:hidden={widget.node.properties.hidden}
</div> >
{#if showHandles} <svelte:component this={widget.node.svelteComponentType} {widget} />
<div class="handle handle-widget" style="z-index: {zIndex+100}" data-drag-item-id={widget.id} on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}/> </div>
{/if} {#if widget.node.properties.hidden && edit}
<div class="handle handle-hidden" class:hidden={!edit} style="z-index: {zIndex+100}"/>
{/if}
{#if showHandles}
<div class="handle handle-widget" style="z-index: {zIndex+100}" data-drag-item-id={widget.id} on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}/>
{/if}
{/key}
{/key} {/key}
{/if} {/if}
@@ -81,6 +96,10 @@
padding: 0.2em; padding: 0.2em;
} }
.hidden:not(.edit) {
display: none;
}
.handle { .handle {
cursor: grab; cursor: grab;
z-index: 99999; z-index: 99999;
@@ -91,6 +110,10 @@
height: 100%; height: 100%;
} }
.handle-hidden {
background-color: #40404080;
}
.handle-widget:hover { .handle-widget:hover {
background-color: #add8e680; background-color: #add8e680;
} }
@@ -100,7 +123,7 @@
color: var(--neutral-400); color: var(--neutral-400);
} }
.widget-edit-outline { .edit {
border: 2px dashed var(--color-blue-400); border: 2px dashed var(--color-blue-400);
margin: 0.2em; margin: 0.2em;
padding: 0.2em; padding: 0.2em;

View File

@@ -15,6 +15,7 @@ import type { FileData as GradioFileData } from "@gradio/upload";
import queueState from "$lib/stores/queueState"; import queueState from "$lib/stores/queueState";
export interface ComfyWidgetProperties extends Record<string, any> { export interface ComfyWidgetProperties extends Record<string, any> {
hidden?: boolean,
defaultValue: any defaultValue: any
} }
@@ -59,6 +60,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
constructor(name: string, value: T) { constructor(name: string, value: T) {
const color = LGraphCanvas.node_colors["blue"] const color = LGraphCanvas.node_colors["blue"]
super(name) super(name)
this.setProperty("hidden", false)
this.value = writable(value) this.value = writable(value)
this.color ||= color.color this.color ||= color.color
this.bgColor ||= color.bgColor this.bgColor ||= color.bgColor
@@ -213,6 +215,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
override onConfigure(o: SerializedLGraphNode) { override onConfigure(o: SerializedLGraphNode) {
this.value.set((o as any).comfyValue); this.value.set((o as any).comfyValue);
this.shownOutputProperties = (o as any).shownOutputProperties; this.shownOutputProperties = (o as any).shownOutputProperties;
this.setProperty("hidden", false)
} }
} }

View File

@@ -24,8 +24,10 @@ export type LayoutState = {
export type AttributesSpec = { export type AttributesSpec = {
name: string, name: string,
type: string, type: string,
location: "widget" | "nodeProps"
editable: boolean, editable: boolean,
values?: string[]
values?: string[],
} }
export type AttributesCategorySpec = { export type AttributesCategorySpec = {
@@ -42,22 +44,56 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
{ {
name: "title", name: "title",
type: "string", type: "string",
editable: true, location: "widget",
},
{
name: "showTitle",
type: "boolean",
editable: true, editable: true,
}, },
{ {
name: "direction", name: "direction",
type: "enum", type: "enum",
location: "widget",
editable: true, editable: true,
values: ["horizontal", "vertical"] values: ["horizontal", "vertical"]
}, },
{ {
name: "classes", name: "classes",
type: "string", type: "string",
location: "widget",
editable: true,
},
{
name: "blockVariant",
type: "enum",
location: "widget",
editable: true,
values: ["block", "hidden"]
},
]
},
{
categoryName: "behavior",
specs: [
{
name: "hidden",
type: "boolean",
location: "nodeProps",
editable: true
},
{
name: "min",
type: "number",
location: "nodeProps",
editable: true,
},
{
name: "max",
type: "number",
location: "nodeProps",
editable: true
},
{
name: "step",
type: "number",
location: "nodeProps",
editable: true, editable: true,
}, },
] ]
@@ -69,7 +105,8 @@ export type Attributes = {
direction: "horizontal" | "vertical", direction: "horizontal" | "vertical",
title: string, title: string,
showTitle: boolean, showTitle: boolean,
classes: string classes: string,
blockVariant?: "block" | "hidden"
} }
export interface IDragItem { export interface IDragItem {
@@ -150,6 +187,7 @@ function addContainer(parent: ContainerLayout | null, attrs: Partial<Attributes>
showTitle: true, showTitle: true,
direction: "vertical", direction: "vertical",
classes: "", classes: "",
blockVariant: "block",
...attrs ...attrs
} }
} }
@@ -377,9 +415,9 @@ function initDefaultLayout() {
isConfiguring: false isConfiguring: false
}) })
const root = addContainer(null, { direction: "horizontal", showTitle: false }); const root = addContainer(null, { direction: "horizontal", title: "" });
const left = addContainer(root, { direction: "vertical", showTitle: false }); const left = addContainer(root, { direction: "vertical", title: "" });
const right = addContainer(root, { direction: "vertical", showTitle: false }); const right = addContainer(root, { direction: "vertical", title: "" });
const state = get(store) const state = get(store)
state.root = root; state.root = root;

View File

@@ -36,6 +36,8 @@ export function startDrag(evt: MouseEvent) {
const item = ls.allItems[dragItemId].dragItem const item = ls.allItems[dragItemId].dragItem
console.debug("startDrag", item)
if (evt.ctrlKey) { if (evt.ctrlKey) {
const index = ls.currentSelection.indexOf(item.id) const index = ls.currentSelection.indexOf(item.id)
if (index === -1) if (index === -1)

View File

@@ -67,12 +67,12 @@
{#key $propsChanged} {#key $propsChanged}
{#if node !== null && nodeValue !== null} {#if node !== null && nodeValue !== null}
<label> <label>
{#if widget.attrs.showTitle} {#if widget.attrs.title !== ""}
<BlockTitle show_label={widget.attrs.showTitle}>{widget.attrs.title}</BlockTitle> <BlockTitle show_label={true}>{widget.attrs.title}</BlockTitle>
{/if} {/if}
<Select <Select
bind:value={option} bind:value={option}
bind:items={node.properties.values} items={node.properties.values}
disabled={node.properties.values.length === 0} disabled={node.properties.values.length === 0}
clearable={false} clearable={false}
on:change on:change