fix(inspector): sync widget value changes and improve callback handling
This commit is contained in:
+8
-6
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false
|
||||
}
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"plugins": ["prettier-plugin-svelte"]
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
let {
|
||||
item = { type: 'NUMBER' },
|
||||
onInput = (e: Event) => console.log(e),
|
||||
onInput = (e: Event, val?: unknown) => console.log(e, val),
|
||||
id = undefined,
|
||||
value = $bindable(''),
|
||||
} = $props()
|
||||
@@ -59,14 +59,16 @@
|
||||
/>
|
||||
{/if}
|
||||
{:else if item.type === 'BOOLEAN'}
|
||||
{@const isChecked = value === true || value === 'true' || value === 1 || value === '1'}
|
||||
{@const isChecked = value === true || value === 'true' || value === 1 || value === '1' || value === 'on'}
|
||||
<label class="mtb-toggle">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isChecked}
|
||||
onchange={(e) => {
|
||||
value = e.currentTarget.checked
|
||||
onInput(e)
|
||||
// ComfyUI toggles use booleans - 'off' string is truthy so doesn't work!
|
||||
const newValue = e.currentTarget.checked
|
||||
value = newValue
|
||||
onInput(e, newValue)
|
||||
}}
|
||||
/>
|
||||
<span class="mtb-toggle-slider"></span>
|
||||
|
||||
@@ -31,7 +31,18 @@
|
||||
} = $props()
|
||||
|
||||
let actions = $state<Record<string, Action>>({})
|
||||
let value = $state<unknown>(item.value ?? item.widgets?.[0]?.value ?? '')
|
||||
|
||||
// Derive value from widget - updates when widget value changes
|
||||
const getWidgetValue = () => item.widgets?.[0]?.value ?? item.value ?? ''
|
||||
let value = $state<unknown>(getWidgetValue())
|
||||
|
||||
// Sync value from widget when it changes (Node → Panel)
|
||||
$effect(() => {
|
||||
const widgetVal = getWidgetValue()
|
||||
if (widgetVal !== value) {
|
||||
value = widgetVal
|
||||
}
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
actions = {
|
||||
@@ -68,14 +79,18 @@
|
||||
})
|
||||
|
||||
const onInput = (e: Event | null, val?: unknown) => {
|
||||
if (!inComfy()) {
|
||||
console.log(`Not in comfy ${(e?.target as HTMLInputElement)?.value}`)
|
||||
return
|
||||
}
|
||||
if (!inComfy()) return
|
||||
if (item.widgets) {
|
||||
for (let i = 0; i < item.widgets.length; i++) {
|
||||
const w = item.widgets[i]
|
||||
w.value = val ?? (e?.target as HTMLInputElement)?.value
|
||||
for (const w of item.widgets as Array<{
|
||||
value: unknown
|
||||
callback?: (value: unknown) => void
|
||||
options?: { callback?: (value: unknown) => void }
|
||||
}>) {
|
||||
const newVal = val ?? (e?.target as HTMLInputElement)?.value
|
||||
w.value = newVal
|
||||
// Trigger widget callback to notify ComfyUI of the change
|
||||
w.callback?.(newVal)
|
||||
w.options?.callback?.(newVal)
|
||||
}
|
||||
// @ts-expect-error - app is global in ComfyUI
|
||||
window.app.canvas.setDirty(true)
|
||||
|
||||
@@ -11,6 +11,9 @@ import { API_INPUT_TYPES, type APIInputType, type APINodeSettings, type MTBNode
|
||||
import { notifyAPIChanged } from './panel.svelte'
|
||||
import CSS from './api_nodes.css?inline'
|
||||
|
||||
/** Symbol to store original callbacks on widgets */
|
||||
const ORIGINAL_CALLBACK = Symbol('mtb_original_callback')
|
||||
|
||||
declare const LiteGraph: {
|
||||
NODE_TITLE_HEIGHT: number
|
||||
NODE_COLLAPSED_RADIUS: number
|
||||
@@ -20,6 +23,51 @@ declare const LiteGraph: {
|
||||
* Manages the creation and lifecycle of API settings widgets on nodes
|
||||
*/
|
||||
export class APISettingsWidgetManager {
|
||||
/**
|
||||
* Wraps widget callbacks to notify panel of value changes
|
||||
*/
|
||||
private wrapWidgetCallbacks(node: MTBNode): void {
|
||||
if (!node.widgets) return
|
||||
|
||||
for (const widget of node.widgets) {
|
||||
if (widget.name === 'apiSettings') continue
|
||||
|
||||
const w = widget as IWidget & {
|
||||
callback?: (...args: unknown[]) => void
|
||||
[ORIGINAL_CALLBACK]?: (...args: unknown[]) => void
|
||||
}
|
||||
|
||||
// Skip if already wrapped
|
||||
if (w[ORIGINAL_CALLBACK]) continue
|
||||
|
||||
// Store original and wrap
|
||||
w[ORIGINAL_CALLBACK] = w.callback
|
||||
w.callback = (...args: unknown[]) => {
|
||||
w[ORIGINAL_CALLBACK]?.(...args)
|
||||
notifyAPIChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores original widget callbacks
|
||||
*/
|
||||
private unwrapWidgetCallbacks(node: MTBNode): void {
|
||||
if (!node.widgets) return
|
||||
|
||||
for (const widget of node.widgets) {
|
||||
const w = widget as IWidget & {
|
||||
callback?: (...args: unknown[]) => void
|
||||
[ORIGINAL_CALLBACK]?: (...args: unknown[]) => void
|
||||
}
|
||||
|
||||
if (w[ORIGINAL_CALLBACK]) {
|
||||
w.callback = w[ORIGINAL_CALLBACK]
|
||||
delete w[ORIGINAL_CALLBACK]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the API settings DOM widget for a node
|
||||
*/
|
||||
@@ -57,6 +105,9 @@ export class APISettingsWidgetManager {
|
||||
hideOnZoom: false,
|
||||
getHeight: () => element.children.length * 80,
|
||||
})
|
||||
|
||||
// Wrap widget callbacks to sync Node → Panel
|
||||
this.wrapWidgetCallbacks(node)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +169,9 @@ export class APISettingsWidgetManager {
|
||||
;(apiSettings as IWidget & { onRemoved?: () => void }).onRemoved?.()
|
||||
;(apiSettings as IWidget & { onRemove?: () => void }).onRemove?.()
|
||||
node.widgets = node.widgets?.filter((w) => w.name !== 'apiSettings')
|
||||
|
||||
// Restore original widget callbacks
|
||||
this.unwrapWidgetCallbacks(node)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user