Trying to fix prop updates reflected in widgets

This commit is contained in:
space-nuko
2023-05-03 20:16:45 -07:00
parent 573970eac6
commit 3d55badda8
7 changed files with 106 additions and 32 deletions

View File

@@ -91,7 +91,6 @@ export default class ComfyApp {
LiteGraph.release_link_on_empty_shows_menu = true; LiteGraph.release_link_on_empty_shows_menu = true;
LiteGraph.alt_drag_do_clone_nodes = true; LiteGraph.alt_drag_do_clone_nodes = true;
LiteGraph.ignore_all_widget_events = true;
this.lGraph.start(); this.lGraph.start();

View File

@@ -1,4 +1,4 @@
import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, LGraph, type INodeInputSlot, type ITextWidget } from "@litegraph-ts/core"; import { LiteGraph, type ContextMenuItem, type LGraphNode, type Vector2, LConnectionKind, LLink, LGraphCanvas, type SlotType, TitleMode, type SlotLayout, LGraph, type INodeInputSlot, type ITextWidget, type INodeOutputSlot } from "@litegraph-ts/core";
import ComfyGraphNode from "./ComfyGraphNode"; import ComfyGraphNode from "./ComfyGraphNode";
import ComboWidget from "$lib/widgets/ComboWidget.svelte"; import ComboWidget from "$lib/widgets/ComboWidget.svelte";
import RangeWidget from "$lib/widgets/RangeWidget.svelte"; import RangeWidget from "$lib/widgets/RangeWidget.svelte";
@@ -7,9 +7,12 @@ import type { SvelteComponentDev } from "svelte/internal";
import { ComfyWidgets } from "$lib/widgets"; import { ComfyWidgets } from "$lib/widgets";
import { Watch } from "@litegraph-ts/nodes-basic"; import { Watch } from "@litegraph-ts/nodes-basic";
import type IComfyInputSlot from "$lib/IComfyInputSlot"; import type IComfyInputSlot from "$lib/IComfyInputSlot";
import { writable, type Unsubscriber, type Writable } from "svelte/store"; import { writable, type Unsubscriber, type Writable, get } from "svelte/store";
import { clamp } from "$lib/utils"
import layoutState from "$lib/stores/layoutState";
export interface ComfyWidgetProperties extends Record<string, any> { export interface ComfyWidgetProperties extends Record<string, any> {
defaultValue: any
} }
/* /*
@@ -21,6 +24,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
abstract properties: ComfyWidgetProperties; abstract properties: ComfyWidgetProperties;
value: Writable<T> value: Writable<T>
propsChanged: Writable<boolean> = writable(true) // dummy to indicate if props changed
unsubscribe: Unsubscriber; unsubscribe: Unsubscriber;
/** Svelte class for the frontend logic */ /** Svelte class for the frontend logic */
@@ -38,7 +42,7 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
override size: Vector2 = [60, 40]; override size: Vector2 = [60, 40];
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.value = writable(value) this.value = writable(value)
@@ -49,10 +53,12 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
"Value", "Value",
"" ""
); );
this.displayWidget.disabled = true; // prevent editing
this.unsubscribe = this.value.subscribe(this.onValueUpdated.bind(this)) this.unsubscribe = this.value.subscribe(this.onValueUpdated.bind(this))
} }
private onValueUpdated(value: any) { private onValueUpdated(value: any) {
console.debug("[Widget] valueUpdated", this, value)
this.displayWidget.value = Watch.toString(value) this.displayWidget.value = Watch.toString(value)
} }
@@ -82,22 +88,50 @@ export abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
for (const key in comfyInput.config) for (const key in comfyInput.config)
this.setProperty(key, comfyInput.config[key]) this.setProperty(key, comfyInput.config[key])
} }
if ("defaultValue" in this.properties)
this.setValue(this.properties.defaultValue)
const widget = layoutState.findLayoutForNode(this.id)
if (widget) {
widget.attrs.title = input.name;
}
console.debug("Property copy", input, this.properties) console.debug("Property copy", input, this.properties)
} }
return true; return true;
} }
onConnectionsChange(
type: LConnectionKind,
slotIndex: number,
isConnected: boolean,
link: LLink,
ioSlot: (INodeOutputSlot | INodeInputSlot)
): void {
this.clampConfig();
}
clampConfig() { clampConfig() {
let changed = false;
for (const link of this.getOutputLinks(0)) { for (const link of this.getOutputLinks(0)) {
if (link) { // can be undefined if the link is removed
const node = this.graph._nodes_by_id[link.target_id] const node = this.graph._nodes_by_id[link.target_id]
if (node) { if (node) {
const input = node.inputs[link.target_slot] const input = node.inputs[link.target_slot]
if (input && "config" in input) if (input && "config" in input) {
this.clampOneConfig(input as IComfyInputSlot) this.clampOneConfig(input as IComfyInputSlot)
changed = true;
} }
} }
} }
}
if (changed) {
// Force trigger reactivity to update component based on new props
this.propsChanged.set(true)
}
}
clampOneConfig(input: IComfyInputSlot) {} clampOneConfig(input: IComfyInputSlot) {}
} }
@@ -111,6 +145,7 @@ export interface ComfySliderProperties extends ComfyWidgetProperties {
export class ComfySliderNode extends ComfyWidgetNode<number> { export class ComfySliderNode extends ComfyWidgetNode<number> {
override properties: ComfySliderProperties = { override properties: ComfySliderProperties = {
defaultValue: 0,
min: 0, min: 0,
max: 10, max: 10,
step: 1, step: 1,
@@ -131,10 +166,10 @@ export class ComfySliderNode extends ComfyWidgetNode<number> {
} }
override clampOneConfig(input: IComfyInputSlot) { override clampOneConfig(input: IComfyInputSlot) {
this.setProperty("min", Math.max(this.properties.min, input.config.min)) // this.setProperty("min", clamp(this.properties.min, input.config.min, input.config.max))
this.setProperty("max", Math.max(this.properties.max, input.config.max)) // this.setProperty("max", clamp(this.properties.max, input.config.max, input.config.min))
this.setProperty("step", Math.max(this.properties.step, input.config.step)) // this.setProperty("step", Math.min(this.properties.step, input.config.step))
this.setValue(Math.max(Math.min(this.properties.value, this.properties.max), this.properties.min)) this.setValue(clamp(this.properties.defaultValue, this.properties.min, this.properties.max))
} }
} }
@@ -146,12 +181,13 @@ LiteGraph.registerNodeType({
}) })
export interface ComfyComboProperties extends ComfyWidgetProperties { export interface ComfyComboProperties extends ComfyWidgetProperties {
options: string[] values: string[]
} }
export class ComfyComboNode extends ComfyWidgetNode<string> { export class ComfyComboNode extends ComfyWidgetNode<string> {
override properties: ComfyComboProperties = { override properties: ComfyComboProperties = {
options: ["A", "B", "C", "D"] defaultValue: "A",
values: ["A", "B", "C", "D"]
} }
static slotLayout: SlotLayout = { static slotLayout: SlotLayout = {
@@ -178,21 +214,23 @@ export class ComfyComboNode extends ComfyWidgetNode<string> {
return false; return false;
const thisProps = this.properties; const thisProps = this.properties;
const otherProps = inputNode.properties; if (!("config" in input))
return true;
const comfyInput = input as IComfyInputSlot;
const otherProps = comfyInput.config;
// Ensure combo options match // Ensure combo options match
if (!(otherProps.options instanceof Array)) if (!(otherProps.values instanceof Array))
return false; return false;
if (otherProps.options.length !== thisProps.options.length) if (thisProps.values.find((v, i) => otherProps.values.indexOf(v) === -1))
return false;
if (otherProps.find((v, i) => thisProps[i] !== v))
return false; return false;
return true; return true;
} }
override clampOneConfig(input: IComfyInputSlot) { override clampOneConfig(input: IComfyInputSlot) {
if (!(this.properties.value in input.config.values)) { if (input.config.values.indexOf(this.properties.value) === -1) {
if (input.config.values.length === 0) if (input.config.values.length === 0)
this.setValue("") this.setValue("")
else else
@@ -214,6 +252,7 @@ export interface ComfyTextProperties extends ComfyWidgetProperties {
export class ComfyTextNode extends ComfyWidgetNode<string> { export class ComfyTextNode extends ComfyWidgetNode<string> {
override properties: ComfyTextProperties = { override properties: ComfyTextProperties = {
defaultValue: "",
multiline: false multiline: false
} }

View File

@@ -98,6 +98,7 @@ type LayoutStateOps = {
groupItems: (dragItems: IDragItem[]) => ContainerLayout, groupItems: (dragItems: IDragItem[]) => ContainerLayout,
ungroup: (container: ContainerLayout) => void, ungroup: (container: ContainerLayout) => void,
getCurrentSelection: () => IDragItem[], getCurrentSelection: () => IDragItem[],
findLayoutForNode: (nodeId: number) => IDragItem | null;
clear: (state?: Partial<LayoutState>) => void, clear: (state?: Partial<LayoutState>) => void,
resetLayout: () => void, resetLayout: () => void,
} }
@@ -362,6 +363,16 @@ function ungroup(container: ContainerLayout) {
store.set(state) store.set(state)
} }
function findLayoutForNode(nodeId: number): WidgetLayout | null {
const state = get(store)
const found = Object.entries(state.allItems).find(pair =>
pair[1].dragItem.type === "widget"
&& (pair[1].dragItem as WidgetLayout).node.id === nodeId)
if (found)
return found[1].dragItem as WidgetLayout
return null;
}
function clear(state: Partial<LayoutState> = {}) { function clear(state: Partial<LayoutState> = {}) {
store.set({ store.set({
root: null, root: null,
@@ -390,6 +401,7 @@ const layoutStateStore: WritableLayoutStateStore =
configureFinished, configureFinished,
getCurrentSelection, getCurrentSelection,
groupItems, groupItems,
findLayoutForNode,
ungroup, ungroup,
clear, clear,
resetLayout resetLayout

View File

@@ -6,6 +6,10 @@ import { get } from "svelte/store"
import layoutState from "$lib/stores/layoutState" import layoutState from "$lib/stores/layoutState"
import type { SvelteComponentDev } from "svelte/internal"; import type { SvelteComponentDev } from "svelte/internal";
export function clamp(n: number, min: number, max: number): number {
return Math.min(Math.max(n, min), max)
}
export function download(filename: string, text: string, type: string = "text/plain") { export function download(filename: string, text: string, type: string = "text/plain") {
const blob = new Blob([text], { type: type }); const blob = new Blob([text], { type: type });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);

View File

@@ -7,14 +7,22 @@
export let widget: WidgetLayout | null = null; export let widget: WidgetLayout | null = null;
let node: ComfyComboNode | null = null; let node: ComfyComboNode | null = null;
let nodeValue: Writable<string> | null = null; let nodeValue: Writable<string> | null = null;
let propsChanged: Writable<boolean> | null = null;
let option: any let option: any
$: widget && setNodeValue(widget); $: widget && setNodeValue(widget);
$: if ($propsChanged && nodeValue !== null) {
setOption($nodeValue)
setNodeValue(widget)
node.properties = node.properties
}
function setNodeValue(widget: WidgetLayout) { function setNodeValue(widget: WidgetLayout) {
if(widget && !node) { if(widget) {
node = widget.node as ComfyComboNode node = widget.node as ComfyComboNode
nodeValue = node.value; nodeValue = node.value;
propsChanged = node.propsChanged;
setOption($nodeValue) // don't react on option setOption($nodeValue) // don't react on option
} }
} }
@@ -23,7 +31,7 @@
option = value; option = value;
} }
$: if (nodeValue && option) { $: if (nodeValue && option && option.value) {
$nodeValue = option.value; $nodeValue = option.value;
} }
</script> </script>
@@ -34,8 +42,8 @@
<BlockTitle show_label={true}>{widget.attrs.title}</BlockTitle> <BlockTitle show_label={true}>{widget.attrs.title}</BlockTitle>
<Select <Select
bind:value={option} bind:value={option}
bind:items={node.properties.options} bind:items={node.properties.values}
disabled={node.properties.options.length === 0} disabled={node.properties.values.length === 0}
clearable={false} clearable={false}
on:change on:change
on:select on:select

View File

@@ -6,16 +6,28 @@
export let widget: WidgetLayout | null = null; export let widget: WidgetLayout | null = null;
let node: ComfySliderNode | null = null; let node: ComfySliderNode | null = null;
let nodeValue: Writable<number> | null = null; let nodeValue: Writable<number> | null = null;
let propsChanged: Writable<boolean> | null = null;
let option: number | null = null; let option: number | null = null;
$: if(widget) { $: widget && setNodeValue(widget);
node = widget.node
function setNodeValue(widget: WidgetLayout) {
if (widget) {
node = widget.node as ComfySliderNode
nodeValue = node.value; nodeValue = node.value;
updateOption(); // don't react on option propsChanged = node.propsChanged;
setOption($nodeValue); // don't react on option
}
}; };
function updateOption() { $: if ($propsChanged && nodeValue !== null) {
option = get(nodeValue); setOption($nodeValue)
setNodeValue(widget)
node.properties = node.properties
}
function setOption(value: any) {
option = value;
} }
function onRelease(e: Event) { function onRelease(e: Event) {