Trying to fix prop updates reflected in widgets
This commit is contained in:
Submodule litegraph updated: 23242ca3d7...ac3621c4bd
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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,21 +88,49 @@ 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)) {
|
||||||
const node = this.graph._nodes_by_id[link.target_id]
|
if (link) { // can be undefined if the link is removed
|
||||||
if (node) {
|
const node = this.graph._nodes_by_id[link.target_id]
|
||||||
const input = node.inputs[link.target_slot]
|
if (node) {
|
||||||
if (input && "config" in input)
|
const input = node.inputs[link.target_slot]
|
||||||
this.clampOneConfig(input as IComfyInputSlot)
|
if (input && "config" in input) {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
nodeValue = node.value;
|
function setNodeValue(widget: WidgetLayout) {
|
||||||
updateOption(); // don't react on option
|
if (widget) {
|
||||||
|
node = widget.node as ComfySliderNode
|
||||||
|
nodeValue = node.value;
|
||||||
|
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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user