fix(subgraphEditor): exclude promoted widgets from candidate list

The candidate filter compared `toKey()` outputs across the active
section (PromotedWidgetView, key includes `:sourceNodeId` suffix) and
the interior section (raw widget, no suffix). The shapes never matched,
so every link-promoted widget kept showing in the Hidden section as
`icon-eye/eye-off`. Compare on the interior `(node.id, widget.name)`
identity instead.

Update the Properties panel test to demote a link-promoted widget via
the source-node context menu (since linked promotions render
`icon-link` and disable the editor toggle by design).

Amp-Thread-ID: https://ampcode.com/threads/T-019e2260-c2ba-70b4-9962-1638be4bf645
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
DrJKL
2026-05-13 15:58:02 -07:00
parent d699ce603e
commit e9db30bfd3
2 changed files with 15 additions and 7 deletions

View File

@@ -667,11 +667,12 @@ test('Properties panel operations @vue-nodes', async ({ comfyPage }) => {
)
})
await editor.togglePromotion(subgraphNode, {
nodeName: 'KSampler',
widgetName: 'steps',
toState: false
})
// Link-promoted widgets render `icon-link` (disabled toggle) in the editor;
// demotion happens via the source-node context menu inside the subgraph.
await comfyPage.vueNodes.enterSubgraph('2')
const ksampler = await comfyPage.vueNodes.getFixtureByTitle('KSampler')
await comfyPage.subgraph.unpromoteWidget(ksampler.root, 'steps')
await comfyPage.subgraph.exitViaBreadcrumb()
await expect(steps, 'Un-promote widget').toBeHidden()
})

View File

@@ -127,9 +127,16 @@ const interiorWidgets = computed<WidgetItem[]>(() => {
const candidateWidgets = computed<WidgetItem[]>(() => {
const node = activeNode.value
if (!node) return []
// An interior widget is "already promoted" iff some active row's source
// identity matches it. PromotedWidgetView carries `sourceNodeId`; preview
// exposures are stored as the interior `[node, widget]` tuple directly.
const promotedSourceKeys = new Set(
activeWidgets.value.map(
([n, w]) => `${getSourceNodeId(w) ?? String(n.id)}:${getWidgetName(w)}`
)
)
return interiorWidgets.value.filter(
(item: WidgetItem) =>
!activeWidgets.value.some((active) => toKey(active) === toKey(item))
([n, w]) => !promotedSourceKeys.has(`${n.id}:${w.name}`)
)
})
const filteredCandidates = computed<WidgetItem[]>(() => {