Node hidden property
This commit is contained in:
@@ -43,12 +43,13 @@
|
||||
{#if container && children}
|
||||
{#key $attrsChanged}
|
||||
<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:root-container={zIndex === 0}
|
||||
class:is-executing={container.isNodeExecuting}
|
||||
class:container-edit-outline={$uiState.uiEditMode === "widgets" && zIndex > 1}>
|
||||
class:edit={$uiState.uiEditMode === "widgets" && zIndex > 1}>
|
||||
<Block>
|
||||
{#if container.attrs.showTitle}
|
||||
{#if container.attrs.title !== ""}
|
||||
<label for={String(container.id)} class={$uiState.uiEditMode === "widgets" ? "edit-title-label" : ""}>
|
||||
<BlockTitle>{container.attrs.title}</BlockTitle>
|
||||
</label>
|
||||
@@ -68,7 +69,9 @@
|
||||
on:finalize="{handleFinalize}"
|
||||
>
|
||||
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
|
||||
{@const hidden = item?.node?.properties?.hidden}
|
||||
<div class="animation-wrapper"
|
||||
class:hidden={hidden}
|
||||
animate:flip={{duration:flipDurationMs}}>
|
||||
<WidgetContainer dragItem={item} zIndex={zIndex+1} />
|
||||
{#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
|
||||
@@ -96,6 +99,10 @@
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
&:not(.edit) > .animation-wrapper.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.empty {
|
||||
border-width: 3px;
|
||||
border-color: var(--color-grey-400);
|
||||
@@ -137,6 +144,25 @@
|
||||
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 {
|
||||
flex-wrap: wrap;
|
||||
gap: var(--layout-gap);
|
||||
@@ -176,14 +202,6 @@
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
.animation-wrapper {
|
||||
position: relative;
|
||||
|
||||
&:not(.edit) {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.handle {
|
||||
cursor: grab;
|
||||
z-index: 99999;
|
||||
@@ -194,6 +212,11 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.animation-wrapper {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.handle-widget:hover {
|
||||
background-color: #add8e680;
|
||||
}
|
||||
@@ -248,14 +271,6 @@
|
||||
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 {
|
||||
border: 2px dashed var(--color-blue-400);
|
||||
margin: 0.2em;
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Block, BlockTitle } from "@gradio/atoms";
|
||||
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 type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
|
||||
import type { ComfyWidgetNode } from "$lib/nodes";
|
||||
|
||||
let target: IDragItem | null = null;
|
||||
let node: LGraphNode | null = null;
|
||||
|
||||
$: if ($layoutState.currentSelection.length > 0) {
|
||||
const targetId = $layoutState.currentSelection.slice(-1)
|
||||
const targetId = $layoutState.currentSelection.slice(-1)[0]
|
||||
target = $layoutState.allItems[targetId].dragItem
|
||||
if (target.type === "widget") {
|
||||
node = target.node
|
||||
node = (target as WidgetLayout).node
|
||||
}
|
||||
else {
|
||||
node = null;
|
||||
@@ -22,19 +25,15 @@
|
||||
node = null;
|
||||
}
|
||||
|
||||
const _entries = [
|
||||
{ name: "title" },
|
||||
{ name: "showTitle" },
|
||||
{ name: "direction" },
|
||||
{ name: "classes" },
|
||||
]
|
||||
let targetType: string = "???"
|
||||
|
||||
function getTargetType(): string {
|
||||
if (node)
|
||||
return "Node"
|
||||
else if (target?.type === "container")
|
||||
"Group"
|
||||
return "???"
|
||||
$: {
|
||||
if (node != null)
|
||||
targetType = node.type || "Widget"
|
||||
else if (target)
|
||||
targetType = "group"
|
||||
else
|
||||
targetType = "???"
|
||||
}
|
||||
|
||||
function updateAttribute(entry: any, value: any) {
|
||||
@@ -45,8 +44,23 @@
|
||||
target.attrs[name] = value
|
||||
target.attrsChanged.set(!get(target.attrsChanged))
|
||||
|
||||
if (node) {
|
||||
node.propsChanged.set(!get(node.propsChanged))
|
||||
if (node && "propsChanged" in node) {
|
||||
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">
|
||||
<span>
|
||||
<span class="title">{target.attrs.title}</span>
|
||||
<span class="type">({getTargetType()})</span>
|
||||
<span class="type">({targetType})</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,12 +80,11 @@
|
||||
{#each ALL_ATTRIBUTES as category(category.categoryName)}
|
||||
<div class="category-name">
|
||||
<span>
|
||||
<span class="title">{target.attrs.title}</span>
|
||||
<span class="title">{category.categoryName}</span>
|
||||
</span>
|
||||
</div>
|
||||
{#each category.specs as spec(spec.name)}
|
||||
{@const has = spec.name in target.attrs}
|
||||
{#if has}
|
||||
{#if spec.location === "widget" && spec.name in target.attrs}
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
@@ -86,6 +99,16 @@
|
||||
on:change={(e) => updateAttribute(spec, e.detail)}
|
||||
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"}
|
||||
<label class="select-wrapper">
|
||||
<BlockTitle>{spec.name}</BlockTitle>
|
||||
@@ -103,6 +126,52 @@
|
||||
</label>
|
||||
{/if}
|
||||
</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}
|
||||
{/each}
|
||||
{/each}
|
||||
@@ -112,7 +181,9 @@
|
||||
|
||||
<style lang="scss">
|
||||
.props-entry {
|
||||
padding: 0.5rem 0.5rem 0 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
@@ -120,6 +191,7 @@
|
||||
.target-name {
|
||||
border-color: var(--neutral-400);
|
||||
background: var(--neutral-300);
|
||||
padding: 0.8rem 1.0rem;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
@@ -127,19 +199,31 @@
|
||||
}
|
||||
|
||||
.category-name {
|
||||
padding: 0.4rem 1.0rem;
|
||||
border-color: var(--neutral-300);
|
||||
background: var(--neutral-200);
|
||||
}
|
||||
|
||||
.target-name, .category-name {
|
||||
border-width: var(--block-border-width);
|
||||
padding: 0.4rem 1.0rem;
|
||||
|
||||
.type {
|
||||
color: var(--neutral-500);
|
||||
}
|
||||
}
|
||||
|
||||
.number-wrapper {
|
||||
width: 100%;
|
||||
|
||||
.number {
|
||||
width: 100%;
|
||||
|
||||
input {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
width: 100%;
|
||||
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
import { startDrag, stopDrag } from "$lib/utils"
|
||||
import BlockContainer from "./BlockContainer.svelte"
|
||||
import { type Writable } from "svelte/store"
|
||||
import type { ComfyWidgetNode } from "$lib/nodes";
|
||||
|
||||
export let dragItem: IDragItem | null = null;
|
||||
export let zIndex: number = 0;
|
||||
export let classes: string[] = [];
|
||||
let container: ContainerLayout | null = null;
|
||||
let attrsChanged: Writable<boolean> | null = null;
|
||||
let propsChanged: Writable<number> | null = null;
|
||||
let widget: WidgetLayout | null = null;
|
||||
let showHandles: boolean = false;
|
||||
|
||||
@@ -20,16 +22,22 @@
|
||||
container = null;
|
||||
widget = null;
|
||||
attrsChanged = null;
|
||||
propsChanged = null;
|
||||
}
|
||||
else if (dragItem.type === "container") {
|
||||
container = dragItem as ContainerLayout;
|
||||
attrsChanged = container.attrsChanged;
|
||||
widget = null;
|
||||
propsChanged = null;
|
||||
}
|
||||
else if (dragItem.type === "widget") {
|
||||
widget = dragItem as WidgetLayout;
|
||||
attrsChanged = widget.attrsChanged;
|
||||
container = null;
|
||||
if (widget.node && "propsChanged" in widget.node)
|
||||
propsChanged = (widget.node as ComfyWidgetNode).propsChanged
|
||||
else
|
||||
propsChanged = null;
|
||||
}
|
||||
|
||||
$: showHandles = $uiState.uiEditMode === "widgets" // TODO
|
||||
@@ -53,17 +61,24 @@
|
||||
<BlockContainer {container} {classes} {zIndex} {showHandles} />
|
||||
{/key}
|
||||
{:else if widget && widget.node}
|
||||
{@const edit = $uiState.uiEditMode === "widgets" && zIndex > 1}
|
||||
{#key $attrsChanged}
|
||||
<div class="widget {widget.attrs.classes} {getWidgetClass()}"
|
||||
class:widget-edit-outline={$uiState.uiEditMode === "widgets" && zIndex > 1}
|
||||
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} />
|
||||
</div>
|
||||
{#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 $propsChanged}
|
||||
<div class="widget {widget.attrs.classes} {getWidgetClass()}"
|
||||
class:edit={edit}
|
||||
class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(widget.id)}
|
||||
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.node.id}
|
||||
class:hidden={widget.node.properties.hidden}
|
||||
>
|
||||
<svelte:component this={widget.node.svelteComponentType} {widget} />
|
||||
</div>
|
||||
{#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}
|
||||
{/if}
|
||||
|
||||
@@ -81,6 +96,10 @@
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
.hidden:not(.edit) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.handle {
|
||||
cursor: grab;
|
||||
z-index: 99999;
|
||||
@@ -91,6 +110,10 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.handle-hidden {
|
||||
background-color: #40404080;
|
||||
}
|
||||
|
||||
.handle-widget:hover {
|
||||
background-color: #add8e680;
|
||||
}
|
||||
@@ -100,7 +123,7 @@
|
||||
color: var(--neutral-400);
|
||||
}
|
||||
|
||||
.widget-edit-outline {
|
||||
.edit {
|
||||
border: 2px dashed var(--color-blue-400);
|
||||
margin: 0.2em;
|
||||
padding: 0.2em;
|
||||
|
||||
@@ -15,6 +15,7 @@ import type { FileData as GradioFileData } from "@gradio/upload";
|
||||
import queueState from "$lib/stores/queueState";
|
||||
|
||||
export interface ComfyWidgetProperties extends Record<string, any> {
|
||||
hidden?: boolean,
|
||||
defaultValue: any
|
||||
}
|
||||
|
||||
@@ -59,6 +60,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
|
||||
constructor(name: string, value: T) {
|
||||
const color = LGraphCanvas.node_colors["blue"]
|
||||
super(name)
|
||||
this.setProperty("hidden", false)
|
||||
this.value = writable(value)
|
||||
this.color ||= color.color
|
||||
this.bgColor ||= color.bgColor
|
||||
@@ -213,6 +215,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
|
||||
override onConfigure(o: SerializedLGraphNode) {
|
||||
this.value.set((o as any).comfyValue);
|
||||
this.shownOutputProperties = (o as any).shownOutputProperties;
|
||||
this.setProperty("hidden", false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,10 @@ export type LayoutState = {
|
||||
export type AttributesSpec = {
|
||||
name: string,
|
||||
type: string,
|
||||
location: "widget" | "nodeProps"
|
||||
editable: boolean,
|
||||
values?: string[]
|
||||
|
||||
values?: string[],
|
||||
}
|
||||
|
||||
export type AttributesCategorySpec = {
|
||||
@@ -42,22 +44,56 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
||||
{
|
||||
name: "title",
|
||||
type: "string",
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
name: "showTitle",
|
||||
type: "boolean",
|
||||
location: "widget",
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
name: "direction",
|
||||
type: "enum",
|
||||
location: "widget",
|
||||
editable: true,
|
||||
values: ["horizontal", "vertical"]
|
||||
},
|
||||
{
|
||||
name: "classes",
|
||||
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,
|
||||
},
|
||||
]
|
||||
@@ -69,7 +105,8 @@ export type Attributes = {
|
||||
direction: "horizontal" | "vertical",
|
||||
title: string,
|
||||
showTitle: boolean,
|
||||
classes: string
|
||||
classes: string,
|
||||
blockVariant?: "block" | "hidden"
|
||||
}
|
||||
|
||||
export interface IDragItem {
|
||||
@@ -150,6 +187,7 @@ function addContainer(parent: ContainerLayout | null, attrs: Partial<Attributes>
|
||||
showTitle: true,
|
||||
direction: "vertical",
|
||||
classes: "",
|
||||
blockVariant: "block",
|
||||
...attrs
|
||||
}
|
||||
}
|
||||
@@ -377,9 +415,9 @@ function initDefaultLayout() {
|
||||
isConfiguring: false
|
||||
})
|
||||
|
||||
const root = addContainer(null, { direction: "horizontal", showTitle: false });
|
||||
const left = addContainer(root, { direction: "vertical", showTitle: false });
|
||||
const right = addContainer(root, { direction: "vertical", showTitle: false });
|
||||
const root = addContainer(null, { direction: "horizontal", title: "" });
|
||||
const left = addContainer(root, { direction: "vertical", title: "" });
|
||||
const right = addContainer(root, { direction: "vertical", title: "" });
|
||||
|
||||
const state = get(store)
|
||||
state.root = root;
|
||||
|
||||
@@ -36,6 +36,8 @@ export function startDrag(evt: MouseEvent) {
|
||||
|
||||
const item = ls.allItems[dragItemId].dragItem
|
||||
|
||||
console.debug("startDrag", item)
|
||||
|
||||
if (evt.ctrlKey) {
|
||||
const index = ls.currentSelection.indexOf(item.id)
|
||||
if (index === -1)
|
||||
|
||||
@@ -67,12 +67,12 @@
|
||||
{#key $propsChanged}
|
||||
{#if node !== null && nodeValue !== null}
|
||||
<label>
|
||||
{#if widget.attrs.showTitle}
|
||||
<BlockTitle show_label={widget.attrs.showTitle}>{widget.attrs.title}</BlockTitle>
|
||||
{#if widget.attrs.title !== ""}
|
||||
<BlockTitle show_label={true}>{widget.attrs.title}</BlockTitle>
|
||||
{/if}
|
||||
<Select
|
||||
bind:value={option}
|
||||
bind:items={node.properties.values}
|
||||
items={node.properties.values}
|
||||
disabled={node.properties.values.length === 0}
|
||||
clearable={false}
|
||||
on:change
|
||||
|
||||
Reference in New Issue
Block a user