mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-03 06:47:33 +00:00
[feat] Add dynamic pricing for API nodes with real-time updates (#3963)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import _ from 'lodash'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
|
||||
import { useNodePricing } from '@/composables/node/useNodePricing'
|
||||
import { useComputedWithWidgetWatch } from '@/composables/node/useWatchWidget'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useExtensionStore } from '@/stores/extensionStore'
|
||||
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
|
||||
@@ -111,10 +112,15 @@ export const useNodeBadge = () => {
|
||||
node.badges.push(() => badge.value)
|
||||
|
||||
if (node.constructor.nodeData?.api_node && showApiPricingBadge.value) {
|
||||
const price = nodePricing.getNodeDisplayPrice(node)
|
||||
// Always add the badge for API nodes, with or without price text
|
||||
const creditsBadge = computed(() => {
|
||||
// Use dynamic background color based on the theme
|
||||
// Get the pricing function to determine if this node has dynamic pricing
|
||||
const pricingConfig = nodePricing.getNodePricingConfig(node)
|
||||
const hasDynamicPricing =
|
||||
typeof pricingConfig?.displayPrice === 'function'
|
||||
|
||||
let creditsBadge
|
||||
const createBadge = () => {
|
||||
const price = nodePricing.getNodeDisplayPrice(node)
|
||||
|
||||
const isLightTheme =
|
||||
colorPaletteStore.completedActivePalette.light_theme
|
||||
return new LGraphBadge({
|
||||
@@ -137,7 +143,24 @@ export const useNodeBadge = () => {
|
||||
? adjustColor('#8D6932', { lightness: 0.5 })
|
||||
: '#8D6932'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (hasDynamicPricing) {
|
||||
// For dynamic pricing nodes, use computed that watches widget changes
|
||||
const relevantWidgetNames = nodePricing.getRelevantWidgetNames(
|
||||
node.constructor.nodeData?.name
|
||||
)
|
||||
|
||||
const computedWithWidgetWatch = useComputedWithWidgetWatch(node, {
|
||||
widgetNames: relevantWidgetNames,
|
||||
triggerCanvasRedraw: true
|
||||
})
|
||||
|
||||
creditsBadge = computedWithWidgetWatch(createBadge)
|
||||
} else {
|
||||
// For static pricing nodes, use regular computed
|
||||
creditsBadge = computed(createBadge)
|
||||
}
|
||||
|
||||
node.badges.push(() => creditsBadge.value)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
85
src/composables/node/useWatchWidget.ts
Normal file
85
src/composables/node/useWatchWidget.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { LGraphNode } from '@comfyorg/litegraph'
|
||||
import { computedWithControl } from '@vueuse/core'
|
||||
import { type ComputedRef, ref } from 'vue'
|
||||
|
||||
import { useChainCallback } from '@/composables/functional/useChainCallback'
|
||||
|
||||
export 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)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user