mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-26 09:19:43 +00:00
## Summary Promoted primitive subgraph inputs (String, Int) render their link anchor at the header position instead of the widget row. Renaming subgraph input labels breaks the match entirely, causing connections to detach from their widgets visually. ## Changes - **What**: Fix widget-input slot positioning for promoted subgraph inputs in both LiteGraph and Vue (Nodes 2.0) rendering modes - `_arrangeWidgetInputSlots`: Removed Vue mode branch that skipped setting `input.pos`. Promoted widget inputs aren't rendered as `<InputSlot>` Vue components (NodeSlots filters them out), so `input.pos` is the only position fallback - `drawConnections`: Added pre-pass to arrange nodes with unpositioned widget-input slots before link rendering. The background canvas renders before the foreground canvas calls `arrange()`, so positions weren't set on the first frame - `SubgraphNode`: Sync `input.widget.name` with the display name on label rename and initial setup. The `IWidgetLocator` name diverged from `PromotedWidgetView.name` after rename, breaking all name-based slot↔widget matching (`_arrangeWidgetInputSlots`, `getWidgetFromSlot`, `getSlotFromWidget`) ## Review Focus - The `_arrangeWidgetInputSlots` rewrite iterates `_concreteInputs` directly instead of building a spread-copy map — simpler and avoids the stale index issue - `input.widget.name` is now kept in sync with the display name (`input.label ?? subgraphInput.name`). This is a semantic shift from using the raw internal name, but it's required for all name-based matching to work after renames. The value is overwritten on deserialize by `_setWidget` anyway - The `_widget` fallback in `_arrangeWidgetInputSlots` is a safety net for edge cases where the name still doesn't match (e.g., stale cache) Fixes #9998 ## Screenshots <img width="847" height="476" alt="Screenshot 2026-03-17 at 3 05 32 PM" src="https://github.com/user-attachments/assets/38f10563-f0bc-44dd-a1a5-f4a7832575d0" /> <img width="804" height="471" alt="Screenshot 2026-03-17 at 3 05 23 PM" src="https://github.com/user-attachments/assets/3237a7ee-f3e5-4084-b330-371def3415bd" /> <img width="974" height="571" alt="Screenshot 2026-03-17 at 3 05 16 PM" src="https://github.com/user-attachments/assets/cafdca46-8d9b-40e1-8561-02cbb25ee8f2" /> <img width="967" height="558" alt="Screenshot 2026-03-17 at 3 05 06 PM" src="https://github.com/user-attachments/assets/fc03ce43-906c-474d-b3bc-ddf08eb37c75" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10195-fix-subgraph-promoted-widget-input-slot-positions-after-label-rename-3266d73d365081dfa623dd94dd87c718) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: jaeone94 <jaeone.prt@gmail.com>
87 lines
3.2 KiB
TypeScript
87 lines
3.2 KiB
TypeScript
import { expect } from '@playwright/test'
|
|
|
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
|
import { getTextSlotPosition } from '../helpers/subgraphTestUtils'
|
|
|
|
test.describe(
|
|
'Subgraph promoted widget-input slot position',
|
|
{ tag: '@subgraph' },
|
|
() => {
|
|
test('Promoted text widget slot is positioned at widget row, not header', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(
|
|
'subgraphs/subgraph-with-promoted-text-widget'
|
|
)
|
|
|
|
// Render a few frames so arrange() runs
|
|
await comfyPage.nextFrame()
|
|
await comfyPage.nextFrame()
|
|
|
|
const result = await getTextSlotPosition(comfyPage.page, '11')
|
|
expect(result).not.toBeNull()
|
|
expect(result!.hasPos).toBe(true)
|
|
|
|
// The slot Y position should be well below the title area.
|
|
// If it's near 0 or negative, the slot is stuck at the header (the bug).
|
|
expect(result!.posY).toBeGreaterThan(result!.titleHeight)
|
|
})
|
|
|
|
test('Slot position remains correct after renaming subgraph input label', async ({
|
|
comfyPage
|
|
}) => {
|
|
await comfyPage.workflow.loadWorkflow(
|
|
'subgraphs/subgraph-with-promoted-text-widget'
|
|
)
|
|
await comfyPage.nextFrame()
|
|
await comfyPage.nextFrame()
|
|
|
|
// Verify initial position is correct
|
|
const before = await getTextSlotPosition(comfyPage.page, '11')
|
|
expect(before).not.toBeNull()
|
|
expect(before!.hasPos).toBe(true)
|
|
expect(before!.posY).toBeGreaterThan(before!.titleHeight)
|
|
|
|
// Navigate into subgraph and rename the text input
|
|
const subgraphNode = await comfyPage.nodeOps.getNodeRefById('11')
|
|
await subgraphNode.navigateIntoSubgraph()
|
|
|
|
const initialLabel = await comfyPage.page.evaluate(() => {
|
|
const graph = window.app!.canvas.graph
|
|
if (!graph || !('inputNode' in graph)) return null
|
|
const textInput = graph.inputs?.find(
|
|
(i: { type: string }) => i.type === 'STRING'
|
|
)
|
|
return textInput?.label || textInput?.name || null
|
|
})
|
|
|
|
if (!initialLabel)
|
|
throw new Error('Could not find STRING input in subgraph')
|
|
|
|
await comfyPage.subgraph.rightClickInputSlot(initialLabel)
|
|
await comfyPage.contextMenu.clickLitegraphMenuItem('Rename Slot')
|
|
await comfyPage.nextFrame()
|
|
|
|
const dialog = '.graphdialog input'
|
|
await comfyPage.page.waitForSelector(dialog, { state: 'visible' })
|
|
await comfyPage.page.fill(dialog, '')
|
|
await comfyPage.page.fill(dialog, 'my_custom_prompt')
|
|
await comfyPage.page.keyboard.press('Enter')
|
|
await comfyPage.page.waitForSelector(dialog, { state: 'hidden' })
|
|
|
|
// Navigate back to parent graph
|
|
await comfyPage.subgraph.exitViaBreadcrumb()
|
|
|
|
// Verify slot position is still at the widget row after rename
|
|
const after = await getTextSlotPosition(comfyPage.page, '11')
|
|
expect(after).not.toBeNull()
|
|
expect(after!.hasPos).toBe(true)
|
|
expect(after!.posY).toBeGreaterThan(after!.titleHeight)
|
|
|
|
// widget.name is the stable identity key — it does NOT change on rename.
|
|
// The display label is on input.label, read via PromotedWidgetView.label.
|
|
expect(after!.widgetName).not.toBe('my_custom_prompt')
|
|
})
|
|
}
|
|
)
|