mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-02 20:22:08 +00:00
c246e23a2f38eb83bdbf7ffdbaea6f0cb9d1ccb6
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bb96e3c95c |
fix: resolve subgraph promoted widget panel regressions (#10648)
## Summary Fix four bugs in the subgraph promoted widget panel where linked promotions were not distinguished from independent ones, causing incorrect UI state in both the SubgraphEditor (Settings) panel and the Parameters tab WidgetActions menu. ## Changes - **What**: Add `isLinkedPromotion` helper to correctly identify widgets driven by subgraph input connections. Fix `disambiguatingSourceNodeId` lookup mismatch that broke `isWidgetShownOnParents` and `handleHideInput` for non-nested promoted widgets. Replace fragile CSS icon selectors with `data-testid` attributes. ## Bugs fixed Companion fix PR for #10502 (red-green test PR). All 4 E2E tests from #10502 now pass: | Bug | Root cause | Fix | |-----|-----------|-----| | Linked promoted widgets have hide toggle enabled | `SubgraphEditor` only checked `node.id === -1` (physical) — linked promotions from subgraph input connections were not detected | Added `isLinkedPromotion` helper that checks `input._widget` bindings; `SubgraphNodeWidget` `:is-physical` prop now covers both physical and linked cases | | Linked promoted widgets show eye icon instead of link icon | Same root cause as above — `isPhysical` prop was only true for `node.id === -1` | Extended the `:is-physical` condition to include `isLinkedPromotion` check | | Widget labels show raw names instead of renamed values | `SubgraphEditor` passed `widget.name` instead of `widget.label \|\| widget.name` | Changed `:widget-name` binding to prefer `widget.label` | | WidgetActions menu shows Hide/Show for linked promotions | `v-if="hasParents"` didn't exclude linked promotions | Added `canToggleVisibility` computed that combines `hasParents` with `!isLinked` check via `isLinkedPromotion` | ### Additional bugs discovered and fixed | Bug | Root cause | Fix | |-----|-----------|-----| | "Show input" always displayed instead of "Hide input" for promoted widgets | `SectionWidgets.isWidgetShownOnParents` used `getSourceNodeId(widget)` which falls back to `widget.sourceNodeId` when `disambiguatingSourceNodeId` is undefined — this mismatches the promotion store key (`undefined`) | Changed to `widget.disambiguatingSourceNodeId` directly | | "Hide input" click does nothing | `WidgetActions.handleHideInput` used `getSourceNodeId(widget)` for the same reason — `demote()` couldn't find the entry to remove | Same fix — use `widget.disambiguatingSourceNodeId` directly | ## Tests added ### E2E (Playwright) — `browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts` | Test | What it verifies | |------|-----------------| | linked promoted widgets have hide toggle disabled | All toggle buttons in SubgraphEditor shown section are disabled for linked widgets (covers 1-level and 2-level nested promotions via `subgraph-nested-promotion` fixture) | | linked promoted widgets show link icon instead of eye icon | Link icons appear for linked widgets, no eye icons present | | widget labels display renamed values instead of raw names | `widget.label` is displayed when set, not `widget.name` | | linked promoted widget menu should not show Hide/Show input | Three-dot menu on Parameters tab omits Hide/Show options for linked promotions, Rename is still available | ### Unit (Vitest) — `src/core/graph/subgraph/promotionUtils.test.ts` 7 tests covering `isLinkedPromotion`: basic matching, negative cases, nested subgraph with `disambiguatingSourceNodeId`, multiple inputs, and mixed linked/independent state. ### Unit (Vitest) — `src/components/rightSidePanel/parameters/WidgetActions.test.ts` - Added `isSubgraphNode: () => false` to mock nodes to prevent crash from new `isLinked` computed ## Review Focus - `isLinkedPromotion` reads `input._widget` (WeakRef-backed, non-reactive) directly in the template. This is intentional — `_widget` bindings are set during subgraph initialization before the user opens the panel, so stale reads don't occur in practice. A computed-based approach was attempted but reverted because `_widget` changes cannot trigger Vue reactivity. - `getSourceNodeId` removal in `SectionWidgets` and `WidgetActions` is intentional — the old fallback (`?? widget.sourceNodeId`) caused key mismatches with the promotion store for non-nested widgets. ## Screenshots Before <img width="723" height="935" alt="image" src="https://github.com/user-attachments/assets/09862578-a0d1-45b4-929c-f22f7494ebe2" /> After <img width="999" height="952" alt="image" src="https://github.com/user-attachments/assets/ed8fe604-6b44-46b9-a315-6da31d6b405a" /> |
||
|
|
4d57c41fdb |
test: subgraph integration contracts and expanded Playwright coverage (#10123)
## Summary Add integration contract tests (unit) and expanded Playwright coverage for subgraph promotion, hydration, navigation, and lifecycle edge behaviors. ## Changes - **What**: 22 unit/integration tests across 9 files covering promotion store sync, widget view lifecycle, input link resolution, pseudo-widget cache, navigation viewport restore, and subgraph operations. 13 Playwright E2E tests covering proxyWidgets hydration stability, promoted source removal cleanup, pseudo-preview unpack/remove, multi-link representative round-trip, nested promotion retarget, and navigation state on workflow switch. - **Helpers**: Added `isPseudoPreviewEntry`, `getPseudoPreviewWidgets`, `getNonPreviewPromotedWidgets` to promotedWidgets helper. Added `SubgraphHelper.getNodeCount()`. ## Review Focus - Test-only PR — no production code changes - Validates existing subgraph behaviors are covered by regression tests before further feature work - Phase 4 (unit/integration contracts) and Phase 5 (Playwright expansion) of the subgraph test coverage plan ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10123-test-subgraph-integration-contracts-and-expanded-Playwright-coverage-3256d73d365081258023e3a763859e00) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: GitHub Action <action@github.com> |
||
|
|
ef4e4a69d5 |
fix: enable enforce-consistent-class-order tailwind lint rule (#9428)
## Summary Enable `better-tailwindcss/enforce-consistent-class-order` lint rule and auto-fix all 1027 violations across 263 files. Stacked on #9427. ## Changes - **What**: Sort Tailwind classes into consistent order via `eslint --fix` - Enable `enforce-consistent-class-order` as `'error'` in eslint config - Purely cosmetic reordering — no behavioral or visual changes ## Review Focus Mechanical auto-fix PR — all changes are class reordering only. This is the largest diff but lowest risk since it changes no class names, only their order. **Stack:** #9417 → #9427 → **this PR** Fixes #9300 (partial — 3 of 3 rules) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9428-fix-enable-enforce-consistent-class-order-tailwind-lint-rule-31a6d73d3650811c9065f5178ba3e724) by [Unito](https://www.unito.io) |
||
|
|
f00a65ced0 |
feat: add collapse/expand all toggle to right panel tabs (#9333)
## Summary Adds a collapse/expand all toggle button to all parameter and error tabs in the right side panel, letting users quickly collapse or expand all accordion sections at once. ## Changes - **New component**: `CollapseToggleButton.vue` — a reusable icon button (list-collapse / list-tree icon) with tooltip, bound via `v-model` - **Error tab**: Toggle collapses/expands all error groups; per-group state is now managed through `isSectionCollapsed` / `setSectionCollapsed` helpers - **Nodes tab** (`TabNodes.vue`): Per-node `collapseMap`; toggle overrides per-section state; defaults to collapsed (nodes tab default was already collapsed) - **Normal inputs tab** (`TabNormalInputs.vue`): Per-node `collapseMap` + `advancedCollapsed`; toggle covers both normal and advanced sections; defaults to collapsed when multiple nodes selected - **Subgraph inputs tab** (`TabSubgraphInputs.vue`): Toggle covers both main and advanced inputs sections - **Global parameters tab** (`TabGlobalParameters.vue`): Toggle bound to single `SectionWidgets` - **i18n**: Added `g.collapseAll` and `g.expandAll` keys ## Review Focus - `isAllCollapsed` getter in each tab: reads from the same per-section state so the toggle accurately reflects current state rather than being independently tracked - `TabNormalInputs`: multi-node selection default collapse behaviour is preserved through `isSectionCollapsed` fallback logic ## Screenshots <img width="778" height="643" alt="image" src="https://github.com/user-attachments/assets/04d07f32-5135-47f9-b029-78ca78a996fb" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9333-feat-add-collapse-expand-all-toggle-to-right-panel-tabs-3176d73d36508123ba22d6e81983bb1b) by [Unito](https://www.unito.io) |
||
|
|
c25f9a0e93 |
feat: synthetic widgets getter for SubgraphNode (proxy-widget-v2) (#8856)
## Summary Replace the Proxy-based proxy widget system with a store-driven architecture where `promotionStore` and `widgetValueStore` are the single sources of truth for subgraph widget promotion and widget values, and `SubgraphNode.widgets` is a synthetic getter composing lightweight `PromotedWidgetView` objects from store state. ## Motivation The subgraph widget promotion system previously scattered state across multiple unsynchronized layers: - **Persistence**: `node.properties.proxyWidgets` (tuples on the LiteGraph node) - **Runtime**: Proxy-based `proxyWidget.ts` with `Overlay` objects, `DisconnectedWidget` singleton, and `isProxyWidget` type guards - **UI**: Each Vue component independently calling `parseProxyWidgets()` via `customRef` hacks - **Mutation flags**: Imperative `widget.promoted = true/false` set on `subgraph-opened` events This led to 4+ independent parsings of the same data, complex cache invalidation, and no reactive contract between the promotion state and the rendering layer. Widget values were similarly owned by LiteGraph with no Vue-reactive backing. The core principle driving these changes: **Vue owns truth**. Pinia stores are the canonical source; LiteGraph objects delegate to stores via getters/setters; Vue components react to store state directly. ## Changes ### New stores (single sources of truth) - **`promotionStore`** — Reactive `Map<NodeId, PromotionEntry[]>` tracking which interior widgets are promoted on which SubgraphNode instances. Graph-scoped by root graph ID to prevent cross-workflow state collision. Replaces `properties.proxyWidgets` parsing, `customRef` hacks, `widget.promoted` mutation, and the `subgraph-opened` event listener. - **`widgetValueStore`** — Graph-scoped `Map<WidgetKey, WidgetState>` that is the canonical owner of widget values. `BaseWidget.value` delegates to this store via getter/setter when a node ID is assigned. Eliminates the need for Proxy-based value forwarding. ### Synthetic widgets getter (SubgraphNode) `SubgraphNode.widgets` is now a getter that reads `promotionStore.getPromotions(rootGraphId, nodeId)` and returns cached `PromotedWidgetView` objects. No stubs, no Proxies, no fake widgets persisted in the array. The setter is a no-op — mutations go through `promotionStore`. ### PromotedWidgetView A class behind a `createPromotedWidgetView` factory, implementing the `PromotedWidgetView` interface. Delegates value/type/options/drawing to the resolved interior widget and stores. Owns positional state (`y`, `computedHeight`) for canvas layout. Cached by `PromotedWidgetViewManager` for object-identity stability across frames. ### DOM widget promotion Promoted DOM widgets (textarea, image upload, etc.) render on the SubgraphNode surface via `positionOverride` in `domWidgetStore`. `DomWidgets.vue` checks for overrides and uses the SubgraphNode's coordinates instead of the interior node's. ### Promoted previews New `usePromotedPreviews` composable resolves image/audio/video preview widgets from promoted entries, enabling SubgraphNodes to display previews of interior preview nodes. ### Deleted - `proxyWidget.ts` (257 lines) — Proxy handler, `Overlay`, `newProxyWidget`, `isProxyWidget` - `DisconnectedWidget.ts` (39 lines) — Singleton Proxy target - `useValueTransform.ts` (32 lines) — Replaced by store delegation ### Key architectural changes - `BaseWidget.value` getter/setter delegates to `widgetValueStore` when node ID is set - `LGraph.add()` reordered: `node.graph` assigned before widget `setNodeId` (enables store registration) - `LGraph.clear()` cleans up graph-scoped stores to prevent stale entries across workflow switches - `promotionStore` and `widgetValueStore` state nested under root graph UUID for multi-workflow isolation - `SubgraphNode.serialize()` writes promotions back to `properties.proxyWidgets` for persistence compatibility - Legacy `-1` promotion entries resolved and migrated on first load with dev warning ## Test coverage - **3,700+ lines of new/updated tests** across 36 test files - **Unit**: `promotionStore.test.ts`, `widgetValueStore.test.ts`, `promotedWidgetView.test.ts` (921 lines), `subgraphNodePromotion.test.ts`, `proxyWidgetUtils.test.ts`, `DomWidgets.test.ts`, `PromotedWidgetViewManager.test.ts`, `usePromotedPreviews.test.ts`, `resolvePromotedWidget.test.ts`, `subgraphPseudoWidgetCache.test.ts` - **E2E**: `subgraphPromotion.spec.ts` (622 lines) — promote/demote, manual/auto promotion, paste preservation, seed control augmentation, image preview promotion; `imagePreview.spec.ts` extended with multi-promoted-preview coverage - **Fixtures**: 2 new subgraph workflow fixtures for preview promotion scenarios ## Review focus - Graph-scoped store keying (`rootGraphId`) — verify isolation across workflows/tabs and cleanup on `LGraph.clear()` - `PromotedWidgetView` positional stability — `_arrangeWidgets` writes to `y`/`computedHeight` on cached objects; getter returns fresh array but stable object references - DOM widget position override lifecycle — overrides set on promote, cleared on demote/removal/subgraph navigation - Legacy `-1` entry migration — resolved and written back on first load; unresolvable entries dropped with dev warning - Serialization round-trip — `promotionStore` state → `properties.proxyWidgets` on serialize, hydrated back on configure ## Diff breakdown (excluding lockfile) - 153 files changed, ~7,500 insertions, ~1,900 deletions (excluding pnpm-lock.yaml churn) - ~3,700 lines are tests - ~300 lines deleted (proxyWidget.ts, DisconnectedWidget.ts, useValueTransform.ts) <!-- Fixes #ISSUE_NUMBER --> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8856-feat-synthetic-widgets-getter-for-SubgraphNode-proxy-widget-v2-3076d73d365081c7b517f5ec7cb514f3) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: GitHub Action <action@github.com> |
||
|
|
b2c8ea3e50 | refactor: use emit events pattern instead of mutating props in widget components (#8549) | ||
|
|
b1b2fd8a4f |
feat: right side panel favorites, no selection state, and more... (#7812)
Most of the features in this pull request are completed and can be
reviewed and merged.
## TODO
- [x] no selection panel
- [x] group selected panel
- [x] tabs
- [x] favorites tab
- [x] global settings tab
- [x] nodes tab
- [x] widget actions menu
- [x] [Bug]: style bugs
- [x] button zoom to the node on canvas.
- [x] rename widgets on widget actions
- [ ] [Bug]: the canvas has not been updated after renaming.
- [x] global settings
- [ ] setting item: "show advanced parameters"
- blocked by other things. skip for now.
- [x] setting item: show toolbox on selection
- [x] setting item: nodes 2.0
- [ ] setting item: "background color"
- blocked by other things. skip for now.
- [x] setting item: grid spacing
- [x] setting item: snap nodes to grid
- [x] setting item: link shape
- [x] setting item: show connected links
- [x] form style reuses the form style of node widgets
- [x] group node cases
- [x] group node settings
- [x] show all nodes in group
- [x] show frame name on nodes when multiple selections are made
- [x] group multiple selections
- [x] [Bug]: nodes without widgets cannot display the location and their
group
- [x] [Bug]: labels layout
- [x] favorites
- [x] the indicator on widgets
- [x] favorite and unfavorite buttons on widgets
- [x] [Bug]: show node name in favorite widgets + improve labels layout
- [ ] [Bug]: After canceling the like, the like list will not be updated
immediately.
- [x] [Bug]: The favorite function does not work for the project on
Subgraph.
- [x] subgraph
- [x] add the node name from where this parameter comes from when node
is subgraph
- [x] show and hide directly on Inputs
- [x] some bugs need to be fixed.
- [x] advanced widgets
- [x] button: show advanced inputs
- Clicking button expands the "Advanced Inputs" section on the right
side panel, regardless of whether the panel is open or not
- [x] [Bug]: style bugs
- [x] advanced inputs section when node is subgraph
- [x] inputs tab rearranging
- [x] favorited inputs rearranging
- [x] subgraph inputs rearranging
- [ ] review and reconstruction to improve complexity and architecture
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7812-feat-right-side-panel-favorites-no-selection-state-and-more-2da6d73d36508134b503d676f9b3d248)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: bymyself <cbyrne@comfy.org>
|