Commit Graph

8148 Commits

Author SHA1 Message Date
Hunter Senft-Grupp
2ce68c8a58 refactor: use axios interceptor instead of watcher for runtime base URL
Reading the base URL at axios.create() time captures whatever
remoteConfig holds at module-load — which on OSS is the empty default,
because static imports are hoisted above main.ts's top-level
'await refreshRemoteConfig()'. A subsequent 'watch' never fires because
the value is already correct by the time the watcher is established.
A request-time interceptor reads the current URL on every call, which
is both simpler and actually correct.
2026-05-31 13:38:20 -04:00
Hunter Senft-Grupp
112b3526a9 test: pin fallback to build-time default when override is absent or empty 2026-05-31 13:36:02 -04:00
Hunter Senft-Grupp
0c31464cc5 feat: honor server-provided comfy api/platform base URLs in all distributions
Previously only the cloud distribution consulted window.__CONFIG__ for
comfy_api_base_url / comfy_platform_base_url; OSS builds were locked to
the build-time URL. This made it impossible to point a local comfyui at
an ephemeral api.comfy.org without rebuilding the frontend.

- main.ts: refreshRemoteConfig({ useAuth: false }) now runs in every
  distribution against the local /api/features endpoint
- config/comfyApi.ts: drop the isCloud short-circuit in
  getComfyApiBaseUrl / getComfyPlatformBaseUrl
- services/comfyRegistryService.ts: route through getComfyApiBaseUrl
  instead of hardcoding api.comfy.org; mirrors customerEventsService's
  reactive watch pattern so the registry/manager calls also follow
  runtime overrides

