refactor(api): ✨ add reactive API change notifications

- introduced MTB_API_CHANGED_EVENT custom event for triggering panel updates
- added notifyAPIChanged() function dispatched on API modifications
- setup event listener in APIPanel constructor to debounce updates via requestAnimationFrame
- improved boolean value handling to recognize numeric 1/'1' as true
- ensures UI stays in sync when API properties change across extension, widget manager, and panel
This commit is contained in:
Mel Massadian
2025-12-07 22:58:57 +01:00
parent e90a7048fb
commit ea6d5d4e59
5 changed files with 36 additions and 4 deletions
+3 -2
View File
@@ -59,17 +59,18 @@
/>
{/if}
{:else if item.type === 'BOOLEAN'}
{@const isChecked = value === true || value === 'true' || value === 1 || value === '1'}
<label class="mtb-toggle">
<input
type="checkbox"
checked={value === true || value === 'true'}
checked={isChecked}
onchange={(e) => {
value = e.currentTarget.checked
onInput(e)
}}
/>
<span class="mtb-toggle-slider"></span>
<span class="mtb-toggle-label">{value ? 'On' : 'Off'}</span>
<span class="mtb-toggle-label">{isChecked ? 'On' : 'Off'}</span>
</label>
{:else if item.type === 'COLOR'}
<ColorPicker
+3 -1
View File
@@ -6,7 +6,7 @@
import { app } from '@/scripts/app'
import * as shared from '@mtb/shared'
import { apiSettingsWidgetManager } from './widget-manager'
import { getAPIPanel } from './panel.svelte'
import { getAPIPanel, notifyAPIChanged } from './panel.svelte'
import type { MTBNode } from './types'
interface ContextMenuItem {
@@ -79,6 +79,7 @@ export function registerMtbApiExtension(): void {
} else {
node.setProperty('useAPI', true)
}
notifyAPIChanged()
},
}
options.push(markApiItem)
@@ -97,6 +98,7 @@ export function registerMtbApiExtension(): void {
apiSettingsWidgetManager.applySettings(node, { isAPIOutput: true })
node.setProperty('useAPI', true)
}
notifyAPIChanged()
},
}
options.push(markOutputItem)
+1 -1
View File
@@ -19,5 +19,5 @@ export { API_COLOR, OUTPUT_COLOR } from './constants'
// Re-export core modules
export { graphToPrompt } from './graph-to-prompt'
export { APISettingsWidgetManager, apiSettingsWidgetManager } from './widget-manager'
export { APIPanel, getAPIPanel } from './panel.svelte'
export { APIPanel, getAPIPanel, notifyAPIChanged, MTB_API_CHANGED_EVENT } from './panel.svelte'
export { registerMtbApiExtension } from './extension'
+25
View File
@@ -30,6 +30,14 @@ function createPanelProps() {
return props
}
/** Custom event for API changes */
export const MTB_API_CHANGED_EVENT = 'mtb:api:changed'
/** Dispatch event to notify panel of changes */
export function notifyAPIChanged(): void {
window.dispatchEvent(new CustomEvent(MTB_API_CHANGED_EVENT))
}
/**
* Controls the API panel UI for managing exposed workflow inputs
*/
@@ -37,9 +45,26 @@ export class APIPanel {
private component: ReturnType<typeof mount> | null = null
private props = createPanelProps()
private sidebarMode = false
private listening = false
constructor() {
// Don't mount automatically - wait for renderInto or show
this.setupChangeListener()
}
/**
* Listen for API changes and update panel reactively
*/
private setupChangeListener(): void {
if (this.listening) return
this.listening = true
window.addEventListener(MTB_API_CHANGED_EVENT, () => {
// Debounce updates slightly
requestAnimationFrame(() => {
this.updateContent()
})
})
}
/**
+4
View File
@@ -8,6 +8,7 @@ import { $el } from '@/scripts/ui'
import * as shared from '@mtb/shared'
import { API_COLOR, OUTPUT_COLOR } from './constants'
import { API_INPUT_TYPES, type APIInputType, type APINodeSettings, type MTBNode } from './types'
import { notifyAPIChanged } from './panel.svelte'
import CSS from './api_nodes.css?inline'
declare const LiteGraph: {
@@ -199,6 +200,7 @@ export class APISettingsWidgetManager {
})
contentContainer.classList.add('mtb_api_disabled')
}
notifyAPIChanged()
})
const title = document.createElement('span')
@@ -236,6 +238,7 @@ export class APISettingsWidgetManager {
this.applySettings(node, {
inputs: { [widget.name]: { type: typeSelect.value as APIInputType } },
})
notifyAPIChanged()
})
// Name input
@@ -252,6 +255,7 @@ export class APISettingsWidgetManager {
this.applySettings(node, {
inputs: { [widget.name]: { name: nameInput.value } },
})
notifyAPIChanged()
})
const separator = document.createElement('hr')