mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-24 06:35:10 +00:00
defd48b49cfd0ad04269c44a0e595ddd29316fde
1823 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
defd48b49c |
fix: add rel="noopener noreferrer" to privacy-policy links opened in new tab
Addresses CodeRabbit review on PR #12415. Hardens the three external privacy-policy anchors with target="_blank" to prevent the opened page from accessing window.opener. |
||
|
|
741be80956 |
fix: redirect /privacy to /privacy-policy on comfy.org
/privacy was 404'ing because the page lives at /privacy-policy. Add a 308 redirect in vercel.json and fix four hardcoded /privacy links (and the browser test asserting the broken URL) to point at /privacy-policy directly. |
||
|
|
551c595bbb |
Remove template vram sorting (#12414)
With the dynamic vram changes, vram is both much more difficult to measure, and much less useful of a metric. To prevent confusion, it has been removed as a metric. See also: #9074 |
||
|
|
a50b3d16da |
Persist splash until graph load completes (#12387)
When an app mode workflow is opened on fresh page load, either from a template url, or a persisted in browser cache, the UI would briefly display the graph view prior to swapping to app mode. This is fixed by continuing to display the splash screen until workflow state has loaded. Share by url brings unique difficulties. The function call does not return until a user has responded to a dialogue. If the splash screen were blocked by this, the user would never be able to see the dialogue. Consequentially, this change is not applied to shared workflow urls and the (very unlikely) url including both a template url and a share url will now prioritize the template url. A best effort e2e test is included, but is a little clunky. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12387-Persist-splash-until-graph-load-completes-3666d73d3650813495e4ccad6052c1e4) by [Unito](https://www.unito.io) |
||
|
|
52d77e6ee0 |
chore: upgrade sparkjs to 2.x and three to 0.184 (#12396)
## Summary - Spark 2.x requires SparkRenderer in scene tree; add it in SceneManager and protect it in clearModel so model reloads don't dispose the splat renderer. - three 0.184 OrbitControls listens on ownerDocument; drop redundant pointermove/up .stop in Load3D containers so the document listener can receive events. - Narrow Texture.image type for 0.184 strict typing. |
||
|
|
f1f65cff61 |
feat: select top asset widget FormDropdown result on Enter (#12209)
## Summary Allow asset/media FormDropdown searches to select the top filtered result when the user presses Enter. This covers image, video, audio, mesh, model-like asset selects, and other `WidgetSelectDropdown`-backed media widgets. ## Implementation Scope This PR implements a **top-result Enter shortcut** for the custom asset/media dropdown path only: - In scope: `WidgetSelectDropdown` -> `FormDropdown` asset/media widgets. - In scope: while the dropdown is open, single-select, and the search text is non-empty, the first current search result becomes the Enter candidate. - In scope: pressing Enter in the search input selects that candidate/top result through the existing selection path. - In scope: candidate feedback for this shortcut, including visual candidate styling and a polite screen-reader announcement for the current top result. - In scope: stale async search protection, empty-query/no-result no-op behavior, multi-select guard behavior, and focus return to the trigger after Enter selection closes the menu. - Out of scope: plain combo widgets (`WidgetSelectDefault` / `SelectPlus`). That path is PrimeVue-based and should be handled separately from this focused asset-widget PR. - Out of scope: full combobox/listbox keyboard navigation, including Tab-to-list focus, ArrowUp/ArrowDown candidate movement, Home/End behavior, scroll-to-active-item behavior, and a full ARIA combobox/listbox refactor. Follow-up arrow-key navigation should validate the interaction model separately. This PR keeps the candidate state narrow and localized so that future work can either extend it into movable active-item state or replace it as part of a fuller combobox/listbox implementation. ## Changes - **What**: Added an explicit Enter event from `FormSearchInput`, routed it through the FormDropdown menu actions, and selected the current top search result in `FormDropdown`. - **What**: Kept the existing `computedAsync` + debounced filtering path for normal typing, while Enter performs a one-off search against the latest input before selecting. Stale async Enter results are ignored if the query or item source changes before resolution. - **What**: Prevented closed FormDropdown state from treating the full unfiltered list as current search results, limited Enter-to-select to single-select dropdowns, and made empty search Enter a no-op. - **What**: Returned focus to the dropdown trigger after single-select selection closes the menu. - **What**: Added candidate styling for the first current FormDropdown result while a search query is active so the Enter target is visible to users. - **What**: Added a polite screen-reader announcement for the current top result candidate. - **What**: Fixed the FormDropdownMenuActions `baseModelSelected` model default to use a `Set` factory instead of a shared instance. - **What**: Added unit coverage for the search Enter event, FormDropdown selection behavior, focus return, debounce/Enter behavior, stale async Enter protection, empty-query no-op behavior, closed-state stale result protection, multi-select guard behavior, and candidate announcement behavior. Added App Mode E2E coverage for asset FormDropdown Enter selection. - **What**: Extracted reusable app-mode dropdown fixture helpers and updated the existing FormDropdown clipping test to use the shared helper. ## Review Focus Please focus review on the asset/media FormDropdown path, especially `getTopSearchResult()`, the single-select/empty-query guards, stale async search protection, trigger focus return after selection, and candidate feedback in grid/list layouts. The plain combo path and full arrow-key navigation are intentionally left for separate follow-up work. ## Screenshots (if applicable) https://github.com/user-attachments/assets/3eb3456d-93a3-4959-91a3-188f8116ccc9 Validation performed: - Latest final-commit validation: - `pnpm test:unit src/renderer/extensions/vueNodes/widgets/components/form/FormSearchInput.test.ts src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdown.test.ts src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenuActions.test.ts src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenu.test.ts` - Commit hook: `pnpm exec stylelint ...`, `pnpm exec oxfmt --write ...`, `pnpm exec oxlint --type-aware --fix ...`, `pnpm exec eslint --cache --fix ...`, `pnpm typecheck` - Push hook: `pnpm knip --cache` - `git diff --check` - Earlier branch validation for this flow: - `pnpm install` - `pnpm typecheck:browser` - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:5173 PLAYWRIGHT_SETUP_API_URL=http://localhost:8188 pnpm test:browser -- --project=chromium browser_tests/tests/appMode.spec.ts -g "Drag and Drop|FormDropdown search Enter selects the top filtered item" --reporter=list` - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:5173 PLAYWRIGHT_SETUP_API_URL=http://localhost:8188 pnpm test:browser -- --project=chromium browser_tests/tests/appMode.spec.ts -g "FormDropdown search Enter selects the top filtered item" --reporter=list` - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:5173 PLAYWRIGHT_SETUP_API_URL=http://localhost:8188 pnpm test:browser -- --project=chromium browser_tests/tests/appModeDropdownClipping.spec.ts -g "FormDropdown popup is not clipped" --reporter=list` |
||
|
|
2717d59451 |
Fix reactivity of vue subgraph price badges (#12029)
When a subgraph contains partner nodes with price badges, those badges are also displayed on the subgraphNode. The reactivity here was spotty: The price badges would fail to display unless the user had navigated into the subgraph on the current page load. Fixing this is performed in 2 steps: - Firing a `node:property:changed` event when the badges contained in a subgraph are updated - Extending the reactivity updates so that badges update in vue mode despite using the litegraph badge getter. This PR also includes a minor styling tweak to fix text alignment on price badges | Before | After | | ------ | ----- | | <img width="360" alt="before" src="https://github.com/user-attachments/assets/56a95cbe-12c9-43b0-8664-34e52b6415ac" /> | <img width="360" alt="after" src="https://github.com/user-attachments/assets/bf4a0d81-21e4-4afc-946e-eba5967f1715" />| Resolves FE-346 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12029-Fix-reactivity-of-vue-subgraph-price-badges-3586d73d3650813cb12fe265090940e4) by [Unito](https://www.unito.io) |
||
|
|
cd2f4677c2 |
FE-719 feat(load3d): add FBX export support (#12323)
## Summary implement fbx export, using our own lib fbx-exporter-three ## Screenshots (if applicable) https://github.com/user-attachments/assets/80012338-d065-4a00-a9a0-0a2e73d67db4 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12323-FE-719-feat-load3d-add-FBX-export-support-3656d73d365081ef901ffe880ae9568a) by [Unito](https://www.unito.io) |
||
|
|
cfd3f9e67b |
fix: classify PLY assets as 3D media (#12319)
## Summary Classify `.ply` as 3D media so PLY outputs are surfaced by queue/assets preview flows. ## Changes - **What**: adds `.ply` to shared 3D extension detection and falls back to the asset `/view` URL when opening 3D assets without `preview_url`. - **Breaking**: none. - **Dependencies**: none. ## Review Focus - This is the tactical FE fix for FE-129; it intentionally does not solve the broader 3D media vs load3d-loadable split. - Assets sidebar 3D viewer still prefers `preview_url`, but now has a usable fallback for assets that only have the normal asset URL. FE-129 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12319-fix-classify-PLY-assets-as-3D-media-3646d73d365081218f0bde401b1601bd) by [Unito](https://www.unito.io) |
||
|
|
3b37488eee |
fix: keep node context menu overflow visible when content fits (#12035)
## Summary Stops the Shape submenu (and any other PrimeVue nested submenu) from being clipped behind the node context menu when the menu fits in the viewport. ## Changes - **What**: `constrainMenuHeight` in `NodeContextMenu.vue` now applies `max-height` + `overflow-y: auto` to the root `<ul>` only when `scrollHeight > availableHeight`. The common case keeps `overflow: visible`. - Added `browser_tests/tests/nodeContextMenuShapeSubmenu.spec.ts` regression spec. ## Review Focus Root cause: setting only `overflow-y: auto` on a `<ul>` coerces `overflow-x` to a non-visible value per CSS spec (`If one of overflow-x/overflow-y is visible and the other isn't, the visible value is computed as auto`). PrimeVue `ContextMenuSub` renders submenus in-tree as a nested `<ul>` with `position: absolute; left: 100%`, so the implicit horizontal clip hides them entirely. The pre-existing overflow scenario (#10824 / #10854) is unchanged — when the menu actually overflows, the clamp still applies and `nodeContextMenuOverflow.spec.ts` continues to verify scroll. Submenu clipping in that overflow case is a known limitation, not introduced by this PR. Fixes FE-570 ## screenshot ### AS IS <img width="788" height="505" alt="Screenshot 2026-05-07 at 12 43 26 PM" src="https://github.com/user-attachments/assets/36d34070-0c57-4385-a130-0394f22f282e" /> ### TO BE <img width="779" height="627" alt="Screenshot 2026-05-07 at 12 42 44 PM" src="https://github.com/user-attachments/assets/00956729-763b-4787-822f-209e8ea42331" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12035-fix-keep-node-context-menu-overflow-visible-when-content-fits-3586d73d365081ad9aaec82f220d401c) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com> |
||
|
|
fc7e6a0935 |
fix(terminal): resync logs console on backend reconnect (#12270)
## Summary When the built-in logs terminal stayed open during a backend restart, the buffer froze on pre-restart entries and live log streaming silently stopped — only closing and reopening the panel resynced. Listen for the api `reconnected` event and rebuild the terminal contents the same way a fresh open would. ## Changes - **What**: - Extract `useLogsTerminal` composable. The SFC is now a thin shell holding `terminal: shallowRef<Terminal>` and forwarding to the composable, so `onMounted`/`onScopeDispose` no longer rely on the child's emit callback timing. - Subscribe to `api`'s `reconnected` event via `useEventListener`, registered synchronously before any awaits. On reconnect: `terminal.reset()` → refetch raw logs → `scrollToBottom()` → `subscribeLogs(true)` (the backend loses the per-client subscription on restart, so re-subscribe is required for live streaming to resume). - Wrap in-flight resync/mount fetches in AbortControllers. Overlapping reconnects abort the prior resync, and unmount mid-fetch suppresses writes to the disposed xterm. - Hide BaseTerminal whenever `errorMessage` is set so the error layout doesn't expose an empty xterm container behind the message; `loading=false` after both load failure and resync success so a later successful reconnect can clear a stuck spinner. - Migrate the load/resync error strings to vue-i18n (`logsTerminal.loadError`, `logsTerminal.resyncError`). ## Review Focus - **Re-subscribe is the non-obvious half of the fix** — without it, even after the WebSocket reconnects the backend never resumes streaming logs to this client because its subscription state was wiped on restart. The visible "stale buffer" is only one symptom; the silent "no new logs" symptom needed the explicit `subscribeLogs(true)` re-call in resync. - `terminal.reset()` lives after a successful raw-logs fetch (not before) so a failed resync leaves the prior buffer visible instead of blanking it; resync errors surface via the same inline error message the mount path uses. - 8 unit tests around the composable: mount + subscribe, resync ordering (reset → write → scroll → subscribe via `invocationCallOrder`), in-flight resync abort on double reconnect, resync error surfacing, mount-failure-then-recovery, unmount-mid-fetch terminal-write suppression, listener cleanup on unmount. - 2 E2E tests using `ws.close()` on the proxied WebSocket as the reconnect trigger and `subscribeLogs` HTTP fetch count as the sync point (same pattern as `wsReconnectStaleJob.spec.ts`). Red-checked: disabling the `reconnected` listener fails exactly the two new tests, all 8 pre-existing tests stay green. Fixes FE-712 ## Screenshots Before - (After rebooting, the console window does not update from its state before the reboot must remount the console window for it to resync.) https://github.com/user-attachments/assets/b1e49c2c-89a4-4a4a-82b4-064412acee12 After - (The console window syncs automatically after a reboot.) https://github.com/user-attachments/assets/54b582c5-ad42-41c0-9886-18f4495859da ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12270-fix-terminal-resync-logs-console-on-backend-reconnect-3606d73d3650812fb13fd1934c632344) by [Unito](https://www.unito.io) |
||
|
|
a97f46b497 |
test: cover job history sidebar with typed route mocks (#12272)
## Summary Add the first product-area browser coverage on top of the merged typed route mock foundation: the docked job history sidebar. ## Changes - **What**: Adds `browser_tests/tests/sidebar/jobHistory.spec.ts` using `jobsRouteFixture`. - **What**: Covers direct sidebar entry, docked QPO history entry, terminal history jobs, active queue jobs, tab filtering, search, clear queue, and clear history. - **What**: Adds typed `POST /api/queue` and `POST /api/history` route helpers that validate request bodies with generated zod schemas. - **What**: Adds stable test ids for the job history sidebar and queue progress overlay so tests avoid structural CSS selectors. - **Dependencies**: Builds on the typed route mock foundation merged in #12267. ## Review Focus Review the product assertions and whether this is the right first coverage slice on top of the typed route mock foundation. This PR intentionally avoids asset sidebar and floating QPO lifecycle coverage; those should remain follow-up PRs. ## Screenshots (if applicable) Not applicable. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12272-test-cover-job-history-sidebar-with-typed-route-mocks-3606d73d3650817481d5f9fac4bfc93c) by [Unito](https://www.unito.io) |
||
|
|
448ad73fae |
refactor(assets): collapse useMediaAssets factory wrapper (FE-727) (#12283)
## Summary https://linear.app/comfyorg/issue/FE-727/refactor-usemediaasset `useMediaAssets` was a factory wrapper that branched on `isCloud` and returned one of two near-identical implementations (`useAssetsApi` / `useInternalFilesApi`). Both implementations delegated to the same `assetsStore` actions (`updateInputs` / `updateHistory` / `loadMoreHistory`) — the real cloud/local fork lives inside `assetsStore.fetchInputFiles` (line 121), not at the composable layer. Collapse the wrapper: 1. Delete `useInternalFilesApi` (identical to `useAssetsApi`). 2. Delete `useMediaAssets` (now a pass-through). 3. Switch the four callers to `useAssetsApi` directly. ## Changes - **What**: - Delete `useInternalFilesApi.ts` and `useMediaAssets.ts`. - `AssetsSidebarTab.vue`, `WidgetSelectDropdown.vue`, `useOutputHistory.ts`: call `useAssetsApi` directly. - `useWidgetSelectItems.ts`: narrow `outputMediaAssets` prop type to `IAssetsProvider` (the interface `useOutputHistory` already implements directly). - Test mocks repointed from `useMediaAssets` → `useAssetsApi`. - Two unrelated tailwind class-order lint errors auto-fixed by `pnpm lint:fix` to keep CI green. - **Breaking**: None — no behavior change. ## Review Focus The real cloud/local fork remains at `assetsStore.ts:121` (`fetchInputFiles = isCloud ? fetchInputFilesFromCloud : fetchInputFilesFromAPI`). That branch is M1-strict and clears once BE-933 lands. This PR only collapses the dead wrapper layer above it. `IAssetsProvider` is intentionally kept — `useOutputHistory.ts:83` directly implements it for a different use case, so the interface still has more than one consumer. Surfaced while working on the FE-678 cloud/local asset branching survey. ## Screenshots (if applicable) N/A — no UI change. --------- Co-authored-by: GitHub Action <action@github.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
0558740c78 |
refactor: migrate default combo widget select to Reka (#12288)
## Summary Migrate the default combo widget select from the PrimeVue `SelectPlus` wrapper to a Reka `Combobox` implementation while preserving the existing Comfy combo widget contract and the node-canvas dropdown behavior. ## Changes - **What**: Rewrites `WidgetSelectDefault.vue` on top of Reka `ComboboxRoot`, `ComboboxTrigger`, `ComboboxInput`, `ComboboxContent`, and `ComboboxItem`. - **What**: Preserves the default combo widget surface: `v-model`, `widget` prop, `aria-label` from the widget name/label, `data-capture-wheel`, disabled state, placeholder/filter placeholder, default slot controls, invalid current value display, array values, dynamic/factory values, and `getOptionLabel` fallback behavior. - **What**: Keeps dynamic `values` compatibility by refreshing function-backed options when the dropdown opens, without re-evaluating the factory on every search keystroke. - **What**: Deletes the now-unused PrimeVue `SelectPlus.vue` wrapper and removes the PrimeVue test plugin/stub path from the default widget select tests. - **What**: Updates App Mode dropdown clipping coverage and combo-widget browser coverage to target the new Reka overlay/viewport structure. - **Breaking**: No breaking change is intended for the documented Comfy combo widget contract. This migration does not preserve incidental PrimeVue `Select` prop pass-through from `widget.options`; that was a side effect of wrapping PrimeVue rather than a stable widget API. - **Dependencies**: No new dependencies. ## Review Focus ### Compatibility choices The goal of this PR is a migration PR, not a broad behavior redesign. The new implementation keeps the Comfy-specific combo contract rather than attempting to emulate PrimeVue internals. In particular: - `values` still accepts arrays and functions, and function values are re-read on open to support dynamic/custom node option sources. - `getOptionLabel(value) || value` is intentionally preserved to match the sibling dropdown path and avoid turning an empty-string label into a blank rendered option. - Invalid/current values that are not present in the option list are still rendered in the trigger instead of disappearing. - `WidgetWithControl` continues to render its default slot in the control area, with trigger text truncation preserved. - App Mode `OverlayAppendToKey='body'` continues to map to a body portal to avoid panel clipping. ### Visual alignment and screenshot updates The previous PrimeVue implementation passed `size="small"`, which injected internal `.p-select-sm .p-select-label` styling. That internal PrimeVue style used its own small-select font size and padding, overriding the surrounding widget sizing intent and making the select trigger subtly taller with slightly larger text than nearby inline node widget controls. The Reka implementation intentionally keeps the normal widget styling path instead of recreating that PrimeVue-specific internal override. This means the trigger follows the same inline widget sizing direction as neighboring controls rather than preserving the incidental PrimeVue height/text-size delta. Because this is an expected visual difference from the migration, the affected E2E screenshots should be recaptured instead of treating the old PrimeVue select height as the target. ### Scrollbar and focus behavior Reka provides the combobox/listbox semantics we want, including search, arrow navigation, highlighted items, and Enter selection. The tricky part is the canvas dropdown scrollbar behavior. The native Reka viewport path hides/owns scrollbar behavior in a way that made it hard to preserve the previous widget dropdown affordances, especially visible scrollbars and mouse wheel capture over the node canvas. To keep the previous behavior, this PR renders a dedicated scrollable viewport inside `ComboboxContent` with the project scrollbar utilities (`scrollbar-thin`, stable gutter, transparent track). That preserves visible scroll affordance and allows wheel events over the dropdown to scroll the list instead of zooming the canvas. There was one Reka interaction to account for: pressing the native scrollbar can be treated as a focus-outside event from the search input, which previously closed the dropdown on mouse down or caused subsequent wheel events to leak back to the canvas. The new `useRestoreFocusOnViewportPointer` composable handles only that short pointer gesture: - viewport pointerdown marks a short-lived scrollbar/viewport interaction, - the next focus-outside event is prevented only if the search input can be restored, - the guard is cleared by `pointerup`, `pointercancel`, and a timeout so normal outside clicks still close the dropdown. ### Tests and regression coverage Unit coverage was updated around the new Reka implementation: - option sources from arrays and functions, - dynamic values refreshed on open but not on each search keystroke, - selection updates and blank/undefined Reka emissions being ignored, - search filtering and Reka keyboard selection behavior, - disabled state, invalid current values, `getOptionLabel`, empty results status, and WidgetWithControl slot preservation, - composable coverage for pointerup, pointercancel, repeated pointerdown listener cleanup, and no-input/no-op behavior. Browser regression coverage now checks the canvas-specific interaction surface: - opening and selecting default combo widget options, - wheel over the dropdown scrolls the list instead of zooming the canvas, - pressing the scrollbar does not close the dropdown, - wheel capture still works after pressing the scrollbar, - opening another node widget closes the previous dropdown, - switching between node widgets preserves dropdown scroll capture, - serialize/reload retains selected combo values. ## Screenshots (if applicable) New <img width="527" height="753" alt="스크린샷 2026-05-18 오전 1 36 27" src="https://github.com/user-attachments/assets/2293d510-6965-4b84-9b12-b8528f8a734f" /> Old <img width="496" height="473" alt="스크린샷 2026-05-18 오전 1 35 57" src="https://github.com/user-attachments/assets/47c0e28a-27df-44a6-81a8-14fcc1f3bd8f" /> Reka Supports Auto highlight top item on search (Search -> Enter -> Select 👍) https://github.com/user-attachments/assets/9d633dfc-c23a-4e7a-8d39-b044c219f1f3 The default combo widget trigger has a small intentional visual delta from the old PrimeVue path because the Reka implementation does not recreate PrimeVue's internal `size="small"` label override. https://github.com/user-attachments/assets/a9053a14-e39e-4d5e-a846-dcf9aeb0caed ## Validation - `pnpm format` - `pnpm lint` (passes; existing warning-only lint output remains in unrelated tests) - `pnpm typecheck` - `pnpm typecheck:browser` - `pnpm test:unit` - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://127.0.0.1:5174 pnpm exec playwright test browser_tests/tests/vueNodes/widgets/combo/comboWidget.spec.ts --project=chromium` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12288-refactor-migrate-default-combo-widget-select-to-Reka-3616d73d365081fd8742c038a7dc7851) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com> Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
058acfe592 |
test: add basic E2E tests for CurveEditor widget (#10730)
## Summary Add Playwright tests covering default rendering, interpolation selector, and click-to-add-point interaction. Includes test workflow asset. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10730-test-add-basic-E2E-tests-for-CurveEditor-widget-3336d73d365081f5a403df837737bc12) by [Unito](https://www.unito.io) |
||
|
|
d955625c20 |
fix(model-library): auto-refresh after upload, plus 'r' key fallback (FE-695) (#12257)
## Summary Model Library sidebar now refreshes automatically when an upload completes, and the `r` keybinding refreshes the library in addition to refreshing combo widgets inside graph nodes. ## Changes - **What**: - `modelStore`: new `refreshModelFolder(name)` for surgical reset+reload of one folder, and `refresh()` that re-loads any folders that had been loaded - `ModelLibrarySidebarTab.vue`: watches `assetDownloadStore.lastCompletedDownload` and refreshes the affected folder; the in-panel refresh button now routes through `refresh()` - `Comfy.RefreshNodeDefinitions` (`r` key): also calls `modelStore.refresh()` so the keyboard fallback actually refreshes the Model Library list ## Review Focus - Both `modelStore` and `assetsStore` exist; the upload wizard was only refreshing the latter, which is what caused the bug. Confirm the new watcher path is the right hook (rather than wiring it inside the wizard) — chose this so it also covers completions that happen after the wizard has been closed. - `refreshModelFolder` falls back to `refresh()` (not raw `loadModelFolders()`) for unknown folder types, to avoid dropping other folders' loaded contents. - Generated tab half of the ticket is intentionally **deferred** until BE-885 (cursor pagination on `GET /api/jobs`) lands — AC items around "no duplicates" and "cursor state maintained" depend on it. Fixes FE-695 (Model Library half). ## Screenshots (if applicable) N/A — behavior change verified by unit tests. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12257-fix-model-library-auto-refresh-after-upload-plus-r-key-fallback-3606d73d3650811a8895ef6e3ef2b4b8) by [Unito](https://www.unito.io) |
||
|
|
d6f632477f |
feat(dialog): migrate Error / NodeSearchBox / SecretForm / VideoHelp / Customization to Reka-UI (Phase 2) (#12109)
## Summary Phase 2 of the dialog migration kicked off in #11719 and continued in #12041. Migrates four medium-complexity dialogs to the Reka-UI primitives. Public API of `useDialogService` / `dialogStore` is unchanged. Parent: [FE-571](https://linear.app/comfyorg/issue/FE-571/dialog-system-migration-primevue-reka-ui-parent) This phase: [FE-574](https://linear.app/comfyorg/issue/FE-574/phase-2-migrate-error-nodesearchbox-secretform-videohelp-customization) Predecessors: #11719 (Phase 0, merged), #12041 (Phase 1, merged) > **NodeSearchBoxPopover deferred** — host of an inner PrimeVue Dialog (filter panel) that teleports to body and conflicts with Reka's DismissableLayer outside-pointer detection (CI dismissed the outer dialog mid-interaction). Tracking as a follow-up PR; FE-574 stays open for it. ## Changes ### `src/services/dialogService.ts` | Call site | Renderer | Size | | --- | --- | --- | | `showExecutionErrorDialog()` | `'reka'` | `lg` | | `showErrorDialog()` | `'reka'` | `lg` | ### `src/components/dialog/content/ErrorDialogContent.vue` - Drops `import Divider from 'primevue/divider'` and `import ScrollPanel from 'primevue/scrollpanel'` - Replaces with `<hr class="border-t border-border-subtle">` + `<div class="h-[400px] w-full max-w-[80vw] overflow-auto">` ### Direct PrimeVue → Reka swaps (no `dialogStore` involvement) | File | Notes | | --- | --- | | `src/components/common/CustomizationDialog.vue` | Reka primitives + DialogTitle/Header/Footer; drops PrimeVue Divider; `:modal="false"` and `pointer-down-outside` overlay guard so the PrimeVue ColorPicker overlay (teleported to body) does not auto-dismiss the dialog | | `src/platform/assets/components/VideoHelpDialog.vue` | Headless Reka content; preserves capture-phase ESC by stopping propagation on `escape-key-down`; `VisuallyHidden` title for a11y | | `src/platform/secrets/components/SecretFormDialog.vue` | Reka primitives, retains `v-model:visible`, autofocus on the provider trigger, form submit/validation | ### Tests - `src/services/dialogService.renderer.test.ts`: extends the regression net to cover both error-dialog call sites (renderer `'reka'`, size `'lg'`) - `src/components/common/CustomizationDialog.test.ts`: swaps PrimeVue Dialog stub for Reka primitive stubs ### screenshot <img width="1236" height="761" alt="Screenshot 2026-05-11 at 10 26 51 PM" src="https://github.com/user-attachments/assets/086cb73f-a98d-41f8-96ee-21922da8dd73" /> <img width="1161" height="786" alt="Screenshot 2026-05-12 at 1 26 39 PM" src="https://github.com/user-attachments/assets/db7383d8-f737-4472-91c0-dab5aa41547b" /> ## Quality gates - [x] `pnpm typecheck` — clean - [x] `pnpm lint` — 0 errors (3 pre-existing warnings unrelated to this PR) - [x] `pnpm format` — applied - [x] `pnpm test:unit` (touched + adjacent areas): - `dialogService.renderer.test.ts` — 5/5 - `CustomizationDialog.test.ts` — 4/4 - All `src/components/dialog` tests — 73/73 - `src/platform/secrets` tests — 39/39 - `NodeBookmarkTreeExplorer.test.ts` — 7/7 - [ ] CI Playwright matrix ## Public API impact None. `useDialogService` / `dialogStore` signatures unchanged. Custom-node extensions calling `app.extensionManager.dialog.*` continue to work. ## Out of scope (later phases) - NodeSearchBoxPopover — follow-up PR under FE-574 - Settings dialog — Phase 3 (FE-575) - Manager dialog — Phase 4 (FE-576) - `ConfirmDialog` callers (`SecretsPanel`, `BaseWorkflowsSidebarTab`) — Phase 5 (FE-577) - Removing PrimeVue `Dialog`/`<style>` overrides in `GlobalDialog.vue` — Phase 6 (FE-578) ## Test plan - [x] Unit: 73/73 dialog-area, 39/39 secrets - [ ] CI: full Vitest + Playwright matrix - [ ] Manual on a backend: - Trigger an execution error → error dialog opens through Reka, scroll body, copy-to-clipboard, close - Add/edit a secret → form submits, validation errors render, ESC and cancel close - Open VideoHelpDialog from `UploadModelFooter` while inside the asset modal → ESC closes only the help dialog - Customize a node bookmark color/icon → apply/reset, color picker overlay works |
||
|
|
1ab63807e9 |
fix: reserve scrollbar gutter on textareas to prevent hover reflow (#12280)
*PR Created by the Glary-Bot Agent* --- Adds the Tailwind 4.3 `scrollbar-gutter-stable` utility to the shared shadcn `Textarea` wrapper and the `ModelInfoPanel` description textarea so the layout no longer shifts when a vertical scrollbar appears on hover or focus. The wrapper edit propagates to all four consumers (`WidgetTextarea`, `WidgetMarkdown`, `ComfyHubDescribeStep`, `ComfyHubCreateProfileForm`). The `ModelInfoPanel` site uses a native `<textarea>` that bypasses the wrapper, so it is fixed inline. The `overflow-hidden hover:overflow-auto` performance pattern from #10804 is intentionally preserved. ## Verification - Typecheck, eslint, oxlint, oxfmt, stylelint clean on changed files - `Textarea.test.ts`, `WidgetTextarea.test.ts`, `WidgetMarkdown.test.ts`, `ComfyHubDescribeStep.test.ts`, `ModelInfoPanel.test.ts` — all 68 tests pass - `pnpm build` succeeds; the production CSS bundle contains the `scrollbar-gutter:stable` rule - Storybook (`UI/Textarea`): confirmed `getComputedStyle(textarea).scrollbarGutter === 'stable'` on Default, WithLabel, and overflowing-content scenarios Fixes FE-697 ## Screenshots   ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12280-fix-reserve-scrollbar-gutter-on-textareas-to-prevent-hover-reflow-3606d73d3650816f9947e20134abf59e) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
f429e1e0c4 |
refactor: promote FormSearchInput to shared ui as AsyncSearchInput (#12185)
## Summary Follow-up to #12183: move the debounced, searcher-driven search input out of `src/renderer/...` and into the shared primitives folder, so both the graph (form dropdown node widget) and the shell UI (templates dialog, right side panel tabs) can use it without crossing the renderer layer. ## Changes - **What**: Renamed and relocated `FormSearchInput` → `AsyncSearchInput` at `src/components/ui/search-input/AsyncSearchInput.vue`, joining the existing `SearchInput` / `SearchAutocomplete` siblings. - **What**: Updated all 9 callers (graph form dropdown, right side panel tabs, templates dialog) to import from the new path/name. Test file moved alongside the component. - **Breaking**: None — pure rename + relocate, behavior is identical. ## Review Focus - New name reflects the component's distinguishing feature (the async `searcher` lifecycle: debounce + cancellation + loading state); see Slack thread. - Slack thread captured the discussion — splitting from #12183 so the perf fix can backport cleanly to the release line. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12185-refactor-promote-FormSearchInput-to-shared-ui-as-AsyncSearchInput-35e6d73d365081c585d8d421ea4353fa) by [Unito](https://www.unito.io) Co-authored-by: Christian Byrne <cbyrne@comfy.org> |
||
|
|
d6b4137eec |
chore: upgrade tailwindcss to 4.3 (#12275)
*PR Created by the Glary-Bot Agent* --- ## Summary Bump `tailwindcss` and `@tailwindcss/vite` from `^4.2.0` to `^4.3.0` in the workspace catalog so the new first-class scrollbar utilities (notably `scrollbar-gutter-stable`) are available — the missing utility behind the FE-697 workaround. Release notes: https://tailwindcss.com/blog/tailwindcss-v4-3. ## Changes - **What**: - `pnpm-workspace.yaml` catalog: `tailwindcss` and `@tailwindcss/vite` → `^4.3.0` (lockfile resolves to `4.3.0` for `tailwindcss`, `@tailwindcss/vite`, `@tailwindcss/node`, and all `@tailwindcss/oxide-*` native packages). - Auto-fix 21 lint errors that `eslint-plugin-better-tailwindcss` now reports because the new utilities have canonical forms: - `VirtualGrid.vue`: `[scrollbar-gutter:stable]` → `scrollbar-gutter-stable` (the FE-697 case) - `VirtualGrid.vue` + `RightSidePanel.vue`: `scrollbar-thin` class-order fix - `TopBarHeader.vue`: `h-6.25 w-6.25` → `size-6.25` (6 button cells) - `Dialogue.vue`: `translate-x-[-50%] translate-y-[-50%]` → `translate-[-50%]` - Minor `-mx-[…]`, `-inset-[…]`, `-ml-[…]` arbitrary-value reorderings in `NodeSearchFilterBar.vue`, `LGraphNode.vue`, `CloudNotificationContent.vue` - All 21 are auto-fixes; all existed on `main` and would have started failing CI on next merge. - **Breaking**: None. v4.3 is purely additive; existing config (CSS-first `@theme`/`@utility`/`@plugin`, custom `lucideStrokePlugin`, `tailwindcss-primeui`, `@iconify/tailwind4`, `tw-animate-css`) is unchanged. ## Review Focus - Confirmed `scrollbar-gutter: stable` compiles into `dist/assets/index-*.css` from `VirtualGrid.vue` after the canonicalization. - Runtime probe via Playwright (preview build) confirmed `scrollbar-gutter-stable`, `scrollbar-thin`, `tab-4`, and `zoom-100` all apply. - `pnpm typecheck`, `pnpm lint`, `pnpm build`, `pnpm knip` all pass. - `pnpm test:unit`: 10687/10696 pass. 1 failure in `GraphView.test.ts` ("reconnect wiring") confirmed to fail identically on `main` (flaky `toHaveBeenCalledTimes(1)` getting `3`/`4`) — unrelated to this change. - Oracle code review: 0 findings. Refs FE-697. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12275-chore-upgrade-tailwindcss-to-4-3-3606d73d3650813185cece1c7315e1c2) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
a3106c4d53 |
fix: open node info panel from context menu (#12205)
## Summary Replaces #12164. Right-clicking a Vue node, using the selection toolbox More Options menu, or clicking the selection toolbox Node Info button now opens the right-side Info tab only when the new-menu UI makes that panel available. Legacy-menu contexts hide the no-op action even when the legacy node library design is selected; node-library help remains isolated to the node library itself. The existing `selection_toolbox_node_info_opened` telemetry fires only after the toolbox button successfully opens node info. No new context-menu telemetry event is added in this PR. ## Changes - **What**: Share the node-info availability/action path across the context menu and selection toolbox, keep legacy-menu state out of the right-side panel public store API, tighten node-info settings tests, and add unit plus E2E regression coverage for new-menu and legacy-menu modes. - **Dependencies**: None ## Review Focus Confirm the node context menu, selection toolbox direct Info button, and selection toolbox More Options entry all respect right-side panel availability, including legacy menu + legacy node library mode, while node-library help behavior remains isolated to the node library. ## Validation - Self-review: checked production path, unit mocks, and Playwright coverage; only gap found was weak E2E coverage for the toolbox direct Info path, now strengthened. - `pnpm test:unit -- src/composables/graph/useSelectionState.test.ts src/components/graph/SelectionToolbox.test.ts src/components/graph/selectionToolbox/InfoButton.test.ts` - `pnpm test:browser:local -- --project=chromium browser_tests/tests/selectionToolboxActions.spec.ts browser_tests/tests/selectionToolboxSubmenus.spec.ts browser_tests/tests/vueNodes/interactions/node/contextMenu.spec.ts --grep "info button opens the right-side info tab|info button is hidden|hides Node Info|should open node info"` - `pnpm typecheck:browser` - `pnpm exec oxlint --type-aware browser_tests/tests/selectionToolboxActions.spec.ts` - `pnpm exec eslint --cache --no-warn-ignored browser_tests/tests/selectionToolboxActions.spec.ts` - `pnpm exec oxfmt --check browser_tests/tests/selectionToolboxActions.spec.ts` - `git diff --check` - Commit hooks: lint-staged + `pnpm typecheck` + `pnpm typecheck:browser` - Push hook: `knip --cache` (existing tag hint only) ## Screenshots (if applicable) Before https://github.com/user-attachments/assets/4b1f6ddb-a01c-4958-81ab-36167f434e59 https://github.com/user-attachments/assets/83433f0d-24f1-46b7-a81d-f0f065812496 After https://github.com/user-attachments/assets/30bd61e5-f8d4-48b7-97e0-26c93e3cb362 https://github.com/user-attachments/assets/afce9f51-a43d-434f-a006-6b357a61ac8f --------- Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
f7ef563b46 |
FE-657: prevent browser zoom on ctrl+wheel in mask editor (#12215)
## Summary Wheel events on the mask editor pointer zone now call preventDefault, matching the main canvas behavior so ctrl+wheel only zooms the mask canvas instead of also triggering page zoom. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12215-FE-657-prevent-browser-zoom-on-ctrl-wheel-in-mask-editor-35f6d73d36508131a9b8dbf2f6640d72) by [Unito](https://www.unito.io) |
||
|
|
9cc09cd46c |
Add additional subgraph test fixtures and tests (#11806)
- Adds functions to SubgraphHelper to perform widget promotion by standard user means - Right Click -> Promote - Properties Panel - Adds new slot fixture code that works with simple `locator.dragTo` operations. - Adds multiple subgraph tests with a focus on historically difficult operations. - Fixes a bug where the litegraph `node.selected` state would not be unset when switching graphs. This made it so 'Selecting a node -> leaving subgraph -> re-enter subgraph -> right click on node' would fail to select the node because it is marked as already selected. ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11806-Add-helper-functions-for-widget-promotion-3536d73d365081f58dd9cd730c1a91a9) by [Unito](https://www.unito.io) --------- Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
86b1e1a965 |
Fix descriptions on core blueprints (#12220)
Core blueprints were storing the description under a different key than expected, which resulted in them displaying a placeholder description. When initializing the description for a subgraph, this alternative field is also checked. | Before | After | | ------ | ----- | | <img width="360" alt="before" src="https://github.com/user-attachments/assets/ed51c4a8-00cf-4927-9cba-880532a9e926" /> | <img width="360" alt="after" src="https://github.com/user-attachments/assets/f19bf80d-adcc-4e9b-a9ba-a5ac8e089e2d" />| Resolves FE-681 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12220-Austin-blueprint-descriptions-35f6d73d3650812fa04df48c203bebd1) by [Unito](https://www.unito.io) |
||
|
|
988d532467 |
fix(queue): contain JobDetailsPopover error message overflow (#12173)
## Summary Cap the Job Details "Error message" block at `max-h-96` (24rem / 384px) with an internal scroll, wrap long unbreakable tokens (filenames, JSON), and preserve newlines so the failed-job popover no longer grows unbounded. ## Changes - **What**: Added `max-h-96 overflow-y-auto whitespace-pre-wrap wrap-break-word` to the error message container in `JobDetailsPopover.vue`, plus a `FailedWithLongError` Storybook story covering the overflow case. ## Review Focus - 24rem cap was set per Alex's spec in the [Slack thread](https://comfy-organization.slack.com/archives/C0A4XMHANP3/p1778506109115989). - `wrap-break-word` (Tailwind 4 canonical of `break-words`) is needed because long underscore-joined filenames don't break naturally; `whitespace-pre-wrap` preserves any newlines in the raw error. - Not in scope: the popover z-index clipping issue Alex flagged later in the same thread — that's a separate follow-up. Fixes FE-660 ## Screenshots **Before** — error block grows unbounded with the panel:  **After** — error block capped at 384px and internally scrollable:  Reproduce locally via Storybook: `pnpm storybook` → Queue → JobDetailsPopover → **FailedWithLongError**. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12173-fix-queue-contain-JobDetailsPopover-error-message-overflow-35e6d73d3650812d9873e5d163cad0c6) by [Unito](https://www.unito.io) --------- Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com> |
||
|
|
9fe19a2afb |
fix(settings): unify settings item heights and use 14px label text (#12180)
## Summary Body text in the settings dialog was still rendering at the inherited 16px (browser default) instead of the 14px design spec, and rows with different control types (toggle, slider, dropdown, radio) collapsed to different heights — making the list look uneven and cramped. ## Changes - **What**: `FormItem` label now uses `text-sm` (14px) and the row enforces `min-h-8` (32px) so toggle/slider/dropdown/radio rows align. `SettingGroup` bumps inter-item margin from `mb-2` to `mb-3` for breathing room between settings. ## Review Focus `FormItem` is also used by `ServerConfigPanel`, so the 14px/32px row also applies there — consistent with the same settings-dialog visual language, but worth a glance. Fixes #FE-525 ## Screenshots Lite Graph panel (1280×900 viewport) showing toggle/slider/dropdown/radio rows side-by-side: | Before (`origin/main`) | After (this PR) | | --- | --- | | <img src="https://raw.githubusercontent.com/Comfy-Org/ComfyUI_frontend/pr-12180-screenshots/before-litegraph.png" width="480"> | <img src="https://raw.githubusercontent.com/Comfy-Org/ComfyUI_frontend/pr-12180-screenshots/after-litegraph.png" width="480"> | Before: label text inherits 16px from `<body>`; toggle-only rows (e.g. "Always snap to grid", "Live selection") shrink to ~24px while dropdown/slider rows stay ~32px, so the list looks uneven and cramped. After: labels are 14px; every row is at least 32px tall so toggles/sliders/dropdowns/radios line up; `mb-3` adds 4px of breathing room between rows. ## References - Linear: https://linear.app/comfyorg/issue/FE-525/verify-settings-text-size-and-item-heights - Figma: https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=6290-75412 - Origin thread: https://comfy-organization.slack.com/archives/C075ANWQ8KS/p1777657610484679?thread_ts=1776808927.654249 --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com> |
||
|
|
3d9c9ce327 |
fix: update color widget colors (#12133)
## Summary The widget was using a different fg/background compared to all other widgets, this updates to use the standard classes. ## Changes - **What**: - update styles ## Screenshots (if applicable) Before <img width="570" height="597" alt="Screen Shot 2026-05-11 at 07 19 12" src="https://github.com/user-attachments/assets/18a5330f-5e9a-4d16-b3f0-0acfab5d6f99" /> After <img width="570" height="597" alt="Screen Shot 2026-05-11 at 07 15 46" src="https://github.com/user-attachments/assets/81c9da58-fdda-4539-ae1e-98727f12b9ac" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12133-fix-update-color-widget-colors-35d6d73d365081da8515cd48a8e8ecc2) by [Unito](https://www.unito.io) No e2e tests for this as they would be simple screenshot asserts on the color of nodes, which is not worthwhile. |
||
|
|
56434ae9ac |
perf: debounce template search input to keep typing responsive (#12183)
## Summary Route the templates dialog search through `FormSearchInput`'s debounced searcher so per-keystroke work no longer trips the heavy filter/render path. ## Changes - **What**: `WorkflowTemplateSelectorDialog` now uses `FormSearchInput` instead of `SearchInput`. The raw input is bound to a local ref; the actual `searchQuery` consumed by `useTemplateFiltering` is only written after the input debounce settles, so dependent computeds (notably `shouldUsePagination`, which used to flip on every keystroke and force a full grid rebuild) stay stable while typing. - **What**: `FormSearchInput` gains optional `debounceMs` (default `250`) and `debounceMaxWaitMs` (default `1000`) props. Existing callers are unchanged; the templates dialog passes `400` / `4000` to match the feel tuned in this PR. ## Review Focus - Reset path: `searchQuery` is still owned by `useTemplateFiltering` and cleared by `resetFilters`; a watch syncs the visible input back to empty when that happens. - `FormSearchInput` is currently under `src/renderer/...` but already imported by workbench-level components (rightSidePanel tabs). This PR follows that existing precedent rather than relocating the component. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12183-perf-debounce-template-search-input-to-keep-typing-responsive-35e6d73d365081b7a11ec4a84323095f) by [Unito](https://www.unito.io) |
||
|
|
25c2d828c0 |
test: enable vitest/consistent-each-for and migrate .each → .for (#12161)
*PR Created by the Glary-Bot Agent*
---
Enables the oxlint rule `vitest/consistent-each-for` (configured to
prefer `.for` for `test`, `it`, `describe`, and `suite`) and migrates
every `.each` parameterized test in the repo to `.for`. Using `.for`
avoids accidentally splatting tuple elements into separate callback
arguments and exposes `TestContext` as the second callback argument.
The first commit covers the 38 lint-detected files (88 callsites):
renames `.each` → `.for` and updates callback signatures to destructure
when the data is an array of tuples (objects/primitives already work
unchanged with `.for`).
The follow-up commit addresses code review feedback: oxlint's rule does
not recognize `test.each` on extended test bases
(`baseTest.extend(...)`) and skips files in `ignorePatterns`
(`src/extensions/core/*`). These were converted manually so the policy
is uniform across the codebase.
## Verification
- `node_modules/.bin/oxlint src` — 0 errors, 0 `consistent-each-for`
violations
- `pnpm typecheck` — passes
- `pnpm test:unit` — all modified test files pass; pre-existing
environmental flakes (`GraphView.test.ts`, `ColorWidget.test.ts`, etc.,
unchanged here and flaky on `main` in this sandbox) are unrelated
- `pnpm lint` / `pnpm knip` — clean
- Manual verification: 362 tests across 6 representative converted
suites re-run in an interactive shell — all passing
Manual UI verification (Playwright/screenshots) is not applicable:
changes are test-file-only refactors with no production runtime or UI
behavior change.
## Notes on `.for` semantics
- Array-of-tuples (`[[a, b], ...]`) passes the tuple as a single arg, so
callbacks were changed from `(a, b) => …` to `([a, b]) => …`.
- Array-of-objects (`[{a}, …]`) already used destructuring — unchanged.
- Array-of-primitives (`['a', …]`) — callback signature unchanged.
- A handful of complex cases use a small `type Case = [...]` alias plus
`it.for<Case>([...])` to preserve tuple inference where TS narrowed
unions otherwise broke parameter types.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12161-test-enable-vitest-consistent-each-for-and-migrate-each-for-35e6d73d3650810c9417e07bdd9f27a2)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
|
||
|
|
1c2ae70343 |
chore(#11843): replace bare string NodeId typings in parameters tab components (#12014)
## Summary Replace `nodeId: string` with canonical `NodeId` type in right-side panel parameters tab components, eliminating redundant `String()` conversions at call sites. ## Changes - `TabNodes.vue`: `isSectionCollapsed` and `setSectionCollapsed` now accept `NodeId` instead of `string`; callers updated to pass `node.id` directly (removing `String()` wrapping) - `TabNormalInputs.vue`: same pattern ## Notes The other 6 files listed in the issue use `nodeId` parameters that carry execution IDs (`NodeExecutionId = string`), not graph node IDs (`NodeId = number | string`). Changing those to `NodeId` would be semantically incorrect. The two files changed here are the clear-cut cases where `node.id` (a `NodeId`) was being unnecessarily stringified before being passed. ## Testing ### Automated - `pnpm typecheck` — passes - `pnpm lint` — passes (0 warnings, 0 errors) - `pnpm format:check` — passes ### E2E Verification Steps 1. Open ComfyUI frontend 2. Load a workflow with multiple nodes 3. Open the right side panel (Parameters tab) 4. Verify node sections collapse/expand correctly per node 5. Verify "Collapse All" / "Expand All" toggle works correctly 6. Repeat with both TabNodes and TabNormalInputs views Fixes #11843 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12014-chore-11843-replace-bare-string-NodeId-typings-in-parameters-tab-components-3586d73d365081ed84caf560277f0553) by [Unito](https://www.unito.io) |
||
|
|
8108967d49 |
feat(dialog): migrate Prompt + Confirmation dialogs to Reka-UI (Phase 1) (#12041)
## Summary Phase 1 of the dialog migration kicked off in #11719. Migrates the two simplest production dialogs — `PromptDialogContent` and `ConfirmationDialogContent` — from PrimeVue `Dialog` onto the Reka-UI primitives landed in Phase 0. Public API of `useDialogService` / `dialogStore` is unchanged. Parent: [FE-571](https://linear.app/comfyorg/issue/FE-571/dialog-system-migration-primevue-reka-ui-parent) This phase: [FE-573](https://linear.app/comfyorg/issue/FE-573/phase-1-migrate-promptdialog-confirmationdialog-closes-11688) Predecessor: #11719 (merged at `0788e7139`) Refs #11688 (closed manually after Phase 0; the actual user-visible max-width fix ships in this PR) ## Changes ### `src/services/dialogService.ts` | Call site | Renderer | Size | Width override | | --- | --- | --- | --- | | `prompt()` | `'reka'` | `md` | — | | `confirm()` | `'reka'` | `md` | — | | `showBillingComingSoonDialog()` | `'reka'` | `sm` | `contentClass: 'max-w-[360px]'` | ### `src/components/dialog/content/ConfirmationDialogContent.vue` - Drops `import Message from 'primevue/message'` — the only PrimeVue dependency in the component - Replaces `<Message>` with a Tailwind `role="status"` alert keeping the `pi pi-info-circle` icon and muted-foreground severity ### `src/stores/dialogStore.ts` + `src/components/dialog/GlobalDialog.vue` - Adds `contentClass?: HTMLAttributes['class']` on `CustomDialogComponentProps` - Forwards it to `<DialogContent :class="...">` on the Reka branch (PrimeVue path keeps using `pt`) ## Why this scope 1. **Smallest content surface** — `PromptDialogContent` is 43 LOC; the only PrimeVue dependency in `ConfirmationDialogContent` is the `<Message>` info banner. 2. **Closes #11688 ergonomics** — Reka's `md` size = `max-w-xl` (576px / 36rem), exactly the max-width the issue reporter asked for. 3. **Three known callers** — all in `dialogService.ts`. No other callers needed to change. 4. **Renderer branch is already proven by Phase 0**; this PR just flips the flag. ## Visual proof Verified live in Storybook (`Components / Dialog / Dialog → Default` and `… → All Sizes`) at viewport `1920×1080`. DOM inspection confirms the rendered widths match the design intent: | Story | size | Rendered width | Computed `max-width` | | --- | --- | --- | --- | | `Default` | `md` | **576 px** | **576 px (= 36rem)** | | `All Sizes` (sm slot) | `sm` | 384 px | 384 px (= 24rem) | The `md` measurement directly answers the #11688 reporter screenshot (1558 px wide PrimeVue dialog → 576 px Reka dialog on the same display). Local screenshot artifacts (not committed): `temp/screenshots/phase1-md-576px-1920w.png`, `temp/screenshots/phase1-md-allsizes-1920w.png`, `temp/screenshots/phase1-sm-384px-1920w.png` — drag-drop into the PR body before marking ready for review. ## Quality gates - [x] `pnpm typecheck` — clean - [x] `pnpm lint` — clean - [x] `pnpm format` — applied (oxfmt) - [x] `pnpm test:unit` (touched files): **26/26 passed** - `ConfirmationDialogContent.test.ts` (9 tests, no longer needs PrimeVue plugin) - `PromptDialogContent.test.ts` (5 tests, unchanged) - `GlobalDialog.test.ts` (9 tests, Phase 0 coverage still passes after the contentClass forwarder addition) - `dialogService.renderer.test.ts` **new** — 3 tests asserting each call site sets `renderer: 'reka'` (regression net) - [ ] `pnpm test:browser:local --grep "@mobile confirm dialog"` — **could not run locally** (no ComfyUI Python backend on `localhost:8188` in this session); CI will gate the existing fixture, which is already renderer-agnostic (`getByRole('dialog')` + `getByRole('button', ...)` in `browser_tests/fixtures/components/ConfirmDialog.ts`). ## Public API impact None. `useDialogService().prompt(...)` / `confirm(...)` / `showBillingComingSoonDialog(...)` keep their existing signatures. Custom-node extensions calling `app.extensionManager.dialog.*` continue to work. ## Out of scope (later phases) - `ErrorDialogContent`, `NodeSearchBox`, `SecretFormDialog`, `VideoHelpDialog`, `CustomizationDialog` — Phase 2 (FE-574) - Settings dialog — Phase 3 (FE-575) - Manager dialog — Phase 4 (FE-576) - `ConfirmDialog` callers (`SecretsPanel`, `BaseWorkflowsSidebarTab`) — Phase 5 (FE-577) - Removing PrimeVue `Dialog` imports + `<style>` cleanup in `GlobalDialog.vue` — Phase 6 (FE-578) - Legacy `ComfyDialog` (`src/scripts/ui/dialog.ts`) - Deduplicating `Dialogue.vue` / `ImageLightbox.vue` ## Screenshot <img width="865" height="497" alt="Screenshot 2026-05-08 at 4 35 45 PM" src="https://github.com/user-attachments/assets/6aead2ad-2e0b-478a-9154-bb632a6bf3d1" /> <img width="1363" height="964" alt="Screenshot 2026-05-08 at 4 38 16 PM" src="https://github.com/user-attachments/assets/10647752-a063-4901-a206-842799cc5d7a" /> <img width="889" height="486" alt="Screenshot 2026-05-08 at 4 46 57 PM" src="https://github.com/user-attachments/assets/81899a81-205a-46f2-bddd-7639624607f6" /> ## Test plan - [x] Unit: 26/26 pass on touched files - [ ] CI: `@mobile confirm dialog` spec on the migrated path - [ ] Manual (post-CI on a real backend): open prompt and confirm dialogs on 1920×1080 viewport, verify ≤ 36rem max-width, ESC closes, backdrop click closes, Enter submits prompt, focus trap holds - [ ] Manual: open Billing Coming Soon dialog — verify it stays at the existing `max-w-[360px]` width |
||
|
|
0ef98de8eb |
fix: make credits help icon a tooltip button in cloud user popover (FE-617) (#12072)
## Summary The help icon (lucide circle-help) next to the credits balance in the cloud user popover was a bare `<i>` with `v-tooltip` and `cursor-help`. PrimeVue tooltip on a bare `<i>` did not fire reliably and the icon had no focus/keyboard semantics, so users saw "no hover action and not clickable". Wrap the icon in `<Button variant="muted-textonly" size="icon-sm">`, matching the existing pattern in `InfoButton.vue` and `MissingPackGroupRow.vue`. Same change applied to `CurrentUserPopoverLegacy.vue` and `CurrentUserPopoverWorkspace.vue`, which shared the broken pattern. - Fixes FE-617 - https://comfy-organization.slack.com/archives/C0A4XMHANP3/p1778191473621829 ## Red-Green CI Verification The branch was force-pushed back to the test-only commit so CI could run against it, then restored to the fix commit. | Commit | CI: Tests Unit | Outcome | |--------|---------------|---------| | `test:` ( |
||
|
|
0bc951fd12 |
fix: clarify unsaved-changes modal buttons and fix sign-out 3-state (#11669)
## Summary The dirtyClose modal had three buttons (`Cancel | No | Save`) and the sign-out flow collapsed two distinct outcomes (deny vs. dismiss) into a single early return — so today clicking "No" *cancels* sign-out instead of signing out without saving, and clicking "Save" never actually saves before logging out. This PR drops `Cancel` for `dirtyClose`, gives each caller a context-specific deny label, and fixes the sign-out 3-state handling. - Fixes [FE-419](https://linear.app/comfyorg/issue/FE-419/unsaved-changes-modal-uses-confusing-button-labels) ## Changes - **What**: - `ConfirmationDialogContent.vue`: hide `Cancel` for `type='dirtyClose'`; add `denyLabel?: string` prop; autofocus `Save` (preserves work on Enter). - `dialogService.confirm()`: accept and forward `denyLabel`. - `useAuthActions.logout`: handle `null` (cancel) / `false` (sign out anyway, no save) / `true` (save each modified workflow, then logout) distinctly. Pass `denyLabel: 'Sign out anyway'`. - `workflowService.closeWorkflow`: pass `denyLabel: 'Close anyway'`. - i18n: add `auth.signOut.signOutAnyway` and `sideToolbar.workflowTab.closeAnyway`. - **Breaking**: none. The `denyLabel` prop is optional and falls back to `g.no`. ## Review Focus - The "Save" branch in `useAuthActions.logout` now iterates `workflowStore.modifiedWorkflows` and awaits `useWorkflowService().saveWorkflow(workflow)` for each before calling `authStore.logout()`. The close-tab path (`workflowService.closeWorkflow`) was already correct — only the sign-out path needed the same shape. - `ConfirmationDialogContent` autofocus moves from `Cancel` (gone for `dirtyClose`) to `Save`. The dialog is still dismissable via ESC / outside-click, which routes through `dialogComponentProps.onClose → resolve(null)` — sign-out and close-tab both treat `null` as cancel. - Out of scope: the native browser `beforeunload` warning (`UnloadWindowConfirmDialog.vue`) is a separate flow and never reaches the in-app modal. ## Tests - Unit (`useAuthActions.test.ts`, new): logout handles `null` / `false` / `true` / no-modified-workflows; saves *every* modified workflow before `authStore.logout`; passes `denyLabel='Sign out anyway'`. - Unit (`ConfirmationDialogContent.test.ts`): Cancel hidden for `dirtyClose`; custom `denyLabel` rendered; falls back to `g.no` when omitted. - E2E (`workflowTabs.spec.ts`): modified-tab close shows `Close anyway` (not `No`) and no `Cancel`; clicking `Close anyway` removes the tab; ESC keeps the tab. ## screenshot ### AS IS <img width="816" height="379" alt="Screenshot 2026-04-27 at 5 40 19 PM" src="https://github.com/user-attachments/assets/a8e39403-bf72-455a-8d86-6ceb1f94ac85" /> <img width="923" height="396" alt="Screenshot 2026-04-27 at 5 40 38 PM" src="https://github.com/user-attachments/assets/08031c7c-b3a6-45d7-a4dc-5dcb4e63cfa0" /> ### TO BE <img width="1661" height="872" alt="Screenshot 2026-04-27 at 5 43 40 PM" src="https://github.com/user-attachments/assets/b89d160b-be66-450e-981e-32b1591f6841" /> <img width="1488" height="584" alt="Screenshot 2026-04-27 at 5 44 21 PM" src="https://github.com/user-attachments/assets/b3a141a7-1f3b-4f25-85a9-49529229c28b" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11669-fix-clarify-unsaved-changes-modal-buttons-and-fix-sign-out-3-state-34f6d73d365081bf8afad8e146b3b990) by [Unito](https://www.unito.io) |
||
|
|
0446ca7a18 |
fix: route default topbar feedback button to Typeform (#11863)
*PR Created by the Glary-Bot Agent* --- ## Summary PR #10890 routed the legacy action bar feedback button and the Help Center feedback item to the nightly Typeform survey, but the **default topbar feedback button** in `WorkflowTabs.vue` still called `buildFeedbackUrl()` and opened Zendesk. Since `Comfy.UI.TabBarLayout` defaults to `Default` (not `Legacy`), most Cloud/Nightly users were clicking the WorkflowTabs button and never reaching the Typeform survey — explaining the lack of survey responses. ## Changes - Added a shared `buildFeedbackTypeformUrl(source)` helper in `platform/support/config.ts` that tags the survey URL with: - `distribution`: `ccloud` / `oss-nightly` / `oss` (preserves the build-tagging the old `buildFeedbackUrl()` sent to Zendesk so responses stay segmented) - `source`: `topbar` / `action-bar` / `help-center` (identifies which UI entry point launched the survey) Tags are passed via the URL fragment (Typeform's hidden-field convention), so they reach the survey but are never sent to the server in the request line. - `WorkflowTabs.vue`: replaced `buildFeedbackUrl()` with `buildFeedbackTypeformUrl('topbar')`. - `cloudFeedbackTopbarButton.ts` and `HelpCenterMenuContent.vue`: use the shared builder with their respective source labels instead of inline URL literals. - Removed the now-unused `buildFeedbackUrl()` and `ZENDESK_FEEDBACK_FORM_ID` (knip-clean). `buildSupportUrl()` is preserved — `Comfy.ContactSupport` (the Help Center "Help" item) still routes to Zendesk as before. - Added unit tests for the builder, the WorkflowTabs feedback button, the legacy action bar button, and the Help Center feedback item (covering both the Cloud/Nightly Typeform path and the OSS `Comfy.ContactSupport` fallback). ## Verification - `pnpm format`, `pnpm lint`, `pnpm typecheck`, `pnpm knip`: clean (one pre-existing unrelated lint warning in `useWorkspaceBilling.test.ts`) - `pnpm test:unit` (impacted scope): 506/506 passing, including 13 new tests ## Review Focus - Cloud/Nightly gating in `WorkflowTabs.vue` (`v-if="isCloud || isNightly"`) is unchanged and matches PR #10890's gating philosophy. - The Help Center "Help" item and `Comfy.ContactSupport` command intentionally still route to Zendesk — feedback ≠ support. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11863-fix-route-default-topbar-feedback-button-to-Typeform-3556d73d3650815fb446dac33095d4be) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
653ee48444 |
FE-557: fix(painter): responsive label layout + correct resize min-height (#12025)
## Summary - WidgetPainter: stack label above widget when controls width < 350px, side-by-side otherwise; labels are always rendered (no longer hidden when narrow). - useNodeResize: re-measure the node's intrinsic min content height on every pointermove instead of capturing it once at drag start, so the height clamp tracks widgets whose controls reflow taller as width shrinks (e.g. painter switching to compact layout). Without this, the node visually sticks at its current height and the user has to release and grab the corner again to free it. - Add unit tests for both changes. ## Screenshots (if applicable) before https://github.com/user-attachments/assets/74889ad5-63a7-439f-b8e4-0185ed95327f after https://github.com/user-attachments/assets/bca77c36-2f90-4685-8603-f8f9c02abe77 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12025-FE-557-fix-painter-responsive-label-layout-correct-resize-min-height-3586d73d365081cf9036f7d52bfabe6c) by [Unito](https://www.unito.io) |
||
|
|
f4358cb161 |
perf: drop useMouseInElement to fix templates search lag (#12023)
## Summary
Replace `useMouseInElement` in `CompareSliderThumbnail` with a local
`@mousemove` handler to eliminate ~1s of forced layout per keystroke in
the templates dialog search.
## Changes
- **What**: Profiling the templates dialog search (4× CPU throttle,
cloud distribution) showed a single `getClientRects` block at 977ms
inside Vue's `flushPostFlushCbs → update` path on every keystroke. The
call traces back to VueUse's `useMouseInElement`, which (a) shares a
global `mousemove` listener via `useMouse`, and (b) runs
`el.getBoundingClientRect()` inside a `watch([targetRef, x, y], …, {
immediate: true })`. Every template card with `thumbnailVariant ===
'compareSlider'` mounts one instance — when search filters change and
the page's compareSlider cards re-mount, each instance's `immediate`
watch fires a rect read against freshly-inserted DOM, forcing
synchronous layout of the entire new subtree. With many cards on screen
the costs stack into the ~977ms block. Replaced with a native
`@mousemove` listener bound directly to the slider container; the rect
is read from `event.currentTarget` only when the mouse is actually over
that one card, so the work no longer scales with mounted instance count
and is gated by real pointer activity.
- **Breaking**: None
- **Dependencies**: None
## Review Focus
- UX is unchanged: `sliderPosition` still follows the mouse during hover
and keeps its last value on mouseleave (matches previous behaviour where
`if (!isHovered) return` simply stopped updates without resetting).
- The same `useMouseInElement` pattern still exists in
`src/platform/workflow/sharing/composables/useSliderFromMouse.ts` (used
by `ComfyHubThumbnailStep` in the publish dialog). That path is
single-instance and off the templates hot path, so it's left untouched
to keep this PR scoped — happy to fix it in a follow-up.
- New tests use `userEvent.pointer({ coords })` with a stubbed
`getBoundingClientRect` to lock in the native mousemove path and the
zero-width guard.
## E2E coverage
No `browser_tests/` changes. The `fix:` commit is a defensive clamp
inside `updateSliderPosition`; the overshoot it guards against comes
from subpixel rounding and stale rects observed during hover-in, neither
of which can be deterministically reproduced under Playwright. The perf
change itself is verified via the DevTools profiler (forced-layout block
disappears) — also not assertable as a stable e2e signal across CI
hardware. Both the mousemove-driven slider behavior and the clamp guard
are covered by unit tests in
`src/components/templates/thumbnails/CompareSliderThumbnail.test.ts` (8
cases, including out-of-range pointer coordinates and zero-width
containers).
|
||
|
|
1ab9752af8 |
fix: keep Reka overlays above PrimeVue dialogs (#12038)
## Summary Temporarily patch FE-569 by keeping the affected portaled Reka dropdowns and menus above their containing PrimeVue dialogs when PrimeVue auto z-index state has been elevated. ## Changes - **What**: Added a small compatibility helper, `usePrimeVueOverlayChildStyle`, that returns an anchor ref plus a computed inline style for child popover content. The helper finds the nearest PrimeVue dialog mask (`.p-dialog-mask` / `.p-overlay-mask`) from the parent surface and, only when found, applies `parent z-index + 1` to the affected Reka overlay content. - **What**: Applied that helper at the exact PrimeVue parent surfaces where the issue was found. This PR does not add a global overlay policy and does not change every Reka select/dropdown in the app. - **What**: Added optional `contentStyle`/`selectContentStyle` plumbing only where needed so the style reaches the actual portaled Reka overlay root. - **What**: Added focused unit coverage for the helper contract: no PrimeVue parent preserves existing stacking, PrimeVue dialog/overlay masks render child content above the parent, low parent z-index values respect the Reka floor, and invalid z-index values do not inject an inline override. - **Approach**: This is intentionally a minimal, parent-scoped band-aid. It avoids a global PrimeVue overlay scanner because global sampling can be polluted by unrelated persistent PrimeVue roots such as Toast and would turn this fix into a broader layering policy. - **Approach**: The patch targets the confirmed failure mode: a Reka child overlay rendering below its owning PrimeVue dialog after PrimeVue autoZIndex has been elevated. It does not attempt to solve PrimeVue z-index globally. - **Lifecycle**: This is temporary migration compatibility. PrimeVue dialogs and controls are being incrementally migrated to Reka UI, so `usePrimeVueOverlayChildStyle` and the optional style props added for FE-569 should be removed once the affected parent surfaces move to Reka. - **Breaking**: None. New props are optional and no public API contract is changed. - **Dependencies**: None. ## Patched Entry Points This PR pinpoints the six affected user-facing surfaces below. Each patch is applied from the PrimeVue dialog parent and passed only to the Reka child overlay content that can render underneath that parent. https://github.com/user-attachments/assets/d0d1522a-ffc7-4934-9e7a-06b83e20f809 1. **Workflow Template Library filters** - **How to enter**: click the Templates button in the left sidebar, or open the Comfy menu and choose **Browse Templates**. - **Affected elements**: the template filter popovers in `WorkflowTemplateSelectorDialog`: **Model**, **Use case**, **Runs on**, and **Sort by**. - **Patch point**: `WorkflowTemplateSelectorDialog.vue` anchors to the template dialog content filter area and passes `selectContentStyle` to the affected `MultiSelect` / `SingleSelect` controls. https://github.com/user-attachments/assets/3641fa24-da51-4392-a904-9085f8a5a2f4 2. **Manager dialog header controls** - **How to enter**: open Manager from the top/menu Manager entry when the new Manager UI is available. - **Affected elements**: the Manager header controls in `ManagerDialog`: search mode `SingleSelect`, search autocomplete suggestions, and **Sort** `SingleSelect`. - **Patch point**: `ManagerDialog.vue` anchors to the dialog header and passes `selectContentStyle` to those three Reka overlays. https://github.com/user-attachments/assets/cf25cc06-f851-48ef-9d9c-9ec2da8afc06 3. **Asset Browser filter bar** - **How to enter**: open the Asset Browser from an eligible model widget browse action, the Model Library flow, or another `useAssetBrowserDialog` caller. - **Affected elements**: `AssetFilterBar` controls: **File formats**, **Base models**, **Ownership**, and **Sort by**. - **Patch point**: `AssetBrowserModal.vue` anchors to the PrimeVue dialog header and passes the style through `AssetFilterBar` to its `MultiSelect` / `SingleSelect` controls. https://github.com/user-attachments/assets/e27bd805-10c0-4b3b-97f3-9e11faa47021 4. **Asset Browser model info panel** - **How to enter**: open Asset Browser, select an asset, then use the right-side model info panel. - **Affected element**: the **Model type** select in `ModelInfoPanel`. - **Patch point**: `AssetBrowserModal.vue` reuses the same parent-scoped style and passes it to `ModelInfoPanel` as `selectContentStyle`. https://github.com/user-attachments/assets/5e9f7ef0-ebd7-4987-ba1b-2137c034086f 5. **Upload Model confirmation step** - **How to enter**: open Asset Browser, click **Upload**, enter/fetch model metadata, then proceed to the confirmation step. - **Affected element**: the **Model type** `SingleSelect` in `UploadModelConfirmation`. - **Patch point**: `UploadModelConfirmation.vue` anchors within the upload dialog content and passes `selectContentStyle` to the model type selector. https://github.com/user-attachments/assets/ec145f26-8621-455b-915e-bedee47e1cbd 6. **Settings > Keybinding panel controls** - **How to enter**: open Settings from the sidebar/menu, then select the **Keybinding** panel. - **Affected elements**: the keybinding preset select, the preset overflow dropdown menu, and the row context menu inside `KeybindingPanel`. - **Patch point**: `KeybindingPanel.vue` anchors to the settings dialog panel and passes `keybindingOverlayContentStyle` only to those Reka overlay roots. ## Review Focus - Confirm the patch stays narrowly scoped to the six known PrimeVue parent + Reka child overlay surfaces above. - Confirm `contentStyle` reaches the actual portaled Reka overlay content in each patched path. - Confirm the fallback behavior preserves existing stacking when no PrimeVue parent overlay is found; in that case the helper returns an empty style object and leaves existing Tailwind z-index classes alone. - Please avoid expanding this into a larger overlay refactor. The goal is a clean, backport-friendly compatibility patch while the Reka migration continues. Validation performed: - `pnpm exec vitest run src/composables/usePopoverSizing.test.ts` - `pnpm typecheck` - `pnpm lint` (passes with existing unrelated warnings only) - `pnpm format:check` - commit hook lint-staged checks (`oxfmt`, `stylelint`, `oxlint`, `eslint --fix`, `pnpm typecheck`) - pre-push `pnpm knip` Linear: FE-569 ## Bug Screenshots https://github.com/user-attachments/assets/e73761af-9867-4c50-ab0d-4e32e59011e1 https://github.com/user-attachments/assets/145daf4d-3268-428b-9987-1e1afd0b866f ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12038-fix-keep-Reka-overlays-above-PrimeVue-dialogs-3596d73d365081e7af49dbc4d3905962) by [Unito](https://www.unito.io) |
||
|
|
5ebf5e03ae |
refactor(load3d): replace PrimeVue Select/Slider/Checkbox with Reka UI (#12020)
Replace PrimeVue components in 3D node viewer controls with the
project's Reka UI equivalents across 7 files.
## Changes
| File | Replaced |
|------|---------|
| `AnimationControls.vue` | `Select` × 2 (speed + animation) |
| `ViewerModelControls.vue` | `Select` × 2 (up direction + material
mode) |
| `ViewerCameraControls.vue` | `Select` + `Slider` (camera type + FOV) |
| `ViewerExportControls.vue` | `Select` (export format) |
| `PopupSlider.vue` | `Slider` |
| `ViewerLightControls.vue` | `Slider` |
| `ViewerSceneControls.vue` | `Checkbox` → native `<input
type="checkbox">` |
## Implementation notes
- `Select` uses `@/components/ui/select/*` compound components. Numeric
model values (animation speed index) are stringified at the binding
boundary and converted back on update, matching Reka `SelectRoot`'s
`string`-only `modelValue` contract.
- `Slider` uses `@/components/ui/slider/Slider.vue`. Single-number
`defineModel` values are wrapped in a `computed` array and unwrapped in
the update handler, following the pattern established in
`LightControls.vue`.
- No new Reka UI wrapper components were created — existing ui/select
and ui/slider primitives were used directly.
## Test
https://github.com/user-attachments/assets/afca0fc8-a7b6-49ee-b221-ee5725bd127e
1. AnimationControls.vue
- **Add Load3D node** → Upload an animated GLB file (e.g., a character
model).
- **Node preview top bar:** Play/Pause button, speed dropdown, animation
name dropdown, and progress bar.
2. PopupSlider.vue
- **Hover over Load3D preview:** Icon buttons appear in the left
toolbar.
- **"Light Intensity" button (bulb icon)** → Slider pops up on the
right.
- **"FOV" button (view icon)** → Slider pops up on the right.
3. ViewerCameraControls.vue
- **Load3D node** → Settings panel (top-right) → **"Camera"** tab.
- **Features:** Camera type dropdown (Perspective / Orthographic), FOV
slider (visible in Perspective mode).
4. ViewerExportControls.vue
- **Settings panel** → **"Export"** tab.
- **Features:** Format dropdown (GLB / OBJ / STL), Export button.
5. ViewerLightControls.vue
- **Settings panel** → **"Light"** tab.
- **Features:** Light intensity slider.
6. ViewerModelControls.vue
- **Settings panel** → **"Model"** tab.
- **Features:** "Up direction" dropdown, Material mode dropdown
(Wireframe / Normal, etc.).
7. ViewerSceneControls.vue
- **Settings panel** → **"Scene"** tab.
- **Features:** Background color picker, "Show grid" checkbox, upload
background image button.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> UI component swap touches multiple interactive viewer controls
(selects/sliders/checkbox), so small binding/typing differences (string
vs number, array slider values) could cause subtle regressions despite
test updates.
>
> **Overview**
> Replaces PrimeVue `Select`, `Slider`, and `Checkbox` usages across
Load3D viewer controls with the project’s Reka UI-based primitives
(`@/components/ui/select/*`, `@/components/ui/slider/Slider.vue`) and a
native checkbox.
>
> Updates v-model wiring to match the new components’ contracts: selects
now bind via string `modelValue` with explicit number casting where
needed, and sliders now wrap single numeric values into `[number]`
arrays with corresponding update handlers. Unit tests are updated to
mock the new UI components and their updated event/value shapes.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
|
||
|
|
0788e71394 |
feat(dialog): introduce Reka-UI dialog primitives + opt-in renderer branch (Phase 0) (#11719)
## Summary Lands the renderer infrastructure for migrating ComfyUI Frontend's central dialog system from PrimeVue to Reka-UI. **Phase 0 of a phased migration.** No production dialog migrates in this PR — every existing dialog continues to render through PrimeVue exactly as before. ## Motivation GitHub issue #11688 surfaced a PrimeVue Dialog `max-width` design limitation that is awkward to address through PrimeVue's pass-through styling. ADR 0004 (Rejected, 2025-08-27) explicitly endorses **selective component replacement with shadcn/Reka-UI** as the path forward for problematic PrimeVue components, and `AGENTS.md` already directs contributors to "Avoid new usage of PrimeVue components." The dialog system is a strong first candidate: clean public API boundary (`useDialogService` / `dialogStore`), bounded surface (~12 dialogs), and Reka-UI is already in use elsewhere in the codebase. The #11688 fix arrives naturally in Phase 1 once `prompt`/`confirm` migrate to the new primitive's `md` default (`max-width: 36rem`). ## Phased migration plan This PR is **Phase 0 only**. Each subsequent phase is shipped as its own PR. | Phase | Scope | Approx LOC | | ----- | ----- | ---------- | | **0 (this PR)** | Reka-UI primitive set under `src/components/ui/dialog/` + opt-in renderer branch in `GlobalDialog.vue` + tests + Storybook | ~600 | | 1 | Migrate `PromptDialogContent` + `ConfirmationDialogContent`; closes #11688 | ~250 | | 2 | Migrate `ErrorDialogContent`, `NodeSearchBox(Popover)`, `SecretFormDialog`, `VideoHelpDialog`, `CustomizationDialog` | ~400 | | 3 | Migrate Settings dialog (workspace + non-workspace variants) — designer review | ~300 | | 4 | Migrate Manager dialog — designer review | ~300 | | 5 | Migrate `ConfirmDialog` callers (`SecretsPanel`, `BaseWorkflowsSidebarTab`) | ~150 | | 6 | Remove PrimeVue Dialog/ConfirmDialog imports + clean up CSS overrides | ~200 | Full plan in `temp/plans/dialog-migration-phase-0.md` and ADR draft at `temp/plans/adr-0009-dialog-reka-migration-DRAFT.md` (will move to `docs/adr/` after team review). ## Changes - **What**: - New shadcn-style primitives at `src/components/ui/dialog/` wrapping Reka-UI's `Dialog*` components: `Dialog`, `DialogPortal`, `DialogOverlay`, `DialogContent`, `DialogHeader`, `DialogFooter`, `DialogTitle`, `DialogDescription`, `DialogClose`. Variants via `cva` with sizes `sm | md | lg | xl | full`. - `dialogStore.CustomDialogComponentProps` gains opt-in `renderer?: 'primevue' | 'reka'` (default `'primevue'`) and `size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'`. - `GlobalDialog.vue` branches the per-stack-item template based on the `renderer` flag. PrimeVue path is byte-identical to before. - Storybook stories: `Default`, `LongContent`, `Headless`, `AllSizes`. - Unit tests verifying branch selection and that the opt-in flag is preserved on the dialog stack item. - **Breaking**: None. Default renderer is `primevue` and no production dialog opts in. - **Dependencies**: None. Reka-UI is already a workspace dependency. ## Review Focus 1. **API surface**: `useDialogService` / `dialogStore` public API is unchanged. Custom-node extensions calling `app.extensionManager.dialog.*` continue to work. 2. **Renderer branch wiring** in `GlobalDialog.vue` — `escape-key-down` / `pointer-down-outside` map to `closeOnEscape` / `dismissableMask`; `mousedown` calls `dialogStore.riseDialog` to mirror the PrimeVue PT-based behavior. 3. **Primitive defaults** — `md` size = 36rem max-width (chosen to resolve #11688 in Phase 1); `full` = `calc(100vw - 1rem)` escape hatch for Settings/Manager later. 4. **No behavior change**: existing dialogs continue to render unchanged because nothing opts into `renderer: 'reka'` in this PR. ## Quality gates - `pnpm typecheck` — clean - `pnpm lint` — clean (1 pre-existing warning unrelated to this PR) - `pnpm test:unit` — 48 dialog-adjacent tests pass including 3 new tests in `GlobalDialog.test.ts` - `pnpm format` — applied knip pre-push noise (unused deps in workspace packages, unused `types.gen.ts`) is pre-existing on `main` and not introduced by this PR. ## Out of scope (deferred) - Migrating any production dialog — Phase 1+ - Removing PrimeVue dependency — Phase 6 - Touching legacy `ComfyDialog` (`src/scripts/ui/dialog.ts`) — separate cleanup - Deduplicating `Dialogue.vue` / `ImageLightbox.vue` against the new primitives — separate cleanup Refs #11688 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11719-feat-dialog-introduce-Reka-UI-dialog-primitives-opt-in-renderer-branch-Phase-0-3506d73d365081fc8c83ceadbffd276c) by [Unito](https://www.unito.io) # test ## checklist | Scenario | Cloud prod | This PR | Notes | |---|---|---|---| | Confirm dialog (delete/sign‑out) | ✅ | ✅ | OK/Cancel, ESC, backdrop click identical | | Prompt dialog (rename / save as) | ✅ | ✅ | Enter submits, ESC cancels, focus trap intact | | Settings dialog open/close | ✅ | ✅ | Tabs, search, ESC, save persistence unchanged | | Manager dialog | ✅ | ✅ | Tab switching, sub‑confirm stacking, z‑index correct | | Stacked dialog ESC handling | ✅ | ✅ | Only top dialog closes; mousedown raises bottom | | Dialog after route change | ✅ | ✅ | No orphaned overlay, no body scroll lock leak | | `.p-dialog` DOM attrs | clean | clean | No `renderer=` / `size=` attribute leak from new optional fields | ## screenshot <img width="1616" height="927" alt="Screenshot 2026-05-06 at 8 43 10 PM" src="https://github.com/user-attachments/assets/c6f668c2-a537-45ae-bf66-8bb0617502de" /> <img width="1419" height="951" alt="Screenshot 2026-05-06 at 8 43 41 PM" src="https://github.com/user-attachments/assets/d82d4b27-cb05-4185-be4a-bd2fb9503130" /> <img width="1884" height="1001" alt="Screenshot 2026-05-06 at 8 46 31 PM" src="https://github.com/user-attachments/assets/dd13f99f-a11e-4b85-9f27-7d30c55cf266" /> <img width="1876" height="1009" alt="Screenshot 2026-05-06 at 8 47 29 PM" src="https://github.com/user-attachments/assets/f9824b57-4a06-44d6-8f18-e1226c764c83" /> |
||
|
|
d29169ff4e |
test: add E2E tests for keybinding settings panel coverage (#11455)
*PR Created by the Glary-Bot Agent* --- ## Summary Adds 22 Playwright E2E tests for the keybinding settings panel (`KeybindingPanel.vue`), covering all 15 previously-untested functions identified via coverage analysis. ## Test Groups | Group | Tests | Functions Covered | |---|---|---| | Row Expansion | 2 | `handleRowClick`, `toggleExpanded`, `expandedRows` | | Double-Click | 2 | `handleRowDblClick`, `addKeybinding`, `editKeybinding` | | Context Menu | 7 | `handleRowContextMenu`, `clearContextMenuTarget`, `ctxChangeKeybinding`, `ctxAddKeybinding`, `ctxResetToDefault`, `ctxRemoveKeybinding` | | Action Buttons | 7 | `editKeybinding`, `resetKeybinding`, `removeSingleKeybinding`, `handleRemoveAllKeybindings`, `handleRemoveKeybindingFromMenu` | | Expanded Row Actions | 2 | `editKeybinding` (expansion), `removeSingleKeybinding` (expansion) | | Reset All | 2 | `resetAllKeybindings` (confirm + cancel) | | Search Filter | 1 | `watch(filters, ...)` clears expansion | ## Flake Prevention Measures - Deterministic test command (`TestCommand.KeybindingPanelE2E.NoBinding`) registered via `app.registerExtension()` for 0-binding scenarios — avoids flaky pagination-dependent row scanning - `pressComboOnInput()` asserts input focus before pressing key combos in the edit dialog - `saveAndCloseKeybindingDialog()` waits for dialog teardown to complete before proceeding - `openContextMenu()` waits for Reka UI menu items to be visible before interacting - Resets `Comfy.Keybinding.NewBindings` and `Comfy.Keybinding.UnsetBindings` in `afterEach` ## Note on Selectors Some locators use Tailwind utility classes (`.pl-4`, `.icon-[lucide--chevron-right]`) because the expansion template and chevron icon in `KeybindingPanel.vue` lack `data-testid` attributes. Adding test IDs would be a follow-up to this test-only PR. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11455-test-add-E2E-tests-for-keybinding-settings-panel-coverage-3486d73d365081d7a902fc68091552f2) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com> |
||
|
|
f6ddd26cef |
fix: use resized QPO thumbnails (#11946)
## Summary Use resized preview URLs for floating QPO row thumbnails so the expanded overlay does not load full-size image assets while the canvas is being navigated. Linear: FE-538 ## Changes - **What**: Pass `ResultItemImpl.previewUrl` into `AssetsListItem` for completed image/video job rows. - **Dependencies**: None. ## Review Focus Confirm this only changes the row thumbnail source; full asset viewing still flows through the existing job/task preview output. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11946-fix-use-resized-QPO-thumbnails-3576d73d365081b68682d1b7b109af30) by [Unito](https://www.unito.io) |
||
|
|
6822a6883d |
test: add tests for layout settings (#11692)
## Summary Adds tests for UI layout settings ## Changes - **What**: - add initialFeatureFlags to allow setting feature flags before initial setup to prevent needing to reload page - tests sidebar + topbar settings ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11692-test-add-tests-for-layout-settings-34f6d73d36508117b1daedbb68176e04) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
3637b61fcd |
Use Reka popover for queue job details (#11540)
## Summary Use ShadCN-style Reka popover primitives for the live queue job list after the unused legacy queue row implementation is removed in #11621. This is the first step in migrating popovers toward the ShadCN UI pattern: local design-system wrappers over Reka UI, rather than ad hoc direct Reka or PrimeVue popovers at each call site. ## Changes - **What**: Added the minimal ShadCN-style popover primitives needed by this fix: `Popover`, `PopoverAnchor`, and `PopoverContent`. - **What**: Migrated `JobAssetsList` job details from manual fixed positioning to these popover primitives with viewport collision handling. - **What**: Removed the obsolete manual hover-position helper after `JobAssetsList` stopped using it. - **Dependencies**: No new dependencies; the primitives wrap the existing `reka-ui` package. - Added browser coverage for bottom-row job details clipping in the queue overlay. ## Review Focus - This PR is stacked on #11621. - The live queue surfaces are `JobAssetsList` consumers: expanded queue progress overlay and job history sidebar. - The new `src/components/ui/popover` files intentionally seed the ShadCN-style migration path, but only include the pieces used here to keep the first PR small. - Follow-up PRs can add `PopoverTrigger` and migrate existing PrimeVue/direct-Reka popovers once there is an actual caller. |
||
|
|
5e16802832 |
refactor: remove @ts-expect-error suppressions in CustomizationDialog (#11339)
… (issue #11092 phase 4b)
## Summary
Part of #11092 — Phase 4b: remove 2 `@ts-expect-error` suppressions from
`CustomizationDialog.vue`.
## Changes
`selectedIcon` ref initialisation and `resetCustomization` assignment
both suppressed a type error on `Array.find()` returning `T |
undefined`.
**Why**
`Array.find()` has no way to statically guarantee a match, so its return
type is always `T | undefined`. Both usages were searching `iconOptions`
— a literal array of 8 entries declared in the same scope — and
TypeScript could not prove that the searched value would always be
found, even though at runtime it always is (the default icon value is
defined from `iconOptions[0]`).
**How**
Added `iconOptions[0]` as a final fallback via `??` in both places.
Because `iconOptions` is a non-empty literal array, `iconOptions[0]` is
provably non-null to TypeScript, which makes the overall expression type
`T` and satisfies the assignment. The explicit generic on `ref<{ name:
string; value: string }>` was also dropped — TypeScript infers the type
correctly from the non-nullable initialiser. In `resetCustomization`,
`||` was replaced with `??` since the values being null-coalesced are
objects (never falsy), making `??` the semantically precise operator for
this case.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Low risk: UI-only refactor that adds explicit fallbacks for
`Array.find()` results and introduces a small unit test suite; behavior
should remain the same except for safer handling of unexpected icon
values.
>
> **Overview**
> Removes two `@ts-expect-error` suppressions in
`CustomizationDialog.vue` by making `selectedIcon` initialization and
`resetCustomization` use a guaranteed fallback (`iconOptions[0]`) via
`??`, ensuring the selected icon is never `undefined`.
>
> Adds `CustomizationDialog.test.ts` to verify `confirm` emits the
expected icon/color for default, provided initial values, and an invalid
`initialIcon` fallback.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
|
||
|
|
9013102db9 |
fix: use capitalize for keybinding badges (#11810)
## Summary Render keybinding badges in sentence case (`Ctrl + Shift + A`) instead of UPPERCASE (`CTRL + SHIFT + A`) by swapping the `uppercase` Tailwind class for `capitalize` in `KeyComboDisplay.vue`. `KeyComboImpl.getKeySequences()` already returns labels in their canonical form (`Ctrl`, `Alt`, `Shift`, plus the raw key). The badge styling was forcing them all to UPPER, which is what FE-524 calls out. `text-transform: capitalize` cleanly handles every case: lower modifier, upper modifier, and single character keys. - Fixes FE-524 ## Before / After | Before (`uppercase`) | After (`capitalize`) | | --- | --- | | <img src="https://raw.githubusercontent.com/Comfy-Org/ComfyUI_frontend/c6bb96fce/docs/screenshots/fe-524/before.png" width="480"> | <img src="https://raw.githubusercontent.com/Comfy-Org/ComfyUI_frontend/c6bb96fce/docs/screenshots/fe-524/after.png" width="480"> | ## Test plan - [ ] Open Settings → Keybinding panel and confirm modifier badges render as `Ctrl`, `Alt`, `Shift` instead of `CTRL`, `ALT`, `SHIFT` - [ ] Confirm single-letter keys (e.g. `A`, `S`) still render uppercase - [ ] Edit a keybinding and verify the live preview badges in the dialog also render in sentence case |
||
|
|
90b3d8a5c6 |
test: add mask editor brush adjustment and layer management browser tests (#11368)
*PR Created by the Glary-Bot Agent* --- ## Summary Adds `browser_tests/tests/maskEditorBrushLayers.spec.ts` covering untested brush settings interaction and tool/layer management in the mask editor. ### Coverage gaps filled - `useBrushDrawing.ts` — brush thickness/opacity/hardness slider interaction - `useToolManager.ts` — tool switching with independent mask data, data preservation across tool switches ### Test cases (5 tests, 2 groups) | Group | Tests | Behavior | |---|---|---| | Brush settings | 3 | Thickness slider changes size, opacity slider changes opacity, hardness slider changes hardness | | Layer management | 2 | Different tools produce independent mask data, switching tools preserves previous mask data | ### References - Reuses patterns from existing `maskEditor.spec.ts` (`loadImageOnNode`, `openMaskEditorDialog`, `drawStrokeOnPointerZone`, `getMaskCanvasPixelData`) - Follows `browser_tests/AGENTS.md` directory structure - Follows `browser_tests/FLAKE_PREVENTION_RULES.md` assertion patterns ### Verification - TypeScript: clean - ESLint: clean - oxlint: clean - oxfmt: formatted ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11368-test-add-mask-editor-brush-adjustment-and-layer-management-browser-tests-3466d73d36508170ae24ebea2b73d60d) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
b83602fd23 |
feat: hide Google SSO button in embedded webviews (#10699)
Hide the Google SSO login/signup button when the app runs inside an embedded webview (Android WebView, iOS WKWebView, social app in-app browsers), where Google blocks OAuth with a `403 disallowed_useragent` error. Fixes #7017 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10699-feat-hide-Google-SSO-button-in-embedded-webviews-3326d73d365081048e35d9d678fe1a2f) by [Unito](https://www.unito.io) --------- Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com> |
||
|
|
285421a87c |
feat: add queue progress overlay feature survey (#11560)
*PR Created by the Glary-Bot Agent* --- ## Summary Registers a new nightly feature survey for the Queue Progress Overlay using the existing feature-survey registry (same pattern as the merged node-search survey, PRs #8175/#8355/#9934). - New registry entry `queue-progress-overlay` → Typeform `HZ5saxry`, threshold **16**, 5s display delay. - `trackFeatureUsed()` wired at the major user-initiated handlers inside the overlay so the survey triggers regardless of panel location (floating-right v1 or docked-left v2). - Run button and other ActionBar items that the overlay pops over from are deliberately **not** tracked — tracking is scoped to interactions that originate inside the job panel / queue progress overlay itself. ## Tracked interactions Both variants share most sub-components, so tracking is instrumented once at each logical surface: - **`QueueProgressOverlay.vue`** (v1 container): `viewAllJobs`, `interruptAll`, `cancelQueuedWorkflows`, `onClearHistoryFromMenu`, `toggleAssetsSidebar`, `onCancelItem`, `onDeleteItem`, `inspectJobAsset` - **`QueueOverlayExpanded.vue`**: job tab switches - **`JobHistorySidebarTab.vue`** (v2 docked): job tab switches, `clearQueuedWorkflows`, `onClearHistory`, `onCancelItem`, `onDeleteItem`, `onViewItem` - **`JobFilterActions.vue`** (shared): workflow filter + sort mode selections - **`JobHistoryActionsMenu.vue`** (shared): docked-history toggle + run-progress-bar toggle Deliberately **not tracked** to keep the signal clean: - Hover handlers (ambient preview behaviour) - Search-box keystrokes (debounced typing) - Context menu open and menu-item dispatch — menu actions either bubble through already-tracked terminal handlers (e.g. inspect-asset → `onViewItem`) or are secondary operations (copy-id, open-workflow, download). Avoids double-counting per code review feedback. ## How it works (inherits from existing infrastructure) 1. `surveyRegistry.ts` drives `NightlySurveyController` → `NightlySurveyPopover`, which handles the Typeform embed. 2. Eligibility already gated on `isNightly && !isCloud && !isDesktop`, once-per-user, 4-day global cooldown across all surveys, and opt-out. 3. Typeform response routing to #C0ALLT6Q3SQ is handled on the Typeform side. ## Verification - `pnpm typecheck` ✅ - `pnpm lint` ✅ (no new warnings) - `pnpm knip` ✅ - `pnpm test:unit` on `src/components/queue`, `src/components/sidebar/tabs/JobHistorySidebarTab`, `src/platform/surveys` → **123/123 passing** - Pre-commit hooks (stylelint, oxfmt, oxlint, eslint, typecheck) all pass - Manual: dev server + backend boot cleanly, app loads without new runtime errors, `localStorage['Comfy.FeatureUsage']` layout verified to match what `useFeatureUsageTracker` writes ## Notes - Survey key `queue-progress-overlay` covers both v1 (floating-right) and v2 (docked-sidebar) per product guidance: _"This should trigger regardless of the location of the panel (docked from left or floating on right)."_ Both surfaces are the same product feature — the survey is intentionally scoped to the whole job-panel experience. ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11560-feat-add-queue-progress-overlay-feature-survey-34b6d73d3650819a9a50fd67fd9b5941) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
af70d88860 |
fix: keep finished badge fully opaque in ProgressToastItem (#11542)
## Summary - fix **[slack](https://comfy-organization.slack.com/archives/C0A4XMHANP3/p1776801170742579)** - Move `opacity-50` off the row container onto the asset-name column only, so the contrast badge (white pill, dark label) is not dimmed to gray-on-gray when a download completes. - Matches the Figma intent that the `FINISHED` badge stands out — designer spec uses `base/foreground` for pill, `base/background` for text, which is unreadable when the parent is 50% opacity. <img width="560" height="269" alt="Screenshot 2026-04-23 at 2 46 17 PM" src="https://github.com/user-attachments/assets/fb84aa57-c348-4a86-9a65-9342c12400e1" /> <img width="764" height="332" alt="Screenshot 2026-04-23 at 2 46 41 PM" src="https://github.com/user-attachments/assets/ecbe6a5f-c2e8-4427-9c1d-f8f123009d2e" /> ## Before / After  ## Repro Cloud → trigger a model download → wait for completion → the `FINISHED` badge is the same tone as the toast surface (see Slack thread screenshots). ## Test plan - [ ] Complete a model download in cloud and confirm the `FINISHED` badge is clearly legible in both themes. - [ ] File name + subtitle still appear dimmed to signal the row is completed. - [ ] Running / failed / pending states unchanged. - Fixes [FE-237](https://linear.app/comfyorg/issue/FE-237/fix-honeytoast-badge-text-color-for-finished-job-matches-background) |
||
|
|
e4e1546458 |
test: add queue notification banners lifecycle browser tests (#11366)
*PR Created by the Glary-Bot Agent* --- ## Summary - Adds `browser_tests/tests/queueNotificationBanners.spec.ts` covering `useQueueNotificationBanners` composable E2E behavior - Adds `data-testid="queue-notification-banner"` to `QueueNotificationBannerHost.vue` for stable test targeting - Registers the new test ID in `TestIds.queue.notificationBanner` ### Test coverage added (7 tests) | Group | Tests | Behavior | |---|---|---| | Queuing lifecycle | 4 | `promptQueueing` → banner appears, `promptQueued` upgrades to queued, batch plural text, requestId mismatch doesn't upgrade | | Auto-dismiss | 1 | Banner disappears after 4s timeout | | FIFO queue | 1 | Second notification shows after first auto-dismisses | | Direct queued | 1 | `promptQueued` without prior `promptQueueing` shows banner directly | ### Approach Tests dispatch `promptQueueing`/`promptQueued` custom events directly via `window.app.api.dispatchCustomEvent()` inside `page.evaluate()`, matching how `app.ts` triggers these events during real queue operations. This avoids needing a running execution pipeline while exercising the full composable → component → DOM rendering chain. ### Verification - TypeScript: zero errors - ESLint: clean - oxlint: clean - oxfmt: formatted - Playwright execution requires running ComfyUI backend (not available in sandbox) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11366-test-add-queue-notification-banners-lifecycle-browser-tests-3466d73d36508172a7ffd3fe3b4fd925) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com> |