WOrkflow/layout fixes
This commit is contained in:
@@ -8,7 +8,7 @@ import { get } from "svelte/store";
|
||||
import type ComfyGraphNode from "./nodes/ComfyGraphNode";
|
||||
import type IComfyInputSlot from "./IComfyInputSlot";
|
||||
import type { ComfyBackendNode } from "./nodes/ComfyBackendNode";
|
||||
import type { ComfyWidgetNode } from "./nodes";
|
||||
import type { ComfyComboNode, ComfyWidgetNode } from "./nodes";
|
||||
|
||||
type ComfyGraphEvents = {
|
||||
configured: (graph: LGraph) => void
|
||||
@@ -108,6 +108,11 @@ export default class ComfyGraph extends LGraph {
|
||||
widgetNode.collapse();
|
||||
widgetNode.pos = [inputPos[0] - 140, inputPos[1] + LiteGraph.NODE_SLOT_HEIGHT / 2];
|
||||
widgetNodesAdded.push(widgetNode)
|
||||
|
||||
// Set combo box as loaded
|
||||
if (widgetNode.type === "ui/combo" && widgetNode.properties.values != null) {
|
||||
(widgetNode as ComfyComboNode).formatValues(widgetNode.properties.values);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import { flip } from 'svelte/animate';
|
||||
import layoutState, { type ContainerLayout, type WidgetLayout, type IDragItem } from "$lib/stores/layoutState";
|
||||
import { startDrag, stopDrag } from "$lib/utils"
|
||||
import type { Writable } from "svelte/store";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import { isHidden } from "$lib/widgets/utils";
|
||||
|
||||
export let container: ContainerLayout | null = null;
|
||||
@@ -22,23 +22,27 @@
|
||||
export let edit: boolean = false;
|
||||
export let dragDisabled: boolean = false;
|
||||
export let isMobile: boolean = false;
|
||||
let isOpen: Writable<boolean> | null = null;
|
||||
|
||||
let attrsChanged: Writable<boolean> | null = null;
|
||||
let children: IDragItem[] | null = null;
|
||||
const flipDurationMs = 100;
|
||||
|
||||
let selectedIndex: number = 0;
|
||||
|
||||
$: if (container) {
|
||||
children = $layoutState.allItems[container.id].children;
|
||||
attrsChanged = container.attrsChanged
|
||||
if (container.isOpen == null) {
|
||||
container.isOpen = container.attrs.openOnStartup
|
||||
}
|
||||
setupState()
|
||||
}
|
||||
else {
|
||||
children = null;
|
||||
attrsChanged = null
|
||||
}
|
||||
|
||||
function setupState() {
|
||||
children = $layoutState.allItems[container.id].children;
|
||||
if (container.isOpen == null) {
|
||||
container.isOpen = writable(container.attrs.openOnStartup)
|
||||
}
|
||||
isOpen = container.isOpen
|
||||
console.warn("REBUILD", container.attrs.title, $isOpen)
|
||||
}
|
||||
|
||||
function handleConsider(evt: any) {
|
||||
@@ -51,8 +55,9 @@
|
||||
// Ensure dragging is stopped on drag finish
|
||||
};
|
||||
|
||||
function handleClick({ clicked }: CustomEvent<boolean>) {
|
||||
function handleClick(e: CustomEvent<boolean>) {
|
||||
navigator.vibrate(20)
|
||||
$isOpen = e.detail
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -104,7 +109,7 @@
|
||||
</Block>
|
||||
{:else}
|
||||
<Block elem_classes={["gradio-accordion"]}>
|
||||
<Accordion label={container.attrs.title} bind:open={container.isOpen} on:click={handleClick}>
|
||||
<Accordion label={container.attrs.title} open={$isOpen} on:click={handleClick}>
|
||||
{#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)}
|
||||
<WidgetContainer dragItem={item} zIndex={zIndex+1} {isMobile} />
|
||||
{/each}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { get } from "svelte/store";
|
||||
import { get, writable, type Writable } from "svelte/store";
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes';
|
||||
import { Button } from "@gradio/button";
|
||||
import { BlockTitle } from "@gradio/atoms";
|
||||
|
||||
@@ -383,7 +383,7 @@ export default class ComfyApp {
|
||||
}
|
||||
|
||||
// Distinguish frontend/backend connections
|
||||
const BACKEND_TYPES = ["CLIP", "CLIP_VISION", "CLIP_VISION_OUTPUT", "CONDITIONING", "CONTROL_NET", "LATENT", "MASK", "MODEL", "STYLE_MODEL", "VAE"]
|
||||
const BACKEND_TYPES = ["CLIP", "CLIP_VISION", "CLIP_VISION_OUTPUT", "CONDITIONING", "CONTROL_NET", "LATENT", "MASK", "MODEL", "STYLE_MODEL", "VAE", "UPSCALE_MODEL"]
|
||||
for (const type of BACKEND_TYPES) {
|
||||
setColor(type, "orange")
|
||||
}
|
||||
@@ -497,6 +497,11 @@ export default class ComfyApp {
|
||||
(node as ComfyGraphNode).onDefaultQueueAction()
|
||||
}
|
||||
}
|
||||
|
||||
if (get(layoutState).attrs.queuePromptButtonRunWorkflow) {
|
||||
this.queuePrompt(0, 1);
|
||||
notify("Prompt queued.");
|
||||
}
|
||||
}
|
||||
|
||||
querySave() {
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
|
||||
let target: IDragItem | null = null;
|
||||
let node: LGraphNode | null = null;
|
||||
let attrsChanged: Writable<boolean> | null = null;
|
||||
let refreshPanel: Writable<number> = writable(0);
|
||||
let attrsChanged: Writable<number> | null = null;
|
||||
|
||||
let refreshPropsPanel: Writable<number> | null
|
||||
|
||||
$: refreshPropsPanel = $layoutState.refreshPropsPanel;
|
||||
|
||||
$: if ($layoutState.currentSelection.length > 0) {
|
||||
const targetId = $layoutState.currentSelection.slice(-1)[0]
|
||||
@@ -50,7 +53,7 @@
|
||||
let value = spec.defaultValue;
|
||||
target.attrs[spec.name] = value;
|
||||
if (spec.refreshPanelOnChange)
|
||||
$refreshPanel += 1;
|
||||
$refreshPropsPanel += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,9 +221,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
function doRefreshPanel() {
|
||||
console.warn("[ComfyProperties] doRefreshPanel")
|
||||
$refreshPanel += 1;
|
||||
function getWorkflowAttribute(spec: AttributesSpec): any {
|
||||
let value = $layoutState.attrs[spec.name]
|
||||
if (value == null)
|
||||
value = spec.defaultValue
|
||||
else if (spec.serialize)
|
||||
value = spec.serialize(value)
|
||||
console.debug("[ComfyProperties] getWorkflowAttribute", spec.name, value, spec, $layoutState.attrs[spec.name])
|
||||
return value
|
||||
}
|
||||
|
||||
function updateWorkflowAttribute(spec: AttributesSpec, value: any) {
|
||||
@@ -236,6 +244,11 @@
|
||||
if (spec.refreshPanelOnChange)
|
||||
doRefreshPanel()
|
||||
}
|
||||
|
||||
function doRefreshPanel() {
|
||||
console.warn("[ComfyProperties] doRefreshPanel")
|
||||
$refreshPropsPanel += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="props">
|
||||
@@ -251,7 +264,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="props-entries">
|
||||
{#key $refreshPanel}
|
||||
{#key $refreshPropsPanel}
|
||||
{#each ALL_ATTRIBUTES as category(category.categoryName)}
|
||||
<div class="category-name">
|
||||
<span>
|
||||
@@ -379,7 +392,7 @@
|
||||
<div class="props-entry">
|
||||
{#if spec.type === "string"}
|
||||
<TextBox
|
||||
value={$layoutState.attrs[spec.name] || spec.defaultValue}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
on:input={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
label={spec.name}
|
||||
@@ -388,7 +401,7 @@
|
||||
/>
|
||||
{:else if spec.type === "boolean"}
|
||||
<Checkbox
|
||||
value={$layoutState.attrs[spec.name] || spec.defaultValue}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
label={spec.name}
|
||||
@@ -396,7 +409,7 @@
|
||||
{:else if spec.type === "number"}
|
||||
<ComfyNumberProperty
|
||||
name={spec.name}
|
||||
value={$layoutState.attrs[spec.name] || spec.defaultValue}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
step={spec.step || 1}
|
||||
min={spec.min || -1024}
|
||||
max={spec.max || 1024}
|
||||
@@ -406,7 +419,7 @@
|
||||
{:else if spec.type === "enum"}
|
||||
<ComfyComboProperty
|
||||
name={spec.name}
|
||||
value={$layoutState.attrs[spec.name] || spec.defaultValue}
|
||||
value={getWorkflowAttribute(spec)}
|
||||
values={spec.values}
|
||||
disabled={!$uiState.uiUnlocked || !spec.editable}
|
||||
on:change={(e) => updateWorkflowAttribute(spec, e.detail)}
|
||||
|
||||
@@ -31,6 +31,12 @@ export type LayoutAttributes = {
|
||||
* Name of the "Queue Prompt" button. Set to blank to hide the button.
|
||||
*/
|
||||
queuePromptButtonName: string,
|
||||
|
||||
/*
|
||||
* If true, clicking the "Queue Prompt" button will run the default subgraph.
|
||||
* Set this to false if you need special behavior before running any subgraphs.
|
||||
*/
|
||||
queuePromptButtonRunWorkflow: boolean,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -86,6 +92,8 @@ export type LayoutState = {
|
||||
* Global workflow attributes
|
||||
*/
|
||||
attrs: LayoutAttributes
|
||||
|
||||
refreshPropsPanel: Writable<number>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -539,6 +547,13 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
|
||||
location: "workflow",
|
||||
editable: true,
|
||||
defaultValue: "Queue Prompt"
|
||||
},
|
||||
{
|
||||
name: "queuePromptButtonRunWorkflow",
|
||||
type: "boolean",
|
||||
location: "workflow",
|
||||
editable: true,
|
||||
defaultValue: true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -612,7 +627,7 @@ export interface ContainerLayout extends IDragItem {
|
||||
// (not serialized)
|
||||
|
||||
// Accordion
|
||||
isOpen?: boolean,
|
||||
isOpen?: Writable<boolean>,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -657,6 +672,7 @@ const store: Writable<LayoutState> = writable({
|
||||
currentSelectionNodes: [],
|
||||
isMenuOpen: false,
|
||||
isConfiguring: true,
|
||||
refreshPropsPanel: writable(0),
|
||||
attrs: {
|
||||
...defaultWorkflowAttributes
|
||||
}
|
||||
@@ -913,6 +929,7 @@ function initDefaultLayout() {
|
||||
currentSelectionNodes: [],
|
||||
isMenuOpen: false,
|
||||
isConfiguring: false,
|
||||
refreshPropsPanel: writable(0),
|
||||
attrs: {
|
||||
...defaultWorkflowAttributes
|
||||
}
|
||||
@@ -1028,12 +1045,16 @@ function deserialize(data: SerializedLayoutState, graph: LGraph) {
|
||||
currentSelectionNodes: [],
|
||||
isMenuOpen: false,
|
||||
isConfiguring: false,
|
||||
refreshPropsPanel: writable(0),
|
||||
attrs: { ...defaultWorkflowAttributes, ...data.attrs }
|
||||
}
|
||||
|
||||
console.debug("[layoutState] deserialize", data, state)
|
||||
console.debug("[layoutState] deserialize", data, state, defaultWorkflowAttributes)
|
||||
|
||||
store.set(state)
|
||||
|
||||
// Ensure properties panel is updated with new state
|
||||
state.refreshPropsPanel.set(get(state.refreshPropsPanel) + 1)
|
||||
}
|
||||
|
||||
function onStartConfigure() {
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
import { Checkbox } from "@gradio/form";
|
||||
import { get, type Writable, writable } from "svelte/store";
|
||||
import { isDisabled } from "./utils"
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
|
||||
export let widget: WidgetLayout | null = null;
|
||||
export let isMobile: boolean = false;
|
||||
let node: ComfyCheckboxNode | null = null;
|
||||
let nodeValue: Writable<boolean> | null = null;
|
||||
let attrsChanged: Writable<boolean> | null = null;
|
||||
let attrsChanged: Writable<number> | null = null;
|
||||
|
||||
$: widget && setNodeValue(widget);
|
||||
|
||||
@@ -22,7 +23,8 @@
|
||||
}
|
||||
};
|
||||
|
||||
function onSelect() {
|
||||
function onSelect(e: CustomEvent<SelectData>) {
|
||||
$nodeValue = e.detail.selected
|
||||
navigator.vibrate(20)
|
||||
}
|
||||
</script>
|
||||
@@ -35,7 +37,7 @@
|
||||
<Checkbox
|
||||
disabled={isDisabled(widget)}
|
||||
label={widget.attrs.title}
|
||||
bind:value={$nodeValue}
|
||||
value={$nodeValue}
|
||||
on:select={onSelect}
|
||||
/>
|
||||
</Block>
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
nodeValue = node.value;
|
||||
propsChanged = node.propsChanged;
|
||||
valuesForCombo = node.valuesForCombo;
|
||||
lastConfigured = $valuesForCombo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<Toolbar bottom>
|
||||
{#if $layoutState.attrs.queuePromptButtonName != ""}
|
||||
<Link on:click={queuePrompt}>
|
||||
{$layoutState.attrs.queuePromptButtonName}
|
||||
{$layoutState.attrs.queuePromptButtonName}
|
||||
</Link>
|
||||
{/if}
|
||||
<Link on:click={refreshCombos}>🔄</Link>
|
||||
|
||||
Reference in New Issue
Block a user