mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-26 09:19:43 +00:00
Move price badges to python nodes (#7816)
## Summary Backend part: https://github.com/Comfy-Org/ComfyUI/pull/11582 - Move API node pricing definitions from hardcoded frontend functions to backend-defined JSONata expressions - Add `price_badge` field to node definition schema containing JSONata expression and dependency declarations - Implement async JSONata evaluation with signature-based caching for efficient reactive updates - Show one decimal in credit badges when meaningful (e.g., 1.5 credits instead of 2 credits) ## Screenshots (if applicable) <!-- Add screenshots or video recording to help explain your changes --> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7816-Move-price-badges-to-python-nodes-2da6d73d365081ec815ef61f7e3c65f7) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -99,6 +99,7 @@ import { computed, onErrorCaptured, ref, toValue, watch } from 'vue'
|
||||
import EditableText from '@/components/common/EditableText.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
import { useNodePricing } from '@/composables/node/useNodePricing'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import { st } from '@/i18n'
|
||||
import { LGraphEventMode, RenderShape } from '@/lib/litegraph/src/litegraph'
|
||||
@@ -183,9 +184,67 @@ const statusBadge = computed((): NodeBadgeProps | undefined =>
|
||||
: undefined
|
||||
)
|
||||
|
||||
const nodeBadges = computed<NodeBadgeProps[]>(() =>
|
||||
[...(nodeData?.badges ?? [])].map(toValue)
|
||||
// Use per-node pricing revision to re-compute badges only when this node's pricing updates
|
||||
const {
|
||||
getRelevantWidgetNames,
|
||||
hasDynamicPricing,
|
||||
getInputGroupPrefixes,
|
||||
getInputNames,
|
||||
getNodeRevisionRef
|
||||
} = useNodePricing()
|
||||
// Cache pricing metadata (won't change during node lifetime)
|
||||
const isDynamicPricing = computed(() =>
|
||||
nodeData?.apiNode ? hasDynamicPricing(nodeData.type) : false
|
||||
)
|
||||
const relevantPricingWidgets = computed(() =>
|
||||
nodeData?.apiNode ? getRelevantWidgetNames(nodeData.type) : []
|
||||
)
|
||||
const inputGroupPrefixes = computed(() =>
|
||||
nodeData?.apiNode ? getInputGroupPrefixes(nodeData.type) : []
|
||||
)
|
||||
const relevantInputNames = computed(() =>
|
||||
nodeData?.apiNode ? getInputNames(nodeData.type) : []
|
||||
)
|
||||
const nodeBadges = computed<NodeBadgeProps[]>(() => {
|
||||
// For ALL API nodes: access per-node revision ref to detect when async pricing evaluation completes
|
||||
// This is needed even for static pricing because JSONata 2.x evaluation is async
|
||||
if (nodeData?.apiNode && nodeData?.id != null) {
|
||||
// Access per-node revision ref to establish dependency (each node has its own ref)
|
||||
void getNodeRevisionRef(nodeData.id).value
|
||||
|
||||
// For dynamic pricing, also track widget values and input connections
|
||||
if (isDynamicPricing.value) {
|
||||
// Access only the widget values that affect pricing
|
||||
const relevantNames = relevantPricingWidgets.value
|
||||
if (relevantNames.length > 0) {
|
||||
nodeData?.widgets?.forEach((w) => {
|
||||
if (relevantNames.includes(w.name)) w.value
|
||||
})
|
||||
}
|
||||
// Access input connections for regular inputs
|
||||
const inputNames = relevantInputNames.value
|
||||
if (inputNames.length > 0) {
|
||||
nodeData?.inputs?.forEach((inp) => {
|
||||
if (inp.name && inputNames.includes(inp.name)) {
|
||||
void inp.link // Access link to create reactive dependency
|
||||
}
|
||||
})
|
||||
}
|
||||
// Access input connections for input_groups (e.g., autogrow inputs)
|
||||
const groupPrefixes = inputGroupPrefixes.value
|
||||
if (groupPrefixes.length > 0) {
|
||||
nodeData?.inputs?.forEach((inp) => {
|
||||
if (
|
||||
groupPrefixes.some((prefix) => inp.name?.startsWith(prefix + '.'))
|
||||
) {
|
||||
void inp.link // Access link to create reactive dependency
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...(nodeData?.badges ?? [])].map(toValue)
|
||||
})
|
||||
const isPinned = computed(() => Boolean(nodeData?.flags?.pinned))
|
||||
const isApiNode = computed(() => Boolean(nodeData?.apiNode))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user