mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-13 01:06:18 +00:00
f0e16cdf46764bb00a02a0c132a339cf51bfb309
7936 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f0e16cdf46 |
ci: handle skipped e2e workflow consumers (#11575)
## Summary Follow-up to #11568 and #11785. Keeps the E2E coverage workflow clean when `CI: Tests E2E` is intentionally skipped and no coverage shard artifacts are produced. ## Changes - Detect whether downloaded E2E coverage shard artifacts contain any `coverage.lcov` files. - Treat missing coverage shards as an intentionally skipped coverage run instead of running lcov, Codecov, or Pages deployment on missing files. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11575-ci-handle-skipped-e2e-workflow-consumers-34b6d73d3650813f92e1d7d6af0bf958) by [Unito](https://www.unito.io) |
||
|
|
0658c1ac9c |
refactor: align asset pagination schema (#11899)
## Summary Align the asset list pagination schema with generated ingest-types metadata and remove the now-unneeded missing `has_more` fallback branch. ## Changes - **What**: Reuse `zListAssetsResponse` for `total` and `has_more`, keep the local loose `AssetItem` shape, and simplify `getAllAssetsByTag()` to trust the required `has_more` contract. - **Breaking**: None. - **Dependencies**: None. ## Review Focus This is PR 1 of 4 in the missing asset follow-up stack: 1. This PR - Asset schema / pagination cleanup 2. #11900 - Missing asset hash verification utility cleanup 3. #11901 - Browser regression coverage for public input assets 4. #11902 - TanStack Query public-input cache replacement The key decision is intentionally narrow: pagination metadata now comes from generated ingest-types, but asset item validation remains locally loose to avoid changing UI/store synthetic asset shapes in this PR. `asset_hash` nullability remains unchanged because absent-vs-null hash semantics are still a backend/API contract follow-up. Addresses #11894 ## Screenshots (if applicable) N/A |
||
|
|
997501d8fb |
test: add e2e test for metadata parsing on workflow load (#11522)
## Summary Adds e2e testing to ensure workflows are correctly loaded from each of the supported file types ## Changes - **What**: - add png generation - add mime types for missing files - add test that loads file and ensures node is present ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11522-test-add-e2e-test-for-metadata-parsing-on-workflow-load-3496d73d36508101ad67d24af1810cec) by [Unito](https://www.unito.io) |
||
|
|
ab6e5ba094 |
feat: boost SaveImageAdvanced node frequency for search ranking (#11853)
*PR Created by the Glary-Bot Agent* --- Adds an entry for the new `SaveImageAdvanced` node to `public/assets/sorted-custom-node-map.json` with the same frequency stat (1762) as the existing `SaveImage` node, so the new Save Image node ranks at the top of search results when typing "save" — matching the original node's behavior. Context: the new Save Image node ([Notion spec](https://www.notion.so/comfy-org/Save-Image-94a77c506ce145fc9b8c477c52091a04)) replaces/deprecates the original `SaveImage`. Search ranking uses the static node frequency map; the new node had no entry and was therefore ranked at frequency 0. Mirroring the original's stat is the manual-boost approach discussed in the thread. ## Changes - `public/assets/sorted-custom-node-map.json`: add `"SaveImageAdvanced": 1762` directly after `"SaveImage": 1762` to preserve descending sort order. ## Verification - `pnpm typecheck`, `pnpm lint`, and `pnpm format` all pass via lint-staged on commit. - JSON validated and entry placement confirmed (position 4, between `SaveImage` and `VAEDecode`). - Review (oracle) ran clean: 0 critical / 0 warning / 0 suggestion. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11853-feat-boost-SaveImageAdvanced-node-frequency-for-search-ranking-3546d73d36508168b058d9d750fc3c56) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
2322a5a497 |
fix: use webm video for VFX use case right asset (#12040)
*PR Created by the Glary-Bot Agent* --- ## Summary Replaces `right1.webp` with `right1.webm` in the VFX panel of `UseCaseSection`. `BlobMedia` already auto-detects `.webm` URLs and mounts a `<video>` element (with the `.webp` as poster), so this single URL swap is the only change required — matching the pattern used by the other 4 use-case panels. ## Files changed - `apps/website/src/components/home/UseCaseSection.vue` — swap `right1.webp` → `right1.webm`. ## Verification - `pnpm exec nx run website:typecheck` — clean - `pnpm exec eslint` on changed file — clean - `pnpm exec oxfmt --check` — clean - pre-commit lint-staged hooks — passed ## Reviewer note (Oracle finding) VFX is the default active panel, so the homepage's initial right-rail asset moves from ~131 KB `.webp` to ~4.1 MB `.webm`. Behaviorally consistent with the other 4 panels (which already use `.webm`), but worth confirming whether `right1.webm` should be re-encoded smaller on the CDN before promoting this PR out of draft. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12040-fix-use-webm-video-for-VFX-use-case-right-asset-3596d73d365081829976f37b733840f1) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
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) |
||
|
|
81d9df61f2 |
fix(website): tighten GitHub ticker spacing on wide screens (#12021)
*PR Created by the Glary-Bot Agent* --- ## Summary The GitHub star ticker reads as detached from the GitHub octocat icon on wider viewports. The CSS gap is constant (`gap-2` = 8px), but the 28px icon next to a 20px badge plus the fixed gap make the pair look like two separate items at `lg+` widths instead of a single coupled unit. ## Change `apps/website/src/components/common/GitHubStarBadge.vue`: - Inner gap `gap-2` → `gap-1` (8px → 4px) so the badge and icon sit closer together. - Icon `size-7` → `size-6 shrink-0` (28px → 24px) so the icon height is closer to the badge height (20px) and the pair reads as one unit. The outer `gap-2` between CTA items in `SiteNav.vue` is intentionally unchanged — it is the correct spacing between unrelated CTA elements. ## Verification - `pnpm typecheck` — 0 errors (1 pre-existing hint in unrelated file). - `pnpm format:check` — clean. - `pnpm exec eslint apps/website/src/components/common/GitHubStarBadge.vue` — clean. - `pnpm test:unit` (apps/website) — 30/30 pass. - Pre-commit hook (oxfmt + oxlint + eslint + typecheck + typecheck:website) — passed. - `/review` (Oracle) — 0 critical, 0 warnings, 0 suggestions. - Manual visual verification at 1280px, 1920px, and 2560px viewports. - Tested longer star strings (`999.9K`, `1.2M`) — no wrapping. ## Before / After (2560px viewport) Before: badge and octocat appear visually detached. After: badge and octocat read as a single tightly-coupled unit. ## Screenshots      ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12021-fix-website-tighten-GitHub-ticker-spacing-on-wide-screens-3586d73d365081be8d66dfbb22b8dc2c) 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> Co-authored-by: Alexander Brown <drjkl@comfy.org>v1.45.0 |
||
|
|
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).
|
||
|
|
5948002dee |
1.45.0 (#12037)
Minor version increment to 1.45.0 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12037-1-45-0-3596d73d365081609323df8b5aac04ce) by [Unito](https://www.unito.io) --------- Co-authored-by: DrJKL <448862+DrJKL@users.noreply.github.com> Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
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) |
||
|
|
e469611f6d |
perf: memoize asset display transform across filter tab switches (#11491)
## Root cause `useAssetBrowser`'s `filteredAssets` computed re-ran `.map(transformAssetForDisplay)` over the full result set on every tab switch. `transformAssetForDisplay` allocates fresh `badges`/`stats` objects, walks `tags`, calls `getAssetBaseModels`, and runs i18n date formatting per asset — none of which were memoized. Switching All / Inputs / Outputs forced N transforms per click and produced brand-new `AssetDisplayItem` references, which also defeated `:key`-based diffing in `AssetGrid` / `VirtualGrid` and re-rendered every visible card. ## Fix Memoize `transformAssetForDisplay` at module scope with a `WeakMap<AssetItem, AssetDisplayItem>`. Unchanged assets reuse the same display item across tab switches; the GC reclaims entries when assets are released. ## Before / after (n=200 assets, 6 tab switches: inputs → outputs → all → inputs → outputs → all) | Metric | Before | After | | ------------------------------- | -----: | ----: | | `transformAssetForDisplay` runs | 800 | 0 | | Wall time (Vitest harness) | 13.2 ms | 8.1 ms | | Reused `AssetDisplayItem` refs | 0 | 200 | Measured via `src/platform/assets/composables/useAssetBrowser.perf.test.ts` plus a temporary `process.stderr.write` harness. ## Red / green | Commit | Purpose | | --------- | ------- | | |
||
|
|
ad6cbf7cbe |
feat: align cloud batch count limit with server-side queue cap (#11876)
*PR Created by the Glary-Bot Agent* --- Raises `Comfy.QueueButton.BatchCountLimit` on cloud from `32` to `100` to match the server-side `MaxQueuedJobsPerUser` cap (`cloud/infrastructure/dynamicconfig/prod/config.json:3`). The desktop default was already `100` and is unchanged — collapsing both branches to the same constant. Addresses Discord feature request: [Increase queue batch limit from 200](https://discord.com/channels/1218270712402415686/1243609826299220039/1499104231381012641). ## Change ```diff - defaultValue: isCloud ? 32 : 100, + defaultValue: 100, ``` The setting is read dynamically by all batch count UIs (`BatchCountEdit.vue`, `LinearControls.vue`). ## Why 100 (not 512) Original ask was 200→512. Investigation showed: - The actual previous default was `100` (desktop) / `32` (cloud), not 200. - Cloud enforces `MaxQueuedJobsPerUser = 100` per workspace server-side. A higher frontend cap can't unlock more queued work — extra prompts just get rejected with `QUEUE_LIMIT`. - Frontend submits prompts as N sequential `POST /prompt` calls (no batched-prompt endpoint), so the UI cap is purely about how many clicks it takes — not throughput. - Going from 32 → 100 lets cloud users match the server cap in one click instead of 4. No new behavior is unlocked. ## Known limitation (pre-existing, not introduced here) The new max equals the absolute server cap, not the user's remaining capacity. A user with already-queued work can hit `QUEUE_LIMIT` mid-batch. The pre-existing 32 limit had the same shape (just at a smaller scale); deriving the UI max from `cap - outstanding` would require polling and reactive state and is out of scope for a one-line setting bump. ## Verification - `pnpm typecheck` — passes - `pnpm lint` — 0 errors (1 pre-existing warning in unrelated test file) - `pnpm test:unit` — `BatchCountEdit.test.ts` (3 tests) + `src/platform/settings/**` (70 tests) all pass - **Manual (Playwright)**: - `settingStore.get('Comfy.QueueButton.BatchCountLimit')` returns `100` at runtime - Typing `999` into the batch count widget clamps to `100` - Increment button is disabled at `100` (max reached) ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11876-feat-align-cloud-batch-count-limit-with-server-side-queue-cap-3566d73d3650819b8d01dbf83d1a8e49) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
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
|
||
|
|
d3ab2be695 |
test: reuse queue button page object in e2e (#11927)
## Summary Reuse the actionbar queue-button page object in the queue mode E2E tests so dropdown selectors live in one helper. ## Changes - **What**: Adds queue mode menu/item helpers to `ComfyActionbar.queueButton` and updates `queueButtonModes.spec.ts` to use them. - **Dependencies**: None. ## Review Focus This is stacked on #11209 and should be reviewed as a test-infra cleanup only; the behavior covered by the spec is unchanged. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11927-test-reuse-queue-button-page-object-in-e2e-3566d73d365081918d59c2d587c4c94a) by [Unito](https://www.unito.io) |
||
|
|
37f0fbcbef |
fix: add guard to prevent user store re-initialization (#11959)
## Summary Make `userStore.initialize()` idempotent and concurrency-safe so the bootstrap, router-guard, and UserSelectView callers share a single getUserConfig fetch instead of racing/duplicaitng calls. ## Changes - **What**: - cache initialize in a promise so callers all re-use the same result - remove now redundant is initialized guard - tests ## Review Focus - Current user switch/logout uses `window.location.reload()`, no callers intentionally call initialize to reinit. In future if this changes we may want to add a parameter to skip the cache or a separate function. - Failed initializes are not cached to allow callers to retry - Not practical for e2e tests, the unit tests prove that the requests are deduped. All a e2e test would do is mock/spy on the network requests to show multiple requests do not happen - which the unit tests do a better job of. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11959-fix-add-guard-to-prevent-user-store-re-initialization-3576d73d3650817db7b0e52cc25f9b7b) by [Unito](https://www.unito.io) |
||
|
|
6ef051f200 |
FE-537: fix(load3d): preserve camera view, fit transform, and first-frame paint after refresh (#11944)
## Summary - Defer thumbnail capture until camera state is restored via new modelReady event so captureThumbnail no longer races with the saved view, fixing the "snap back to default on hover" regression. - Repaint the live scene at the end of captureThumbnail so the canvas is not left with the offscreen mask/normal pass when the render loop is gated. - Persist post-fitToViewer model.scale + model.position into the existing modelConfig.gizmo slot so a refresh reapplies them via the existing applyGizmoConfigToLoad3d path; rotation stays owned by upDirection. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11944-FE-537-fix-load3d-preserve-camera-view-fit-transform-and-first-frame-paint-after-re-3576d73d365081429653ea4740612617) by [Unito](https://www.unito.io) |
||
|
|
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" /> |
||
|
|
d3f802de10 |
feat(pricing): add concurrent API jobs feature to Creator and Pro tiers (#12000)
*PR Created by the Glary-Bot Agent* --- ## Summary Adds a new feature bullet to the Creator and Pro plans on the [cloud pricing page](https://comfy.org/cloud/pricing) to call out included API concurrency: - **Creator**: `3 concurrent API jobs` - **Pro**: `5 concurrent API jobs` Free and Standard tiers do not include API access, so they are not changed. This matches the language landing in the docs PR: [Comfy-Org/docs#965](https://github.com/Comfy-Org/docs/pull/965). ## Changes - `apps/website/src/components/pricing/PriceSection.vue`: added `feature2` to the Creator and Pro plan feature lists. - `apps/website/src/i18n/translations.ts`: added `pricing.plan.creator.feature2` and `pricing.plan.pro.feature2` for `en` and `zh-CN`. - Updated `pricing-tiers-{1-sm,2-md,3-lg,4-xl}` visual regression snapshots in `apps/website/e2e/visual-responsive.spec.ts-snapshots/` to match the new copy. ## Verification - `pnpm nx run @comfyorg/website:typecheck` — clean - ESLint and `oxfmt` clean on changed files (pre-commit lint-staged also passed) - `pnpm exec playwright test --project visual -g "pricing-tiers"` — 4/4 passing against the regenerated snapshots - Manually rendered `localhost:4321/cloud/pricing`; confirmed copy appears in both desktop and mobile layouts and that Free / Standard are unchanged. Screenshots below. ## Screenshots ### Desktop  ### Mobile — Creator  ### Mobile — Pro  ## Screenshots    ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12000-feat-pricing-add-concurrent-API-jobs-feature-to-Creator-and-Pro-tiers-3586d73d365081559acfc44eb5024c52) 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> |
||
|
|
d78c630d36 |
test(maskeditor): expand useBrushDrawing behavioral coverage (#12001)
Adds targeted behavioral tests for the slimmed `useBrushDrawing`
orchestration composable (Phase E of the brush-drawing refactor).
## Changes
- 5 new tests covering previously untested branches:
- `compositeStroke` receives `isRgb=true` when active layer is `rgb`
- `compositeStroke` receives `isErasing=true` when tool is `eraser`
- Mask canvas opacity is restored after drawing on the mask layer
- `globalCompositeOperation` is set to `destination-out` during
`handleDrawing` when tool is eraser
- `globalCompositeOperation` is set to `destination-out` during
`handleDrawing` when right mouse button is held
## Coverage (useBrushDrawing.ts)
| Metric | Before | After |
|--------|--------|-------|
| Statements | 86.33% | 87.05% |
| Branches | 68.75% | 70.00% |
| Functions | 90.00% | 90.00% |
| Lines | 89.23% | 90.00% |
All 18 tests pass. GPU paths remain `/* c8 ignore */` excluded
(untestable without WebGL).
- Fixes #0
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Low risk: changes are limited to unit tests, adding coverage for
eraser/right-click composition and `drawEnd` GPU compositing/opacity
restoration paths without altering production logic.
>
> **Overview**
> Adds new `useBrushDrawing` test cases to cover previously untested
branches: setting `globalCompositeOperation` to `destination-out` during
`handleDrawing` when erasing (tool or right-click), and verifying
`drawEnd` passes correct `isRgb`/`isErasing` flags to
`gpu.compositeStroke`.
>
> Also asserts mask-layer opacity is restored after `drawEnd`,
increasing behavioral coverage around stroke completion and canvas
visibility cleanup.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
|
||
|
|
aa4343a98b |
test: add perf test for subgraph transition bottleneck (#10480)
## Summary Add a `@perf` test measuring the cost of entering a subgraph containing 80 interior nodes. Establishes a CI baseline for the synchronous mount/unmount bottleneck. ## Changes - **What**: Add `subgraph transition (enter and exit)` perf test to `performance.spec.ts` and a test workflow asset (`large-subgraph-80-nodes.json`) with a single subgraph node containing 80 Note nodes. ## Review Focus This is PR 1 of 2. The test establishes a baseline on main so the optimization PR (PR 2) can show a CI-proven delta for `taskDurationMs` and `totalBlockingTimeMs`. The test: 1. Loads the 80-node subgraph workflow 2. Enters and exits once to warm up 3. Measures a fresh enter transition (start → 80 nodes mounted → layout settled) 4. Records `taskDurationMs`, `layouts`, and `TBT` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10480-test-add-perf-test-for-subgraph-transition-bottleneck-32d6d73d3650811b9b6eec03a9591f82) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com> Co-authored-by: Connor Byrne <c.byrne@comfy.org> |
||
|
|
270c7e34f4 |
fix: hide Google free-tier copy in webviews (#11924)
Stacked on #10699. - Hide Google-specific free-tier promo copy when embedded webviews block Google SSO. - Use GitHub-only fallback copy when returning from email signup in that state. - Remove the unused export from the Google SSO blocked-reason type so knip stays clean. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11924-fix-hide-Google-free-tier-copy-in-webviews-3566d73d36508168be7ed28cbe455d9f) by [Unito](https://www.unito.io) --------- Co-authored-by: bymyself <cbyrne@comfy.org> |
||
|
|
666684e6e6 |
fix: stop PreviewAny widgets from triggering re-execution (#12010)
## Summary Preview as Text (`PreviewAny`) nodes were re-executing on every prompt submission because the rendered preview text was being echoed back to the backend as input values, mutating the cache signature. ## Changes - **What**: Set `widget.options.serialize = false` on the three widgets the `Comfy.PreviewAny` extension adds (`preview_markdown`, `preview_text`, `previewMode`) so they are excluded from the API prompt sent to the backend. ## Root cause The extension was setting `widget.serialize = false`, which only controls **workflow JSON** persistence (checked in `LGraphNode.serialize`). The **API prompt** serializer in `executionUtil.graphToPrompt` checks `widget.options.serialize` instead — a distinct property documented in litegraph's `WIDGET_SERIALIZATION` convention. After `onExecuted` writes the rendered text into the widget value, the next `graphToPrompt` call serialized that text into `inputs.preview_text` / `inputs.preview_markdown`. The backend cache signature in `comfy_execution/caching.py` hashes all keys in `node["inputs"]`, so the changing text invalidated the cache and forced a redundant execution every time. ## Review Focus Two commits, red-green TDD: 1. `test:` failing unit + e2e tests asserting the desired behavior. 2. `fix:` adds `options.serialize = false` to make them pass. Tests verify the widgets are excluded from the API prompt; e2e additionally simulates a prior execution populating widget values to mirror the real bug condition. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12010-fix-stop-PreviewAny-widgets-from-triggering-re-execution-3586d73d3650810585cdd077f3ac64f5) by [Unito](https://www.unito.io) |
||
|
|
4484b62854 |
test: add E2E tests for queue button modes (#11209)
## Changes Add Playwright E2E tests for queue button modes in the topbar (6 tests): - Run button visibility in topbar - Queue mode trigger menu visibility - Opening the mode menu via trigger click - Verifying available modes are shown as menu item radios - Menu closes after selecting a mode - Run button sends prompt when clicked (via intercepted `/api/prompt` route) ## Test ```bash pnpm test:browser:local -- browser_tests/tests/queueButtonModes.spec.ts ``` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11209-test-add-E2E-tests-for-queue-button-modes-3416d73d365081a5bf10f8b9c6bdc2a7) by [Unito](https://www.unito.io) --------- Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com> |
||
|
|
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> |
||
|
|
3e6f3444e5 |
1.44.18 (#11998)
Patch version increment to 1.44.18 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11998-1-44-18-3586d73d3650812c990ad3ba0d222d0a) by [Unito](https://www.unito.io) --------- Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com> Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> Co-authored-by: DrJKL <DrJKL0424@gmail.com>v1.44.18 |
||
|
|
e46667b33f |
feat: replace spin logo video with Three.js instance in homepage HeroSection (#11964)
Replace the pre-rendered `.webm` video in the homepage hero section with an interactive Three.js 3D logo. ## Changes - Add `three` dependency to the website package and pnpm catalog - Add `useHeroLogo` composable that sets up the Three.js scene: - Extruded Comfy "C" logo with stencil-masked image sequence slideshow - Auto-rotation with drag interaction and cursor tilt - Graceful degradation if some textures fail to load - Update `HeroSection.vue` to use the composable instead of a `<video>` element - Upload 16 image sequence frames to `gs://comfy-org-videos/website/homepage/hero-logo-seq/` <img width="1000" height="648" alt="Kapture 2026-05-05 at 20 54 05" src="https://github.com/user-attachments/assets/7a7b1634-2da3-4aa2-871a-f64d4d337b39" /> @coderabbitai approve ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11964-feat-replace-spin-logo-video-with-Three-js-instance-in-homepage-HeroSection-3576d73d365081bbab0ed19dd121830c) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
d5121d3fed |
fix: converge asset tag cache and server on partial-failure (#11695)
## Summary `assetsStore.updateAssetTags` issues `removeAssetTags` and `addAssetTags` as two separate network calls. When the remove succeeds server-side but the subsequent add rejects, the cache rolls back to the original tags while the server has already dropped the removed tags — cache and backend diverge until the next refetch. This adds a compensating action: if remove succeeded and add then fails, attempt to re-add the just-removed tags so the server returns to its prior state. If the compensating add also fails, invalidate the category cache so the next access refetches fresh state. - Fixes #11694 - Fixes [FE-473](https://linear.app/comfyorg/issue/FE-473/fix-converge-asset-tag-cache-and-server-on-partial-failure) ## Changes - `src/stores/assetsStore.ts`: track which tags were already removed server-side; on add-failure, re-add them; if compensation fails, invalidate the resolved category cache via `resolveCategory` + `invalidateCategory`. - `src/stores/assetsStore.test.ts`: extend the cloud asset-service mock with `addAssetTags` / `removeAssetTags` and add a `updateAssetTags partial-failure compensation` describe block: - re-adds removed tags when add fails so cache and server converge - invalidates the category cache when compensation also fails - does not attempt compensation when only the add was attempted (no remove ran) ## Test plan - [x] `pnpm exec vitest run src/stores/assetsStore.test.ts` — 42 passed (39 prior + 3 new) - [x] `pnpm typecheck` - [x] `pnpm lint` - [x] `pnpm knip` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11695-fix-converge-asset-tag-cache-and-server-on-partial-failure-34f6d73d365081149900f95b6ee4bfa9) by [Unito](https://www.unito.io) |
||
|
|
733917d5cf |
[chore] Update Comfy Registry API types from comfy-api@69cbc3b (#11994)
## Automated API Type Update This PR updates the Comfy Registry API types from the latest comfy-api OpenAPI specification. - API commit: 69cbc3b - Generated on: 2026-05-05T15:48:16Z These types are automatically generated using openapi-typescript. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11994-chore-Update-Comfy-Registry-API-types-from-comfy-api-69cbc3b-3576d73d3650818f85e4c98c783a6490) by [Unito](https://www.unito.io) Co-authored-by: james00012 <96548424+james00012@users.noreply.github.com> |
||
|
|
08967bc684 |
Fix pruning of uninitialized promoted primitives (#11987)
Primitive nodes do not create their widgets until their `onAfterGraphConfigured` method is called. Previously, when a subgraphNode contains a linked widget, any proxyWidgets that do not resolve to a real widget are pruned at time of configure. Since this occurs prior to initialization of the primitive, the primitive value would be de-promoted before the widget could initialize This is resolved by the minimally disruptive change of allowing proxied promotions to primitive nodes to be kept so long as the node itself can be found. |
||
|
|
fb32b9a5c5 |
fix(website): prevent HeroSection fade from bleeding into CloudBannerSection on /download (#11974)
*PR Created by the Glary-Bot Agent* --- ## Summary The left side of `CloudBannerSection` on `/download` showed an unintended fade-out: the bottom-left of the banner appeared darker than the rest of the bar. ## Root cause `product/local/HeroSection.vue` renders an SVG illustration whose container has `lg:z-1` and whose SVG element has `overflow-visible`. The SVG contains a left-edge fade `<rect x="300" y="150" width="250" height="900" fill="url(#localHeroFadeLeft)" />` that paints outside the SVG's `viewBox` (`400 200 550 800`) — including upward into the area occupied by the preceding `CloudBannerSection`. Because `CloudBannerSection` had `position: static` and `z-auto`, the positively-stacked illustration painted over the banner's bottom-left, producing the visible darkening. ## Fix Establish a stacking context on `CloudBannerSection` (`relative z-20`) so it always renders above the hero illustration's overflow on every page that includes the banner (download, api, cloud/enterprise, and zh-CN equivalents). This is a minimal, isolated change to the shared component — no logic or markup structure changes. ## Verification - Reproduced visually at `lg` breakpoint and confirmed the fade is gone after the fix. - Verified `/download`, `/api`, `/cloud/enterprise`, and `/zh-CN/download` render correctly. - `pnpm typecheck` and `pnpm typecheck:website` pass (run automatically by pre-commit hook). - `oxfmt`, `oxlint`, `eslint`, `stylelint` all pass. ### Before  ### After  ## Follow-up Consider adding a Playwright visual regression test for the banner/hero seam on `/download` to catch future stacking regressions (called out by review). ## Screenshots   ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11974-fix-website-prevent-HeroSection-fade-from-bleeding-into-CloudBannerSection-on-downloa-3576d73d3650813d8924fb54d5f78cee) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
6474faaa17 |
fix(website): prevent illustration cutoff in enterprise hero section (#11973)
*PR Created by the Glary-Bot Agent* --- ## Summary The illustration in the `HeroSection` of `/cloud/enterprise` was getting cut off at the top — the topmost ripple ring was clipped by the section's `overflow-y-clip`/`overflow-hidden`. **Root cause:** the SVG wrapper has `scale-150`, which makes the rendered illustration 50% larger than its layout box and overflows symmetrically (~25% above, ~25% below). The section only had `lg:pb-[min(8vw,10rem)]` — bottom padding — and on lg the wrapper had `lg:translate-y-[40px]` to nudge it down, but that wasn't enough room for the top to escape clipping. On mobile there was no padding at all, so the same issue occurred. **Fix:** add symmetric vertical padding (`pt-16` on mobile, `lg:pt-[min(8vw,10rem)]` mirroring the existing bottom value on lg) so the scaled illustration has room above and below. Removed the now-unnecessary `lg:translate-y-[40px]` since symmetric padding keeps the illustration vertically centered within the flex row. Verified at 375px (mobile), 1024px (lg), and 1440px (xl) viewports — all four ripple rings render fully without clipping at the top. ## Verification - `pnpm typecheck:website` ✅ - `pnpm exec oxfmt --check` on edited file ✅ - `pnpm exec oxlint` on edited file ✅ - `pnpm --filter @comfyorg/website build` ✅ - Pre-commit hooks (stylelint, oxfmt, oxlint, eslint, typecheck, typecheck:website) ✅ - Visual verification with Playwright at mobile / lg / xl ## Before vs After **Desktop (1440px) — before:** the topmost ripple ring is clipped at the top of the section. **Desktop (1440px) — after:** all four ripple rings are fully visible. **Mobile (375px) — before:** the top of the outermost ring is cut off by the section's top edge. **Mobile (375px) — after:** the full illustration (rings + blocks) is visible. ## Screenshots     ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11973-fix-website-prevent-illustration-cutoff-in-enterprise-hero-section-3576d73d3650813f9f04c3f93b9b42d6) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
da6a3e0722 |
test: add tests for dragging workflow tabs (#11971)
## Summary Adds tests for drag to reorder workflow tabs ## Changes - **What**: - test drag start/end, ensure active tab is maintained ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11971-test-add-tests-for-dragging-workflow-tabs-3576d73d365081d090fccfc8804fa6aa) by [Unito](https://www.unito.io) |
||
|
|
eecbaa8f39 |
fix(website): change 'Blogs' to 'Blog' in Resources nav dropdown (#11970)
*PR Created by the Glary-Bot Agent* --- ## Summary Corrects the "Blogs" label to "Blog" (singular) in the website header's Resources dropdown menu, as requested in #website-and-docs Slack channel. ## Changes - `apps/website/src/i18n/translations.ts`: `nav.blogs` English value `Blogs` → `Blog` (zh-CN translation `博客` left unchanged since it was already correct) This also makes the header consistent with the footer, which already labeled the same link as "Blog". ## Verification - `pnpm typecheck` (astro check): 0 errors, 0 warnings, 0 hints (87 files) - `pnpm test:unit`: 30 tests passed across 4 files - `pnpm exec eslint apps/website/src/i18n/translations.ts`: clean - Manual verification via Playwright on `pnpm dev` — Resources dropdown now displays "Blog" - Code review (oracle): 0 issues found ## Screenshot Resources dropdown after the change: ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11970-fix-website-change-Blogs-to-Blog-in-Resources-nav-dropdown-3576d73d365081638245d235bec04230) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
0e110bec0d |
fix(i18n): rename OpenAI GPT Image 1 to GPT Image 2 across locales (#11968)
## Summary Aligns the `OpenAIGPTImage1` node display name in all 11 non-English `nodeDefs.json` locale files with the English source-of-truth, which was already updated to `OpenAI GPT Image 2`. ## Changes - **What**: Updates `display_name`, the description string, and the prompt tooltip in `ja`, `ru`, `zh`, `zh-TW`, `ar`, `pt-BR`, `ko`, `fr`, `es`, `fa`, and `tr` locales from `GPT Image 1` to `GPT Image 2` (and `GPT Görüntü 1` → `GPT Görüntü 2` in Turkish, `GPT صورة 1` → `GPT صورة 2` in Arabic). Other tooltips already referenced `GPT Image 2` and are unchanged. - **Breaking**: None — the registry node id `OpenAIGPTImage1` is preserved (it is an internal identifier, not user-facing). ## Review Focus - Translations were updated mechanically — please confirm the version-number change is acceptable as-is for non-Latin scripts (Arabic, Persian, Korean, Japanese, Chinese) where the version number was kept as `2` per the existing pattern. - The English locale already used `OpenAI GPT Image 2`, so this PR brings the other locales into sync; no English copy was changed. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11968-fix-i18n-rename-OpenAI-GPT-Image-1-to-GPT-Image-2-across-locales-3576d73d365081bfa204cbf528d84bf3) by [Unito](https://www.unito.io) Co-authored-by: Marwan Ahmed <marwan@Marwans-MacBook-Pro.local> |
||
|
|
32984459bf |
ci: exclude release branches from website previews (#11952)
## Summary Exclude core and cloud minor release branches from Vercel website preview deployments. ## Changes - **What**: Added `pull_request.branches-ignore` entries for `core/*` and `cloud/*` to the Vercel Website Preview workflow. ## Review Focus Confirm the branch exclusion patterns match the minor release branch naming convention. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11952-ci-exclude-release-branches-from-website-previews-3576d73d36508194b835eda9bc12f174) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
0307281ff2 |
fix: highlight missing input slots on Vue nodes (#11950)
## Summary Restores required-input validation highlighting on Vue node input slots. ## Changes - **What**: Passes validation error state from `NodeSlots` to `InputSlot` using node locator IDs, including subgraph and nested subgraph execution IDs. - **What**: Adds unit coverage for root, one-level subgraph, and nested subgraph slot error mapping. - **What**: Adds a Vue Nodes screenshot regression test that asserts the missing required input slot itself receives the error highlight. - **Dependencies**: None. ## Review Focus - Required input errors on Vue-rendered node's slots. - The new Playwright screenshot expectation will need the `New Browser Test Expectation` label for Linux baseline generation. ## Screenshots (if applicable) Before <img width="499" height="324" alt="스크린샷 2026-05-05 오후 3 00 44" src="https://github.com/user-attachments/assets/285fdf91-6d7e-480b-99b9-715705f78914" /> After <img width="482" height="356" alt="스크린샷 2026-05-05 오후 3 01 11" src="https://github.com/user-attachments/assets/51b8db49-eb9c-4155-8aa5-109c0bd7699b" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11950-fix-highlight-missing-input-slots-on-Vue-nodes-3576d73d365081bd85bfd1ea149d45c5) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
21406dceb1 |
fix: skip nested subgraph containers in replay scan (#11908)
## Summary Fixes the Cloud-only nested subgraph missing-model false positive covered by the stacked regression test in #11907. When returning from an outer subgraph to the root graph, the Vue graph node manager replays `onNodeAdded` for existing graph nodes. The realtime error-clearing hook handled a subgraph container by recursively scanning all interior nodes. For nested subgraphs, that also scanned the nested subgraph container itself. Nested subgraph container widgets are promoted synthetic views of interior widgets. Scanning them as real model-loader nodes is wrong: the container node type is the subgraph UUID, not `UNETLoader`, so Cloud asset resolution can classify an installed promoted model as missing. ## Changes - Skip nested subgraph container nodes during parent subgraph replay scans. - Keep scanning real active interior leaf nodes. - Add unit coverage proving the replay scan visits the `UNETLoader` leaf but not the nested subgraph container. - Remove the `test.fail()` annotation from the Cloud E2E regression test added in #11907. ## Stacked PR This PR is stacked on #11907. After #11907 lands, this branch should be rebased or retargeted onto `main`. ## Verification - `pnpm exec vitest run src/composables/graph/useErrorClearingHooks.test.ts -t "skips nested subgraph containers during parent subgraph replay scan"` - `pnpm exec oxfmt --check src/composables/graph/useErrorClearingHooks.ts src/composables/graph/useErrorClearingHooks.test.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts` - `pnpm exec eslint src/composables/graph/useErrorClearingHooks.ts src/composables/graph/useErrorClearingHooks.test.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts` - `pnpm exec oxlint src/composables/graph/useErrorClearingHooks.ts src/composables/graph/useErrorClearingHooks.test.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts --type-aware` - `pnpm typecheck` - `pnpm typecheck:browser` - `pnpm build:cloud` - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188 pnpm exec playwright test browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts --project=cloud` - commit hook: `pnpm typecheck`, `pnpm typecheck:browser` - push hook: `pnpm knip` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11908-fix-skip-nested-subgraph-containers-in-replay-scan-3566d73d3650819c8687d6ab74add1b9) by [Unito](https://www.unito.io) |
||
|
|
14320a131f |
test: add Playwright regression test for nested subgraph Cloud missing model (#11907)
## Summary Adds a Cloud Playwright regression test for the nested subgraph case where an installed Lotus diffusion model is incorrectly surfaced as missing after returning to the root graph. The fixture keeps the reproduction small: root graph -> subgraph node -> nested subgraph node -> `UNETLoader` using `lotus-depth-d-v1-1.safetensors`. The test stubs `/api/assets` through the shared asset API fixture so that model is explicitly present as a `diffusion_models` asset. This test is intentionally written as an XFAIL regression guard. Its setup and precondition checks are outside the XFAIL section: initial workflow load must not show the error overlay, the Errors tab must initially stay hidden, subgraph entry must succeed, root return must succeed, and the replay scan must run. Only the final `Errors` tab visibility assertion is expected to fail on current Cloud behavior. ## What a green run means A green CI run for this PR means the Cloud-only bug was reproduced at the intended point. The test reaches the root-return replay scan, verifies that the replay scan ran, and then current Cloud behavior makes the Errors tab visible even though the Lotus model exists in `/api/assets`. If any earlier setup or navigation step fails, or if the root-return replay scan does not run, the test fails normally because those checks happen before `test.fail()` is applied. Locally, removing `test.fail()` produces the expected red result after the replay-scan precondition passes, with `panel-tab-errors` visible. The intended post-fix contract is that the replay scan still runs, but the Errors tab remains hidden. ## Why this is XFAIL This PR intentionally ships only the regression test, not the production fix. The final behavioral assertion is annotated with `test.fail()` because the current Cloud replay path still treats the nested subgraph promoted model widget as missing. When the follow-up fix lands, Playwright will report this test as an unexpected pass until the `test.fail()` annotation is removed. That is the handoff point for converting this regression guard into a normal passing E2E test. ## Follow-up The stacked fix PR is #11908. It updates the replay scan so nested subgraph container nodes are skipped, then removes the `test.fail()` annotation from this test. ## Verification - `pnpm exec oxfmt --check browser_tests/fixtures/assetApiFixture.ts browser_tests/tests/cloud-asset-default.spec.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts` - `pnpm exec oxlint browser_tests/fixtures/assetApiFixture.ts browser_tests/tests/cloud-asset-default.spec.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts --type-aware` - `pnpm exec eslint browser_tests/fixtures/assetApiFixture.ts browser_tests/tests/cloud-asset-default.spec.ts browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts` - `pnpm typecheck:browser` - `pnpm typecheck` - `pnpm lint` - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188 pnpm exec playwright test browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts browser_tests/tests/cloud-asset-default.spec.ts --project=cloud` - Temporarily removed `test.fail()` locally and verified the test fails only after the replay-scan precondition passes, with `panel-tab-errors` visible ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11907-test-add-Playwright-regression-test-for-nested-subgraph-Cloud-missing-model-3566d73d3650810b86d4de916c2852f9) by [Unito](https://www.unito.io) |
||
|
|
a763c7132c |
feat(website): add "comfyui app" SEO keywords to product pages (#11834)
*PR Created by the Glary-Bot Agent* --- ## Summary Adds "comfyui app" / "comfyui web app" / "comfy ui application" keywords to the titles and meta descriptions of the home, download, and Comfy Cloud pages (and zh-CN equivalents) to recover organic traffic for those queries. ## Context Organic traffic for the query **"comfyui app"** dropped after `https://docs.comfy.org/interface/app-mode` started outranking the product/landing pages. The docs page about app-mode converts worse than the product pages, so we want Google to prefer comfy.org product pages for that query. The cleanest, lowest-risk lever is on-page SEO metadata. ## Changes - **What**: - `apps/website/src/pages/index.astro` → title `ComfyUI App — Professional Control of Visual AI` + product-focused description. - `apps/website/src/pages/download.astro` → title `Download the ComfyUI App — Run Visual AI Locally` + desktop-app description. - `apps/website/src/pages/cloud/index.astro` → title `Comfy Cloud — The ComfyUI Web App` + web-app description. - `apps/website/src/pages/zh-CN/{index,download,cloud/index}.astro` → localised Chinese titles and descriptions so the zh-CN product pages no longer fall back to the English `BaseLayout` default. - `apps/website/src/layouts/BaseLayout.astro` → unchanged net-net (touched then reverted to neutral copy after review feedback so non-product / non-localised pages keep their existing, generic fallback). - **Breaking**: none. Visual content, routing, and components are untouched — only `<title>` and `<meta>` tags change. ## Review Focus - The keyword copy reads naturally (no stuffing) and stays under typical SERP truncation limits (≤ ~165 chars). - zh-CN pages get Chinese descriptions — they intentionally don't repeat the English keywords, since "comfyui app" is an English-language query. - Pre-existing behaviour preserved: zh-CN pages **without** an explicit description still inherit the English `BaseLayout` default. Fixing that fallback for the whole zh-CN tree is out of scope for this PR — happy to follow up if desired. ## Verification - `pnpm typecheck` — 0 errors - `pnpm build` — 39 pages built clean - `pnpm test:unit` — 23/23 pass - `pnpm format:check apps/website/src` — clean - Manually verified rendered `<title>` and `<meta name="description">` via Playwright on `/`, `/download`, `/cloud`, and the zh-CN equivalents. ## Screenshots Home page rendered with the new title (visible in browser tab / SERP preview); visual content unchanged. ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11834-feat-website-add-comfyui-app-SEO-keywords-to-product-pages-3546d73d3650819da11bd665c2fcfb88) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
3f223dbbb4 |
test: add jobs api browser mock fixture (#11280)
## Summary
Add a typed Playwright jobs API mock and migrate the floating queue
overlay browser spec onto it.
## Changes
- replace the backend/seed terminology with `JobsApiMock`,
`jobsApiMockFixture`, `mockJobs()`, and `MockJobRecord`
- keep the mock at the network boundary with `page.route()` for
`/api/jobs`, `/api/jobs/{id}`, and `/api/history`
- remove backend-like query behavior that these browser tests do not
use, including sort handling, workflow filtering, and strict limit
validation
## Why
This keeps jobs coverage fast and profile-independent while avoiding
backend architecture changes for test setup. The fixture now serves only
the response shapes the UI consumes instead of pretending to be a
general in-memory backend.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11280-test-add-in-memory-jobs-backend-fixture-3436d73d365081bb87e8c9771654496c)
by [Unito](https://www.unito.io)
|
||
|
|
60f789d580 |
test: add OutputHistory.vue component tests (#11140)
## Summary Add 29 Vitest component tests for `OutputHistory.vue`, which previously had 0% coverage (132 missed lines). ## Changes - **What**: New test file `src/renderer/extensions/linearMode/OutputHistory.test.ts` covering rendering, selection behavior, emit updateSelection, workflow tab switch, media change watcher, and keyboard navigation. ## Review Focus - Mock setup for stores (`linearOutputStore`, `workflowStore`, `appModeStore`, `queueStore`) and composables (`useOutputHistory`) - Keyboard navigation tests dispatching events on `document.body` - Selection emission tests verifying `updateSelection` event payloads ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11140-test-add-OutputHistory-vue-component-tests-33e6d73d3650811692cdc36fdd41e9ba) by [Unito](https://www.unito.io) --------- Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com> |
||
|
|
24fc11aa3e |
fix(website): remove placeholder author info for groove-jones customer story (#11937)
*PR Created by the Glary-Bot Agent* --- Removes the placeholder "GROOVE JONES CONTRIBUTORS" author card from the Groove Jones customer story (`/customers/groove-jones`). The card was rendering with `TBD` / `待补充` values for the contributor name and role. ## Change Deletes 3 i18n keys from `apps/website/src/i18n/translations.ts`: - `customers.detail.groove-jones.topic-10.block.2.label` - `customers.detail.groove-jones.topic-10.block.2.name` - `customers.detail.groove-jones.topic-10.block.2.role` Block types in `ContentSection.vue` are inferred from the presence of suffix keys (`.role` → `author` block) via `deriveSections` in `apps/website/src/config/contentSections.ts`. Removing the keys causes the author card to drop out of the rendered output entirely. The other two blocks in topic-10 (intro paragraph + Dale Carman blockquote) remain unchanged. ## Verification - `pnpm typecheck` — passes - `pnpm lint` — 0 errors (1 pre-existing warning, unrelated) - `pnpm format` — applied - `pnpm knip` — clean (1 pre-existing warning, unrelated) - Manual: ran `pnpm dev` for the website app, navigated to `/customers/groove-jones`, confirmed the conclusion section ends naturally — no `TBD` text, no orphan `CONTRIBUTORS` label, no broken card. Code review (Oracle): 0 critical / 0 warning / 0 suggestion. ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11937-fix-website-remove-placeholder-author-info-for-groove-jones-customer-story-3576d73d36508193b1a0c0c3cd887686) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
055486cac0 |
feat(website): add 4 team photos and remove infinite scroll loop (#11945)
## Summary Add 4 new team photos and remove the infinite scroll behavior from the careers page team photos carousel. ## Changes - **What**: - Add 4 new photos (team4–team7) to `TeamPhotosSection.vue` - Remove the infinite scroll loop (`loopedPhotos`, `onScroll` handler, `onMounted` scroll initializer) <img width="1000" height="308" alt="Kapture 2026-05-05 at 11 02 16" src="https://github.com/user-attachments/assets/f5f6737f-c6bf-4abf-8780-d72c895f4015" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11945-feat-website-add-4-team-photos-and-remove-infinite-scroll-loop-3576d73d365081cabebecbc06666b9d9) by [Unito](https://www.unito.io) Co-authored-by: Amp <amp@ampcode.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. |
||
|
|
d1df5fadf8 |
fix(website): update payment-failed heading to "Unable to complete payment" (#11943)
*PR Created by the Glary-Bot Agent* --- ## Summary Reword the `payment.failed.title` copy on `comfy.org/payment/failed` from "Payment was not completed" to "Unable to complete payment" — a more active, distinguishing phrasing per design feedback. ## Changes - `apps/website/src/i18n/translations.ts` — update English (`Unable to complete payment`) and Chinese (`无法完成支付`) translations - `apps/website/e2e/payment.spec.ts` — update both English and zh-CN heading assertions to match ## Verification - `pnpm --filter website typecheck` — passes - `pnpm --filter website test:unit` — 30 tests passing - Pre-commit hooks (oxfmt, oxlint, eslint, typecheck, typecheck:website) — all pass - Manual visual verification with Playwright on `/payment/failed` and `/zh-CN/payment/failed` — both render the new heading correctly (screenshots attached) ## Screenshots   ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11943-fix-website-update-payment-failed-heading-to-Unable-to-complete-payment-3576d73d3650817e85e2e7a3891cc307) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
7d67fe364b |
[chore] Update Comfy Registry API types from comfy-api@274f83b (#11948)
## Automated API Type Update This PR updates the Comfy Registry API types from the latest comfy-api OpenAPI specification. - API commit: 274f83b - Generated on: 2026-05-05T04:14:20Z These types are automatically generated using openapi-typescript. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11948-chore-Update-Comfy-Registry-API-types-from-comfy-api-274f83b-3576d73d3650813b9a39f8d0f7183445) by [Unito](https://www.unito.io) Co-authored-by: coderfromthenorth93 <213232275+coderfromthenorth93@users.noreply.github.com> |