From 7aa17581d078e019e4d46ef8fd19388162e8c593 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Wed, 10 May 2023 15:35:22 -0500 Subject: [PATCH] Improve combo refresh logic --- src/lib/ComfyGraph.ts | 3 +- src/lib/components/AccordionContainer.svelte | 8 +- src/lib/components/ComfyApp.ts | 108 ++++++++++++------ src/lib/components/TabsContainer.svelte | 6 +- .../components/gradio/app/Accordion.svelte | 44 +++++++ src/lib/nodes/ComfyWidgetNodes.ts | 29 +++-- src/lib/widgets/ComboWidget.svelte | 81 ++++++++----- 7 files changed, 204 insertions(+), 75 deletions(-) create mode 100644 src/lib/components/gradio/app/Accordion.svelte diff --git a/src/lib/ComfyGraph.ts b/src/lib/ComfyGraph.ts index 17a9836..e0f9f19 100644 --- a/src/lib/ComfyGraph.ts +++ b/src/lib/ComfyGraph.ts @@ -82,8 +82,9 @@ export default class ComfyGraph extends LGraph { const [index, spec] = pair const input = comfyNode.inputs[index] as IComfyInputSlot; input.defaultWidgetNode = spec.defaultWidgetNode; - if (spec.config) + if (spec.config) { input.config = spec.config + } } } } diff --git a/src/lib/components/AccordionContainer.svelte b/src/lib/components/AccordionContainer.svelte index b37f0f8..81235e6 100644 --- a/src/lib/components/AccordionContainer.svelte +++ b/src/lib/components/AccordionContainer.svelte @@ -1,6 +1,6 @@ {#if container && children} @@ -100,7 +104,7 @@ {:else} - + {#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item(item.id)} {/each} diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index d0f21d0..1d0d519 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -19,7 +19,7 @@ import ComfyGraphCanvas, { type SerializedGraphCanvasState } from "$lib/ComfyGra import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode"; import * as widgets from "$lib/widgets/index" import queueState from "$lib/stores/queueState"; -import type { SvelteComponentDev } from "svelte/internal"; +import { type SvelteComponentDev } from "svelte/internal"; import type IComfyInputSlot from "$lib/IComfyInputSlot"; import type { SerializedLayoutState } from "$lib/stores/layoutState"; import layoutState from "$lib/stores/layoutState"; @@ -27,8 +27,9 @@ import { toast } from '@zerodevx/svelte-toast' import ComfyGraph from "$lib/ComfyGraph"; import { ComfyBackendNode } from "$lib/nodes/ComfyBackendNode"; import { get } from "svelte/store"; +import { tick } from "svelte"; import uiState from "$lib/stores/uiState"; -import { download, jsonToJsObject, promptToGraphVis, workflowToGraphVis } from "$lib/utils"; +import { download, jsonToJsObject, promptToGraphVis, range, workflowToGraphVis } from "$lib/utils"; import notify from "$lib/notify"; import configState from "$lib/stores/configState"; @@ -72,6 +73,12 @@ export type Progress = { max: number } +type BackendComboNode = { + comboNode: nodes.ComfyComboNode + inputSlot: IComfyInputSlot, + backendNode: ComfyBackendNode +} + function isActiveBackendNode(node: ComfyGraphNode, tag: string | null): boolean { if (!node.isBackendNode) return false; @@ -764,47 +771,78 @@ export default class ComfyApp { async refreshComboInNodes(flashUI: boolean = false) { const defs = await this.api.getNodeDefs(); - for (let nodeNum in this.lGraph._nodes) { - const node = this.lGraph._nodes[nodeNum]; - if (node.type === "ui/combo") { - (node as nodes.ComfyComboNode).valuesForCombo = null; - (node as nodes.ComfyComboNode).comboRefreshed.set(true); + const toUpdate: BackendComboNode[] = [] + + const isComfyComboNode = (node: LGraphNode): boolean => { + return node + && node.type === "ui/combo" + && "doAutoConfig" in node; + } + + // Node IDs of combo widgets attached to a backend node + let backendCombos: Set = new Set() + + console.debug("[refreshComboInNodes] start") + + // Figure out which combo nodes to update. They need to be connected to + // an input slot on a backend node with a backend config in the input + // slot connected to. + for (const node of this.lGraph.iterateNodesInOrder()) { + if (!(node as any).isBackendNode) + continue; + + const backendNode = (node as ComfyBackendNode) + const inputIndex = range(backendNode.inputs.length) + .find(i => { + const input = backendNode.inputs[i] + const inputNode = backendNode.getInputNode(i) + + // Does this input autocreate a combo box on creation? + const isComfyInput = "config" in input + && "widgetNodeType" in input + && input.widgetNodeType === "ui/combo"; + + return isComfyComboNode(inputNode) && isComfyInput + }); + + if (inputIndex != null) { + const comboNode = backendNode.getInputNode(inputIndex) as nodes.ComfyComboNode + const inputSlot = backendNode.inputs[inputIndex] as IComfyInputSlot; + const def = defs[backendNode.type]; + + const hasBackendConfig = def["input"]["required"][inputSlot.name] !== undefined + console.log("hasBackendConfig", node.title, inputSlot.name, hasBackendConfig) + + if (hasBackendConfig) { + backendCombos.add(comboNode.id) + toUpdate.push({ comboNode, inputSlot, backendNode }) + } } } - let seen = new Set() + console.debug("[refreshComboInNodes] found:", toUpdate.length, toUpdate) - for (let nodeNum in this.lGraph._nodes) { - const node = this.lGraph._nodes[nodeNum]; + // Mark combo nodes without backend configs as being loaded already. + for (const node of this.lGraph.iterateNodesInOrder()) { + if (isComfyComboNode(node) && !backendCombos.has(node.id)) { + const comboNode = node as nodes.ComfyComboNode; + comboNode.formatValues(comboNode.properties.values); + } + } - const def = defs[node.type]; + await tick(); - for (let index = 0; index < node.inputs.length; index++) { - const input = node.inputs[index]; - if ("config" in input) { - const comfyInput = input as IComfyInputSlot; + // Load definitions from the backend. + for (const { comboNode, inputSlot, backendNode } of toUpdate) { + const def = defs[backendNode.type]; + const rawValues = def["input"]["required"][inputSlot.name][0]; - if (comfyInput.defaultWidgetNode == nodes.ComfyComboNode && def["input"]["required"][comfyInput.name] !== undefined) { - const rawValues = def["input"]["required"][comfyInput.name][0]; + console.warn("[ComfyApp] Reconfiguring combo widget", backendNode.type, "=>", comboNode.type, inputSlot.config.values.length) + comboNode.doAutoConfig(inputSlot, { includeProperties: new Set(["values"]), setWidgetTitle: false }) - comfyInput.config.values = rawValues; - const inputNode = node.getInputNode(index) - - if (inputNode && "doAutoConfig" in inputNode && comfyInput.widgetNodeType === inputNode.type && !seen.has(inputNode.id)) { - seen.add(inputNode.id) - console.warn("[ComfyApp] Reconfiguring combo widget", inputNode.type, comfyInput.config.values.length) - const comfyComboNode = inputNode as nodes.ComfyComboNode; - comfyComboNode.doAutoConfig(comfyInput, { includeProperties: new Set(["values"]), setWidgetTitle: false }) - - comfyComboNode.formatValues(rawValues) - if (!comfyInput.config.values.includes(get(comfyComboNode.value))) { - comfyComboNode.setValue(comfyInput.config.defaultValue || comfyInput.config.values[0]) - } - if (flashUI) - comfyComboNode.comboRefreshed.set(true) - } - } - } + comboNode.formatValues(rawValues) + if (!inputSlot.config.values.includes(get(comboNode.value))) { + comboNode.setValue(inputSlot.config.defaultValue || inputSlot.config.values[0]) } } } diff --git a/src/lib/components/TabsContainer.svelte b/src/lib/components/TabsContainer.svelte index 72e471f..f61400e 100644 --- a/src/lib/components/TabsContainer.svelte +++ b/src/lib/components/TabsContainer.svelte @@ -61,6 +61,10 @@ return tabName } + + function handleSelect() { + navigator.vibrate(20) + } {#if container && children} @@ -113,7 +117,7 @@ {/if} {:else} - + {#each children.filter(item => item.id !== SHADOW_PLACEHOLDER_ITEM_ID) as item, i(item.id)} {@const tabName = getTabName(container, i)} diff --git a/src/lib/components/gradio/app/Accordion.svelte b/src/lib/components/gradio/app/Accordion.svelte new file mode 100644 index 0000000..a26f663 --- /dev/null +++ b/src/lib/components/gradio/app/Accordion.svelte @@ -0,0 +1,44 @@ + + +
+ {label} + + ▼ + +
+
+ +
+ + diff --git a/src/lib/nodes/ComfyWidgetNodes.ts b/src/lib/nodes/ComfyWidgetNodes.ts index aae520f..a93fe53 100644 --- a/src/lib/nodes/ComfyWidgetNodes.ts +++ b/src/lib/nodes/ComfyWidgetNodes.ts @@ -273,9 +273,15 @@ export abstract class ComfyWidgetNode extends ComfyGraphNode { console.debug("Property copy", input, this.properties) this.setValue(get(this.value)) + + this.onAutoConfig(input); + this.notifyPropsChanged(); } + onAutoConfig(input: IComfyInputSlot) { + } + notifyPropsChanged() { const layoutEntry = layoutState.findLayoutEntryForNode(this.id) if (layoutEntry && layoutEntry.parent) { @@ -430,18 +436,20 @@ export class ComfyComboNode extends ComfyWidgetNode { override defaultValue = "A"; override saveUserState = false; - comboRefreshed: Writable; - - valuesForCombo: any[] | null = null; + // True if at least one combo box refresh has taken place + // Wait until the initial graph load for combo to be valid. + firstLoad: Writable; + valuesForCombo: Writable; // Changed when the combo box has values. constructor(name?: string) { super(name, "A") - this.comboRefreshed = writable(false) + this.firstLoad = writable(false) + this.valuesForCombo = writable(null) } override onPropertyChanged(property: any, value: any) { if (property === "values" || property === "convertValueToLabelCode") { - this.formatValues(this.properties.values) + // this.formatValues(this.properties.values) } } @@ -455,10 +463,12 @@ export class ComfyComboNode extends ComfyWidgetNode { if (this.properties.convertValueToLabelCode) formatter = new Function("value", this.properties.convertValueToLabelCode) as (v: string) => string; else - formatter = (value) => `${value}`; + formatter = (value: any) => `${value}`; + + let valuesForCombo = [] try { - this.valuesForCombo = this.properties.values.map((value, index) => { + valuesForCombo = this.properties.values.map((value, index) => { return { value, label: formatter(value), @@ -468,7 +478,7 @@ export class ComfyComboNode extends ComfyWidgetNode { } catch (err) { console.error("Failed formatting!", err) - this.valuesForCombo = this.properties.values.map((value, index) => { + valuesForCombo = this.properties.values.map((value, index) => { return { value, label: `${value}`, @@ -477,7 +487,8 @@ export class ComfyComboNode extends ComfyWidgetNode { }) } - this.comboRefreshed.set(true); + this.firstLoad.set(true) + this.valuesForCombo.set(valuesForCombo); } onConnectOutput( diff --git a/src/lib/widgets/ComboWidget.svelte b/src/lib/widgets/ComboWidget.svelte index 8481028..fd26fd8 100644 --- a/src/lib/widgets/ComboWidget.svelte +++ b/src/lib/widgets/ComboWidget.svelte @@ -13,8 +13,8 @@ let node: ComfyComboNode | null = null; let nodeValue: Writable | null = null; let propsChanged: Writable | null = null; - let comboRefreshed: Writable | null = null; - let wasComboRefreshed: boolean = false; + let valuesForCombo: Writable | null = null; + let lastConfigured: any = null; let option: any = null; export let debug: boolean = false; @@ -40,23 +40,26 @@ node = widget.node as ComfyComboNode nodeValue = node.value; propsChanged = node.propsChanged; - comboRefreshed = node.comboRefreshed; - if ($comboRefreshed) - flashOnRefreshed(); + valuesForCombo = node.valuesForCombo; } } - $: node.valuesForCombo && updateActiveIndex(node.valuesForCombo) + $: $valuesForCombo != null && updateActiveIndex($valuesForCombo) function updateActiveIndex(values: any) { const value = $nodeValue; activeIndex = values.findIndex(v => v.value === value); } - $: $comboRefreshed && flashOnRefreshed(); + $: $valuesForCombo != lastConfigured && flashOnRefreshed(); + let lightUp = false; function flashOnRefreshed() { - setTimeout(() => ($comboRefreshed = false), 1000); + lastConfigured = $valuesForCombo + if (lastConfigured != null) { + lightUp = true; + setTimeout(() => (lightUp = false), 1000); + } } function getLinkValue() { @@ -69,7 +72,10 @@ } function onFocus() { - navigator.vibrate(20) + // console.warn("FOCUS") + if (listOpen) { + navigator.vibrate(20) + } } function onSelect(e: CustomEvent) { @@ -79,7 +85,7 @@ const item = e.detail - console.warn("SELECT", item, item.index) + console.debug("[ComboWidget] SELECT", item, item.index) $nodeValue = item.value; activeIndex = item.index; listOpen = false; @@ -94,14 +100,14 @@ let end = 0; function handleHover(index: number) { - console.warn("HOV", index) + // console.warn("HOV", index) hoverItemIndex = index; } function handleSelect(index: number) { - console.warn("SEL", index) + // console.warn("SEL", index) navigator.vibrate(20) - const item = node.valuesForCombo[index] + const item = $valuesForCombo[index] activeIndex = index; $nodeValue = item.value listOpen = false; @@ -130,13 +136,13 @@ -
- {#key $comboRefreshed} +
+ {#key $valuesForCombo} {#if node !== null && nodeValue !== null} - {#if node.valuesForCombo == null} + {#if $valuesForCombo == null} Loading... {:else} - Count {node.valuesForCombo.length} + Count {$valuesForCombo.length}