Requires the companion comfyui change that adds comfy_api_base_url and
comfy_platform_base_url to /features.
2026-05-31 13:31:04 -04:00
Comfy Org PR Bot
9e32b7db51 1.46.6 (#12535)
Patch version increment to 1.46.6

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.46.6
2026-05-30 06:33:16 +00:00
Deep Mehta
9813eee22f fix(website): make comfy.org favicon Google-compliant (#12536)
## Summary

Fix the comfy.org favicon declaration so Google Search can actually
refresh it — the logo has shown the old Astro starter chevron in Google
results for weeks even though the icon files were fixed in May.

## Changes

- **What**: The site declared two SVG favicons gated by
`prefers-color-scheme` media queries, no PNG favicon, and a
`favicon.ico` with only a 64×64 image. Google Search's favicon pipeline
can't resolve a single valid icon from that, so on recrawl it keeps the
last good cached icon (the old Astro chevron) instead of the current
mark. Replaced with the standard, checker-clean set:
- `favicon.svg` — single adaptive SVG (light/dark swap via internal CSS)
replacing the two media-query SVG links
- `favicon-96x96.png` — explicit desktop PNG at a Google-recommended
size (multiple of 48)
  - `favicon.ico` — rebuilt to contain 16/32/48 (was 64×64 only)
  - `site.webmanifest` + `web-app-manifest-192x192.png` / `-512x512.png`
- `<head>` simplified to RealFaviconGenerator's recommended markup +
`theme-color`
- **Breaking**: none. Pure static-asset + `<head>` change, no runtime
code touched.

## Review Focus

- This resolves the 3 errors + 3 warnings RealFaviconGenerator reported
for comfy.org (2 SVG favicons / no desktop PNG / ICO missing 16-32-48 /
no web manifest).
- The old `favicon-light.svg` and `favicon-dark.svg` are left in place
(now unreferenced) to avoid deleting assets in this PR — safe to remove
in a follow-up if desired.
- **Post-merge, required to actually fix Search**: in Google Search
Console, run URL Inspection → Request Indexing on `https://comfy.org/`.
The Search favicon only refreshes when Googlebot recrawls the homepage;
without a clean icon set + a recrawl nudge it will not self-correct.

## Before / After

### Before
<img width="939" height="388" alt="image"
src="https://github.com/user-attachments/assets/5fa95fd6-2248-4ed9-921e-9c516f4c0c3e"
/>

### After
<img width="1145" height="447" alt="image"
src="https://github.com/user-attachments/assets/dc33a99d-f7f6-41d4-a83d-03b3b99d0b0d"
/>

## Screenshots (if applicable)
2026-05-30 05:21:29 +00:00
Dante
c7238dd395 fix: Remove duplicate app workflow validation (#12208)
## Summary

`extra.linearData.inputs` validation rejected the entire workflow
whenever a single entry didn't match the strict `z.union([3-tuple,
2-tuple])`, surfacing as `Failed to load shared workflow: invalid
workflow data` for some published cloud shares (e.g.
`share=21e32125c692`).

## Changes

- **What**: Shared workflow load now matches the regular load policy at
`scripts/app.ts:1191-1198` — if Zod validation fails, fall back to the
raw `workflow_json` instead of throwing. Share service no longer runs
schema validation directly; `app.loadGraphData()` continues to validate
and apply the same raw fallback under `Comfy.Validation.Workflows`.
- **Breaking**: None — workflows that previously failed to load through
the share path now load with the same permissive behavior as workflows
opened through any other entry point.

## Review Focus

The original approach added a `tolerantArray` combinator to drop bad
`linearData.inputs` entries inside the schema. After review, the cleaner
direction is to keep schemas strict as the canonical spec and apply
tolerance at the consumer boundary — which `scripts/app.ts:1191-1198`
already does for the regular load path (`graphData = validatedGraphData
?? graphData` with the comment "Ideally we should not block users from
loading the workflow"). This PR aligns the share path with that existing
policy and removes a cross-path inconsistency rather than introducing a
new schema-level concept.

Consequences:
- Schemas in `workflowSchema.ts` stay unchanged (canonical spec).
- The `extra.*` shape drift problem is now solved generally, not just
for `linearData.inputs` — future fields hitting the same class of issue
will load instead of blocking.
- `validateComfyWorkflow` still logs the Zod error via `console.warn`
for debugging.

## Tests

- Existing schema tests stay strict (3-tuple, 2-tuple unions reject bad
shapes).
- New regression test in `workflowShareService.test.ts`: a
`workflow_json` that passes the share response envelope
(`record<unknown>`) but fails `ComfyWorkflowJSON` is returned raw, not
thrown.

### current prod

<img width="1144" height="565" alt="Screenshot 2026-05-13 at 1 29 00 PM"
src="https://github.com/user-attachments/assets/b1abf45b-a588-4ef5-a9ec-d14bd1096b6d"
/>

### test
`/?share=21e32125c692`

<img width="765" height="826" alt="Screenshot 2026-05-13 at 1 27 40 PM"
src="https://github.com/user-attachments/assets/96a3c405-e5fe-4732-9047-fed90768e6f6"
/>

## Follow-up

Structural issue separately: cloud OpenAPI defines `workflow_json` as
opaque `z.record(z.unknown())`
(`packages/ingest-types/src/zod.gen.ts:137,325,375,397,457`), so ingest
schema CI cannot enforce inner shape. Matt Miller is driving the
lift-schema-to-core-OpenAPI direction; tracked separately, not in this
PR.

Fixes FE-690.
2026-05-30 05:20:50 +00:00
Alexander Brown
cedb4e6761 chore: drop no-op security overrides for brace-expansion and ws (#12533)
## Summary

Removes three security overrides added in #12345/#12501 that are now
no-ops: every consumer in the tree already pulls a version at or above
the GHSA-patched release.

## Changes

- **What**: Drops `brace-expansion@^1.0.0`, `brace-expansion@^2.0.0`,
and `ws@^8.0.0` overrides from
[pnpm-workspace.yaml](pnpm-workspace.yaml).
- **Breaking**: None.

| Override removed | Patched at | Natural resolution | GHSA |
|---|---|---|---|
| `brace-expansion@^1.0.0: ^1.1.13` | 1.1.13 | 1.1.15 |
[GHSA-f886-m6hf-6m8v](https://github.com/advisories/GHSA-f886-m6hf-6m8v)
|
| `brace-expansion@^2.0.0: ^2.0.3` | 2.0.3 | 2.1.1 |
[GHSA-f886-m6hf-6m8v](https://github.com/advisories/GHSA-f886-m6hf-6m8v)
|
| `ws@^8.0.0: ^8.20.1` | 8.20.1 | 8.21.0 |
[GHSA-58qx-3vcg-4xpx](https://github.com/advisories/GHSA-58qx-3vcg-4xpx)
|

The remaining security overrides (`lodash`, `yaml`, `minimatch@^9`,
`minimatch@^10`, `ajv@^8`) were re-tested and are still required —
without them dependabot-vulnerable versions (`lodash@4.17.23`,
`yaml@2.7.1`, `minimatch@9.0.1`, `minimatch@10.2.1`,
`ajv@8.12.0`/`8.13.0`) resolve into the tree.

## Review Focus

- The `brace-expansion@5.x` and `minimatch@5.x`/`8.x` lines in the
lockfile naturally resolve to safe versions (`5.0.6`, `5.1.9`, `8.0.7`)
on `main`, so no new overrides are needed alongside this removal.
- Verified by removing each override one-by-one and inspecting `pnpm why
-r`.

## Verification

- `pnpm install` — clean
- `pnpm typecheck` (apps/website + root) → 0 errors
- `pnpm test:unit` (apps/website) → 89/89 passing
- `pnpm lint` (root) → 0 errors (3 pre-existing warnings)
2026-05-29 23:34:37 +00:00
imick-io
13e67561cf fix(website): tweak gallery contact heading and enterprise card color (#12530)
## Summary
- Add `<br>` after "Built something cool with ComfyUI?" in the gallery
contact heading (en + zh-CN) so the Submit link wraps to a new line.
- Switch the Enterprise product card background from
`bg-illustration-forest` to `bg-secondary-cool-gray`.

## Test plan
- [ ] Visit the gallery contact section and confirm the heading wraps
after "ComfyUI?" in both English and Chinese.
- [ ] Verify the Enterprise card on the product cards section renders
with the cool gray background.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-05-29 21:43:36 +00:00
imick-io
a0411d9beb refactor(website): centralize gallery items into src/data/gallery.ts (#12526)
## Summary

Establish a single source of truth for gallery items. Previously the
items array and the `GalleryItem` interface lived inline in
`GallerySection.vue`, with three sibling components importing the type
*out of a `.vue` file* — coupling pure data and types to a presentation
component.

- **One canonical list.** Items now live in `src/data/gallery.ts`
alongside the `GalleryItem` interface.
- **Per-item visibility without deletion.** New optional `visible?:
boolean` field on each item
- **Stable identity.** Each item now carries a required `id` kebab-slug
derived from its title.
- **lookup helper.** `getGalleryItemById(id)` for callers that want a
single item.

The sibling gallery components (`GalleryCard`, `GalleryDetailModal`,
`GalleryItemAttribution`) now import the `GalleryItem` type from
`../../data/gallery` instead of from the `.vue` file.

## Scope note

I also audited the rest of the website for other surfaces that should
consume the centralized data.

**I need this refactor for the new page `models` coming soon.**

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2026-05-29 21:14:07 +00:00
AustinMroz
e97c4b6ab9 Remove flake screenshot (#12529) 2026-05-29 21:08:46 +00:00
Comfy Org PR Bot
f830314429 1.46.5 (#12516)
Patch version increment to 1.46.5

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.46.5
2026-05-29 19:56:09 +00:00
jaeone94
fb58a76a53 fix: preserve validation errors on execution start (#12493)
## Summary

Preserve validation node errors and their overlay when a valid active
root starts execution, so partial workflow runs no longer hide
validation failures.

## Changes

- **What**: Split execution-start clearing from full error clearing;
`execution_start` now clears transient execution/prompt state without
clearing validation `lastNodeErrors`.
- **What**: Keep the ErrorOverlay open when validation errors are still
present, and show it for successful prompt responses that include
`node_errors`.
- **Dependencies**: None.

## Review Focus

Please check the error-clearing boundary between prompt
submission/workflow changes and WebSocket `execution_start`. Full
clearing still happens through `clearAllErrors`; execution start now
uses the narrower clearing path and only dismisses the overlay when
there are no validation node errors to show.

Linear: FE-851

## Red-Green Verification

- Red: `76bcf34c4 test: add failing validation error preservation e2e`
- Green: `9766172ea fix: preserve validation errors on execution start`
- Follow-up: `321c95aba fix: keep validation error overlay during
execution start`
- Coverage: `7b5fab577 test: cover prompt node error overlay`

## Test Plan

- `pnpm exec vitest run src/scripts/app.test.ts`
- `pnpm exec vitest run src/stores/executionStore.test.ts`
- `pnpm exec vitest run src/scripts/app.test.ts
src/stores/executionStore.test.ts --coverage`
- `pnpm format:check -- src/stores/executionErrorStore.ts
src/stores/executionStore.ts src/stores/executionStore.test.ts
src/scripts/app.ts src/scripts/app.test.ts
browser_tests/fixtures/helpers/ExecutionHelper.ts
browser_tests/tests/execution.spec.ts`
- `pnpm exec oxlint src/stores/executionErrorStore.ts
src/stores/executionStore.ts src/stores/executionStore.test.ts
src/scripts/app.ts src/scripts/app.test.ts
browser_tests/tests/execution.spec.ts --type-aware`
- `pnpm typecheck`
- `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://127.0.0.1:5175 pnpm
exec playwright test browser_tests/tests/execution.spec.ts:132`

## Screenshots (Before/After)
Before


https://github.com/user-attachments/assets/04a212b6-66f9-4c77-9056-58bdc642d96e

After


https://github.com/user-attachments/assets/db7813c7-bf8a-4e19-9b66-7f49fd01c305
2026-05-29 17:20:54 +00:00
AustinMroz
dda9822a93 Fix interrupted audio playback from assets panel (#12425)
Under some circumstances (Only firefox+FLAC outputs for me, but reliably
reproducible), clicking the play button on audio outputs in the assets
sidebar tab will fail to start playback. This appears to be caused by
unusual interactions between blob urls, `preload="metadata"`, and FLAC
not defining total content length in the header.

Instead of managing the lifecycle of a blob url, the real audio source
is left in place and caching can be done on the browser side.

I put some extensive time into trying to find a regression test that
works on chromium, but did not see results and decided it's better this
be merged without a test than never get fixed.
2026-05-29 16:59:30 +00:00
AustinMroz
b7990f7645 Fix ghost links on IO remove slot (#12473)
Context menu operations on subgraph IO slots only set the foreground
canvas as dirty, so links would visually persist until a different
operation caused a background draw.
2026-05-29 16:57:52 +00:00
jaeone94
79f2904937 Add special runtime error messaging (#12466)
## Summary

This PR extends the error catalog with targeted runtime error messaging
for common workflow execution failures.

The goal is to show clearer, user-facing copy in the error panel while
preserving the raw API `message` and `details` fields on each error
item. The resolver continues to add display-only fields (`displayTitle`,
`displayMessage`, `displayDetails`, `displayItemLabel`, `toastTitle`,
`toastMessage`) so the UI can render friendlier messages without
changing the underlying error contract.

## What This PR Targets

This PR adds catalog handling for common runtime failure categories,
including:

- General execution failure fallback for uncataloged node runtime errors
- Content blocked / safety check failures
- Credits, subscription, access, and sign-in related failures
- Rate limit and busy-server style failures
- Runtime timeout, stalled generation, and preparation failure cases
- Server stopped / unavailable cases
- Out-of-memory failures
- Image load failures
- Invalid CLIP input failures
- Invalid prompt and invalid workflow request failures
- Request/start/end failure cases
- Model access and model download failures
- Unexpected service failures

Unknown node execution errors resolve to the general runtime fallback:

- Title: `Execution failed`
- Message: `Node threw an error during execution.`
- Item label: node name
- Toast title: `{nodeName} failed`
- Toast message: `This node threw an error during execution. Check its
inputs or try a different configuration.`


## Implementation Notes

### Resolver Split

The previous resolver file was doing too much, so this PR splits
source-specific catalog logic into smaller modules:

- `catalogIds.ts` defines FE-resolved catalog IDs that normalize
multiple sources or do not map 1:1 to an API error type.
- `catalogI18n.ts` owns shared translation/fallback helpers.
- `validationErrorResolver.ts` keeps validation catalog logic.
- `promptErrorResolver.ts` keeps prompt-specific catalog logic and
handles non-node-scoped failures before falling back to prompt-specific
keys.
- `executionErrorResolver.ts` handles node-scoped runtime errors.
- `runtimeErrorMatcher.ts` owns conservative runtime error matching.
- `runtimeErrorCopy.ts` builds runtime display/toast fields from catalog
IDs and fallback copy.
- `missingErrorResolver.ts` keeps the existing missing-model/node/media
group display copy.

`errorMessageResolver.ts` is now a thin facade over these smaller
resolvers.

### Conservative Matching

Runtime errors can share generic exception labels, so this PR keeps
matching narrow: exact strings or stable prefixes only. The matcher is
ordered, and the first matching rule wins, so specific user-actionable
failures are checked before broader fallbacks.

The matching is intentionally not fuzzy. For example, arbitrary messages
that merely mention moderation terminology are not treated as
content-blocked unless they match one of the known client-visible
failure strings. This avoids false positives while covering the targeted
high-volume cases.

### Raw Detail Preservation

Some cataloged errors keep the original message as `displayDetails` when
it contains useful troubleshooting context. This is display-only; the
raw API `message` and `details` fields remain unchanged on the original
error item.

## Out Of Scope / Follow-Ups

This PR does not redesign the error overlay or right-side error panel
UI. It only provides the resolved fields those surfaces can consume.

Planned follow-up work remains:

- Regroup execution errors by message/catalog type where appropriate
- Error overlay copy/layout updates for single vs multiple errors
- Right panel visual design updates
- Rendering `displayItemLabel` in the revised panel design
- More specific UI actions for auth, retry, and similar flows
- Broader fallthrough observability if we decide it is needed

Non-English locale files are intentionally not updated here. The
repository uses `src/locales/en/main.json` as the source of truth and
the release i18n sync flow generates the other locale files.

## Validation

Ran successfully:

- `pnpm format`
- `pnpm lint:unstaged`
- `pnpm typecheck`
- `pnpm test:unit`
- `pnpm vitest run
src/platform/errorCatalog/errorMessageResolver.test.ts`
- `pnpm vitest run
src/platform/errorCatalog/errorMessageResolver.test.ts
src/components/rightSidePanel/errors/useErrorGroups.test.ts`
- `pnpm build`
- `pnpm knip`

Notes:

- `pnpm build` still prints existing asset/icon and dynamic import
warnings, but exits successfully.
- `pnpm knip` exits successfully and still reports the existing
`flac.ts` tag hint.

## Screenshots (Before / After) 

[Diff.zip](https://github.com/user-attachments/files/28300639/Diff.zip)
Uploaded as a ZIP file due to the image upload limit
2026-05-29 13:48:44 +00:00
Dante
c57944f315 fix: hide duplicate LiteGraph Resize/Collapse/Expand entries from Vue node menu (FE-867) (#12487)
## Summary

https://linear.app/comfyorg/issue/FE-867/bug-node-expand-menu-doesnt-work-nodes-immediately-collapse-after
Recreates #12175 on a fresh `main` base (original branch's CI failed
only because its `frontend-dist` artifact had expired — not a code
issue). Original work by @christian-byrne / Glary-Bot, cherry-picked
here so it can land while he's offline.

The Vue right-click "More Options" node menu shows duplicates for
collapse/expand functionality:

- **Vue source**: `Minimize Node` / `Expand Node` (works)
- **LiteGraph source**: `Resize`, `Collapse`, `Expand` (silently no-op
in this menu — the converter wrapper invokes
`LGraphCanvas.onMenuNodeCollapse` without the `node` arg it expects)

Suppress the LiteGraph duplicates in `convertContextMenuToOptions` by
matching the built-in **callback identity**
(`LGraphCanvas.onMenuResizeNode`, `LGraphCanvas.onMenuNodeCollapse`),
not the raw label. Matching by identity avoids accidentally hiding
extension-provided items that share those labels.

Also align `CORE_MENU_ITEMS` / `MENU_ORDER` on the Vue label `Expand
Node` so the toggled Minimize/Expand pair sorts correctly.

## Scope of suppression

Only the Vue node menu (via `convertContextMenuToOptions`) is affected.
The raw `LGraphCanvas.getNodeMenuOptions` output is untouched, so:

- The legacy right-click menu (`Comfy.UseNewMenu` disabled) still has
`Collapse` / `Resize`.
- `useLoad3d.ts`, which calls `new
LiteGraph.ContextMenu(app.canvas.getNodeMenuOptions(node), ...)`, is
unaffected.
- Extensions that monkey-patch `getNodeMenuOptions` continue to receive
the full option list.

## Tests

- `contextMenuConverter.test.ts`: covers both that built-in entries are
dropped by identity AND that extension-provided items with the same
labels survive.
- E2E `selectionToolboxMoreActions.spec.ts`: asserts the Vue "More
Options" menu shows `Minimize Node` but no `Resize`/`Collapse`/`Expand`.
- `pnpm typecheck` clean.

Supersedes #12175.

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
2026-05-29 22:28:08 +09:00
Terry Jia
26dfa5c547 refactor: drop redundant rotation field from Load3D camera_info (#12515)
## Summary
As discussed in slack, we would like to remove rotation field
2026-05-29 09:02:01 -04:00
Terry Jia
07d7b0c84f refactor(load3d): simplify model_info schema and align naming to model_3d (#12519)
## Summary
1. Drop multi-object identity fields (uuid, name, type) since
multi-object support is not yet in scope, these are renderer-side
identifiers with no link back to the ComfyUI asset.
2. Drop rotation and matrix as redundant encodings of the same
transform, applying the same Jacob-redundancy point used on camera_info
3. Rename ModelTransform -> Model3DTransform and ModelInfo ->
Model3DInfo to align with the existing Load3D / File3D / model_3d naming
and disambiguate from AI 'model' (per Alexis).
4. The output key also moves from model_info to model_3d_info to match.
2026-05-29 09:01:32 -04:00
jaeone94
d86483a6af refactor: consolidate middle-button pan handling (#12491)
## Summary

Refactors middle mouse button pan handling around the intent of #11409,
dropping the outdated implementation details from that PR and aligning
the core behavior with the current main branch.

## Changes

- **What**: Centralized phase-specific middle mouse button handling in
`src/base/pointerUtils.ts`, added a shared Vue widget forwarding helper,
and updated canvas, LiteGraph, Vue node, and mask editor call sites to
use the same semantics.
- **Breaking**: None expected. This keeps existing middle-click pan
behavior while making pointerdown, pointermove, pointerup, and auxclick
checks explicit for their event phases.
- **Dependencies**: None.

## Review Focus

This PR is intentionally narrower than #11409. That PR had the right
goal, but its implementation became outdated against main: mask editor
tests now have helper coverage on main, Vue node/widget code has
shifted, and a blanket replacement with `isMiddlePointerInput` would
lose the bitmask behavior needed during pointermove drags.

The core difference is that this PR preserves the useful part of #11409,
namely removing scattered ad-hoc MMB checks, while avoiding stale
changes that no longer fit the current codebase.

Key behavior changes:

- `isMiddlePointerInput` is the conservative pointerdown-style check:
changed middle button or strict middle-only `buttons === 4`.
- `isMiddleButtonHeld` handles pointermove-style held-button bitmasks so
chorded drags with the middle button still pan.
- `isMiddleButtonEvent` handles pointerup/auxclick-style changed-button
events.
- Call sites now choose the phase-specific helper directly instead of
routing through an event-type dispatcher.
- String and markdown widgets now share
`forwardMiddleButtonToCanvas(...)` instead of duplicating three pointer
listeners each.
- The widget helper intentionally keeps the existing
`app.canvas.processMouseDown/Move/Up` forwarding route and only
centralizes the duplicated listener logic.
- Mask editor pan handling, Vue node pointer forwarding, graph canvas
pan forwarding, LiteGraph middle-click checks, input indicators, and
transform settling now use the centralized helpers.

Coverage added or updated:

- Unit coverage for middle-button helper semantics, including chorded
pointermove drags and pointercancel held-bit behavior.
- Unit coverage for widget forwarding helper down/move/up routing.
- Regression coverage for canvas, mask editor, Vue node media preview,
and transform-settling pointer handling.
- Browser coverage for middle-click drag panning on a Vue node, a
multiline string widget, and the mask editor canvas.

Validation run:

- `pnpm format`
- `pnpm lint`
- `pnpm typecheck`
- `pnpm test:unit src/base/pointerUtils.test.ts
src/renderer/extensions/vueNodes/widgets/utils/forwardMiddleButtonToCanvas.test.ts
src/renderer/extensions/vueNodes/widgets/composables/useStringWidget.test.ts
src/renderer/extensions/vueNodes/widgets/composables/useMarkdownWidget.test.ts
src/renderer/core/canvas/useCanvasInteractions.test.ts
src/composables/maskeditor/useToolManager.test.ts
src/renderer/core/layout/transform/useTransformSettling.test.ts
src/composables/node/useNodeImage.test.ts
src/composables/node/useNodeAnimatedImage.test.ts
src/components/graph/SelectionToolbox.test.ts
src/lib/litegraph/src/LGraphCanvas.slotHitDetection.test.ts`
- `pnpm typecheck:browser`
- `pnpm test:browser:local
browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts
browser_tests/tests/vueNodes/widgets/text/multilineStringWidget.spec.ts
browser_tests/tests/maskEditor.spec.ts --project chromium --grep
"Middle-click drag"`
- Commit hook: staged file format/lint, `pnpm typecheck`

## Screenshots (if applicable)

Not applicable; this is interaction behavior covered by unit and browser
tests.
2026-05-29 16:12:12 +09:00
Alexander Brown
671e0cecdf chore: upgrade ESLint to v10.4.0 (#12517)
## Summary

Upgrade ESLint from v9 to v10.4.0, bump compatible plugins/configs, and
fix the 32 violations introduced by the new `eslint:recommended` rules.

## Changes

- **What**:
  - Catalog bumps in `pnpm-workspace.yaml`:
    - `eslint` 9.39.1 → 10.4.0
    - `@eslint/js` 9.39.1 → 10.0.1
    - `typescript-eslint` 8.49.0 → 8.60.0
    - `eslint-plugin-vue` 10.6.2 → 10.9.1
    - `eslint-plugin-import-x` 4.16.1 → 4.16.2
    - `eslint-plugin-unused-imports` 4.3.0 → 4.4.1
    - `@intlify/eslint-plugin-vue-i18n` 4.1.1 → 4.5.0
- Fixed 4 `preserve-caught-error` violations by passing `{ cause }` to
rethrown errors in `useMaskEditorLoader.ts` and `usePainter.ts`.
- Fixed 28 `no-useless-assignment` violations across litegraph
(`LGraph.ts`, `LGraphCanvas.ts`, `LiteGraphGlobal.ts`, `polyfills.ts`)
and seven other files (`colorUtil.ts`, `linkFixer.ts`, `usePaste.ts`,
`audioService.ts`, `versionUtil.ts`, `subscriptionCheckoutTracker.ts`,
`ShiftClick.test.ts`) by removing dead initializers or redundant writes.
- Removed two now-unnecessary `@ts-expect-error` directives on
`importX.flatConfigs.*` (newer plugin ships correct types).
- **Breaking**: None for runtime; consumers using ESLint locally must
use Node ≥20.19/22.13/24 (already required by repo `engines.node:
">=25"`).

## Review Focus

- Audit of the [v10 migration
guide](https://eslint.org/docs/latest/use/migrate-to-10.0.0) found no
other patterns in this codebase to address (no `eslint-env` comments, no
removed `context.*`/`SourceCode` APIs, no `RuleTester` usage, no
affected rule configs).
- `no-useless-assignment` fixes were chosen conservatively: prefer
keeping the variable with a typed declaration over deleting the
statement, except where the assignment was clearly dead after the last
read.
- Per-file diffs in litegraph (especially `LGraphCanvas.ts`) — worth a
glance to confirm intent.

## Verification

- `pnpm exec eslint --version` → v10.4.0
- `pnpm exec eslint src` → 0 errors
- `pnpm typecheck` → clean
- Unit tests for every touched file pass (319/319 in spot checks:
`colorUtil`, `linkFixer`, `audioService`, `usePaste`, `usePainter`,
`versionUtil`, `LGraph`, `litegraph`, all `LGraphCanvas.*`,
`ShiftClick`).

Co-authored-by: Amp <amp@ampcode.com>
2026-05-28 23:25:45 -07:00
guill
e02ee17d3d fix(website): memoize GitHub stars fetch to one call per build (#12495)
*PR Created by the Glary-Bot Agent*

---

## Problem

The GitHub star badge silently disappears from the comfy.org navigation.
Verified by curl-ing the live homepage:

```
props="{...&quot;github-stars&quot;:[0,&quot;&quot;]}"
```

`SiteNav.vue` only renders the badge when `githubStars` is truthy, so an
empty string hides it.

## Root cause

`apps/website/src/layouts/BaseLayout.astro` `await`s
`fetchGitHubStars('Comfy-Org', 'ComfyUI')` in its frontmatter. Astro
evaluates layout frontmatter **per rendered page** in SSG. With 379
pages (46 source `.astro` files × locales/dynamic routes), the
unauthenticated GitHub REST endpoint is called hundreds of times per
build, blasting past the 60 req/h anonymous rate limit. Once GitHub
returns 403 the existing `try/catch` returns `null`, `githubStars`
becomes `''`, and the badge vanishes — with no log line to indicate why.

## Fix

Cache the in-flight promise in a module-scope `Map` keyed by
`${owner}/${repo}` so every page in a single build shares one request.
Already-resolved counts stay cached, and the existing
`WEBSITE_GITHUB_STARS_OVERRIDE` env-var escape hatch still
short-circuits first.

While in the file:
- Pass an injectable `fetchImpl` so tests can stub without
`vi.spyOn(globalThis, 'fetch')`.
- Replace the implicit-`any` `data.stargazers_count ?? null` with a
narrow `readStargazerCount(data: unknown)` guard.
- In `BaseLayout.astro`, change `rawStars ? ...` to `rawStars !== null ?
...` so a hypothetical 0-star repo wouldn't be hidden (the old check
treated 0 as missing).

## Verification

- `pnpm --filter @comfyorg/website test:unit` → 89/89 pass (5 new test
cases: memoization, per-key isolation, non-2xx → null, throw → null,
override).
- `pnpm typecheck:website` → 0 errors.
- `pnpm format:check` → clean.
- `pnpm --filter @comfyorg/website build` → 379 pages built; with no
override set, output HTML contains `"github-stars":[0,"115K"]` (the live
count) on every page; with `WEBSITE_GITHUB_STARS_OVERRIDE=110000`, it
contains `"110K"` and `fetch` is never called.
- Playwright on the local preview confirms the badge renders at the
top-right of the nav with `aria-label="ComfyUI on GitHub — 110K stars"`.

## Scope

102 lines changed across 3 files (40 non-test). Deliberately leaves the
broader "snapshot fallback / build-data source" refactor to the existing
`codex/website-github-stars-once` branch — this PR just unblocks the
user-visible symptom.

## Screenshots

![Site navigation showing the 110K GitHub star badge restored next to
the DOWNLOAD LOCAL / LAUNCH CLOUD
buttons](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/d921afa7b2cb2a9088080967634aeb3e2e67ee09a8ac13f6e434c1c7589434c1/pr-images/1779918781152-ccaeab7f-9150-4d8b-ae4e-f20bbf49091b.png)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-05-29 03:04:41 +00:00
Alexis Rolland
dc1bc4c9f8 Update utils category to utilities (#12498)
## Summary

Update frontend only nodes categories to consolidate utility nodes into
a `utilities` category (instead of utils). Paired with changes done in
the core repo here: https://github.com/Comfy-Org/ComfyUI/pull/14145

## Changes

- **What**:
  - Rename frontend only nodes category from `utils` to `utilities`
- Move frontend only Primitive node from `utils` to
`utilities/primitive`

## Screenshots

<img width="563" height="352" alt="image"
src="https://github.com/user-attachments/assets/a768ec48-fb87-4fa3-934a-bd593bb35f3d"
/>

<img width="1181" height="773" alt="image"
src="https://github.com/user-attachments/assets/a3e09e25-3412-4d23-abe8-220948b87258"
/>
2026-05-29 02:03:44 +00:00
AustinMroz
767bd17077 Fix "open tutorial button" not working in templates (#12511)
The "open tutorial" button only existed in the DOM when the template
card as actively hovered. For reasons I can not comprehend (probably
overzealous pointer handlers somewhere), the act of clicking on the
button would fire a mouseleave event. This caused the button to
disappear for the exact moment it was clicked alike to a mischievous
dondurma vendor.

This is resolved by keeping the button always in DOM, but making it
invisible when the card isn't hovered.

The PR also removes a deeply nested `v-bind='$attrs'`. I'm assuming it
must be a mistake that attributes applied to the entire template
selector dialogue would be bound to every deeply nested tutorial button
on individual workflow cards.
2026-05-28 23:38:15 +00:00
imick-io
0d0231453a fix(website): stack role title above team and location on careers list (#12510)
## Summary
- Long role titles wrapped awkwardly next to the inline department label
on the careers list, especially on narrow viewports.
- Restructured the role link so the title sits on its own row with the
arrow icon on the right, and the department + location wrap together on
a metadata row beneath (16px gap between them).

## Test plan
- [ ] Open `/careers` on mobile width and confirm long titles (e.g.
"Senior Software Engineer, Frontend") no longer collide with the
department label.
- [ ] Confirm desktop layout still reads cleanly.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 19:49:53 +00:00
Luke Mino-Altherr
cc29a3d72d Add unreviewed merge detector for SOC 2 compliance (#12497)
## Summary

- Adds a GitHub Actions workflow that detects PRs merged to `main`
without an approving review
- Creates tracking issues in
[`Comfy-Org/unreviewed-merges`](https://github.com/Comfy-Org/unreviewed-merges)
(private) for SOC 2 audit purposes
- Supports inline justification via `Justification: <reason>` in PR body
or comments

## How it works

Triggers on `push` to `main`. Uses the GitHub API to find the associated
PR and check for approving reviews. If none found, creates a tracking
issue with the `unreviewed-merge` label. No code checkout required — API
calls only.

## Test plan

- [ ] Verify workflow YAML is valid
- [ ] Merge a test PR without approval and confirm issue creation in
`unreviewed-merges` repo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-05-28 19:22:46 +00:00
AustinMroz
62430d6311 Remove unneeded overrides, add new ones (#12501)
Adds additional version overrides to handle 16 of the remaining 18
dependabot alerts.

Removes overrides which are no longer needed.
2026-05-28 19:18:54 +00:00
jaeone94
dc8471c6d3 fix: show workflow refresh loading state (#12509)
## Summary

Adds visible loading feedback to the Workflows sidebar refresh button so
users can tell when a workflow sync request is in flight.

## Changes

- **What**: Exposes `isSyncLoading` from the workflow store and binds
the Workflows sidebar refresh button to disabled, `aria-busy`, and
spinning icon states while sync is pending.
- **What**: Adds stable E2E selectors for the workflows refresh button
and covers the loading state with unit and browser tests.
- **Dependencies**: None.

## Review Focus

Please verify the refresh control behavior while
`/api/userdata?dir=workflows` is pending, especially that the button is
disabled, exposes busy state, and returns to idle after sync completes.

## Validation

- `pnpm format`
- `pnpm test:unit
src/components/sidebar/tabs/BaseWorkflowsSidebarTab.test.ts`
- `pnpm test:browser:local browser_tests/tests/sidebar/workflows.spec.ts
-g "Shows loading state while refreshing workflows"`
- `pnpm lint`
- Commit hooks: `oxfmt`, `oxlint`, `eslint`, `typecheck`,
`typecheck:browser`

## Screenshots (if applicable)


https://github.com/user-attachments/assets/e8b893ae-a91d-45c9-81ea-adaf164de227
2026-05-28 17:31:42 +00:00
Comfy Org PR Bot
c070df72d4 1.46.4 (#12499)
Patch version increment to 1.46.4

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.46.4
2026-05-28 17:19:09 +00:00
jaeone94
c3dc7f45d4 Fix load3d unused exported camera types (#12505)
## Summary

This PR fixes a `knip` failure introduced by exported Load3D camera
helper interfaces that are only used inside
`src/extensions/core/load3d/interfaces.ts`.

## Problem

`knip --cache` reports these exported types as unused exports:

- `CameraQuaternion`
- `CameraRotation`
- `CameraFrustum`

They are implementation details for the local `CameraState` interface
and are not imported outside the module.

## Fix

Remove `export` from those three interfaces and keep them as file-local
types. This keeps the `CameraState` shape unchanged while avoiding an
unnecessary public type surface.

## Validation

Ran successfully:

- `pnpm exec oxfmt --write src/extensions/core/load3d/interfaces.ts`
- `pnpm typecheck`
- `pnpm knip --cache`

Notes:

- `pnpm knip --cache` still reports the existing tag hints for `flac.ts`
and `apps/website/src/utils/video.ts`, but no longer reports the Load3D
unused exported types.
2026-05-28 17:05:39 +09:00
Terry Jia
c2ef961834 feat: output model_info from Load3D node (#12494)
Expose per-object gizmo transforms (uuid, name, type, position,
rotation, quaternion, scale, up, visible, matrix) as a new `model_info`
output on the Load3D node.

`GizmoManager.getModelInfo()` reads the live target object and the
Load3D widget `serializeValue` writes it into the node payload. The
payload is a list to support multiple objects later; the viewer
currently renders a single main object, so it emits a one-element list.

Requires backend Comfy-Org/ComfyUI#14144 (adds the `LOAD3D_MODEL_INFO`
type and the output socket).
2026-05-27 23:54:46 -04:00
Terry Jia
78c16368d7 feat: expose camera intrinsics in Load3D camera_info (#12492)
## Summary
Add quaternion, rotation, fov, aspect, near, far and orthographic
frustum bounds (left/right/top/bottom) to the camera state captured by
CameraManager.getCameraState(), so the Load3D camera_info output carries
enough information for backend nodes to fully reconstruct the camera.

https://github.com/Comfy-Org/ComfyUI/pull/14143

Quaternion and Euler rotation are serialized as plain objects to avoid
THREE.js private underscore-prefixed fields leaking into the payload.

<img width="1807" height="1333" alt="image"
src="https://github.com/user-attachments/assets/8c0a6ab9-be5f-40d1-8015-7bd0d6c731a5"
/>
2026-05-27 23:09:46 -04:00
Christian Byrne
8206022982 fix(subgraph): validate URL hash and redirect to root when subgraph missing (#12169)
*PR Created by the Glary-Bot Agent*

---

## Summary

Fix FE-559: browser forward/back to a deleted subgraph used to leave the
canvas on stale state (and sometimes triggered unrelated tab navigation)
because the subgraph id in the URL hash was looked up with no validation
or fallback.

## Changes

- **What**:
- Added `src/schemas/subgraphIdSchema.ts` — `zSubgraphId =
z.string().uuid()` + `isValidSubgraphId(value)` type guard, matching how
subgraph ids are persisted in `workflowSchema.ts` and generated by
`createUuidv4()`.
- `subgraphNavigationStore.navigateToHash()` now (a) validates the hash
with `isValidSubgraphId` before any lookup, (b) redirects to the root
graph (`router.replace('#' + root.id)` + `canvas.setGraph(root)`) when
the locator is malformed, missing from `root.subgraphs`, or still
unresolved after a workflow-load attempt.
- Replaced the `console.error('subgraph poofed after load?')` dead-end
with the same redirect helper.
- Re-ordered the "already on this graph" short-circuit so a stale canvas
reference to a now-deleted subgraph doesn't suppress the redirect.

## Review Focus

- TDD: 6 new tests in `subgraphNavigationStore.navigateToHash.test.ts`
cover valid navigation, deleted-subgraph hash, malformed (non-UUID)
hash, no-op when target equals current, empty-hash root case, and
stale-canvas recovery. 15 new tests in `subgraphIdSchema.test.ts` lock
down the validator.
- `redirectToRoot()` toggles `blockHashUpdate` while calling
`router.replace`, so the new redirect doesn't re-trigger `updateHash()`
and clobber the canvas state.
- Generalized validation: the new schema lives in `src/schemas/` and can
be reused anywhere a subgraph id crosses an untrusted boundary (URL,
IPC, etc.).

## Manual Verification

Ran ComfyUI backend (`--cpu --port 8188`) + frontend dev server, then
drove Playwright through three scenarios:

| Input hash | Result | Console |
|---|---|---|
| `#11111111-2222-4333-8444-555555555555` (UUID-shaped, non-existent) |
URL replaced with `#<root-id>` | `[subgraphNavigation] subgraph not
found: 11111111-…; redirecting to root graph` |
| `#not-a-valid-uuid` (malformed) | URL replaced with `#<root-id>` |
`[subgraphNavigation] invalid subgraph id in hash: not-a-valid-uuid;
redirecting to root graph` |
| `#aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee` (UUID-shaped, non-existent) |
URL replaced with `#<root-id>` | (same redirect message) |

Screenshot below shows the redirected viewport.

Fixes FE-559

## Screenshots

![ComfyUI canvas after a hash referencing a deleted subgraph was
rewritten to the root graph
hash](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/fe7f8846b3efdc95461cd63995dd10808073dd86c561eff9d8816742eb892687/pr-images/1778562546959-43f5ead4-3e13-45de-a0ac-988c3424368b.png)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12169-fix-subgraph-validate-URL-hash-and-redirect-to-root-when-subgraph-missing-35e6d73d3650819f840af1475b9f44d4)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: jaeone94 <89377375+jaeone94@users.noreply.github.com>
2026-05-28 00:34:42 +00:00
Dante
5f2b2f2e87 fix: show cloud models in IC-LoRA Loader Model Only node (FE-838) (#12488)
## Summary

### before
<img width="1107" height="958" alt="before-buggy"
src="https://github.com/user-attachments/assets/1fcbd909-e008-4bd3-967f-87cdabb2baf6"
/>

### after
<img width="1107" height="958" alt="after-fixed"
src="https://github.com/user-attachments/assets/0d3c6f3f-36d6-4556-bd29-b3826ae20216"
/>


The **IC-LoRA Loader Model Only** node (`LTXICLoRALoaderModelOnly`, from
ComfyUI-LTXVideo) didn't show cloud models from `supported_models.json`,
while the native **Load LoRA** node did.

## Changes

- **What**: Add `['loras', 'LTXICLoRALoaderModelOnly', 'lora_name']` to
`MODEL_NODE_MAPPINGS`. Whether a combo widget swaps to the cloud asset
browser is gated by `assetService.shouldUseAssetBrowser` →
`isAssetBrowserEligible`, which only returns true for node types
registered in `MODEL_NODE_MAPPINGS` (via `modelToNodeStore`). The custom
IC-LoRA loader was absent from that list, so its `lora_name` widget fell
back to the plain combo that lists only filesystem models — never the
cloud-injected ones.
- **Breaking**: none

## Review Focus

Root cause verified live on `cloud.comfy.org` (asset API enabled, custom
node installed) via CDP:
- `LoraLoaderModelOnly` (native) → registry `lora_name`, eligible `true`
→ cloud models shown
- `LTXICLoRALoaderModelOnly` (bug) → not in registry, eligible `false` →
cloud models missing
- After registering the mapping live → eligible `true`, category `loras`
→ cloud models shown

Same class of bug as FE-492 (custom loaders missing from the mapping);
long-term, auto-detecting model-folder-backed combos would remove the
need to register each custom loader by hand.

Fixes FE-838

## Red-Green Verification

| Commit | CI | Purpose |
|--------|-----|---------|
| [`test:`
64d099f6c](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/26508637513)
| 🔴 Red (failure) | Proves the test catches the bug |
| [`fix:`
6b91a570d](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/26509067631)
| 🟢 Green (success) | Proves the fix resolves it |

## Test Plan

- [x] CI red on test-only commit
- [x] CI green on fix commit
- [x] Unit regression in `src/stores/modelToNodeStore.test.ts`
- [ ] E2E not applicable (custom node + cloud asset API not available in
CI)
2026-05-27 22:51:26 +00:00
Dante
a931acadd3 feat(dialog): migrate Settings dialog to Reka-UI (Phase 3) (#12182)
## Summary

Phase 3 of the dialog migration. Closes the parity gaps in the Reka
renderer (maximize affordance, headless layout mode, overlay-class
plumbing), then flips `useSettingsDialog` onto the Reka path. 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-575](https://linear.app/comfyorg/issue/FE-575/phase-3-migrate-settings-dialog-workspace-non-workspace-designer)
Predecessors: #11719 (Phase 0, merged), #12041 (Phase 1, merged), #12109
(Phase 2, **stacked PR base**)

> **Stacked on Phase 2**: this PR targets
`jaewon/dialog-reka-migration-phase-2`. Rebase onto `main` after #12109
lands.

## Changes

### Reka primitives — parity gaps closed

| File | Change |
| --- | --- |
| `src/components/ui/dialog/dialog.variants.ts` | New `maximized`
variant. `false` keeps the centered/sized layout; `true` switches to
`inset-2 top-2 left-2 size-auto max-h-none max-w-none sm:max-w-none` for
full-screen mode |
| `src/components/ui/dialog/DialogContent.vue` | Accepts `maximized`
prop, forwards to variants |
| `src/components/ui/dialog/DialogMaximize.vue` **(new)** | Icon-only
button toggling `lucide--maximize-2` / `lucide--minimize-2`; emits
`toggle`; uses `g.maximizeDialog` / `g.restoreDialog` i18n |
| `src/stores/dialogStore.ts` | Adds `overlayClass?:
HTMLAttributes['class']` to `CustomDialogComponentProps` (Reka-only;
PrimeVue path uses `pt.mask`) |
| `src/components/dialog/GlobalDialog.vue` | (a) Forwards `overlayClass`
to `DialogOverlay`; (b) passes `:maximized` to `DialogContent`; (c)
renders `DialogMaximize` in the header when `maximizable`, wired to a
local `toggleMaximize`; (d) when `headless: true`, skips the inner
`flex-1 overflow-auto px-4 py-2` wrapper so layout dialogs control their
own chrome |

### Settings flip

| File | Change |
| --- | --- |
| `src/platform/settings/composables/useSettingsDialog.ts` | Adds
`dialogComponentProps: { renderer: 'reka', size: 'full', contentClass:
'\<...\>', overlayClass }`. `contentClass` is `w-[90vw] max-w-[960px]
sm:max-w-[960px] h-[80vh] max-h-none rounded-2xl overflow-hidden` —
matches the previous `BaseModalLayout size="sm"` (960px × 80vh).
`overlayClass: 'p-8'` only when `isCloud && teamWorkspacesEnabled`
(preserves the workspace breathing-room contract) |
| `src/components/dialog/GlobalDialog.vue` | Drops the now-dead
`getDialogPt` workspace special case and the orphan
`.settings-dialog-workspace` CSS. Removes unused imports (`merge`,
`computed`, `useFeatureFlags`, `isCloud`, `DialogPassThroughOptions`) |

### Tests

- `src/platform/settings/composables/useSettingsDialog.test.ts`
**(new)** — 5 tests: renderer flip + sizing, workspace `overlayClass`
toggle, panel forwarding, `showAbout()`

## Quality gates

- [x] `pnpm typecheck` — clean
- [x] `pnpm lint` — 0 errors (3 pre-existing warnings unrelated to this
PR)
- [x] `pnpm format` — applied
- [x] `pnpm test:unit` (touched + adjacent areas):
  - `useSettingsDialog.test.ts` — 5/5
  - `dialogService.renderer.test.ts` — 5/5
  - `GlobalDialog.test.ts` — 9/9
  - All `src/components/dialog/` — 73/73
  - All `src/platform/settings/` — 75/75
  - `CustomizationDialog.test.ts` — 4/4
- [ ] CI Playwright matrix
- [ ] Manual verification on a backend

## Screenshots

End-to-end verification of the Reka flip on a local dev server:

| | |
| --- | --- |
| Settings dialog rendered via Reka (non-modal, focus stays in dialog
body) |
![Settings](https://raw.githubusercontent.com/Comfy-Org/ComfyUI_frontend/c454af1888d5d8d88092475b498cff7d2adac1a1/temp/summaries/settings-dialog-reka.png)
|
| Keybinding panel inside the Reka Settings dialog |
![Keybinding](https://raw.githubusercontent.com/Comfy-Org/ComfyUI_frontend/c454af1888d5d8d88092475b498cff7d2adac1a1/temp/summaries/keybinding-panel.png)
|
| Nested PrimeVue **Modify keybinding** dialog stacked on top —
`document.activeElement` is the `<input autofocus>`, proving the
focus-trap fix | ![Modify
keybinding](https://raw.githubusercontent.com/Comfy-Org/ComfyUI_frontend/c454af1888d5d8d88092475b498cff7d2adac1a1/temp/summaries/nested-modify-keybinding.png)
|


## Public API impact

None. `useSettingsDialog().show()` keeps the same signature. Reka
primitives gain optional `maximized` prop and `overlayClass` field —
additive, non-breaking.

## Out of scope (later phases)

- Manager dialog — Phase 4 (FE-576) — will consume the new `maximizable`
affordance
- `ConfirmDialog` callers — Phase 5 (FE-577)
- Removing PrimeVue `Dialog`/`<style>` overrides in `GlobalDialog.vue` —
Phase 6 (FE-578)

## Review focus

1. **Sizing strategy** — `contentClass` overrides Reka's default content
sizing (matching the existing `BaseModalLayout size="sm"` of 960 ×
80vh). Worth a designer pass per FE-575's acceptance criteria.
2. **`overlayClass: 'p-8'` workspace mode** — Reka's `DialogContent` is
positioned with viewport coordinates, so overlay padding does not
constrain it the way the old PrimeVue `mask.p-8` did. Cosmetic gutter
only. If designer flags missing breathing room, follow-up by shrinking
`contentClass` in workspace mode.
3. **`headless: true` semantics for Reka** — now skips the inner padding
wrapper. Existing migrated dialogs (Phases 1–2) all set a header, so no
visible impact. The Reka-headless path is new with this PR.
4. **Maximize wiring** — `toggleMaximize` mutates
`item.dialogComponentProps.maximized` directly (Pinia deep-reactive
proxy). The store's `onMaximize` / `onUnmaximize` callbacks are still
wired for the PrimeVue path; not double-fired.

## Test plan

- [x] Unit: 102/102 across touched + adjacent areas
- [ ] CI: full Vitest + Playwright matrix
- [ ] Manual on a backend:
- Open Settings via gear icon / keyboard shortcut → renders through
Reka, search works, panel navigation works, ESC closes
- Open Settings → trigger a reset confirmation (stacked confirm) →
confirm renders above Settings, ESC closes only the confirm
- Cloud workspace mode: Settings opens with workspace panel;
`overlayClass` applied
- Cloud non-workspace mode: Settings opens without workspace panel; no
`overlayClass`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12182-feat-dialog-migrate-Settings-dialog-to-Reka-UI-Phase-3-35e6d73d36508144bb4af88f83c5ab20)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: GitHub Action <action@github.com>
2026-05-27 22:08:30 +00:00
Luke Mino-Altherr
db6b7a315c chore: remediate 51 Dependabot vulnerabilities (#12345)
## Summary

Remediate 51 of 63 open Dependabot security alerts by bumping direct
dependencies, bumping parent dependencies, and adding targeted pnpm
overrides for transitive dependencies.

## Changes

- **What**: Two batches of dependency security fixes
- **Batch 1**: Bump catalog minimums for axios, dompurify, happy-dom,
vite, uuid. Fix axios header type narrowing in api.ts.
- **Batch 2**: Bump parent deps (@iconify/tailwind4, vue, knip) to pull
fixed transitive deps. Add tilde-pinned pnpm overrides for protobufjs,
flatted, defu where no parent fix is available. Unexport 6 unused types
flagged by knip upgrade.
- **Dependencies**: vue 3.5.13->3.5.34 required two type fixes
(LazyImage ClassValue, dialogStore deep instantiation)

## Review Focus

- pnpm overrides in package.json: protobufjs ~7.6.0, flatted ~3.4.2,
defu ~6.1.7
- Vue 3.5.34 type narrowing fixes in LazyImage.vue and dialogStore.ts

## Remaining (12 alerts, separate PRs)

- minimatch (4H) - 4 major version lines, needs per-consumer analysis
- picomatch (2M) - two major version lines
- brace-expansion (2M) - multiple major version lines
- astro (2: 1L+1M) - major version bump 5->6
- postcss 8.5.8 (1M) - dev-only, from @vue/compiler-sfc@3.5.28 via
storybook/devtools
- yaml 1.10.2 (1M) - from cosmiconfig->nx, no upstream fix in yaml v1
- lodash/lodash-es (4: 2H+2M) - dev-only, upstream still uses 4.17.x
- @babel/plugin-transform-modules-systemjs (1H) - dev-only via nx
- fast-uri (2H) - dev-only via ajv->nx/stylelint

Fixes #FE-762

---------

Co-authored-by: Austin Mroz <austin@comfy.org>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-05-27 14:07:34 -07:00
AustinMroz
b89940134f Better preview grid tiling (#12463)
The previous image preview tiling code was less than ideal. It had fixed
breakpoints based on the number of images. Outputs with many images
would become comically long.

This PR instead tiles images to fill the available space.
| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/e793ce65-8efc-44ca-b049-98f066a65b7d"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/ca891ce2-335f-42ce-aeec-a99579f669c8"
/>|
2026-05-27 20:26:44 +00:00
Christian Byrne
7ac1cbbd53 test: add E2E coverage for NE, SW, NW corner node resizing (#11408)
*PR Created by the Glary-Bot Agent*

---

## Summary

- Adds parameterized Playwright E2E tests covering all non-SE resize
corners (NE, SW, NW), closing the coverage gap in the `useNodeResize.ts`
switch statement
- Adds `resizeFromCorner()` and `getResizeHandle()` to `VueNodeFixture`
for reuse across tests
- Test cases are derived from the production `RESIZE_HANDLES` config so
they stay in sync with the actual handle definitions

## Test Groups (8 new tests)

| Group | Tests | Coverage |
|-------|-------|----------|
| Corner resize directions | NE, SW, NW — size increases and correct
edges shift | Lines 110-124, 184 |
| Opposite edge anchoring | NE, SW, NW — opposite corner stays fixed |
Position compensation end-to-end |
| Minimum size enforcement | SW width clamp (≥ MIN_NODE_WIDTH), NE
height clamp | Lines 162-176 |

## Design Decisions

**Locator-based handle discovery**: `resizeFromCorner()` finds handles
via `getByRole('button', { name: ariaLabel })` instead of coordinate
offsets. The resize handles have `opacity-0 pointer-events-auto`,
meaning they're always interactive even when visually transparent —
Playwright considers elements with `opacity: 0` as visible (it only
gates on `visibility: hidden` / `display: none` / zero-size bounding
box). If this approach turns out to be flaky in CI due to handle
discoverability, we can fall back to coordinate-based targeting
(computing offsets from the node's bounding box corners), which is what
the original SE-corner test uses.

**Parameterization from production config**: Tests import
`RESIZE_HANDLES` from `resizeHandleConfig.ts` and derive test case data
(drag direction, which axes move) from the corner name. An upfront guard
throws if any expected corner is missing from the config, preventing
silent coverage loss.

**Aria-label coupling**: `RESIZE_HANDLE_LABELS` in `VueNodeFixture`
hardcodes the English aria-label strings. This is intentional — tests
run in English locale, and aria-labels are the accessibility interface
contract. If a more stable hook is needed (e.g., `data-testid` per
handle), that can be added to `LGraphNode.vue` in a follow-up.

**Frame settlement**: `resizeFromCorner()` calls `nextFrame()` after the
mouse-up to ensure layout settles before assertions run, per
`FLAKE_PREVENTION_RULES.md`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11408-test-add-E2E-coverage-for-NE-SW-NW-corner-node-resizing-3476d73d3650818d8a5ce5d6d535b38c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: jaeone94 <89377375+jaeone94@users.noreply.github.com>
Co-authored-by: Connor Byrne <c.byrne@comfy.org>
2026-05-27 17:26:59 +00:00
jaeone94
7caba4408d fix: open model library for desktop model downloads (#12478)
## Summary

Open the Model Library sidebar when a missing-model download starts on
desktop so users can immediately see Electron download progress. This is
a temporary UX patch until the dedicated desktop missing-model download
progress bar lands.

## Changes

- **What**: Activates the `model-library` sidebar tab before starting
desktop missing-model downloads.
- **Dependencies**: None.

## Review Focus

Confirm the minimal behavior is scoped to the desktop download path and
covers both individual downloads and Download all through the shared
`downloadModel()` helper. This should remain small and easy to remove
once the progress bar flow is available.

## Screenshots (if applicable)



https://github.com/user-attachments/assets/d5b01db7-46b5-4a52-bb11-45e75a422474



## Test Plan

- `pnpm format`
- `pnpm lint`
- `pnpm typecheck`
- `pnpm test:unit`
- `pnpm knip`
2026-05-27 15:25:26 +00:00
jaeone94
fa4ffe3254 Fix node tooltip metadata i18n parsing (#12469)
## Summary

Fix node slot and widget tooltips so localized nodeDef tooltip text is
displayed without treating JSON examples from node metadata as vue-i18n
placeholders.

## Changes

- **What**: Added a tooltip-specific helper that keeps the existing
`te(key)` lookup behavior. When a translation key exists, it reads the
current locale message with `tm(key)` instead of compiling it with
`t(key)`.
- **What**: This does **not** disable tooltip localization. Existing
nodeDef tooltip translations are still used; the object_info metadata
tooltip remains the fallback when no translation key exists.
- **What**: Used the helper for node input, output, and widget tooltips
while keeping node descriptions on the existing translation path.
- **What**: Added regression coverage for the SAM3 Detect
`positive_coords` input tooltip, input-based widget tooltip, and output
slot tooltip paths.
- **Dependencies**: None.

## Review Focus

Please verify that slot and widget tooltips still prefer bundled nodeDef
translations while avoiding vue-i18n placeholder compilation for
metadata strings like `[{"x": int, "y": int}]`.

Linear: FE-813

## Validation

- `pnpm format`
- `pnpm lint`
- `pnpm typecheck`
- `pnpm exec vitest run
src/renderer/extensions/vueNodes/composables/useNodeTooltips.test.ts`
- `pnpm test:unit`
- E2E was not added because this is covered more directly by unit tests
for the i18n tooltip string path; a browser flow would add little signal
for this regression.
2026-05-27 11:51:53 +00:00
Comfy Org PR Bot
d05eadba97 1.46.3 (#12474)
Patch version increment to 1.46.3

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.46.3
2026-05-27 07:55:47 +00:00
Alexander Brown
0157b47024 feat(subgraph): Subgraph Link Only Promotion (ADR 0009) + migration/store hygiene (#12197)
## Summary

Introduces **Subgraph Link Only Promotion** (ADR 0009) — a new model for
surfacing inner subgraph widgets on the parent SubgraphNode by
*promoting through links* rather than by duplicating widget state on the
host. Ships with the hygiene/refactor pass on the migration, store, and
event layers that the new model depends on.

## What changes

### Subgraph Link Only Promotion (ADR 0009)

Promoted widgets are defined by the link from a SubgraphNode input to
the interior node, not by a duplicated widget instance on the host.
Consequences:

- A SubgraphNode renders inner widgets purely as a **projection** of the
interior widgets and links — no host-side state to drift.
- **Per-host independence**: multiple instances of the same SubgraphNode
render and edit their own values without cross-talk.
- **Reversible promote/demote**: structural link operation, so demote
preserves host slots and external connections (#12278).

### Supporting refactors

- **Migration** — Planner/classifier/repair/quarantine helpers collapsed
into a single `proxyWidgetMigration` entry point with black-box
round-trip coverage. Honors the source-node-id disambiguator on
`proxyWidgets`, so deduplicated names (e.g. `text`, `text_1`) resolve to
the right interior widget.
- **Widget identity** — `appMode` unified on `WidgetEntityId`; promoted
widget state is keyed by entityId across the store, DOM, and migration
paths.
- **SubgraphNode** — 3-key promoted-view cache replaced with a single
version counter + explicit `invalidatePromotedViews()` at mutation
sites; `id === -1` sentinel removed.
- **Events** — `LGraph.trigger()` now dispatches node trigger payloads
through `this.events`, replacing a leaky `onTrigger` monkey-patch.
`SubgraphEditor` reactivity is driven from subgraph events instead of
imperative refresh.
- **Stores** — `appModeStore` migration helpers collapsed into
`upgradeAndValidateInput`; `nodeOutputStore.*ByExecutionId` derived from
the locator index; `previewExposureStore` cleanup and cycle-detection
double-warn fix.
- **Misc** — `Outcome` types consolidated; mutable accumulators replaced
with `flatMap`; new ESLint rule forbids litegraph imports under
`src/world/`.

### Tests

- Browser tests for promoted widgets retagged `@vue-nodes` and rewritten
to assert against the rendered Vue node DOM (via `getNodeLocator` /
`getByRole('textbox')` / `enterSubgraph`) instead of `page.evaluate`
graph introspection.
- Per-host widget independence asserted via DOM.
- Migration coverage moved to black-box round-trip tests.
- Added coverage for duplicate-named promoted widget identity (ADR 0009)
and the per-parent demote branch in `WidgetActions`.

## Review focus

- ADR 0009 conformance of the link-only promotion model.
- Disambiguator resolution path in `proxyWidgetMigration`.
- Single-version-counter promoted-view cache and its
`invalidatePromotedViews()` call sites.
- `LGraph.trigger()` event dispatch and the `AppModeWidgetList.vue`
migration off `onTrigger` (FE-667 tracks the remaining
`useGraphNodeManager` conversion).

## Breaking changes

None for users. Internal subgraph promotion APIs changed — see ADR 0009.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12197-feat-subgraph-link-only-widget-promotion-migration-store-hygiene-35e6d73d365081fd882cf3a69bc09956)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: AustinMroz <austin@comfy.org>
2026-05-27 00:29:11 -07:00
Robin Huang
7599c6a1ca chore: remove EA logo from website social proof bar (#12477)
Removes the EA (Electronic Arts) logo from the client social proof bar
on the website. Drops it from the logos list in
`SocialProofBarSection.vue` and deletes the now-unused `EA.svg` asset.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 05:05:36 +00:00
AustinMroz
876ed502c9 Mock sign-in request in test (#12482)
Mock the sign in request in e2e tests to ensure tests success isn't tied
to external variables.
2026-05-27 05:05:26 +00:00
AustinMroz
ff8a19f233 Fix errant subscription popups with workspaces (#12472)
Under unreliable circumstances, users who have an active subscription
through a workspace, but no personal subscription would see a popup
suggesting they must purchase a workspace subscription.

This is suspected to be a consequence of the feature flag for team
workspaces having its initialization delayed such that subscription
state is incorrectly decided by the user's subscription state. As a
temporary remedy, the feature flag state is cached to local storage.

This caching is typed to the feature flag itself to ensure this PR gets
cleaned up when the feature flag is changed or removed in the future.
2026-05-27 01:58:25 +00:00
AustinMroz
c638ad194b Fix restoring values to dynamic combos (#12211)
`DynamicCombo`s redefined `widget.value` without going through the
store. This would result in desync of state. Most noticeably, swapping
to and from a workflow would break vue reactivity and cause the default
option to display visually

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12211-Fix-restoring-values-to-dynamic-combos-35f6d73d3650814ba12ccda42615239a)
by [Unito](https://www.unito.io)
2026-05-26 21:19:59 +00:00
Alexander Brown
b166532b24 chore: One more unnecessary -- (#12468)
` -- `
2026-05-26 21:05:48 +00:00
pythongosssss
5a7b1d6a90 fix: improve read only vue node widget contrast (#12455)
## Summary

When a text widget in Vue nodes has an upstream node connected to it,
the widget becomes read-only. However, the disabled state token color is
virtually the same as the default node background color, making it
difficult to visually distinguish that the widget is disabled.

This update changes readonly widgets to use a darker background

## Changes

- **What**: 
- add read only widget bg style

## Screenshots (if applicable)

Before
<img width="2556" height="1858" alt="Screen Shot 2026-05-25 at 06 05 43"
src="https://github.com/user-attachments/assets/897a5157-8d4a-4258-9bca-41ca0289bfb6"
/>

After
<img width="2556" height="1858" alt="Screen Shot 2026-05-25 at 06 04 59"
src="https://github.com/user-attachments/assets/a052d040-8a26-4bea-a998-9dde1734a71a"
/>

Light theme:
<img width="550" height="654" alt="image"
src="https://github.com/user-attachments/assets/52d898c7-0c71-4bd8-a5bd-426e8dc5e8b0"
/>
2026-05-26 09:05:36 +00:00
Comfy Org PR Bot
682bd14061 1.46.2 (#12458)
Patch version increment to 1.46.2

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.46.2
2026-05-26 04:23:34 +00:00
Alexander Brown
5b48bf67a9 docs: drop misleading pnpm test:unit -- examples (#12460)
## Summary

- Drop the `--` separator from all in-repo `pnpm test:unit -- <args>`
examples. The separator is unnecessary (pnpm forwards extra args
automatically) and on Windows PowerShell it mangles quoted args like `-t
"restores host values by input name"`, splitting them into multiple
tokens.
- Add a short note in `docs/guidance/vitest.md` explaining the
substring-match semantics of the positional filter and that `-t` matches
`it()`/`test()` names only (not `describe()` blocks).
- Fix `pnpm test:unit -- run <files>` in the backport-management skill:
because `test:unit` is already `vitest run`, the literal `run` token was
a positional path filter that silently narrowed the suite to files whose
paths contain "run".

## Test plan

- [ ] `pnpm test:unit useConflictAcknowledgment` matches
`useConflictAcknowledgment.test.ts`
- [ ] `pnpm test:unit SubgraphWidgetPromotion.test.ts -t "restores host
values"` filters to a single test
- [ ] `git grep "pnpm test:unit -- "` returns no in-repo matches
2026-05-26 03:08:24 +00:00
AustinMroz
bbaaa82125 Fix missing value control on 'Primitive Int' (#12431)
#8505 added support for specifying default values for
`control_after_generate`. Unbeknown to me, this exact same format of
assigning `control_after_generate` to a string in the schema already
served a function of renaming the control widget. As a result, control
widgets with a default value set would use a different internal name,
but due to other overlapping systems, would either have a label of
`control_after_generate` or `control_before_generate`.

The fix here, is incredibly simple and low scope. Instead of trying to
filter control widgets by name, the dedicated `IS_CONTROL_WIDGET` symbol
is used.

| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/5917e093-124a-4923-80ff-321fc0a94ef3"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/c6d95b5a-2764-4e71-a09f-dcae5ddcfdbb"
/>|
2026-05-26 01:49:44 +00:00