mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-24 14:45:36 +00:00
bdb92c845e846d97a00e4ea1bec2d8fb9d2bbb22
5722 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bdb92c845e |
fix: include share_id when importing published assets (FE-603) (#12055)
*PR Created by the Glary-Bot Agent* https://comfy-organization.slack.com/archives/C0A4XMHANP3/p1778150003248989 FE-603 --- ## Summary Send `share_id` alongside `published_asset_ids` from `workflowShareService.importPublishedAssets`, and type / zod-validate the body against `ImportPublishedAssetsRequest` + `zImportPublishedAssetsRequest` from `@comfyorg/ingest-types`. This is a **parameter-schema change**, not a live bug fix. The original `BAD_REQUEST: share_id is required` failure is **no longer reproducible in production** — the backend rolled back the required-field enforcement in [BE-855](https://linear.app/comfyorg/issue/BE-855) (cloud [#3587](https://github.com/Comfy-Org/cloud/pull/3587) / [#3588](https://github.com/Comfy-Org/cloud/pull/3588)). Today the import succeeds without `share_id`. - Closes FE-603 ## Why we still ship this Frontend goes first so the backend can later re-tighten the contract without breaking shared-workflow imports a second time. Agreed sequence in [Slack](https://comfy-organization.slack.com/archives/C0A4XMHANP3/p1778535349236419) — BE-6 was split into two sub-issues to make the chain explicit: 1. **[BE-898](https://linear.app/comfyorg/issue/BE-898)** (blocks FE-603) — BE makes `share_id` **optional**, validating when present. [cloud PR #3633](https://github.com/Comfy-Org/cloud/pull/3633), In Review. 2. **FE-603** (this PR) — FE sends `share_id`. 3. **[BE-899](https://linear.app/comfyorg/issue/BE-899)** (blocked by FE-603) — BE flips `share_id` back to **required** after FE-603 deploys. Steps 1 and 2 ship in order: BE-898 → FE-603 → BE-899. ## Changes - `workflowShareService.importPublishedAssets` now takes `shareId: string` and types the request body with `ImportPublishedAssetsRequest`. The body is parsed through `zImportPublishedAssetsRequest` before the network call so future contract drift surfaces as a typecheck or zod parse failure rather than a silent runtime 400. - `useSharedWorkflowUrlLoader.ts` threads `payload.shareId` (already in scope from `SharedWorkflowPayload`) into the import call. - Unit tests assert `share_id` is sent and that an empty `share_id` is rejected before fetch. ## Verification - `pnpm typecheck` — clean - `pnpm lint` — 0 errors / 0 warnings on changed files (3 pre-existing warnings in unrelated files) - `pnpm format` — applied - `pnpm vitest run` on both affected files — 34/34 passing ### Live in-browser smoke test (Vite dev server) Loaded the built module in the browser, intercepted `fetch`, and exercised the new code path. The new `importPublishedAssets` produces the correct wire format and zod rejects bad input before fetch: ```json // Happy path — sent to /api/assets/import (POST) { "published_asset_ids": ["pa-1", "pa-2", "pa-3"], "share_id": "share-abc" } ``` ```text // Empty share_id — zod rejects, fetch is never called [ { code: "too_small", minimum: 1, path: ["share_id"], message: "String must contain at least 1 character(s)" } ] fetchCallsBetweenAttempts: 0 ``` ## Why typecheck didn't catch the original miss `api.fetchApi(route: string, options?: RequestInit)` accepts the standard DOM `RequestInit`, so `body` is just `BodyInit | null`. Once the call site does `JSON.stringify({ ... })`, the inline object is erased into a string and TS has no schema to enforce. There was no `@ts-ignore` or `as any` — the generated types were simply never imported. This PR plugs that one call site; the same pattern should be applied wherever the frontend hits ingest endpoints (the broader hey-zod migration gap mentioned in #bug-dump). Reported in #bug-dump. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12055-fix-include-share_id-when-importing-published-assets-3596d73d365081c0a1c7e69102f5cfcc) 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> |
||
|
|
d96be3d668 |
feat(#3410): add centralized assert() utility in src/base/ (#11824)
## Summary Add a shared `assert(condition, message)` utility in `src/base/` that centralizes DEV-throw / Desktop-Sentry / nightly-toast / `console.error` policy for invariant reporting across the codebase. ## Changes - **`src/base/assert.ts`**: New `assert()` utility with `setAssertReporter()` registration pattern - `console.error` always fires on failure - Throws `Error` in DEV mode (surfaces bugs immediately) - Delegates to registered reporter otherwise (Sentry, toast, etc.) - No imports from `platform/` — respects layer architecture (`base → platform → workbench → renderer`) - **`src/main.ts`**: Registers Sentry + nightly-toast reporter after `Sentry.init()` - **`src/scripts/changeTracker.ts`**: Migrates `reportInactiveTrackerCall()` to use `assert()`, removing inline `Sentry.captureMessage` + `console.warn` calls - **`src/scripts/changeTracker.test.ts`**: Mocks `@/base/assert` to prevent DEV-mode throws in existing no-op tests ## Testing ### Automated - `src/base/assert.test.ts` — 6 tests covering: no-op on true, console.error on false, DEV throw, non-DEV no-throw, reporter invocation, reporter not called on true - `src/scripts/changeTracker.test.ts` — 16 tests all pass (pre-existing) - Coverage: 100% for assert.ts ### E2E Verification Steps 1. Run `pnpm test:unit` — all tests pass 2. Build the app and open browser devtools 3. In DEV mode: trigger a lifecycle violation (call an inactive tracker method) — should see error thrown in console 4. In production build: same trigger — should see `console.error` only, no throw ## Review Focus - `setAssertReporter()` is called in `main.ts` once at startup — appropriate for a singleton reporter. In tests that import `assert`, the reporter is reset to a no-op in `afterEach`. - Layer architecture respected: `base/assert.ts` has zero imports, upper layers wire in side effects via `setAssertReporter()`. Fixes #11373 <!-- Pipeline-Ticket: pick-issue-3410 --> ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11824-feat-3410-add-centralized-assert-utility-in-src-base-3546d73d3650819d96afdf4018161c26) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: GitHub Action <action@github.com> Co-authored-by: Connor Byrne <c.byrne@comfy.org> |
||
|
|
1869416185 |
fix: surface error dialog when Open Workflow from Job Queue fails (FE-215) (#12071)
## Summary `openJobWorkflow` (Job Queue → "Open Workflow in New Tab") had no error handling around `workflowService.openWorkflow`. When workflow JSON contained nodes that fail `LiteGraph.configure()` (e.g. rgthree `DisplayAny`, stale `GetNode/SetNode aux_id`), the menu action appeared to do nothing — either an early return after a generic "Load Workflow Error" dialog (`app.ts:1340-1347`) or a context-less toast from the surrounding `wrapWithErrorHandlingAsync`. This PR catches the error inside `openJobWorkflow` and routes it to `dialogService.showErrorDialog` with a Job-Queue-specific `reportType` so users get a clear, actionable message tied to the action they invoked. - Fixes #8841 - Linear: [FE-215](https://linear.app/comfyorg/issue/FE-215/open-workflow-from-frontend-not-working) ## Red-Green Verification | Commit | CI Status | Run | |--------|-----------|-----| | `test: add failing test for openJobWorkflow swallowing load errors` ( |
||
|
|
a3106c4d53 |
fix: open node info panel from context menu (#12205)
## Summary Replaces #12164. Right-clicking a Vue node, using the selection toolbox More Options menu, or clicking the selection toolbox Node Info button now opens the right-side Info tab only when the new-menu UI makes that panel available. Legacy-menu contexts hide the no-op action even when the legacy node library design is selected; node-library help remains isolated to the node library itself. The existing `selection_toolbox_node_info_opened` telemetry fires only after the toolbox button successfully opens node info. No new context-menu telemetry event is added in this PR. ## Changes - **What**: Share the node-info availability/action path across the context menu and selection toolbox, keep legacy-menu state out of the right-side panel public store API, tighten node-info settings tests, and add unit plus E2E regression coverage for new-menu and legacy-menu modes. - **Dependencies**: None ## Review Focus Confirm the node context menu, selection toolbox direct Info button, and selection toolbox More Options entry all respect right-side panel availability, including legacy menu + legacy node library mode, while node-library help behavior remains isolated to the node library. ## Validation - Self-review: checked production path, unit mocks, and Playwright coverage; only gap found was weak E2E coverage for the toolbox direct Info path, now strengthened. - `pnpm test:unit -- src/composables/graph/useSelectionState.test.ts src/components/graph/SelectionToolbox.test.ts src/components/graph/selectionToolbox/InfoButton.test.ts` - `pnpm test:browser:local -- --project=chromium browser_tests/tests/selectionToolboxActions.spec.ts browser_tests/tests/selectionToolboxSubmenus.spec.ts browser_tests/tests/vueNodes/interactions/node/contextMenu.spec.ts --grep "info button opens the right-side info tab|info button is hidden|hides Node Info|should open node info"` - `pnpm typecheck:browser` - `pnpm exec oxlint --type-aware browser_tests/tests/selectionToolboxActions.spec.ts` - `pnpm exec eslint --cache --no-warn-ignored browser_tests/tests/selectionToolboxActions.spec.ts` - `pnpm exec oxfmt --check browser_tests/tests/selectionToolboxActions.spec.ts` - `git diff --check` - Commit hooks: lint-staged + `pnpm typecheck` + `pnpm typecheck:browser` - Push hook: `knip --cache` (existing tag hint only) ## Screenshots (if applicable) Before https://github.com/user-attachments/assets/4b1f6ddb-a01c-4958-81ab-36167f434e59 https://github.com/user-attachments/assets/83433f0d-24f1-46b7-a81d-f0f065812496 After https://github.com/user-attachments/assets/30bd61e5-f8d4-48b7-97e0-26c93e3cb362 https://github.com/user-attachments/assets/afce9f51-a43d-434f-a006-6b357a61ac8f --------- Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
b59b342a43 |
1.45.7 (#12238)
Patch version increment to 1.45.7 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12238-1-45-7-3606d73d36508138981ee641470344e4) 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> |
||
|
|
95e616b894 |
fix: clear media upload errors via widget change (#12212)
## Summary Clear missing media validation errors after paste/drop media uploads by emitting the existing widget-change event path. ## Changes - **What**: Emit `node.onWidgetChanged` after image/video upload completion updates the file combo widget. - **What**: Emit the same widget-change path after Load Audio upload completion. - **What**: Add unit coverage for upload completion emitting `onWidgetChanged` and for missing media clearing through that existing hook path. - **What**: Add E2E coverage for Load Image drag/drop and paste clearing validation rings, with red/green verified from a fresh `main` base. - **Dependencies**: None. ## Review Focus Please check that paste/drop upload paths now reuse the existing widget-change error-clearing path instead of expanding `widget.callback` patching. Also check the Load Image E2E helper path for synthetic paste/drop behavior. Supersedes #12207. Ref: FE-687 ## Screenshots Before https://github.com/user-attachments/assets/2cee52bc-b1c8-4dff-8a02-5b18a69ae639 After https://github.com/user-attachments/assets/e1ecd147-1d8a-470e-b77d-13345d473ef3 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12212-fix-clear-media-upload-errors-via-widget-change-35f6d73d365081bcb1a0dfc042d417eb) by [Unito](https://www.unito.io) |
||
|
|
5738c7a539 |
Add SaveAudioAdvanced to whitelisted nodes (#12213)
## Summary Add `SaveAudioAdvanced` to whitelisted nodes in order to display the audio player. This PR goes with the core PR: https://github.com/Comfy-Org/ComfyUI/pull/13871 ## Changes - **What**: Display audio player on new node `SaveAudioAdvanced` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12213-Add-SaveAudioAdvanced-to-whitelisted-nodes-35f6d73d3650815783fac52d3d37e1e1) by [Unito](https://www.unito.io) |
||
|
|
b36b601a1c |
refactor(litegraph): prune dead surfaces (AUDIT-LG implementation, draft) (#12228)
Implements the AUDIT-LG verdicts (#12223, #12224, #12225) as a single deletion PR off main. > **DRAFT — sequencing.** Per the AUDIT-LG framing doc, deletions land **after Phase B ECS migration** (Alex's #11939 + #11811). Open early to capture the diff and let CI cascade-flag any unused-export fallout. Flip to ready-for-review post-Alex-rebase. ## Status | commit | scope | status | |---|---|---| | 1 | Delete 6 LGraph stepping hooks (`onAfterStep`, `onBeforeStep`, `onPlayEvent`, `onStopEvent`, `onAfterExecute`, `onExecuteStep`) + their dispatch sites in `start()`/`stop()`/`runStep()` | ✅ landed (this commit) | | 2 | Delete the rest of the dead executor cluster (`start()`/`stop()`/`runStep()`/`sendEventToAllNodes()` + state fields + `STATUS_*` constants) | follow-up | | 3 | Delete `LGraphNode` dead event hooks (~22 fields per #12224) | follow-up | | 4 | Delete trigger/action subsystem (~22 symbols, #12223) | follow-up | | 5 | `ON_EVENT` deprecation cycle, release N (#12225) | follow-up | ## Verification (commit 1) ``` $ pnpm lint && pnpm format:check && pnpm knip ✓ format: All matched files use the correct format ✓ lint: pre-existing icon-name warnings only ✓ knip: no new unused exports flagged ``` ## Verdicts the deletion is grounded in - AUDIT-LG.7 master verdict table (146 surfaces classified DELETE-NOW / DEPRECATE / KEEP) - AUDIT-LG.9 per-symbol attribution sweep (confirmed zero functional callers for the trigger cluster + the dead hooks) For each surface in this PR: - **`internal_count` from rg over `src/`, `browser_tests/`, `packages/` excluding `lib/litegraph/`:** 0 - **External use from touch-points DB:** 0 (per AUDIT-LG.3 + AUDIT-LG.9 per-symbol attribution) - **Host methods (`start()`/`stop()`/`runStep()`):** `@deprecated 'Will be removed in 0.9'` already The dispatch sites (`this.onAfterStep?.()` etc.) are inside the deprecated host methods — removing the dispatchers does not change observable behaviour because no listener is attached to begin with. ## Why batched, why draft Per AUDIT-LG framing, the deletion sequences behind Alex's PR #11939 (ECS world-combo). Opening as draft lets CI run early and flags any unused-export cascades the audit script missed. Each follow-up commit will be its own atomic deletion (one feature per commit) so any single one can be reverted in isolation if needed. cc @drjkl @christian-byrne ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12228-refactor-litegraph-prune-dead-surfaces-AUDIT-LG-implementation-draft-35f6d73d365081e0b72cd292228e2ca6) by [Unito](https://www.unito.io) Co-authored-by: Connor Byrne <c.byrne@comfy.org> |
||
|
|
f7ef563b46 |
FE-657: prevent browser zoom on ctrl+wheel in mask editor (#12215)
## Summary Wheel events on the mask editor pointer zone now call preventDefault, matching the main canvas behavior so ctrl+wheel only zooms the mask canvas instead of also triggering page zoom. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12215-FE-657-prevent-browser-zoom-on-ctrl-wheel-in-mask-editor-35f6d73d36508131a9b8dbf2f6640d72) by [Unito](https://www.unito.io) |
||
|
|
9cc09cd46c |
Add additional subgraph test fixtures and tests (#11806)
- Adds functions to SubgraphHelper to perform widget promotion by standard user means - Right Click -> Promote - Properties Panel - Adds new slot fixture code that works with simple `locator.dragTo` operations. - Adds multiple subgraph tests with a focus on historically difficult operations. - Fixes a bug where the litegraph `node.selected` state would not be unset when switching graphs. This made it so 'Selecting a node -> leaving subgraph -> re-enter subgraph -> right click on node' would fail to select the node because it is marked as already selected. ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11806-Add-helper-functions-for-widget-promotion-3536d73d365081f58dd9cd730c1a91a9) by [Unito](https://www.unito.io) --------- Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
de1c1ee1f2 |
fix: add support for parsing python generated json with NaN/infinite (#12217)
## Summary API and other legacy JSON generated by python `json.dumps` can contain `NaN` and `Infinity` which cannot be parsed with JS `JSON.parse`. This adds regex to replace these invalid tokens with `null`. ## Changes - **What**: - add regex replace on bare NaN/infinity tokens after JSON.parse fails - update call sites - tests ## Review Focus - The regex should only rewrite bare NaN/-Infinity/Infinity and not touch string values or other invalid tokens. - A small regex was chosen over JSON5 due to package size (30.3kB Minified, 9kB Minified + Gzipped) or a manual parser due to the unnecessarily complexity vs a single regex replace. - The happy path is run first, the safe parse is only executed if that failed, meaning no overhead the vast majority of the time and no possiblity of corrupting valid workflows due to a bug in the fallback parser - Multiple call sites had to be updated due to pre-existing architecture of the various parsers, an issue for unifying these is logged for future cleanup - New binary fixtures added for validating e2e import using real files ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12217-fix-add-support-for-parsing-python-generated-json-with-NaN-infinite-35f6d73d365081889fc7f4af823f29c1) by [Unito](https://www.unito.io) |
||
|
|
86b1e1a965 |
Fix descriptions on core blueprints (#12220)
Core blueprints were storing the description under a different key than expected, which resulted in them displaying a placeholder description. When initializing the description for a subgraph, this alternative field is also checked. | Before | After | | ------ | ----- | | <img width="360" alt="before" src="https://github.com/user-attachments/assets/ed51c4a8-00cf-4927-9cba-880532a9e926" /> | <img width="360" alt="after" src="https://github.com/user-attachments/assets/f19bf80d-adcc-4e9b-a9ba-a5ac8e089e2d" />| Resolves FE-681 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12220-Austin-blueprint-descriptions-35f6d73d3650812fa04df48c203bebd1) by [Unito](https://www.unito.io) |
||
|
|
4321013798 |
fix: resolve widget input link position drift on reload (#12214)
## Summary The position of a link relative to its slot was able to drift on load, due to widgets inside a node being able to resize without triggering an node-level resize event (min-height node with space at the bottom could have widgets expand into free space, causing misalignment). Recreation: 1. Add KSampler 2. Add Float 3. Connect Float to KSamper.denoise 4. Reload workflow (F5) 5. Observe misalignment ## Changes - **What**: - track widget grid element as signal only that triggers resync - node bound calculations skipped for widget signals - prevent setDirty on non-graph nodes (e.g. LGraphNodePreview) - tests ## Review Focus This is a small focused approach to fix the reported issue - it does not address the underlying issue of the layout not being a SSOT. This fix is a small bandaid and investigation into resolving the layout SOT issue is not impacted by this. ## Screenshots (if applicable) Before: <img width="673" height="374" alt="image" src="https://github.com/user-attachments/assets/2d34b8e3-0731-4fd2-8553-4dd429010ced" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12214-fix-resolve-widget-input-link-position-drift-on-reload-35f6d73d3650814eb31bebb3042ff58b) by [Unito](https://www.unito.io) |
||
|
|
7ce0973386 |
fix: prevent first user template popup when following shared link (#12024)
## Summary When a user who has not used the app before first loads up, they are presented with the template selection dialog. This conflicts when the first-time user visits the app via a share link - both the share & template dialog are triggered. ## Changes - **What**: - Skip the templates browser when share param is in URL - Tests - Add `url` to `setup`/`goto` to allow specifying the `share` parameter ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12024-fix-prevent-first-user-template-popup-when-following-shared-link-3586d73d365081cbbcecdba45a1ad1ea) by [Unito](https://www.unito.io) |
||
|
|
6e9be7b164 |
feat: add Anthropic partner icon (#12216)
*PR Created by the Glary-Bot Agent* --- ## Summary Adds the Anthropic logo to the partner-node icon set so nodes whose category ends in `Anthropic` (e.g. the Claude node added in Comfy-Org/ComfyUI#13867 with `category="api node/text/Anthropic"`) render the correct provider badge in the node library. ## Changes - `packages/design-system/src/icons/anthropic.svg` — new auto-discovered partner icon (Anthropic A glyph, sourced from [lobehub/lobe-icons](https://github.com/lobehub/lobe-icons), uses `fill="currentColor"` for theme adaptation) - `src/utils/categoryUtil.ts` — register Anthropic's brand coral `#D97757` as the badge border color - `packages/design-system/src/css/style.css` — add `anthropic` to the dynamic comfy-icon safelist so Tailwind/Iconify emits CSS for `icon-[comfy--anthropic]` in production builds - `src/utils/categoryUtil.test.ts` — regression tests for `getProviderIcon('Anthropic')` and `getProviderBorderStyle('Anthropic')` ## Verification - `pnpm typecheck` ✓ - `pnpm lint` ✓ (0 errors; 3 pre-existing warnings in unrelated files) - `pnpm format:check` ✓ - `pnpm test:unit -- src/utils/categoryUtil.test.ts` ✓ (13/13) - `pnpm build` ✓ — confirmed `comfy--anthropic` class is emitted into `dist/assets/index-*.css` - Manual visual check via Playwright against `pnpm dev`: injected `<i class="icon-[comfy--anthropic]">` elements at badge size (10px) and 48px alongside the existing OpenAI and BFL icons and confirmed the Anthropic "A" glyph renders correctly in coral. See screenshot. End-to-end visual verification of the live badge in the node library requires Comfy-Org/ComfyUI#13867 to land first (the Claude node is what produces the `Anthropic` category that triggers the icon lookup). Related: Comfy-Org/ComfyUI#13867 ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12216-feat-add-Anthropic-partner-icon-35f6d73d36508133a134fcafaf72f4f8) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
4b5b184cad |
FE-566: fix Painter mask submission edge cases on cloud (#12196)
## Summary Rework the painter always hands the backend a valid asset reference: - Drop the `hasStrokes` flag and the `isCanvasEmpty` check. - `serializeValue` falls back to the existing `modelValue` when the canvas element is transiently unmounted, reuses the cached upload when not dirty and a value is present, and otherwise uploads the current canvas (a fully transparent PNG is a valid no-op mask, Painter's Python `execute()` treats painter_alpha=0 the same as "no mask painted"). - `handleClear` now also clears `modelValue` so a user-initiated clear doesn't resurrect a stale upload on the next serialize. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12196-FE-566-fix-Painter-mask-submission-edge-cases-on-cloud-35e6d73d365081dd8856ddb785952526) by [Unito](https://www.unito.io) |
||
|
|
129bfd9f1b |
fix: fix drop location and zindex of dragged in images (#12194)
## Summary Images dragged into the canvas were placed at the last graph mouse position, which is not updated during the drag event - meaning nodes were created in "random" locations. Additionally, the z-index was not set so newly created nodes can appear under other nodes. ## Changes - **What**: - ensure added nodes are at top level - update graph mouse pos with position from drop event - tests ## Screenshots (if applicable) Before / After https://github.com/user-attachments/assets/34b4652e-a834-4c22-b191-2875a2404ac5 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12194-fix-fix-drop-location-and-zindex-of-dragged-in-images-35e6d73d3650814781edc9f4b4b5b223) by [Unito](https://www.unito.io) |
||
|
|
a0150ffe17 |
1.45.6 (#12204)
Patch version increment to 1.45.6 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12204-1-45-6-35f6d73d365081fc8539ca25a55aac74) 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> |
||
|
|
c92030b158 |
refactor: deduplicate Civitai hostname logic in getSourceName (#11822)
## Summary
- Extract `isCivitaiHost` private helper from `isCivitaiModelUrl` in
`formatUtil.ts` for DRY hostname checking
- Add `isCivitaiUrl` exported function for hostname-only Civitai URL
detection (distinct from `isCivitaiModelUrl` which also validates the
path format)
- Refactor `getSourceName` in `assetMetadataUtils.ts` to use the shared
`isCivitaiUrl` instead of inline duplicate hostname checks
- Add tests for `isCivitaiUrl` covering `.com`, `.red`, subdomain, and
invalid URL cases
## Changes
- `packages/shared-frontend-utils/src/formatUtil.ts` — add
`isCivitaiHost` private helper + export `isCivitaiUrl`; refactor
`isCivitaiModelUrl` to use helper
- `packages/shared-frontend-utils/src/formatUtil.test.ts` — add
`isCivitaiUrl` test suite
- `src/platform/assets/utils/assetMetadataUtils.ts` — import
`isCivitaiUrl` from `@/utils/formatUtil`; remove inline hostname logic
from `getSourceName`
## Testing
### Automated
- Added `isCivitaiUrl` test suite (6 cases: `.com`, `.red`, subdomains,
non-Civitai, invalid URL)
- All 71 existing `formatUtil` tests pass
- All 53 existing `assetMetadataUtils` tests pass (behavior preserved)
- TypeScript typecheck passes
### E2E Verification Steps
1. Run unit tests: `npx vitest run
packages/shared-frontend-utils/src/formatUtil.test.ts
src/platform/assets/utils/assetMetadataUtils.test.ts`
2. Expected: all tests pass
3. Verify `getSourceName('https://civitai.red/models/123')` returns
`'Civitai'`
4. Verify `isCivitaiUrl('https://civitai.com/models/any-path')` returns
`true`
5. Verify `isCivitaiModelUrl` still rejects non-API paths while
`isCivitaiUrl` accepts them
## Review Focus
`isCivitaiUrl` (new, hostname-only) vs `isCivitaiModelUrl` (existing,
hostname+path format): `getSourceName` needs to recognize ANY Civitai
URL as a source, so using `isCivitaiModelUrl` directly would incorrectly
reject valid browse URLs like `civitai.com/models/123`.
Closes #11357
┆Issue is synchronized with this [Notion
page](https://app.notion.com/p/PR-11822-refactor-deduplicate-Civitai-hostname-logic-in-getSourceName-3546d73d36508110974ccc3b7384d82b)
by [Unito](https://www.unito.io)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||
|
|
988d532467 |
fix(queue): contain JobDetailsPopover error message overflow (#12173)
## Summary Cap the Job Details "Error message" block at `max-h-96` (24rem / 384px) with an internal scroll, wrap long unbreakable tokens (filenames, JSON), and preserve newlines so the failed-job popover no longer grows unbounded. ## Changes - **What**: Added `max-h-96 overflow-y-auto whitespace-pre-wrap wrap-break-word` to the error message container in `JobDetailsPopover.vue`, plus a `FailedWithLongError` Storybook story covering the overflow case. ## Review Focus - 24rem cap was set per Alex's spec in the [Slack thread](https://comfy-organization.slack.com/archives/C0A4XMHANP3/p1778506109115989). - `wrap-break-word` (Tailwind 4 canonical of `break-words`) is needed because long underscore-joined filenames don't break naturally; `whitespace-pre-wrap` preserves any newlines in the raw error. - Not in scope: the popover z-index clipping issue Alex flagged later in the same thread — that's a separate follow-up. Fixes FE-660 ## Screenshots **Before** — error block grows unbounded with the panel:  **After** — error block capped at 384px and internally scrollable:  Reproduce locally via Storybook: `pnpm storybook` → Queue → JobDetailsPopover → **FailedWithLongError**. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12173-fix-queue-contain-JobDetailsPopover-error-message-overflow-35e6d73d3650812d9873e5d163cad0c6) by [Unito](https://www.unito.io) --------- Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com> |
||
|
|
9fe19a2afb |
fix(settings): unify settings item heights and use 14px label text (#12180)
## Summary Body text in the settings dialog was still rendering at the inherited 16px (browser default) instead of the 14px design spec, and rows with different control types (toggle, slider, dropdown, radio) collapsed to different heights — making the list look uneven and cramped. ## Changes - **What**: `FormItem` label now uses `text-sm` (14px) and the row enforces `min-h-8` (32px) so toggle/slider/dropdown/radio rows align. `SettingGroup` bumps inter-item margin from `mb-2` to `mb-3` for breathing room between settings. ## Review Focus `FormItem` is also used by `ServerConfigPanel`, so the 14px/32px row also applies there — consistent with the same settings-dialog visual language, but worth a glance. Fixes #FE-525 ## Screenshots Lite Graph panel (1280×900 viewport) showing toggle/slider/dropdown/radio rows side-by-side: | Before (`origin/main`) | After (this PR) | | --- | --- | | <img src="https://raw.githubusercontent.com/Comfy-Org/ComfyUI_frontend/pr-12180-screenshots/before-litegraph.png" width="480"> | <img src="https://raw.githubusercontent.com/Comfy-Org/ComfyUI_frontend/pr-12180-screenshots/after-litegraph.png" width="480"> | Before: label text inherits 16px from `<body>`; toggle-only rows (e.g. "Always snap to grid", "Live selection") shrink to ~24px while dropdown/slider rows stay ~32px, so the list looks uneven and cramped. After: labels are 14px; every row is at least 32px tall so toggles/sliders/dropdowns/radios line up; `mb-3` adds 4px of breathing room between rows. ## References - Linear: https://linear.app/comfyorg/issue/FE-525/verify-settings-text-size-and-item-heights - Figma: https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=6290-75412 - Origin thread: https://comfy-organization.slack.com/archives/C075ANWQ8KS/p1777657610484679?thread_ts=1776808927.654249 --------- Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com> |
||
|
|
469a5edf99 |
feat: cloud-nodes catalog at /cloud/supported-nodes (#11903)
*PR Created by the Glary-Bot Agent*
---
## Summary
Adds a comfy.org page that lists every custom-node pack supported on
Comfy Cloud, with per-pack detail subpages. Data is fetched at build
time from `cloud.comfy.org/api/object_info` (gated by
`WEBSITE_CLOUD_API_KEY`), sanitized of user content, joined with public
registry metadata from `api.comfy.org/nodes`, and falls back to a
committed snapshot — mirroring the existing Ashby careers integration
pattern.
- Index: `/cloud/supported-nodes` (en) and
`/zh-CN/cloud/supported-nodes` (zh-CN)
- Detail: `/cloud/supported-nodes/[pack]` and
`/zh-CN/cloud/supported-nodes/[pack]`, generated via `getStaticPaths()`
from the same fetcher as the index so the two routes can never diverge.
## What's new
**Shared package (extracted)**
- `@comfyorg/object-info-parser` — Zod schemas (`zComfyNodeDef`,
`validateComfyNodeDef`), node-source classifier (`getNodeSource`,
`isCustomNode`, `CORE_NODE_MODULES`), and helpers (`groupNodesByPack`,
`sanitizeUserContent`). `src/schemas/nodeDefSchema.ts` and
`src/types/nodeSource.ts` become 1-line re-export shims; existing
imports keep compiling.
**Build-time pipeline**
- `apps/website/src/utils/cloudNodes.ts` — Ashby-style fetcher:
retry/backoff `[1s, 2s, 4s]`, 10 s timeout via AbortController, Zod
envelope + per-node validation, snapshot fallback, memoized via
module-level `inflight` promise.
- `apps/website/src/utils/cloudNodes.registry.ts` — Public registry
enrichment (no auth, batches of 50, single retry, soft-fail).
- `apps/website/src/utils/cloudNodes.ci.ts` — GitHub Actions annotations
+ step summary mirroring the Ashby reporter.
- `apps/website/src/utils/cloudNodes.build.ts` — Single
`loadPacksForBuild()` consumed by both index and detail pages so they
share one source of truth.
- `apps/website/scripts/refresh-cloud-nodes-snapshot.ts` — atomic-rename
refresh CLI that walks pack/node string fields with a user-content
extension regex *before* renaming the snapshot into place.
- Mandatory user-content sanitization strips uploaded filenames from
combo lists (`LoadImage`, `LoadImageMask`, `LoadImageOutput`,
`LoadVideo`, `LoadAudio` zeroed; any combo value matching
`/\.(png|jpe?g|webp|gif|mp4|mov|webm|wav|mp3|flac|ogg|safetensors|ckpt|pt)$/i`
filtered).
**Page + components**
- `apps/website/src/pages/cloud/supported-nodes.astro` (en) + zh-CN
twin.
- `apps/website/src/pages/cloud/supported-nodes/[pack].astro` detail
(en) + zh-CN twin, async `getStaticPaths` driven by
`loadPacksForBuild()`.
-
`apps/website/src/components/cloud-nodes/{HeroSection,PackGridSection,PackCard,PackBanner,NodeList,PackDetail}.vue`
— Vue 3.5 destructured props, `cn()` from `@comfyorg/tailwind-utils`,
design-system tokens only, no PrimeVue.
- Pack card name links to its detail page; banner uses the shared
`fallback-gradient-avatar.svg` asset (copied into
`apps/website/public/assets/images/`) when `banner_url` and `icon` are
missing.
- 25 new `cloudNodes.*` i18n keys in `en` + `zh-CN`.
**Tests**
- 33 unit tests in `@comfyorg/object-info-parser` (schemas, classifier,
sanitizer, grouping).
- 19 new website unit tests covering fetcher (10), CI reporter (6),
registry enrichment (3) — Ashby patterns mirrored.
- E2E: index smoke + search + banner + detail click-through + direct
visit + zh-CN parity.
## Required maintainer follow-up
GitHub Apps cannot push `.github/workflows/*` changes (push was rejected
with `refusing to allow a GitHub App to create or update workflow …
without workflows permission`), so the workflow edits prepared in this
branch were reverted in commit `9be2abce8`. The intended diffs are
documented as copy-paste-ready snippets in `apps/website/README.md`
under the new "Cloud nodes integration → CI wiring" section.
A maintainer must:
1. Provision `WEBSITE_CLOUD_API_KEY` in the repo secrets and the Vercel
project env.
2. Apply the `ci-website-build.yaml` and
`ci-vercel-website-preview.yaml` diffs documented in the README directly
to `main` (or as a follow-up commit on this branch with a maintainer
account).
The committed snapshot lets builds succeed without the secret while the
maintainer step is pending — pages render from
`apps/website/src/data/cloud-nodes.snapshot.json`.
## Self-review (Oracle)
Two warnings caught and fixed in commits `deba5ab02` and `99dfc3381`:
- Index/detail pages now share a single source of truth
(`loadPacksForBuild`), so a fresh fetch can't expose packs whose detail
routes weren't generated.
- Refresh script validates parsed snapshot fields *before* the atomic
rename, instead of regex-scanning the serialized JSON after the file is
already in place.
## Quality gates (local)
```
pnpm --filter @comfyorg/object-info-parser test → 33 passed
pnpm --filter @comfyorg/website test:unit → 42 passed
pnpm --filter @comfyorg/website typecheck → 0 errors
pnpm --filter @comfyorg/website build → 47 pages built (incl. 6 cloud-nodes routes)
pnpm lint → 0 errors (1 pre-existing warning in unrelated test file)
pnpm knip → 0 errors (1 pre-existing tag hint in unrelated file)
```
E2E (`pnpm --filter @comfyorg/website test:e2e`) is intended to be run
by the Vercel/CI pipelines.
## Manual verification
Built `dist/`, served locally on port 4321, drove with Playwright:
- `/cloud/supported-nodes` renders both pack cards, search input, sort
dropdown
- `/cloud/supported-nodes/comfyui-impact-pack` renders the metadata grid
(publisher, downloads, stars, version, license, last updated) and 3
categorized node sections with 5 nodes total
- `/zh-CN/cloud/supported-nodes` localizes hero (`Comfy Cloud 上的自定义节点`),
label (`云端节点目录`), search placeholder (`搜索节点包或节点名称`), sort
- `/zh-CN/cloud/supported-nodes/comfyui-controlnet-aux` localizes every
metadata label (`查看仓库`, `发布者`, `下载量`, `GitHub 星标`, `最新版本`, `许可证`,
`最后更新`) and renders dates with `Intl.DateTimeFormat('zh-CN')`
(`2026年4月27日`)
- Search input narrows pack count from 2 to 1 when typing `impact`
(verified via DOM count)
Banners render the shared `fallback-gradient-avatar.svg` when the
snapshot's image URL doesn't resolve — expected in the local sandbox.
## Preview URL (after CI completes)
`https://comfy-website-preview-pr-{N}.vercel.app/cloud/supported-nodes`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11903-feat-cloud-nodes-catalog-at-cloud-supported-nodes-3566d73d36508194afdec5f389897585)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
|
||
|
|
681915275e |
fix: remove failed to export toast when cancelling export workflow (#12134)
## Summary An incorrect error toast currently shows when cancelling the workflow export from an asset ## Changes - **What**: - skip toast on cancel - add e2e & unit tests - refactor asset tab open helper to wait by default & cleanup usage ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12134-fix-remove-failed-to-export-toast-when-cancelling-export-workflow-35d6d73d3650815b839ff26edd70a472) by [Unito](https://www.unito.io) |
||
|
|
3d9c9ce327 |
fix: update color widget colors (#12133)
## Summary The widget was using a different fg/background compared to all other widgets, this updates to use the standard classes. ## Changes - **What**: - update styles ## Screenshots (if applicable) Before <img width="570" height="597" alt="Screen Shot 2026-05-11 at 07 19 12" src="https://github.com/user-attachments/assets/18a5330f-5e9a-4d16-b3f0-0acfab5d6f99" /> After <img width="570" height="597" alt="Screen Shot 2026-05-11 at 07 15 46" src="https://github.com/user-attachments/assets/81c9da58-fdda-4539-ae1e-98727f12b9ac" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12133-fix-update-color-widget-colors-35d6d73d365081da8515cd48a8e8ecc2) by [Unito](https://www.unito.io) No e2e tests for this as they would be simple screenshot asserts on the color of nodes, which is not worthwhile. |
||
|
|
e765eb1bb2 |
fix: suppress missing media scan during uploads (#12111)
## Summary - Prevent missing media detection from scanning media loader nodes while a drag/drop, paste, or file-select upload is still in progress. - Align LoadAudio with the existing media upload lifecycle by setting `node.isUploading`, blocking concurrent uploads, and clearing the flag after upload completion. - Keep added-node model and missing-node scans on the original one-microtask path, while deferring added-node media scanning by one extra microtask so upload handlers can mark transient upload state before the scan reads widget values. ## Why Drag/drop and paste can create media loader nodes before the backing upload has settled. During that short window, the widget may contain a local filename that is not yet backend-resolvable, so missing media detection can surface a false missing asset. Refreshing works because the upload has completed by then. ## Follow-up - E2E coverage for this upload race will be handled in a follow-up PR together with E2E coverage for the annotated output-media path changes from #12069. ## Validation - `pnpm format` - `pnpm lint` - `pnpm typecheck` - `pnpm vitest run src/composables/graph/useErrorClearingHooks.test.ts src/platform/missingMedia/missingMediaScan.test.ts src/extensions/core/uploadAudio.test.ts src/composables/node/useNodeImageUpload.test.ts` - Re-ran `pnpm typecheck` after rebasing onto latest `main` - Pre-push `knip` hook passed Fixes FE-620 ## Screenshots Before https://github.com/user-attachments/assets/db7891de-a4b5-4cde-aa76-6340e6cdf7b2 After https://github.com/user-attachments/assets/9b99bb13-0d5b-4ff7-8f52-66eea6e417ec ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12111-fix-suppress-missing-media-scan-during-uploads-35b6d73d365081f3b54eed02874ccaa4) by [Unito](https://www.unito.io) |
||
|
|
56434ae9ac |
perf: debounce template search input to keep typing responsive (#12183)
## Summary Route the templates dialog search through `FormSearchInput`'s debounced searcher so per-keystroke work no longer trips the heavy filter/render path. ## Changes - **What**: `WorkflowTemplateSelectorDialog` now uses `FormSearchInput` instead of `SearchInput`. The raw input is bound to a local ref; the actual `searchQuery` consumed by `useTemplateFiltering` is only written after the input debounce settles, so dependent computeds (notably `shouldUsePagination`, which used to flip on every keystroke and force a full grid rebuild) stay stable while typing. - **What**: `FormSearchInput` gains optional `debounceMs` (default `250`) and `debounceMaxWaitMs` (default `1000`) props. Existing callers are unchanged; the templates dialog passes `400` / `4000` to match the feel tuned in this PR. ## Review Focus - Reset path: `searchQuery` is still owned by `useTemplateFiltering` and cleared by `resetFilters`; a watch syncs the visible input back to empty when that happens. - `FormSearchInput` is currently under `src/renderer/...` but already imported by workbench-level components (rightSidePanel tabs). This PR follows that existing precedent rather than relocating the component. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12183-perf-debounce-template-search-input-to-keep-typing-responsive-35e6d73d365081b7a11ec4a84323095f) by [Unito](https://www.unito.io) |
||
|
|
25c2d828c0 |
test: enable vitest/consistent-each-for and migrate .each → .for (#12161)
*PR Created by the Glary-Bot Agent*
---
Enables the oxlint rule `vitest/consistent-each-for` (configured to
prefer `.for` for `test`, `it`, `describe`, and `suite`) and migrates
every `.each` parameterized test in the repo to `.for`. Using `.for`
avoids accidentally splatting tuple elements into separate callback
arguments and exposes `TestContext` as the second callback argument.
The first commit covers the 38 lint-detected files (88 callsites):
renames `.each` → `.for` and updates callback signatures to destructure
when the data is an array of tuples (objects/primitives already work
unchanged with `.for`).
The follow-up commit addresses code review feedback: oxlint's rule does
not recognize `test.each` on extended test bases
(`baseTest.extend(...)`) and skips files in `ignorePatterns`
(`src/extensions/core/*`). These were converted manually so the policy
is uniform across the codebase.
## Verification
- `node_modules/.bin/oxlint src` — 0 errors, 0 `consistent-each-for`
violations
- `pnpm typecheck` — passes
- `pnpm test:unit` — all modified test files pass; pre-existing
environmental flakes (`GraphView.test.ts`, `ColorWidget.test.ts`, etc.,
unchanged here and flaky on `main` in this sandbox) are unrelated
- `pnpm lint` / `pnpm knip` — clean
- Manual verification: 362 tests across 6 representative converted
suites re-run in an interactive shell — all passing
Manual UI verification (Playwright/screenshots) is not applicable:
changes are test-file-only refactors with no production runtime or UI
behavior change.
## Notes on `.for` semantics
- Array-of-tuples (`[[a, b], ...]`) passes the tuple as a single arg, so
callbacks were changed from `(a, b) => …` to `([a, b]) => …`.
- Array-of-objects (`[{a}, …]`) already used destructuring — unchanged.
- Array-of-primitives (`['a', …]`) — callback signature unchanged.
- A handful of complex cases use a small `type Case = [...]` alias plus
`it.for<Case>([...])` to preserve tuple inference where TS narrowed
unions otherwise broke parameter types.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12161-test-enable-vitest-consistent-each-for-and-migrate-each-for-35e6d73d3650810c9417e07bdd9f27a2)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
|
||
|
|
ceb9936058 |
fix(i18n): clamp unsupported browser locales to a shipped tag (#11712)
## Summary
Sidebar buttons rendered literal i18n keys (e.g.
`sideToolbar.labels.assets`) on a fresh install when the user's
`navigator.language` base tag wasn't one of the 12 shipped locales —
German/Italian/Polish/Dutch/Brazilian-Portuguese users among others.
## Changes
- **What**: Add `resolveSupportedLocale()` that tries the full BCP-47
tag first (preserves `zh-TW`, `pt-BR`), then the base tag, then `'en'`.
Wire through both entry points (`createI18n`'s initial locale,
`Comfy.Locale`'s `defaultValue`) and clamp inside `loadLocale`,
propagating the resolved tag to `GraphView` so a stale stored
`Comfy.Locale='de'` from older builds also recovers.
- **Side benefit**: Brazilian Portuguese users were previously falling
through `pt-BR` → `pt` (unshipped) → broken. The full-tag-first lookup
now correctly lands them on the `pt-BR` bundle.
- **Breaking**: None.
- **Dependencies**: None.
## Root Cause
Three-link chain:
1. `Comfy.Locale`'s default was `() => navigator.language.split('-')[0]
|| 'en'`. German → `'de'` (unshipped).
2. `loadLocale('de')` silently `console.warn`'d and returned without
throwing.
3. `GraphView` then ran `i18n.global.locale.value = 'de'` anyway.
4. `st(key, fallback) = te(key) ? t(key) : fallback`. vue-i18n's `te()`
checks **only** the current locale and ignores `fallbackLocale` — every
key missed → `st()` returned the literal key string.
Two pathways reached the broken state (defaultValue path, and
unset-setting path through `createI18n`'s own `navigator.language`
snapshot); the new helper closes both.
## Review Focus
- `loadLocale` now returns `SupportedLocale` (was `void`). Old `void`
callers continue to compile; the only change is `GraphView` consuming
the return value.
- Unit-tested in `src/i18n.test.ts` (added `resolveSupportedLocale`
block + updated the `loadLocale` unsupported-locale case from "warn" to
"clamp to en").
- Self-reproduced via Playwright with `navigator.language='de-DE'` +
fresh-install state on both `main` (shows the bug) and this branch
(shows the fix). Spec saved at
`temp/scripts/issue-10563-locale-bug.spec.ts`.
Fixes #10563
FE-480 — https://linear.app/comfyorg/issue/FE-480
## Screenshots
**Before** (from #10563, on `main`):
<img width="258" height="399" alt="Sidebar with literal i18n keys"
src="https://github.com/user-attachments/assets/098d1d76-8e89-4237-813f-5f030b34e51e"
/>
**After** (this branch, same `navigator.language='de-DE'`):
<img width="367" height="793" alt="Screenshot 2026-04-28 at 2 07 38 PM"
src="https://github.com/user-attachments/assets/9d279de3-50a8-4774-999f-ab4c3018a9ef"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11712-fix-i18n-clamp-unsupported-browser-locales-to-a-shipped-tag-3506d73d3650812f89d2f0fe3199de3a)
by [Unito](https://www.unito.io)
|
||
|
|
bb420fe2c7 |
feat: add model-to-node mappings for new model directories (#12151)
## Summary Add entries to `MODEL_NODE_MAPPINGS` so the model browser's "Use" button correctly creates a loader node for five model directories that currently have no mapping. ## Changes - **What**: 5 new entries in `src/platform/assets/mappings/modelNodeMappings.ts`: - `background_removal` → `LoadBackgroundRemovalModel` / `bg_removal_name` (ComfyUI v0.21+ core) - `frame_interpolation` → `FrameInterpolationModelLoader` / `model_name` (ComfyUI v0.21+ core) - `film` → `FILM VFI` / `ckpt_name` (ComfyUI-Frame-Interpolation) - `ultralytics/bbox` → `UltralyticsDetectorProvider` / `model_name` (ComfyUI-Impact-Pack) - `ultralytics/segm` → `UltralyticsDetectorProvider` / `model_name` (ComfyUI-Impact-Pack) - **Breaking**: none ## Review Focus - Node class names and input keys were cross-checked against the ComfyUI v0.21.0 source and the published Impact-Pack / Frame-Interpolation node definitions - Both `ultralytics/bbox` and `ultralytics/segm` map to the same node (`UltralyticsDetectorProvider`); its `model_name` combo accepts values from both subdirectories (`bbox/...` and `segm/...`) - `film` and `frame_interpolation` are separate directories serving different node packs — keeping them as distinct entries rather than collapsing under a parent ## Test plan - [ ] In the model browser, clicking "Use" on `birefnet.safetensors` creates a `LoadBackgroundRemovalModel` node with the model preselected - [ ] Same for one model in each of: `frame_interpolation`, `film`, `ultralytics/bbox`, `ultralytics/segm` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12151-feat-add-model-to-node-mappings-for-new-model-directories-35d6d73d365081ff834bf6eb610da160) by [Unito](https://www.unito.io) |
||
|
|
1290bbd359 |
1.45.5 (#12152)
Patch version increment to 1.45.5 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12152-1-45-5-35e6d73d365081c39aecddf42d8f7a2a) 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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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:` ( |
||
|
|
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> |
||
|
|
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) |
||
|
|
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) |