Files
ComfyUI_frontend/src/composables/node/useWatchWidget.ts
Alexander Brown 874ef3ba0c Lint: Add eslint import plugin (#5955)
## Summary

Adds the linter, turns on the recommended and a few extra rules, fixes
existing violations.

Doesn't prohibit `../../...` imports yet, that'll be it's own PR.

## Changes

- **What**: Consistent and fixable imports
- **Dependencies**: The plugin and parser

## Review Focus

How do you feel about the recommended rules?
What about the extra ones?
[Any
more](https://github.com/un-ts/eslint-plugin-import-x?tab=readme-ov-file#rules)
you'd want to turn on?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5955-Lint-Add-eslint-import-plugin-2856d73d3650819985c0fb9ca3fa94b0)
by [Unito](https://www.unito.io)
2025-10-07 20:31:00 -07:00

110 lines
3.4 KiB
TypeScript

import { computedWithControl } from '@vueuse/core'
import { ref } from 'vue'
import type { ComputedRef } from 'vue'
import { useChainCallback } from '@/composables/functional/useChainCallback'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
interface UseComputedWithWidgetWatchOptions {
/**
* Names of widgets to observe for changes.
* If not provided, all widgets will be observed.
*/
widgetNames?: string[]
/**
* Whether to trigger a canvas redraw when widget values change.
* @default false
*/
triggerCanvasRedraw?: boolean
}
/**
* A composable that creates a computed that has a node's widget values as a dependencies.
* Essentially `computedWithControl` (https://vueuse.org/shared/computedWithControl/) where
* the explicitly defined extra dependencies are LGraphNode widgets.
*
* @param node - The LGraphNode whose widget values are to be watched
* @param options - Configuration options for the watcher
* @returns A function to create computed that responds to widget changes
*
* @example
* ```ts
* const computedWithWidgetWatch = useComputedWithWidgetWatch(node, {
* widgetNames: ['width', 'height'],
* triggerCanvasRedraw: true
* })
*
* const dynamicPrice = computedWithWidgetWatch(() => {
* return calculatePrice(node)
* })
* ```
*/
export const useComputedWithWidgetWatch = (
node: LGraphNode,
options: UseComputedWithWidgetWatchOptions = {}
) => {
const { widgetNames, triggerCanvasRedraw = false } = options
// Create a reactive trigger based on widget values
const widgetValues = ref<Record<string, any>>({})
// Initialize widget observers
if (node.widgets) {
const widgetsToObserve = widgetNames
? node.widgets.filter((widget) => widgetNames.includes(widget.name))
: node.widgets
// Initialize current values
const currentValues: Record<string, any> = {}
widgetsToObserve.forEach((widget) => {
currentValues[widget.name] = widget.value
})
widgetValues.value = currentValues
widgetsToObserve.forEach((widget) => {
widget.callback = useChainCallback(widget.callback, () => {
// Update the reactive widget values
widgetValues.value = {
...widgetValues.value,
[widget.name]: widget.value
}
// Optionally trigger a canvas redraw
if (triggerCanvasRedraw) {
node.graph?.setDirtyCanvas(true, true)
}
})
})
if (widgetNames && widgetNames.length > widgetsToObserve.length) {
//Inputs have been included
const indexesToObserve = widgetNames
.map((name) =>
widgetsToObserve.some((w) => w.name == name)
? -1
: node.inputs.findIndex((i) => i.name == name)
)
.filter((i) => i >= 0)
node.onConnectionsChange = useChainCallback(
node.onConnectionsChange,
(_type: unknown, index: number, isConnected: boolean) => {
if (!indexesToObserve.includes(index)) return
widgetValues.value = {
...widgetValues.value,
[indexesToObserve[index]]: isConnected
}
if (triggerCanvasRedraw) {
node.graph?.setDirtyCanvas(true, true)
}
}
)
}
}
// Returns a function that creates a computed that responds to widget changes.
// The computed will be re-evaluated whenever any observed widget changes.
return <T>(computeFn: () => T): ComputedRef<T> => {
return computedWithControl(widgetValues, computeFn)
}
}