mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-12 08:50:17 +00:00
## Summary Refactor node footer from absolute overlay to inline flow layout, fixing the selection bounding box not encompassing footer buttons and collapsed node dimensions. ## Background The node footer (Enter Subgraph, Advanced, Error buttons) was rendered as an absolute overlay (`absolute top-full`) outside the node body. This caused: 1. **Selection bounding box** did not include footer height — the dashed multi-select border cut through footer buttons 2. **Footer offset compensation** required 3 hardcoded computed classes (`footerStateOutlineBottomClass`, `footerRootBorderBottomClass`, `footerResizeHandleBottomClass`) with magic pixel values (31px, 35px, etc.) that had to stay in sync with CSS ## Solution: Inline Footer with `isolate -z-1` The footer is moved into normal document flow (no longer `absolute top-full`). The key challenge was keeping the footer visually behind the body's rounded bottom edge (the "tuck under" effect) without adding `z-index` to the body — because adding `z-index` to the body creates a stacking context that traps slot connection dots, making them appear behind overlay borders. The solution uses CSS `isolation: isolate` combined with `-z-1` on the footer wrapper: - **`isolate`** creates an independent stacking context for the footer, so internal z-index (Error button `z-10` above Enter button) does not leak to the parent - **`-z-1`** places the entire footer behind the body (`z-index: auto`), achieving the visual overlap without touching the body's stacking behavior - **Slot dots remain free** — the body has no explicit z-index, so slots participate in the root stacking context and are never trapped behind overlay borders This eliminates all 3 footer offset computed classes and their hardcoded pixel values. ## Selection Box: `min-height` on root + unified size path Moving `min-h-(--node-height)` from the body (`node-inner-wrapper`) to the root element makes the footer height naturally included in `node.size` via ResizeObserver → layoutStore → litegraph sync. This means `boundingRect` is automatically correct for expanded nodes — no callbacks or overrides needed. For collapsed nodes, a pre-existing issue (since v1.40) caused `_collapsed_width` to fall back to `NODE_COLLAPSED_WIDTH = 80px` because Vue nodes lack a canvas context for text measurement. The fix lets collapsed dimensions flow through the **same** `batchUpdateNodeBounds` path as expanded nodes — no parallel data structure, no separate accessor, no cache: 1. ResizeObserver writes the collapsed DOM dimensions to `layoutStore.size` via `batchUpdateNodeBounds` 2. `useLayoutSync` syncs `layoutStore.size` → `liteNode.size` as it does for any other size change 3. The expanded size survives the collapse→expand round trip via CSS custom properties — the `isCollapsed` watcher in `LGraphNode.vue` swaps `--node-width` to `--node-width-x` on collapse and restores it on expand 4. `measure()` reads `this.size` directly for Vue collapsed nodes via a one-line gate: `if (!this.flags?.collapsed || LiteGraph.vueNodesMode)`. Legacy behavior is unchanged. ## Changes - **NodeFooter.vue**: `absolute top-full` overlay → inline flow with `isolate -z-1` wrappers, Error/Enter button layering via `-mr-5` + DOM order, reactive props destructuring, static `RADIUS_CLASS` lookup for Tailwind scanning, Vue 3.3+ `defineEmits` property syntax - **LGraphNode.vue**: Move `min-h-(--node-height)` from body to root; remove `footerStateOutlineBottomClass`, `footerRootBorderBottomClass`, `footerResizeHandleBottomClass`, `hasFooter` computed; replace dynamic `beforeShapeClass` interpolation with static `bypassOverlayClass`/`mutedOverlayClass` computeds for Tailwind scanning - **LGraphNode.ts**: `measure()` collapsed branch gated by `|| LiteGraph.vueNodesMode` — Vue mode defers to `this.size`; legacy path unchanged - **useVueNodeResizeTracking.ts**: Collapsed and expanded nodes both flow through `batchUpdateNodeBounds`; narrowed `useVueElementTracking` parameter from `MaybeRefOrGetter<string>` to `string`; `deferredElements.delete(element)` on unmount to prevent memory retention - **selectionBorder.ts**: Unchanged — `createBounds` just works because `boundingRect` is now correct - **12 parameterized E2E tests**: Vue mode (subgraph/regular × expanded/collapsed × bottom-left/bottom-right) + legacy mode (expanded/collapsed × bottom-left/bottom-right), driven by `keyboard.collapse()` (Alt+C) - **Unit tests**: `measure()` branching (legacy fallback, Vue `this.size` usage, expanded parity) - **Shared test helpers**: `repositionNodes`, `KeyboardHelper.collapse`, `measureSelectionBounds`, `assertSelectionEncompassesNodes` ## Review Focus - `isolate -z-1` CSS layering pattern — is this acceptable long-term? - `measure()` collapsed branch gated on `LiteGraph.vueNodesMode` — one-line gate to avoid the canvas-ctx-less fallback in Vue mode - Footer button overlap design (`-mr-5` with DOM order for painting) ## Screenshots <img width="1392" height="800" alt="image" src="https://github.com/user-attachments/assets/abaebff5-bb8c-4b5b-8734-8d44fdee4cb9" /> <img width="1493" height="872" alt="image" src="https://github.com/user-attachments/assets/6b9c77f9-e3ae-4d4e-81dc-acfa9a24c768" /> <img width="813" height="515" alt="image" src="https://github.com/user-attachments/assets/ce15bafb-e157-408c-971b-a650088f316a" /> <img width="1031" height="669" alt="image" src="https://github.com/user-attachments/assets/20fdc336-4bc2-4d47-ab7e-c0cbcee0d150" /> <img width="753" height="525" alt="image" src="https://github.com/user-attachments/assets/2dccbe31-7d18-49bc-9ed4-158b1659fddf" /> <img width="730" height="370" alt="image" src="https://github.com/user-attachments/assets/ab87edfa-a4b4-46f7-86ae-4965a4509b42" /> <img width="1132" height="465" alt="image" src="https://github.com/user-attachments/assets/54643f5b-4a31-4c3d-9475-c433f87aedb0" /> <img width="1102" height="449" alt="image" src="https://github.com/user-attachments/assets/9c045df3-e1f5-481e-b1cb-ead1db1626f5" /> --------- Co-authored-by: github-actions <github-actions@github.com>
117 lines
2.4 KiB
JSON
117 lines
2.4 KiB
JSON
{
|
|
"id": "selection-bbox-test",
|
|
"revision": 0,
|
|
"last_node_id": 3,
|
|
"last_link_id": 1,
|
|
"nodes": [
|
|
{
|
|
"id": 2,
|
|
"type": "e5fb1765-9323-4548-801a-5aead34d879e",
|
|
"pos": [300, 200],
|
|
"size": [400, 200],
|
|
"flags": {},
|
|
"order": 0,
|
|
"mode": 0,
|
|
"inputs": [
|
|
{
|
|
"name": "positive",
|
|
"type": "CONDITIONING",
|
|
"link": null
|
|
}
|
|
],
|
|
"outputs": [
|
|
{
|
|
"name": "LATENT",
|
|
"type": "LATENT",
|
|
"links": [1]
|
|
}
|
|
],
|
|
"properties": {},
|
|
"widgets_values": []
|
|
},
|
|
{
|
|
"id": 3,
|
|
"type": "EmptyLatentImage",
|
|
"pos": [800, 200],
|
|
"size": [400, 200],
|
|
"flags": {},
|
|
"order": 1,
|
|
"mode": 0,
|
|
"inputs": [
|
|
{
|
|
"name": "latent",
|
|
"type": "LATENT",
|
|
"link": 1
|
|
}
|
|
],
|
|
"outputs": [
|
|
{
|
|
"name": "LATENT",
|
|
"type": "LATENT",
|
|
"links": null
|
|
}
|
|
],
|
|
"properties": {},
|
|
"widgets_values": [512, 512, 1]
|
|
}
|
|
],
|
|
"links": [[1, 2, 0, 3, 0, "LATENT"]],
|
|
"groups": [],
|
|
"definitions": {
|
|
"subgraphs": [
|
|
{
|
|
"id": "e5fb1765-9323-4548-801a-5aead34d879e",
|
|
"version": 1,
|
|
"state": {
|
|
"lastGroupId": 0,
|
|
"lastNodeId": 1,
|
|
"lastLinkId": 0,
|
|
"lastRerouteId": 0
|
|
},
|
|
"revision": 0,
|
|
"config": {},
|
|
"name": "Test Subgraph",
|
|
"inputNode": {
|
|
"id": -10,
|
|
"bounding": [100, 200, 120, 60]
|
|
},
|
|
"outputNode": {
|
|
"id": -20,
|
|
"bounding": [500, 200, 120, 60]
|
|
},
|
|
"inputs": [
|
|
{
|
|
"id": "c5cc99d8-a2b6-4bf3-8be7-d4949ef736cd",
|
|
"name": "positive",
|
|
"type": "CONDITIONING",
|
|
"linkIds": [],
|
|
"pos": { "0": 200, "1": 220 }
|
|
}
|
|
],
|
|
"outputs": [
|
|
{
|
|
"id": "9bd488b9-e907-4c95-a7a4-85c5597a87af",
|
|
"name": "LATENT",
|
|
"type": "LATENT",
|
|
"linkIds": [],
|
|
"pos": { "0": 520, "1": 220 }
|
|
}
|
|
],
|
|
"widgets": [],
|
|
"nodes": [],
|
|
"groups": [],
|
|
"links": [],
|
|
"extra": {}
|
|
}
|
|
]
|
|
},
|
|
"config": {},
|
|
"extra": {
|
|
"ds": {
|
|
"scale": 1,
|
|
"offset": [0, 0]
|
|
}
|
|
},
|
|
"version": 0.4
|
|
}
|