mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-24 14:45:36 +00:00
9558d26e8f93b4fdf72d69a8d60ca02b2dbfa6fe
7978 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9558d26e8f |
fix: use lucide circle-x icon for cancelled state
Per design request, the cancelled state icon should mirror the failed state icon's properties but use circle-x instead of circle-alert. Also normalize the failed icon name from the alert-circle alias to the canonical circle-alert used throughout the rest of the codebase. |
||
|
|
0615de1f76 |
feat: distinguish cancelled state in job context menu
Address review feedback: cancelled jobs were falling through to the default non-terminal context menu which showed a no-op Cancel job action. Add a dedicated cancelled menu branch that mirrors the failed menu but omits the error-message actions, since cancelled jobs have no execution_error to report. |
||
|
|
859b898220 |
feat: distinguish Cancelled state from Failed in job queue UI
The /jobs API exposes cancelled and failed as distinct statuses, but the frontend was collapsing them into a single failed state. As a result, cancelled jobs displayed a Failed label and rendered an empty error message container in the details popover. - Add cancelled to JobState and a dedicated mapping in jobStateFromTask - Render cancelled jobs with their own icon, labels, and detail rows - Add a Cancelled filter tab that only appears when cancelled jobs exist - Skip the error message section for cancelled jobs in the popover - Allow cancelled jobs to be deleted from the list like failed jobs Fixes empty error container for cancelled jobs. |
||
|
|
74caeb0b0b |
fix: detect V1/V2 draft storage keys in new-user check (#11728)
## Problem `checkIsNewUser()` in `useNewUserService` only checked legacy pre-V1 localStorage keys (`workflow`, `Comfy.PreviousWorkflow`) to determine if a user had prior workflow history. A returning user who had only ever used the V1 or V2 draft persistence system would have neither of those keys set, causing `isNewUser()` to return `true` and the getting-started tab to appear in the workflow templates dialog after a settings reset. ## Solution Extend the check to also cover: - **V1 draft store keys**: `Comfy.Workflow.Drafts`, `Comfy.Workflow.DraftOrder` - **V2 draft index key**: `Comfy.Workflow.DraftIndex.v2:personal` The `personal` scope is hardcoded for the V2 check because at the time `checkIsNewUser()` runs, the cloud workspace ID (stored in sessionStorage) may not be set yet. This is fine — any genuine new user will have no personal workspace index regardless. The original legacy keys are preserved for users who may still have them from older installs. ## Tests Added three new test cases covering V1 draft store keys, V1 draft order key, and V2 draft index key. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11728-fix-detect-V1-V2-draft-storage-keys-in-new-user-check-3506d73d3650819ca4cfc8e83d95c258) by [Unito](https://www.unito.io) --------- Co-authored-by: Connor Byrne <c.byrne@comfy.org> |
||
|
|
ced7c93e63 |
testing: Improve custom checks in .coderabbit.yaml (#12141)
Update coderabbit end-to-end check logic. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12141-testing-Improve-custom-checks-in-coderabbit-yaml-35d6d73d3650818e8be2f0b7d403683b) by [Unito](https://www.unito.io) --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
759ed3d4e2 |
feat(website): add community-workflows demo page (#11942)
*PR Created by the Glary-Bot Agent* --- Adds a new interactive demo page at `comfy.org/demos/community-workflows` for the [Explore and Use a Community Workflow from the Hub](https://app.arcade.software/flows/mqZh17oWDuWIyhK0xwEV/view) Arcade walkthrough. Built on top of the demo infrastructure merged in #11436. ## Changes - `apps/website/src/config/demos.ts` — register the new demo - `apps/website/src/i18n/translations.ts` — add en + zh-CN strings (title, description, transcript) - `apps/website/public/images/demos/community-workflows-og.png` — 1200×630 OG image so email/social previews render correctly - `apps/website/public/images/demos/community-workflows-thumb.webp` — 1280×720 WebP thumbnail - `apps/website/e2e/demos.spec.ts` — refactored to iterate `demos` from config so every demo (current + future) is exercised in both en and zh-CN, and the iframe `src` is asserted to contain the correct Arcade ID Adding a new demo only requires editing `demos.ts` + `translations.ts` going forward; the e2e refactor is a one-time generalization that gives future demos coverage automatically. ## Verification - `pnpm typecheck:website`: 0 errors, 0 warnings, 0 hints - Pre-commit hook ran `pnpm typecheck`, `oxfmt`, `oxlint`, `eslint` — all clean on staged files - `npx astro build`: 53 pages built; `/demos/community-workflows/` and `/zh-CN/demos/community-workflows/` generated and present in `sitemap-0.xml` - Page rendered in Playwright preview: hero (title, GETTING STARTED, BEGINNER, ~2 min), Arcade embed loads, transcript section present, "What's Next" links to `image-to-video` - zh-CN page shows localized title (探索并使用社区工作流), description, badges, and "What's Next" heading - OG meta tag references the new 1200×630 PNG ## Screenshots   ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11942-feat-website-add-community-workflows-demo-page-3576d73d36508139b647c774b1d39323) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
5d53e75d23 |
test: add tests for deprecated & api node badge visibility settings (#11681)
## Summary Adds coverage for untested node badges ## Changes - **What**: - add shared addNode helper - will follow up to standardize across tests - add deprecated & api node badge tests in LiteGraph & Vue nodes ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11681-test-add-tests-for-deprecated-api-node-badge-visibility-settings-34f6d73d365081569129ecffa608122e) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
d23e86d9a4 |
[chore] Update Ingest API types from cloud@0a03f3a (#12043)
## Automated Ingest API Type Update This PR updates the Ingest API TypeScript types and Zod schemas from the latest cloud OpenAPI specification. - Cloud commit: 0a03f3a - Generated using @hey-api/openapi-ts with Zod plugin These types cover cloud-only endpoints (workspaces, billing, secrets, assets, tasks, etc.). Overlapping endpoints shared with the local ComfyUI Python backend are excluded. --------- Co-authored-by: MillerMedia <7741082+MillerMedia@users.noreply.github.com> Co-authored-by: GitHub Action <action@github.com> |
||
|
|
d901c63a0b |
feat: convert careers CategoryNav to scroll-spy locator (#12110)
*PR Created by the Glary-Bot Agent*
---
Converts the `CategoryNav` in the careers `RolesSection` from a
click-to-filter button into a scroll-spy section locator, matching the
pattern already used by `ContentSection.vue` (customer story details,
TOS, privacy policy).
## Changes
- **`apps/website/src/components/careers/RolesSection.vue`**
- Replaced category-based filtering with anchor navigation: clicking a
department in the sidebar smooth-scrolls (via existing Lenis/GSAP
`scrollTo` helper) to that department's section with a `-144px` header
offset.
- Removed the `ALL` button — every department is always rendered as its
own scroll target with `id="careers-dept-{key}"`.
- Added `useIntersectionObserver` (rootMargin `-20% 0px -60% 0px`) that
updates the active nav item as the user scrolls. An `isScrolling` guard
prevents the observer from fighting click-jumps mid-animation.
- Added a viewport-entry fade/slide-up animation on each department
section, gated by `motion-safe:` so users with `prefers-reduced-motion`
see content immediately. The reveal state is sticky (one-way) so
sections don't disappear once revealed.
- Active state is driven by raw department keys; both the nav model and
the observer's id-to-key mapping use a single consistent identifier.
- **`apps/website/e2e/careers.spec.ts`**
- Replaced the obsolete "ENGINEERING filter narrows the list" test with
one that validates locator behavior: clicking the department button
scrolls the section into the viewport, sets `aria-pressed="true"`, and
keeps the full role list rendered.
## Verification
- `pnpm --filter @comfyorg/website typecheck` — clean.
- `pnpm exec oxfmt` / `pnpm exec eslint` / `pnpm exec oxlint` — clean.
- Pre-commit lint-staged hooks (stylelint, oxfmt, oxlint, eslint,
typecheck) — passing.
- Manual smoke test via Playwright on `astro dev`: careers page renders
all departments stacked vertically, active department in the sidebar
highlights based on viewport position (DESIGN active on initial scroll),
nav items reflect each department instead of including an `ALL` button.
## Screenshots

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12110-feat-convert-careers-CategoryNav-to-scroll-spy-locator-35b6d73d3650818a9226e5dcb1244756)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Glary <glary@bot.local>
|
||
|
|
5ca9f3e7e6 |
feat(website): remove left-edge fade-out from local hero illustration (#12137)
*PR Created by the Glary-Bot Agent* --- Removes the left-edge fade-out gradient overlay on the hexagonal hero illustration on the `/download` (local) page. The hex cluster now reads fully edge-to-edge instead of being blended into the page background on the left side. Tracked in [FE-650: Remove fade-out effect on local page hero illustration](https://linear.app/comfyorg/issue/FE-650/remove-fade-out-effect-on-local-page-hero-illustration). ## Change Drops the `<!-- Left-edge fade -->` `<rect>` and its `<defs><linearGradient id="localHeroFadeLeft">…</linearGradient></defs>` from `apps/website/src/components/product/local/HeroSection.vue`. Animation logic (panel expansion + hex ring rotation) is untouched. ## Verification - `pnpm nx typecheck website` — pass (0 errors) - `pnpm nx build website` — pass (51 pages built) - `pnpm exec eslint apps/website/src/components/product/local/HeroSection.vue` — clean - `pnpm format:check apps/website/src/components/product/local/HeroSection.vue` — clean - Manual: `pnpm dev` + visited `/download` at 390×844 (mobile), 1280×800, and 1440×900. Mobile screenshots clearly show the fade is gone; the leftmost hexagons are now fully visible. ## Screenshots Mobile (390×844), before — note the dark fade obscuring the left side of the hex cluster: ## Screenshots    ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12137-feat-website-remove-left-edge-fade-out-from-local-hero-illustration-35d6d73d365081cba690ed7d46a19882) by [Unito](https://www.unito.io) Co-authored-by: Glary Bot <bot@glary.dev> |
||
|
|
6d5fa743b3 |
fix: seamless SocialProofBar marquee loop (#12139)
*PR Created by the Glary-Bot Agent* --- ## Summary The partner-logo marquee on the homepage `SocialProofBar` glitched on every loop restart — a visible jump where the strip snapped back to the start. ## Root cause The previous implementation rendered all logos as siblings of a single flex container and animated the track from `translateX(0)` to `translateX(-50%)`. Because the `gap` utility inserts spacing between every adjacent pair of items (including the seam between the two duplicated halves), `-50%` of the total width does not equal the distance from one half-start to the next half-start. The mismatch (`gap / 2`) is exactly what the eye sees as a jump. ## Fix - Wrap each duplicated half in its own flex group. - Place the two groups as siblings of an outer `flex w-max gap-X` track with a gap that matches the inner gap. - Animate each group by `translateX(calc(-100% - var(--marquee-gap)))`, where `--marquee-gap` is set inline to the same value as the Tailwind gap class. - Scope the `animation` declaration to `@media (prefers-reduced-motion: no-preference)` so reduced-motion users get a stable, non-animated client list instead of the global "snap to 0.01ms" jump. At `t = end`, the second group sits at `x = 0` — exactly where the first group started — so the next animation cycle is visually indistinguishable from the previous frame. The duplicate carries `aria-hidden="true"` so screen readers don't read the client list twice. ## Verification - `pnpm typecheck`, `pnpm format`, `npx eslint` on changed files: clean. - Geometry verified at runtime on desktop (1440×900) and mobile (390×844): copy widths match, second copy lands at `x = 0` at animation end. - New Playwright regression tests (`apps/website/e2e/responsive.spec.ts`) pause the CSS animation, sample bounding rects at `t=0` and `t≈duration`, and assert the seam invariant — covering desktop forward, mobile forward, and mobile reverse marquees. All 5 SocialProofBar tests pass on both `desktop` and `mobile` projects. - Reduced-motion behavior verified in the browser: `animationName: none`, `transform: none`, tracks at their natural positions. Fixes FE-649 ## Screenshots     ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12139-fix-seamless-SocialProofBar-marquee-loop-35d6d73d36508141b6ccf0167016b8c8) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
603dd3eb4e |
fix(assets): recognise m4v and mkv as video extensions (#12088)
## Summary `.m4v` and `.mkv` files render as a generic file icon in the assets sidebar instead of a video preview because `VIDEO_EXTENSIONS` doesn't list them, even though both formats are widely produced by ComfyUI custom nodes and are browser-playable when written with common codecs. ## Changes - **What**: Add `m4v` and `mkv` to `VIDEO_EXTENSIONS` in `packages/shared-frontend-utils/src/formatUtil.ts` and extend the existing test cases. Aligns with ComfyUI core's canonical video extension list (`tests-unit/folder_paths_test/filter_by_content_types_test.py:13`), which includes both. The frontend's format registry at `src/platform/workflow/core/types/formats.ts` also lists `.m4v` with mime `video/x-m4v` — `formatUtil.ts` was the inconsistent surface. - **Breaking**: None. - **Dependencies**: None. ## Review Focus `m4v` is Apple's MP4 container variant; `mkv` is the Matroska container. ComfyUI custom nodes most commonly produce both with H.264/VP9 codecs, which play natively in modern browsers via `<video>`. Adding the extensions routes those files through the existing `MediaVideoTop` component without any new rendering logic. If a user's `.mkv` happens to use an exotic codec the browser can't play (e.g. H.265/HEVC in Chrome), they get the same controllable failure mode as today's `.avi` entries — a `<video>` element with the browser's native unsupported-source UI. That is no worse than the current "show a generic file icon" behavior, and strictly better in the common case. ## Screenshots (if applicable) > **Note**: Screenshots taken from the OSS *input* assets sidebar with [#12086](https://github.com/Comfy-Org/ComfyUI_frontend/pull/12086) also applied locally. That PR fixes a separate regression where OSS input filenames carry a `[input]` annotation suffix that breaks all extension-based media detection — without it, `m4v`/`mkv` files (and every other file in that sidebar) still render as the generic icon. This PR alone is sufficient for cloud assets and OSS output history; the input-sidebar previews require both PRs. Before: <img width="1197" height="714" alt="2026-05-09-031123_hyprshot" src="https://github.com/user-attachments/assets/5c6ebc2d-aac2-411f-a2e4-51a111033184" /> After: <img width="1042" height="723" alt="2026-05-09-031005_hyprshot" src="https://github.com/user-attachments/assets/f0acc2cf-8571-4fd0-b0cd-2b8b87ff9b74" /> |
||
|
|
d767a325a2 |
FE-604: fix(website): activate last section badge when scrolled to bottom (#12057)
*PR Created by the Glary-Bot Agent* --- ## Summary Fixes the bug where the last badge in `ContentSection`'s sticky sidebar nav stays unhighlighted when the user scrolls to the very bottom of the page on tall viewports (reported on a 14" MacBook M4 Pro at 3024×1964 / 2016×1310 logical, both Chrome and Safari). ## Root cause The scroll-spy uses an IntersectionObserver with `rootMargin: '-20% 0px -60% 0px'`, which makes only a 20%–40% horizontal band from the viewport top "active". When multiple intersecting entries are reported, the callback picks the one whose `boundingClientRect.top` is smallest (highest up on screen). On tall viewports, when the page is scrolled to the absolute bottom, the last *and* the second-to-last sections frequently both sit inside that 20%–40% band at the same time. The "smallest top" tiebreak then selects the second-to-last section, leaving the last badge inactive even though the user has reached the end of the page. ## Fix `apps/website/src/components/common/ContentSection.vue`: 1. Add `isAtBottom()` — true when the viewport bottom has reached the document bottom (within 4px to absorb sub-pixel rounding). 2. The IntersectionObserver callback bails out when `isAtBottom()` so it cannot overwrite the choice below. 3. A passive `scroll` listener (and a one-shot `onMounted` call) sets `activeSection` to the last section whenever the page is at the bottom — including when the component mounts already at the bottom (e.g. hash navigation to a trailing anchor, restored scroll position, or a page shorter than the viewport). 4. Both the scroll handler and the IO callback honor the existing `isScrolling` flag, so click-driven smooth scroll-to-section behavior is unchanged. ## Verification Reproduced the bug at viewport 2016×1310 (14" M4 Pro "More Space" mode) on `/privacy-policy`: - Before fix: at absolute bottom, IntersectionObserver picks `australian-privacy` (second-to-last) — bug confirmed via DOM inspection that showed multiple sections intersecting the active band, with the second-to-last winning the "smallest top" tiebreak. - After fix: - Scrolled to bottom → last badge `CONTACT` is active. - Scrolled to top → first badge `INTRO` is active. - Scrolled mid-page → correct mid-section is active. - Click on a badge → smooth scrolls and that badge becomes active. - Initial render at bottom (loaded `/privacy-policy#contact`, browser scrolls to the bottom on mount) → `CONTACT` active immediately. `pnpm typecheck` and `pnpm typecheck:website` pass; `pnpm lint` reports 0 errors; existing website unit tests pass. Note: The website app currently has no Vue component test setup (`vitest.config.ts` is configured for `node` env, no DOM). Adding component tests for this scroll-spy interaction would require setting up `happy-dom` and `@testing-library/vue` for the website app, which is out of scope for this bug fix. Fixes FE-604 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12057-FE-604-fix-website-activate-last-section-badge-when-scrolled-to-bottom-3596d73d365081faa243f4dd8e6ee54a) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
39b2bb5eab |
[chore] Update Comfy Registry API types from comfy-api@dfcca37 (#12087)
## Automated API Type Update This PR updates the Comfy Registry API types from the latest comfy-api OpenAPI specification. - API commit: dfcca37 - Generated on: 2026-05-07T19:11:35Z These types are automatically generated using openapi-typescript. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12087-chore-Update-Comfy-Registry-API-types-from-comfy-api-dfcca37-35a6d73d365081f49beec09f418d7c1a) by [Unito](https://www.unito.io) Co-authored-by: coderfromthenorth93 <213232275+coderfromthenorth93@users.noreply.github.com> |
||
|
|
c643438601 |
fix: hide image buttons if load failed (#12136)
## Summary When an image fails to load in the image preview, the context buttons are still visible - clicking these does not work (Mask editor opens and closes, download does nothing) - this hides the buttons if load fails. ## Changes - **What**: - hide buttons if load failed - tests ## Review Focus <!-- Critical design decisions or edge cases that need attention --> <!-- If this PR fixes an issue, uncomment and update the line below --> <!-- Fixes #ISSUE_NUMBER --> ## Screenshots (if applicable) Current: <img width="622" height="857" alt="image" src="https://github.com/user-attachments/assets/26e391a0-5538-4c6c-ac8a-b6f2b6acabae" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12136-fix-hide-image-buttons-if-load-failed-35d6d73d365081579c71f1849b9ab1bd) by [Unito](https://www.unito.io) |
||
|
|
02e1ba2968 |
fix: Load Image preview retains deleted asset (FE-230) (#11493)
## Summary After deleting an asset, the Load Image node kept displaying the deleted thumbnail — both in the node body and in the picker dropdown (All / Imported / Generated tabs), even after a workflow reload. - Fixes FE-230 - Source: Slack https://comfy-organization.slack.com/archives/C0A4XMHANP3/p1776715727656809 ## Root Cause Three distinct paths kept the deleted asset visible: 1. **Node-body preview cache** — `useMediaAssetActions.deleteAssets` never cleared `node.imgs` / `node.videoContainer` / the `nodeOutputStore` Vue ref, so the canvas renderer kept its cached frame. 2. **Live-delete dropdown gap** — the picker reads from `outputMediaAssets.media` (the asset list) and from `missingMediaStore.missingMediaCandidates` (verified-missing names). On live delete, neither was updated for the deleted asset, so the dropdown filter had nothing to drop. 3. **Synthetic "selected" placeholder** — `useWidgetSelectItems.missingValueItem` rebuilt any orphaned `modelValue` as a fake item with a `/api/view?filename=...` preview URL. Browsers had cached that URL pre-delete, so the deleted thumbnail still rendered with a blue checkmark even after the filter dropped the real asset entry. A subtler issue compounded #2/#3: candidate names stored in `missingMediaStore` are raw widget values (e.g. `sub/foo.png [output]`), but the dropdown computed comparison keys differently per source (asset list uses bare `asset.name`, widget option list uses bare filename). Names with a subfolder prefix slipped through the filter. ## Fix - **`clearNodePreviewCacheForFilenames`** (existing helper, refactored): exports `findNodesReferencingFilenames` + `extractFilenameFromWidgetValue`. Uses `nodeOutputStore.removeNodeOutputs` so the **reactive** Pinia ref updates, not just the legacy `app.nodeOutputs` mirror. Also clears `node.videoContainer` for Load Video. - **`markDeletedAssetsAsMissingMedia`** (new): on successful deletion, surfaces the affected widgets through `missingMediaStore` immediately so the dropdown filter has something to drop without waiting for verification. - **`useMissingMediaPreviewSync`** (new): watches `missingMediaStore` and clears `node.imgs` / `node.videoContainer` / Vue preview source for nodes referencing confirmed-missing media on workflow load — covers the post-reload case. - **`useWidgetSelectItems`**: normalizes both sides of the missing-media filter via `extractFilenameFromWidgetValue` (strips `[input|output|temp]` annotation + subfolder prefix), and suppresses `missingValueItem` when the value is in the missing-media store so the cached-thumbnail "selected" placeholder doesn't appear. ## Red-Green Verification | Commit | CI Status | Run | |--------|-----------|-----| | `test: FE-230 add failing test for Load Image preview cache clearing` | 🔴 Failure — test caught the bug | https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/24700188700 | | `fix: FE-230 clear Load Image preview cache when asset is deleted` | 🟢 Success | https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/24700265884 | ## Test Plan - [x] Unit coverage: 78 tests across 5 files (preview-cache helper, mark-deleted-as-missing, missing-media-preview-sync, widget-select-items missing-media filter incl. subfolder-prefix case, useMediaAssetActions integration) - [x] Live delete: Load Image node preview clears, dropdown drops the asset across All / Imported / Generated, no synthetic "selected" placeholder - [x] Post-reload: missing-media verification → `useMissingMediaPreviewSync` clears the preview, dropdown drops the asset - [x] Linear FE-230 auto-links via the Source line ## Scope note In-session and session-restore are both covered. If the backend/CDN continues serving the deleted `filename`/`asset_hash` after deletion, a cross-session reopen may still render stale bytes from cache — that's a backend/CDN concern tracked separately. ## demo ### before https://github.com/user-attachments/assets/e4d3a40e-0d46-43ad-985c-22ce7e0d3faf ### after https://github.com/user-attachments/assets/fcac9387-4c07-4be2-bcdd-d1a6192fe962 |
||
|
|
15b8771cc2 |
fix: clear active job on reconnect if no longer in queue (#12067)
## Summary When a socket disconnects messages can be missed and lead to a stale UI state, this updates the state on reconnect and clears the active job if it is no longer running ## Changes - **What**: - add call to update queue on reconnect - clear active job if job not in queue response - tests ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12067-fix-clear-active-job-on-reconnect-if-no-longer-in-queue-3596d73d365081f79d42d73966420c50) by [Unito](https://www.unito.io) |
||
|
|
e68d50e677 |
1.45.4 (#12118)
Patch version increment to 1.45.4 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12118-1-45-4-35d6d73d365081fcb5f5d06dec17bb59) 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>v1.45.4 |
||
|
|
48b5e0165a |
1.45.3 (#12113)
Patch version increment to 1.45.3 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12113-1-45-3-35c6d73d365081468180cefef02dca03) 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>v1.45.3 |
||
|
|
fe1de3b254 |
refactor: remove dedup complexity from reportInactiveTrackerCall (#11833)
## Summary Remove the module-level `reportedInactiveCalls: Set<string>` and the early-return dedup check from `reportInactiveTrackerCall()` in `src/scripts/changeTracker.ts`. Every invocation now emits `console.warn` and (on Desktop) `Sentry.captureMessage` unconditionally. ## Why The dedup was added in #11328 but is unnecessary: - Every first-party call site already goes through the `activeWorkflow?.changeTracker` guard, so flooding from in-repo code is unlikely. - Repeated identical alerts may actually provide more diagnostic signal than the first-only approach suppresses. ## Changes - Drop `reportedInactiveCalls` Set - Drop the per-`(method, workflowPath)` early-return - Trim the JSDoc accordingly No behavior change for callers (`deactivate`, `captureCanvasState`); only the reporting frequency increases. ## Verification - `pnpm test:unit -- src/scripts/changeTracker.test.ts` — 16/16 passing - `pnpm typecheck` — clean - ESLint / oxfmt — clean - Fixes #11372 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11833-refactor-remove-dedup-complexity-from-reportInactiveTrackerCall-3546d73d365081fabf57cbf1fa17051f) by [Unito](https://www.unito.io) |
||
|
|
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) |
||
|
|
8f68be5699 |
fix: handle annotated output media paths in missing media scan (#12069)
## Summary
This PR fixes missing-media false positives for annotated media widget
values such as:
```txt
photo.png [output]
clip.mp4 [input]
147257c95a3e957e0deee73a077cfec89da2d906dd086ca70a2b0c897a9591d6e.png [output]
clip.mp4[input] // Cloud compact form
```
The change is intentionally scoped to the missing-media detection
pipeline for:
- `LoadImage`
- `LoadImageMask`
- `LoadVideo`
- `LoadAudio`
It preserves the raw widget value on `MissingMediaCandidate.name` for UI
display, grouping, replacement, and user-facing missing-media rows.
Normalized values are used only as comparison keys during verification.
## Diff Size
`main...HEAD` line diff is currently:
- Production/runtime code: `+478 / -37` (`515` changed lines)
- Unit test code: `+960 / -47` (`1,007` changed lines)
- Total: `+1,438 / -84` (`1,522` changed lines)
The PR looks large mostly because it locks both Cloud and OSS/Core
runtime paths with unit coverage; the production/runtime change is about
one third of the total diff.
## What Changed
- Added missing-media-scoped annotation helpers for detection-only path
normalization.
- Core/OSS recognizes spaced suffixes like `file.png [output]`.
- Cloud also recognizes compact suffixes like `file.png[output]`.
- User-selectable trailing `input` and `output` annotations are
normalized for matching.
- Unknown annotations and middle-of-filename annotations are left
unchanged.
- Added shared file-path helpers in `formatUtil`:
- `joinFilePath(subfolder, filename)`
- `getFilePathSeparatorVariants(filepath)`
- Updated media verification to compare candidates against both raw and
normalized match keys.
- Kept input candidates and generated output candidates in separate
identifier sets so an input asset cannot accidentally satisfy an output
reference with the same name.
- Moved missing-media source loading into `missingMediaAssetResolver` so
`missingMediaScan` remains focused on scan/verification orchestration.
- Updated Cloud generated-media verification to use the Cloud assets API
instead of job history:
- Cloud input candidates use input/public assets.
- Cloud output candidates use `output` tagged assets.
- Kept OSS/Core generated-media verification history-based, matching the
current generated-picker/widget availability model.
## Runtime Verification Paths
### Cloud
Cloud stores generated outputs as asset records. For an annotated output
value, this PR verifies against the `output` asset tag rather than job
history.
```txt
Widget value
"147257...d6e.png [output]"
|
v
Detection keys
"147257...d6e.png [output]"
"147257...d6e.png"
|
v
Cloud asset sources
input candidates -> /api/assets?include_tags=input&include_public=true
output candidates -> /api/assets?include_tags=output&include_public=true
|
v
Match against
asset.name
asset.asset_hash
subfolder/asset.name
subfolder/asset.asset_hash
slash and backslash separator variants
```
Example:
```ts
candidate.name = 'abc123.png [output]'
asset.name = 'ComfyUI_00001_.png'
asset.asset_hash = 'abc123.png'
asset.tags = ['output']
// Result: not missing
```
### OSS / Core
Core widget options for the normal loader nodes are input-folder based.
Annotated output values are resolved by Core through
`folder_paths.get_annotated_filepath()`, but the current generated
picker path is history-backed. This PR keeps OSS generated verification
aligned with that widget availability model instead of treating the full
output folder as the source of truth.
```txt
Widget value
"subfolder/photo.png [output]"
|
v
Detection keys
"subfolder/photo.png [output]"
"subfolder/photo.png"
|
v
OSS generated source
fetchHistoryPage(...)
|
v
History preview_output
filename: "photo.png"
subfolder: "subfolder"
|
v
Generated match keys
"subfolder/photo.png"
"subfolder\\photo.png"
```
This means OSS/Core verification is about whether the generated media is
currently available through the same generated/history-backed path the
widget uses, not a full disk-level executability check across the entire
output directory.
## Why Not Consolidate All Annotated Path Parsers
There are existing annotated-path parsers in image widget, Load3D, and
path creation code. This PR does not replace them.
The helper added here is detection-only: it strips annotations to build
comparison keys for missing-media verification. Parser consolidation
across widget implementations is intentionally left out of scope to keep
this fix narrow.
## Known Follow-Ups / Out Of Scope
- FE-620 tracks the separate video drag-and-drop upload race between
upload completion and missing-media detection.
- Published/shared workflow assets are still not fully represented by
`/api/assets?include_public=true`; that remains a backend/API contract
issue.
- A future backend/API contract that answers “is this workflow media
executable?” would be preferable to stitching together runtime-specific
FE sources.
- OSS/Core full output-folder scanning via `/internal/files/output` was
considered, but that endpoint is internal, shallow (`os.scandir`), and
not the same source currently used by the generated picker flow.
## Validation
- `pnpm test:unit -- missingMediaAssetResolver missingMediaScan
mediaPathDetectionUtil formatUtil`
- touched files `oxfmt`
- touched files `oxlint --fix`
- touched files `eslint --cache --fix --no-warn-ignored`
- `pnpm typecheck`
- pre-commit `pnpm knip --cache`
- pre-push `pnpm knip --cache`
`knip` passes with the existing tag hint:
```txt
Unused tag in src/scripts/metadata/flac.ts: getFromFlacBuffer → @knipIgnoreUnusedButUsedByCustomNodes
```
## Screenshots
Before
https://github.com/user-attachments/assets/50eab565-3160-4a57-a758-87ec2c09071e
After
https://github.com/user-attachments/assets/08adcbbd-c3fc-43f9-b86c-327e4eb5abd8
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12069-fix-handle-annotated-output-media-paths-in-missing-media-scan-3596d73d365081f4afa3d4dd45cad3da)
by [Unito](https://www.unito.io)
|
||
|
|
653ef1a4f0 |
Handle Load3D "none" model selection in frontend (#11178)
## Summary Load3D now supports panoramic images and HDRI loading, it can serve as a viewer for those without requiring a 3D model. Previously, the node required a model file to execute. Rather than making the input optional (which would break existing workflows that rely on it being required), a "none" option is added to the combo list, allowing users to run Load3D with no model loaded. BE change is https://github.com/Comfy-Org/ComfyUI/pull/13379 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11178-Handle-Load3D-none-model-selection-in-frontend-3416d73d365081e589b3d89bc67f75e7) by [Unito](https://www.unito.io) |
||
|
|
c16052e2e3 |
feat: sort right-click context menu categories alphabetically (#12039)
*PR Created by the Glary-Bot Agent*
---
## Summary
Sort the canvas right-click "Add Node" context menu by display name
(case-insensitive, natural numeric). Previously, both category submenus
and leaf nodes appeared in node-registration order, making the menu hard
to scan for users browsing for nodes.
This change is scoped specifically to the **smaller right-click
contextual menu**. It does NOT affect the double-click search menu or
the left-side Nodes panel.
## Changes
- `src/lib/litegraph/src/LGraphCanvas.ts` — In `onMenuAdd` →
`inner_onMenuAdded`, sort the deduplicated category submenu entries and
the leaf-node entries by `content` using `localeCompare` with `{
numeric: true, sensitivity: 'base' }`. Categories still appear before
leaf nodes within a level (preserves existing UX).
- `src/lib/litegraph/src/LGraphCanvas.onMenuAdd.test.ts` — New unit
tests that mock `LiteGraph.ContextMenu` and assert: case-insensitive
sort, natural numeric ordering (`Cat1`, `Cat2`, `Cat10`), leaf-node
sorting inside a category, and category-before-leaf placement.
## Verification
- `pnpm vitest run src/lib/litegraph/src/LGraphCanvas.onMenuAdd.test.ts`
— 4/4 pass
- `pnpm typecheck` — clean (ran via pre-commit hook on initial commit)
- `oxfmt` / `oxlint` / `eslint` — clean
- Oracle review against `main` returned 0 critical / 1 warning (test
coverage) / 1 suggestion (numeric sort) — both addressed in this PR.
## Notes
- The sort is applied at the menu-build site rather than inside
`LiteGraphGlobal.getNodeTypesCategories`/`getNodeTypesInCategory` to
keep the change scoped to the menu UX and avoid changing the iteration
order seen by extensions that consume those public methods.
- Per user request, this is opening as a draft PR for self-review +
CodeRabbit feedback in a single follow-up pass; manual browser
verification (right-click screenshots) was deferred to that pass.
- Slack thread context: user reported the contextual menu is "a mess"
for discovering native nodes; alphabetical sorting addresses the
discoverability problem without touching the search-oriented menus.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12039-feat-sort-right-click-context-menu-categories-alphabetically-3596d73d36508107a87ffec1c353994e)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: Alexis Rolland <alexis@comfy.org>
|
||
|
|
3e94459340 |
1.45.2 (#12096)
Patch version increment to 1.45.2 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12096-1-45-2-35b6d73d36508193be00c1c878d42c2a) 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>v1.45.2 |
||
|
|
ca54877f9d |
fix(assets): strip directory annotation from input filenames (#12086)
## Summary
Imported assets render as a generic check-check icon instead of a
thumbnail because the OSS `/internal/files/{type}` endpoint returns
annotated filenames (`photo.png [input]`) that the assets-sidebar mapper
passes through verbatim, which breaks extension-based media-type
detection.
## Changes
- **What**: Strip ComfyUI's trailing directory-type annotation (`
[input]`, ` [output]`, `[temp]`) in `mapInputFileToAssetItem` so `name`,
`id`, and the generated `/view?filename=…` URL all use the canonical
on-disk filename. Adds a focused unit test.
- **Breaking**: None.
- **Dependencies**: None.
## Review Focus
### Root cause
ComfyUI core PR
[comfyanonymous/ComfyUI#13078](https://github.com/comfyanonymous/ComfyUI/pull/13078)
(April 2026) changed `/internal/files/{type}` to append the directory
type to each entry:
```python
# api_server/routes/internal/internal_routes.py
return web.json_response(
[f"{entry.name} [{directory_type}]" for entry in sorted_files], status=200
)
```
The annotation is the wire format `LoadImage`-style widgets expect, so
the backend change is correct. The assets-sidebar mapper treated the
response strings as raw filenames. After
[#8914](https://github.com/Comfy-Org/ComfyUI_frontend/pull/8914) changed
`getMediaTypeFromFilename` to default unknown extensions to `'other'`,
every input asset now routes to `MediaOtherTop` and renders as
`icon-[lucide--check-check]`:
```
getMediaTypeFromFilename("photo.png [input]").split('.').pop() === "png [input]" → 'other'
```
The strip happens at data ingestion so every consumer of
`AssetItem.name` (sidebar grid, list, filter, gallery, drag-drop,
delete) gets the canonical filename automatically. OSS-only — Cloud
paths get clean names from the cloud API and are unaffected.
Reproduces locally on stock OSS ComfyUI on `main` of both repos; no
public issue tracker entry.
## Screenshots (if applicable)
Before:
<img width="1091" height="718" alt="image"
src="https://github.com/user-attachments/assets/ff1f070d-da39-4e5a-bc6d-99b7214f7da8"
/>
After:
<img width="1089" height="716" alt="image"
src="https://github.com/user-attachments/assets/7123d9bf-f7dd-4430-b6f7-f6702b70baaa"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12086-fix-assets-strip-directory-annotation-from-input-filenames-35a6d73d365081e9b9eed7d8630d6f0b)
by [Unito](https://www.unito.io)
|
||
|
|
a4faaa0159 |
fix: disable ultralytics asset-browser registration (#12075)
*PR Created by the Glary-Bot Agent* --- ## Summary Disable the `UltralyticsDetectorProvider` model-to-node mapping so the node falls back to the static combo populated from `/api/object_info`, restoring pre-#8468 behavior on cloud. ## Why PR #8468 opted `UltralyticsDetectorProvider` into the cloud asset-widget path, which exposed a latent mismatch in cloud asset metadata for nested-directory model folders. The bug has two independent halves, and a fix that addresses only one will still leave the workflow broken at execution time: - **Tag lookup mismatch.** Cloud stores tags as combined values like `ultralytics/bbox`, while the asset query asks for split tags (`models` + `ultralytics`) with exact-match filtering — so the dropdown returns no results. - **Submitted value mismatch.** Cloud stores filenames as basenames, but the node expects subdirectory-prefixed values (e.g. `bbox/face_yolov8m.pt`) that the static combo path normally produces. Both halves require cloud-side fixes (asset ingestion + metadata) before the asset-browser registration can be safely re-enabled. Until then, removing the registration restores the working static-combo behavior so users are unblocked. ## Changes - `src/platform/assets/mappings/modelNodeMappings.ts`: comment out the `['ultralytics', 'UltralyticsDetectorProvider', 'model_name']` entry with a note pointing at BE-689 and the re-enablement criteria. - `src/stores/modelToNodeStore.test.ts`: drop the now-stale ultralytics expectations from `EXPECTED_DEFAULT_TYPES`, `MOCK_NODE_NAMES`, and the hierarchical-fallback `it.each` cases. ## Verification Local quality gates: - `pnpm typecheck` — clean - `pnpm lint` — clean (3 pre-existing warnings, 0 errors) - `pnpm format:check` — clean - `pnpm knip` — clean (1 pre-existing warning unrelated to this change) - `pnpm test:unit -- src/stores/modelToNodeStore.test.ts` — 51/51 passing Manual runtime verification (dev server + Playwright against the live module): - `MODEL_NODE_MAPPINGS` no longer contains any entry where `[0] === 'ultralytics'` or `[1] === 'UltralyticsDetectorProvider'` (84 entries total, 0 ultralytics). - `useModelToNodeStore().getNodeProvider('ultralytics')` returns `null` after `registerDefaults()`, so the asset-widget path is no longer triggered for this node. - `getNodeProvider('ultralytics/bbox')` also returns `null`, confirming hierarchical fallback no longer resolves to the disabled mapping. - `getNodeProvider('checkpoints')` still resolves to `CheckpointLoaderSimple`, confirming unrelated mappings are intact. End-to-end cloud verification (actually exercising the asset-browser path against cloud-seeded ultralytics metadata) is not possible in the local sandbox, since the regression depends on the cloud's asset ingestion data shape. The change is a single mapping-table removal that reverts to the well-exercised static-combo path covered by the updated unit tests. Long-term cloud-side fix is tracked in BE-689. - Fixes BE-689 ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12075-fix-disable-ultralytics-asset-browser-registration-35a6d73d36508179b394f0915e69742e) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
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:` ( |
||
|
|
88866fc564 |
fix: restore nightly publish_types build (#12073)
## Summary The nightly `Release Draft Create` -> `publish_types / Build types` job has been failing on every run since 1.45.1 ([failed run](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/25531376650)). Reproduced locally on `main` (`c8c0e5386`). ## Root cause `pnpm build:types` ends with `FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory`. The `vite-plugin-dts` rolled-up type generation now exceeds Node's default ~4GB heap on the GH runner. The `TS2742`/`TS4082` warnings printed earlier are non-fatal diagnostics from the plugin pre-pass — the api-extractor rollup itself completes once the heap is large enough. ## Fix Set `NODE_OPTIONS='--max-old-space-size=8192'` in `build:types`, matching the existing pattern already used by `build` and `build:cloud` (only one-line change). ## Verification - `pnpm build:types` exits 0 locally with the change (built in ~40s). - `dist/index.d.ts` (1.9MB) emitted with the public types intact: `ComfyExtension`, `ComfyApi`, `ComfyApp`, `ComfyNodeDef`, `InputSpec`, `DOMWidget`, etc. - `dist/package.json` correctly produced by `scripts/prepare-types.js`. - Reverting the change reproduces the OOM crash. ## Test plan - [ ] Trigger `Release NPM Types` workflow manually (or wait for next nightly) and confirm `Build types` step succeeds. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12073-fix-restore-nightly-publish_types-build-35a6d73d3650819e95aecfbd8a66847c) by [Unito](https://www.unito.io) |
||
|
|
1f4a4af079 |
docs: add subgraph promoted widgets ADR (#11997)
## Summary Adds an ADR documenting the canonical subgraph promoted-widget model and legacy proxy-widget ratchet. ## Changes - **What**: Defines linked `SubgraphInput` promoted widgets, host-owned sparse value overlays, proxy-widget repair/quarantine behavior, primitive-node repair, and separate display-only preview exposures. - **Breaking**: None; documentation only. - **Dependencies**: None. ## Review Focus - Whether the ADR cleanly separates value-owning promoted widgets from display-only preview exposures. - Whether the legacy ratchet, quarantine, primitive repair, and UI identity decisions are clear enough for implementation review. ## Screenshots (if applicable) N/A @Coderabbitai why would a docs update need an end-to-end test? ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11997-docs-add-subgraph-promoted-widgets-ADR-3576d73d36508133bf1ee8d49282cac1) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com> |
||
|
|
c8c0e53865 |
fix: remove asset hash verification (#12061)
## Summary This PR removes the `/api/assets/hash/:hash` verification path from missing media/model detection. I decided to remove this path for two reasons: 1. The Cloud runtime implementation and the OpenAPI/generated FE contract do not agree on the hash format that this endpoint represents. In current Cloud data, the dominant asset_hash shape is <64-hex>.<extension> (for example, abc123....png), while the OpenAPI/generated FE contract expects a blake3:<hash> style value. That makes this path either dead code that should never be reached, or, when it is reached, a request that always returns 400 and only adds unnecessary noise. 2. Even if the format is reconciled, the Cloud implementation is a global deduplication-oriented lookup, not an access-aware check for whether the current workflow can use a resource. In theory, it can return success for another user's personal asset, so it is the wrong primitive for missing asset detection. Because of that, this PR makes the existing asset list/store based checks the primary verification path and removes the hash-specific helpers, service method, and tests. ## Known follow-ups These are known issues that are intentionally not solved in this PR: 1. Published assets are not exposed through `/api/assets?...include_public=true`. This is a backend issue and can still cause mismatch between missing-asset detection and resources that preview/run successfully. 2. Shared workflow import has an ordering issue. The API contract issue is being hotfixed separately under FE-603. 3. Annotated media paths can still be detected incorrectly. I will prepare follow-up PRs for these, starting with the annotated media path issue because that is the most critical frontend-side gap. ## Validation - `pnpm exec vitest run src/platform/assets/services/assetService.test.ts src/platform/missingMedia/missingMediaScan.test.ts src/platform/missingModel/missingModelScan.test.ts` - `pnpm lint:unstaged` - `pnpm typecheck` - `pnpm knip` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12061-fix-remove-asset-hash-verification-3596d73d365081a088f8dfc874724c1d) by [Unito](https://www.unito.io) |
||
|
|
c8360a092f |
1.45.1 (#12070)
Patch version increment to 1.45.1 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12070-1-45-1-35a6d73d365081e9a4bffc19d791b727) 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>v1.45.1 |
||
|
|
68843967cf |
App Mode tests (#10633)
Adds tests for - Mobile app mode. - Drag and drop operations in app mode - Basic widget interaction in app mode. - The read only state when in builder mode. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10633-App-Mode-tests-3306d73d36508154aa25d8096119a32c) by [Unito](https://www.unito.io) |
||
|
|
8c295e7c68 |
fix: remove transition delay from FeedbackSection progress bar (#12059)
*PR Created by the Glary-Bot Agent* --- ## Summary The horizontal progress bar in `FeedbackSection.vue` lagged behind the carousel scroll position because the bar's width was driven reactively by `useScroll`, but the `transition-all duration-200` utility animated each width change over 200ms. As scroll continuously emits new target widths, the bar visibly trailed the scroll position. Removing the transition makes the bar track scroll synchronously. ## Verification - Reproduced the lag locally on `/customers`. - Verified post-fix that `bar.style.width` updates in the same frame as `scrollLeft` changes (samples at scrollLeft 0 / 944 / 1600 → width 0% / 59% / 100%, with `transitionDuration: 0s`). - `pnpm exec eslint`, `pnpm exec oxfmt --check`, `pnpm nx typecheck website`, and `pnpm test:unit` all pass. ## Notes No regression test added — the customers section components have no existing unit/E2E coverage in this repo, and standing up a new test harness for a one-line CSS fix would be disproportionate. Worth following up on broader carousel coverage as a separate task. ## Screenshot After fix, scrolled to second slide — progress bar tracks scroll position synchronously. ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12059-fix-remove-transition-delay-from-FeedbackSection-progress-bar-3596d73d36508107bc80dc38ea7ab79e) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
219a574eed |
test: add failing test for deleting inner nodes with promoted widgets (#12058)
## Summary Adds a failing test for a current bug with subgraphs where deleting an inner node where the widget is promoted does not remove the outer widget ## Changes - **What**: - add failing test ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12058-test-add-failing-test-for-deleting-inner-nodes-with-promoted-widgets-3596d73d3650811f9629c0ed3f4bc3c8) by [Unito](https://www.unito.io) |
||
|
|
fef2cab31e |
fix(website): prevent video glitch when c-projection.webp loads on /customers (#12060)
*PR Created by the Glary-Bot Agent* --- ## Summary Fix the video position glitch on `/customers` caused by `c-projection.webp` loading. ## Root cause In `HeroSection.vue` the hero `<img>` for `c-projection.webp` had no `width`/`height` attributes, so the browser reserved no space for it. When the image finished loading, the layout shifted ~192px, pushing the `VideoPlayer` below it. `useHeroAnimation` registers a GSAP `ScrollTrigger` parallax against the video in `onMounted` (before the image is loaded), so the cached scroll geometry then went stale and the video visibly glitched. ## Fix - Add explicit `width="1568"` / `height="1763"` to the `<img>` (the image's native size) so the browser reserves the correct aspect-ratio'd space upfront. - Add `h-auto` so the height attribute doesn't override the responsive layout. - Refresh `ScrollTrigger` on the image's `@load` (with `refresh(true)` so measurements happen after layout has settled) as a defensive measure for any sub-pixel adjustments. ## Test coverage Added `apps/website/e2e/customers.spec.ts` with a regression guard that: 1. Asserts the hero `<img>` declares numeric `width`/`height` attributes. 2. Asserts the unloaded image still reserves vertical space (>100px), which is the exact property that prevents the video from jumping when the image finishes loading. Verified the test fails when the `width`/`height` attributes are removed and passes with the fix applied. ## Verification - `pnpm typecheck` clean. - `pnpm test:unit` (website app) — 30/30 pass. - `pnpm test:e2e customers.spec.ts --project=desktop` — passes. - ESLint + oxfmt clean on changed files. - Manual Playwright verification confirmed the video bounding rect stays at `top=785, height=628` through the full image load lifecycle. Reverting the fix in DevTools and re-loading the image reproduces the original ~192px shift. ## Out of scope `apps/website/src/components/contact/FormSection.vue` uses the same `c-projection.webp` pattern (also without `width`/`height`). It runs `useHeroAnimation` with `parallax: false`, so the symptom is much smaller — leaving as a follow-up to keep this PR minimal. - Fixes [FE-607](https://linear.app/comfyorg/issue/FE-607/bug-video-on-customers-shifts-position-when-c-projectionwebp-finishes) ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12060-fix-website-prevent-video-glitch-when-c-projection-webp-loads-on-customers-3596d73d365081ebbcb8db25aaa5c451) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
20ee262f78 |
fix: prevent enter subgraph/toggle advanced when nodes were dragged (#12051)
## Summary In Vue nodes mode, if you drag a node by the footer button (e.g. enter subgraph) after you finish dragging, it then unexpectedly still enters the subgraph, same applies to advanced widgets. ## Changes - **What**: - store `isDraggingVueNodes` before click event and only emit if false ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12051-fix-prevent-enter-subgraph-toggle-advanced-when-nodes-were-dragged-3596d73d365081929173d8e9371b1332) by [Unito](https://www.unito.io) |
||
|
|
6a8c453659 |
test: add missing emitModelReady to mock object (#12056)
## Summary
Unblock CI due to missing function in mock
```
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: this.load3d.emitModelReady is not a function
❯ src/extensions/core/load3d/Load3DConfiguration.ts:313:19
311| }
312|
313| this.load3d.emitModelReady()
| ^
314| }
315| }
This error originated in "src/extensions/core/load3d/Load3DConfiguration.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
The latest test that might've caused the error is "prefers persisted Scene/Camera/Light Config over settings". It might mean one of the following:
- The error was thrown, while Vitest was running this test.
- If the error occurred after the test had been completed, this was the last documented test before it was thrown.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Test Files 773 passed (773)
Tests 10417 passed | 8 skipped (10425)
Errors 2 errors
Start at 12:06:06
Duration 446.79s (transform 32.89s, setup 113.26s, import 589.18s, tests 109.19s, environment 228.45s)
```
## Changes
- **What**:
- add `emitModelReady` fn to mock
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12056-test-add-missing-emitModelReady-to-mock-object-3596d73d36508130a087f5c7713e932f)
by [Unito](https://www.unito.io)
|
||
|
|
ea277dec4d |
FE-446: test(load3d): cover Load3D/Preview3D extensions and config persistence (#11969)
## Summary Add unit tests for src/extensions/core/load3d.ts (Load3D and Preview3D nodeCreated/onExecuted, beforeRegisterNodeDef, getNodeMenuItems, camera matrix generation guard) and extend Load3DConfiguration tests for the Scene/Camera/Light config persistence + settingStore fallback paths. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11969-FE-446-test-load3d-cover-Load3D-Preview3D-extensions-and-config-persistence-3576d73d365081c2a847e0dc9621a75d) by [Unito](https://www.unito.io) |
||
|
|
a7aa124c10 |
FE-407: fix(assets): show 3D thumbnail without reopening the panel (#11972)
## Summary After persistThumbnail uploads the preview, patch the in-memory asset by name (the cross-API stable id) so an open Asset panel reflects the new preview_url. Media3DTop also picks up the patched preview_url directly, bypassing the IntersectionObserver gate. ## Screenshots (if applicable) before https://github.com/user-attachments/assets/ba0b753f-fede-43c0-b790-5c19a82455f9 after https://github.com/user-attachments/assets/6273ec0b-0d2e-4355-9889-68c3550f1a72 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11972-FE-407-fix-assets-show-3D-thumbnail-without-reopening-the-panel-3576d73d365081febcbfe6ae84a4a68b) by [Unito](https://www.unito.io) |
||
|
|
9c62bbc74a |
FE-412: fix(load3d): persist SaveGLB last model so it survives tab switch (#11965)
## Summary Mirror the Preview3D pattern: store the executed file path and folder on node.properties and reload them in nodeCreated so the mesh re-appears when the node is recreated (e.g. after switching workflow tabs). ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11965-FE-412-fix-load3d-persist-SaveGLB-last-model-so-it-survives-tab-switch-3576d73d365081a98bcadbd9a81539d0) by [Unito](https://www.unito.io) |
||
|
|
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) |