Start of properties panel

This commit is contained in:
space-nuko
2023-05-05 00:49:34 -05:00
parent 85d676b0f9
commit 7f64b743a7
7 changed files with 278 additions and 68 deletions

View File

@@ -16,14 +16,17 @@
export let zIndex: number = 0;
export let classes: string[] = [];
export let showHandles: boolean = false;
let attrsChanged: Writable<boolean> | null = null;
let children: IDragItem[] | null = null;
const flipDurationMs = 100;
$: if (container) {
children = $layoutState.allItems[container.id].children;
attrsChanged = container.attrsChanged
}
else {
children = null;
attrsChanged = null
}
function handleConsider(evt: any) {
@@ -38,7 +41,8 @@
</script>
{#if container && children}
<div class="container {container.attrs.direction} {container.attrs.classes} {classes.join(' ')}"
{#key $attrsChanged}
<div class="container {container.attrs.direction} {container.attrs.classes} {classes.join(' ')}"
class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(container.id)}
class:root-container={zIndex === 0}
class:is-executing={container.isNodeExecuting}
@@ -46,13 +50,7 @@
<Block>
{#if container.attrs.showTitle}
<label for={String(container.id)} class={$uiState.uiEditMode === "widgets" ? "edit-title-label" : ""}>
<BlockTitle>
{#if $uiState.uiEditMode === "widgets"}
<input class="edit-title" bind:value={container.attrs.title} type="text" minlength="1" />
{:else}
{container.attrs.title}
{/if}
</BlockTitle>
<BlockTitle>{container.attrs.title}</BlockTitle>
</label>
{/if}
<div class="v-pane"
@@ -83,7 +81,8 @@
<div class="handle handle-container" style="z-index: {zIndex+100}" data-drag-item-id={container.id} on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}/>
{/if}
</Block>
</div>
</div>
{/key}
{/if}
<style lang="scss">

View File

@@ -16,6 +16,7 @@
import { LGraph } from "@litegraph-ts/core";
import LightboxModal from "./LightboxModal.svelte";
import ComfyQueue from "./ComfyQueue.svelte";
import ComfyProperties from "./ComfyProperties.svelte";
import queueState from "$lib/stores/queueState";
export let app: ComfyApp = undefined;
@@ -23,6 +24,7 @@
let queue: ComfyQueue = undefined;
let mainElem: HTMLDivElement;
let uiPane: ComfyUIPane = undefined;
let props: ComfyProperties = undefined;
let containerElem: HTMLDivElement;
let resizeTimeout: NodeJS.Timeout | null;
let hasShownUIHelpToast: boolean = false;
@@ -64,15 +66,27 @@
}
}
let sidebarSize = 20;
let propsSidebarSize = 15;
function toggleSidebar() {
if (sidebarSize == 0) {
sidebarSize = 20;
function toggleProps() {
if (propsSidebarSize == 0) {
propsSidebarSize = 15;
app.resizeCanvas();
}
else {
sidebarSize = 0;
propsSidebarSize = 0;
}
}
let queueSidebarSize = 15;
function toggleQueue() {
if (queueSidebarSize == 0) {
queueSidebarSize = 15;
app.resizeCanvas();
}
else {
queueSidebarSize = 0;
}
}
@@ -153,6 +167,11 @@
<div id="dropzone" class="dropzone"></div>
<div id="container" bind:this={containerElem}>
<Splitpanes theme="comfy" on:resize={refreshView}>
<Pane bind:size={propsSidebarSize}>
<div class="sidebar-wrapper pane-wrapper">
<ComfyProperties bind:this={props} />
</div>
</Pane>
<Pane>
<Splitpanes theme="comfy" on:resize={refreshView} horizontal="{true}">
<Pane>
@@ -165,7 +184,7 @@
</Pane>
</Splitpanes>
</Pane>
<Pane bind:size={sidebarSize}>
<Pane bind:size={queueSidebarSize}>
<div class="sidebar-wrapper pane-wrapper">
<ComfyQueue bind:this={queue} />
</div>
@@ -179,8 +198,11 @@
<Button variant="secondary" on:click={toggleGraph}>
Toggle Graph
</Button>
<Button variant="secondary" on:click={toggleSidebar}>
Toggle Sidebar
<Button variant="secondary" on:click={toggleProps}>
Toggle Props
</Button>
<Button variant="secondary" on:click={toggleQueue}>
Toggle Queue
</Button>
<Button variant="secondary" on:click={doSave}>
Save

View File

@@ -0,0 +1,166 @@
<script lang="ts">
import { Block, BlockTitle } from "@gradio/atoms";
import { TextBox, Checkbox } from "@gradio/form";
import layoutState, { ALL_ATTRIBUTES } from "$lib/stores/layoutState"
import { get } from "svelte/store"
let target: IDragItem | null = null;
let node: LGraphNode | null = null;
$: if ($layoutState.currentSelection.length > 0) {
const targetId = $layoutState.currentSelection.slice(-1)
target = $layoutState.allItems[targetId].dragItem
if (target.type === "widget") {
node = target.node
}
else {
node = null;
}
}
else {
target = null
node = null;
}
const _entries = [
{ name: "title" },
{ name: "showTitle" },
{ name: "direction" },
{ name: "classes" },
]
function getTargetType(): string {
if (node)
return "Node"
else if (target?.type === "container")
"Group"
return "???"
}
function updateAttribute(entry: any, value: any) {
if (target) {
const name = entry.name
console.warn("updateAttribute", name, value)
target.attrs[name] = value
target.attrsChanged.set(!get(target.attrsChanged))
if (node) {
node.propsChanged.set(!get(node.propsChanged))
}
}
}
</script>
<div class="props">
{#if target}
<div class="top">
<div class="target-name">
<span>
<span class="title">{target.attrs.title}</span>
<span class="type">({getTargetType()})</span>
</span>
</div>
</div>
<div class="props-entries">
{#each ALL_ATTRIBUTES as category(category.categoryName)}
<div class="category-name">
<span>
<span class="title">{target.attrs.title}</span>
</span>
</div>
{#each category.specs as spec(spec.name)}
{@const has = spec.name in target.attrs}
{#if has}
<div class="props-entry">
{#if spec.type === "string"}
<TextBox
value={target.attrs[spec.name]}
on:change={(e) => updateAttribute(spec, e.detail)}
label={spec.name}
max_lines={1}
/>
{:else if spec.type === "boolean"}
<Checkbox
value={target.attrs[spec.name]}
on:change={(e) => updateAttribute(spec, e.detail)}
label={spec.name}
/>
{:else if spec.type === "enum"}
<label class="select-wrapper">
<BlockTitle>{spec.name}</BlockTitle>
<div class="select">
<select
value={target.attrs[spec.name]}
on:change={(e) => updateAttribute(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}
{/each}
{/each}
</div>
{/if}
</div>
<style lang="scss">
.props-entry {
padding: 0.5rem 0.5rem 0 0.5rem;
display: flex;
flex-direction: row;
}
.target-name {
border-color: var(--neutral-400);
background: var(--neutral-300);
.title {
font-weight: bold;
}
}
.category-name {
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);
}
}
.select-wrapper {
width: 100%;
.select {
width: 100%;
select {
width: 100%
}
}
}
.select-title {
padding: 0.2rem;
}
.bottom {
/* width: 100%;
height: auto;
position: absolute;
bottom: 0;
padding: 0.5em; */
}
</style>

View File

@@ -5,11 +5,13 @@
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
import { startDrag, stopDrag } from "$lib/utils"
import BlockContainer from "./BlockContainer.svelte"
import { type Writable } from "svelte/store"
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 widget: WidgetLayout | null = null;
let showHandles: boolean = false;
@@ -17,13 +19,16 @@
dragItem = null;
container = null;
widget = null;
attrsChanged = null;
}
else if (dragItem.type === "container") {
container = dragItem as ContainerLayout;
attrsChanged = container.attrsChanged;
widget = null;
}
else if (dragItem.type === "widget") {
widget = dragItem as WidgetLayout;
attrsChanged = widget.attrsChanged;
container = null;
}
@@ -35,13 +40,22 @@
$: if ($queueState && widget && widget.node) {
dragItem.isNodeExecuting = $queueState.runningNodeId === widget.node.id;
}
function getWidgetClass() {
const title = widget.node.type.replace("/", "-").replace(".", "-")
return `widget--${title}`
}
</script>
{#if container}
{#key $attrsChanged}
<BlockContainer {container} {classes} {zIndex} {showHandles} />
{/key}
{:else if widget && widget.node}
<div class="widget" class:widget-edit-outline={$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}
>
@@ -50,6 +64,7 @@
{#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}
{/if}
<style lang="scss">

View File

@@ -24,7 +24,8 @@ export type LayoutState = {
export type AttributesSpec = {
name: string,
type: string,
editable: boolean
editable: boolean,
values?: string[]
}
export type AttributesCategorySpec = {
@@ -50,8 +51,9 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
},
{
name: "direction",
type: "string",
type: "enum",
editable: true,
values: ["horizontal", "vertical"]
},
{
name: "classes",
@@ -74,7 +76,8 @@ export interface IDragItem {
type: string,
id: DragItemID,
isNodeExecuting?: boolean,
attrs: Attributes
attrs: Attributes,
attrsChanged: Writable<boolean>
}
export interface ContainerLayout extends IDragItem {
@@ -141,6 +144,7 @@ function addContainer(parent: ContainerLayout | null, attrs: Partial<Attributes>
const dragItem: ContainerLayout = {
type: "container",
id: `${state.currentId++}`,
attrsChanged: writable(false),
attrs: {
title: "Container",
showTitle: true,
@@ -166,6 +170,7 @@ function addWidget(parent: ContainerLayout, node: ComfyWidgetNode, attrs: Partia
type: "widget",
id: `${state.currentId++}`,
node: node,
attrsChanged: writable(false),
attrs: {
title: widgetName,
showTitle: true,
@@ -436,7 +441,8 @@ function deserialize(data: SerializedLayoutState, graph: LGraph) {
const dragItem: IDragItem = {
type: entry.dragItem.type,
id: entry.dragItem.id,
attrs: entry.dragItem.attrs
attrs: entry.dragItem.attrs,
attrsChanged: writable(false)
};
const dragEntry: DragItemEntry = {

View File

@@ -67,7 +67,9 @@
{#key $propsChanged}
{#if node !== null && nodeValue !== null}
<label>
<BlockTitle show_label={true}>{widget.attrs.title}</BlockTitle>
{#if widget.attrs.showTitle}
<BlockTitle show_label={widget.attrs.showTitle}>{widget.attrs.title}</BlockTitle>
{/if}
<Select
bind:value={option}
bind:items={node.properties.values}

View File

@@ -1 +1 @@
@import "gradio"
@import "gradio";