Commit Graph

7523 Commits

Author SHA1 Message Date
Christian Byrne
194baf7aee fix(ci): restore pnpm version input for core/* branch compatibility (#10952)
## Summary

Restores `version: 10` to the `pnpm/action-setup` step in
`release-version-bump.yaml`.

## Why

PR #10687 removed the explicit `version: 10` input, relying on
`packageManager` in `package.json` instead. However, this workflow
checks out a **target branch** (e.g. `core/1.42`, `core/1.41`) that
predates #10687 and lacks the `packageManager` field — causing the
action to fail with:

> Error: No pnpm version is specified.

Failed run:
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/24110739586

Adding `version: 10` back is safe — the action only errors when
`version` and `packageManager` **conflict**, and `pnpm@10.x` is
compatible.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10952-fix-ci-restore-pnpm-version-input-for-core-branch-compatibility-33c6d73d3650819282bbf6c194d0d2f1)
by [Unito](https://www.unito.io)
2026-04-07 17:45:42 -07:00
Comfy Org PR Bot
5770837e07 1.43.15 (#10951)
Patch version increment to 1.43.15

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10951-1-43-15-33c6d73d36508183b029ccd217a8403d)
by [Unito](https://www.unito.io)

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
v1.43.15
2026-04-07 17:24:14 -07:00
Johnpaul Chiwetelu
4078f8be8f test: add E2E test for subgraph duplicate independent widget values (#10949)
## Summary
- Add E2E test verifying duplicated subgraphs maintain independent
widget values (convert CLIP node to subgraph, duplicate, set different
text in each, assert no bleed)
- Extract `clickMenuItemExact` and `openForVueNode` into `ContextMenu`
fixture for reuse across Vue node tests
- Refactor `contextMenu.spec.ts` to delegate to the new fixture methods

## Test plan
- [x] `pnpm typecheck:browser` passes
- [x] `pnpm lint` passes
- [x] New test passes locally (`pnpm test:browser:local --
browser_tests/tests/subgraph/subgraphDuplicateIndependentValues.spec.ts`)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10949-test-add-E2E-test-for-subgraph-duplicate-independent-widget-values-33b6d73d3650818191c1f78ef8db4455)
by [Unito](https://www.unito.io)
2026-04-08 00:20:23 +01:00
Christian Byrne
ff0453416a ci: pass CODECOV_TOKEN and add codecov.yml for PR comments (#10774)
## What

Follow-up to #10575. Pass `CODECOV_TOKEN` secret to codecov upload
action and add `codecov.yml` config so Codecov posts coverage diff
comments on PRs.

## Changes

- `ci-tests-unit.yaml`: add `token: ${{ secrets.CODECOV_TOKEN }}`
- `codecov.yml`: configure PR comment layout (header, diff, flags,
files)

## Manual Step Required

An admin needs to add the `CODECOV_TOKEN` secret to the repo:

1. Go to [codecov.io](https://app.codecov.io) → sign in → find
`Comfy-Org/ComfyUI_frontend` → Settings → General → copy the Repository
Upload Token
2. Go to [repo
secrets](https://github.com/Comfy-Org/ComfyUI_frontend/settings/secrets/actions)
→ New repository secret → name: `CODECOV_TOKEN`, value: the token

## Testing

Config-only change. Once the secret is added, the next PR will get a
Codecov coverage comment.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10774-ci-pass-CODECOV_TOKEN-and-add-codecov-yml-for-PR-comments-3346d73d36508169bac5e61eecc94063)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-07 15:52:34 -07:00
Benjamin Lu
fd9c67ade8 test: type asset helper update payload (#10751)
What changed

- Typed the `PUT /assets/:id` mock body in `AssetHelper` as
`AssetUpdatePayload` instead of treating it as an untyped record.

Why

- Keeps the mock aligned with the frontend update contract used by the
asset service.
- Narrows the helper without changing behavior, so follow-up typing work
can build on a smaller base.

Validation

- `pnpm typecheck`
- `pnpm typecheck:browser`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10751-test-type-asset-helper-update-payload-3336d73d365081fd8f1bc0c7c49c5ddb)
by [Unito](https://www.unito.io)
2026-04-07 14:26:56 -07:00
Christian Byrne
83f4e7060a test(infra): AssetHelper with builder pattern + deterministic fixtures (#10545)
## What

Adds `AssetHelper` — a builder-pattern helper for mocking asset-related
API endpoints in Playwright E2E tests, plus deterministic fixture data.

## Why

12+ asset-related API endpoints need mocking for asset browser tests
(PNL-02), cloud dialog testing (DLG-08), and other asset-dependent E2E
scenarios. Random mock data from existing `createMockAssets()` is
unsuitable for deterministic E2E assertions.

## What's included

### `AssetHelper.ts` (307 LOC)
- Fluent builder API: `assetHelper.withModels(3).withImages(5).mock()`
- Stateful mock store (Map) for upload→verify→delete flows
- Endpoint coverage: GET/POST/PUT/DELETE `/assets`, download progress
- `mockError()` for error state testing
- `clearMocks()` cleanup matching QueueHelper/FeatureFlagHelper pattern

### `assetFixtures.ts` (304 LOC)
- 11 stable named constants (checkpoints, loras, VAE, embedding, inputs,
outputs)
- Factory functions: `generateModels()`, `generateInputFiles()`,
`generateOutputAssets()`
- Fixed IDs/dates/sizes — no randomness, safe for screenshot comparisons

### ComfyPage integration
- Available as `comfyPage.assets` in all tests

## Testing
- TypeScript compiles clean
- Follows existing QueueHelper/FeatureFlagHelper conventions

## Unblocks
- PNL-02: Asset browser tests (@Jaewon Yoon)
- DLG-08: Assets modal / cloud dialog testing

Part of: Test Coverage Q2 Overhaul

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10545-test-infra-AssetHelper-with-builder-pattern-deterministic-fixtures-32f6d73d365081d3985ef079ff3dbede)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-07 14:23:36 -07:00
AustinMroz
31c789c242 Support svg outputs in assets panel (#10470)
A trivial fix to support svg outputs in the assets panel

| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/2fca84f7-40f1-4966-b3dd-96facb8a4067"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/cad1a9fc-f511-43bc-8895-80d931baad1c"
/>|

Note: SVG do not display on cloud in node, or the assets panel because
they are being served with the incorrect content-type of `text/plain`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10470-Support-svg-outputs-in-assets-panel-32d6d73d3650815fbba1fe788297e715)
by [Unito](https://www.unito.io)
2026-04-07 13:53:37 -07:00
pythongosssss
1b05927ff4 test: App mode - setting widget value test (#10746)
## Summary

Adds a test for setting various types of widgets in app mode, then
validating the /prompt API is called with the expected values

## Changes

- **What**: 
- extract duplicated enableLinearMode
- add AppModeWidgetHelper for setting values

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10746-test-App-mode-setting-widget-value-test-3336d73d365081739598fb5280d0127e)
by [Unito](https://www.unito.io)
2026-04-07 13:53:01 -07:00
AustinMroz
97853aa8b0 Update autogrow to always show one optional beyond min (#10748)
It can be a little unclear that autogrow inputs will add more slots as
connections are made. To assist with this, the first optional input
beyond the minimum is always displayed. This ensures users always see a
slot with an optional indicator.

| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/1dd1241e-c6a4-46a6-a0b9-08b568decd10"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/79650f9a-7cc6-4484-83a3-2b25e2f1af33"
/>|

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10748-Update-autogrow-to-always-show-one-optional-beyond-min-3336d73d36508184bcc6c79381a62436)
by [Unito](https://www.unito.io)
2026-04-07 12:53:32 -07:00
AustinMroz
ac922fe6aa Remove flex styling from svg (#10941)
This cleans up a minor warning message from the dev server.:
` WARN  Removing unexpected style on "svg": flex`

The icon is displayed in the header of some templates and is not
visually impacted by the change.
<img width="268" height="365" alt="image"
src="https://github.com/user-attachments/assets/a0c03e85-5275-4671-b903-8458d7ba3517"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10941-Remove-flex-styling-from-svg-33b6d73d36508113a7dce77c269fe4cb)
by [Unito](https://www.unito.io)
2026-04-07 12:31:21 -07:00
pythongosssss
6f8e58bfa5 test: App Mode - widget reordering (#10685)
## Summary

Adds tests that simulate the user sorting the inputs and ensures they
are persisted and visible in app mode

## Changes

- **What**: 
- rework input/output selection helpers to accept widget/node names
- extract additional shared helpers

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10685-test-App-Mode-widget-reordering-3316d73d3650813b8348da5f29fd01f8)
by [Unito](https://www.unito.io)
2026-04-07 11:56:43 -07:00
Arthur R Longbottom
6cd3b59d5f fix: don't override loadGraphData viewport on cache miss (#10810)
## Summary

Fix regression from #10247 where template workflows (e.g. LTX2.3) loaded
with a broken viewport.

## Problem

`restoreViewport()` called `fitView()` on every cache miss via rAF. This
raced with `loadGraphData`'s own viewport restore (`extra.ds` for saved
workflows, or its own `fitView()` for templates at line 1266 of app.ts).
The second `fitView()` overwrote the correct viewport, causing templates
with subgraphs to display incorrectly.

## Fix

On cache miss, check if any nodes are already visible in the current
viewport before calling `fitView()`. If `loadGraphData` already
positioned things correctly, we don't override it. Only intervene when
the viewport is genuinely empty (first visit to a subgraph with no prior
cached state AND no loadGraphData restore).

## Review Focus

Single-file change in `subgraphNavigationStore.ts`. The visibility check
mirrors the same pattern used in `app.ts:1272-1281` where loadGraphData
itself checks for visible nodes.

## E2E Regression Test

The existing Playwright tests in
`browser_tests/tests/subgraphViewport.spec.ts` (added in #10247) already
cover viewport restoration after subgraph navigation. The specific
regression (template load viewport race) is not practically testable in
E2E because:
1. Template loading requires the backend's template API which returns
different templates per environment
2. The race condition depends on exact timing between `loadGraphData`'s
viewport restore and the rAF-deferred `restoreViewport` — Playwright
cannot reliably reproduce frame-level timing races
3. The fix is a guard condition (skip fitView if nodes visible) that
makes the behavior idempotent regardless of timing

## Alternative to #10790

This can replace the full revert in #10790 — it preserves the viewport
persistence feature while fixing the template regression.

Fixes regression from #10247
2026-04-07 10:01:54 -07:00
pythongosssss
0b83926c3e fix: Ensure zero uuid root graphs get assigned a valid id (#10825)
## Summary

Fixes an issue where handlers would be leaked causing Vue node rendering
to be corrupted (Vue nodes would not render) due to the
00000000-0000-0000-0000-000000000000 ID being used on the root graph.

## Changes

- **What**: 
- LGraph clear() skips store cleanup for the zero uuid, leaking handlers
that cause the node manager/handlers to be overwritten during operations
such as undo due to stale onNodeAdded hooks
- Ensures that graph configuration assigns a valid ID for root graphs

## Screenshots (if applicable)

Before fix, after doing ctrl+z after entering subgraph
<img width="1011" height="574" alt="image"
src="https://github.com/user-attachments/assets/1ff4692b-b961-4777-bf2d-9b981e311f91"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10825-fix-Ensure-zero-uuid-root-graphs-get-assigned-a-valid-id-3366d73d3650817d8603c71ffb5e5742)
by [Unito](https://www.unito.io)

---------

Co-authored-by: jaeone94 <89377375+jaeone94@users.noreply.github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-04-07 08:50:13 -07:00
Dante
858946b0f5 fix: use getAssetFilename in ModelInfoPanel filename field (#10836)
# Summary

* Model Info sidebar panel displays `asset.name` (registry name) instead
of the actual filename from `user_metadata.filename`
* Other UI components (asset cards, widgets, missing model scan)
correctly use `getAssetFilename()` which prefers
`user_metadata.filename` over `asset.name`
* One-line template fix: `{{ asset.name }}` → `{{
getAssetFilename(asset) }}`
* Fixes #10598 

# Bug

`ModelInfoPanel.vue:35` used raw `asset.name` for the "File Name" field.
When `user_metadata.filename` differs from `asset.name` (e.g. registry
name vs actual path like `checkpoints/v1-5-pruned.safetensors`), users
see inconsistent filenames across the UI.

# AS-IS / TO-BE

<img width="800" height="600" alt="before-after-10598"
src="https://github.com/user-attachments/assets/15beb6c8-4bad-4ed2-9c85-6f8c7c0b6d3e"
/>


| | File Name field shows |
| :--- | :--- |
| **AS-IS** (bug) | `sdxl-lightning-4step` — raw `asset.name` (registry
display name) |
| **TO-BE** (fix) | `checkpoints/sdxl_lightning_4step.safetensors` —
`getAssetFilename(asset)` (actual file path) |

# Red-Green Verification

| Commit | CI Status | Purpose |
| :--- | :--- | :--- |
| `test: add failing test for ModelInfoPanel showing wrong filename` | 🔴
Red | Proves the test catches the bug |
| `fix: use getAssetFilename in ModelInfoPanel filename field` | 🟢 Green
| Proves the fix resolves the bug |

# Test Plan

- [x] CI red on test-only commit
- [x] CI green on fix commit
- [x] Unit test: `prefers user_metadata.filename over asset.name for
filename field`
- [ ] Manual: open Asset Browser → click a model → verify File Name in
Model Info panel matches the actual file path (requires
`--enable-assets`)
2026-04-07 21:26:47 +09:00
Christian Byrne
c5b183086d test: add unit tests for commandStore, extensionStore, widgetStore (STORE-04) (#10647)
## Summary

Adds 43 unit tests covering three priority Pinia stores that previously
had zero test coverage.

### commandStore (18 tests)
- `registerCommand` / `registerCommands` — single and batch
registration, duplicate warning
- `getCommand` — retrieval and undefined for missing
- `execute` — successful execution, metadata passing, error handler
delegation, missing command error
- `isRegistered` — presence check
- `loadExtensionCommands` — extension command registration with source,
skip when no commands
- `ComfyCommandImpl` — label/icon/tooltip resolution (string vs
function), menubarLabel defaulting

### extensionStore (16 tests)
- `registerExtension` — name validation, duplicate detection, disabled
extension warning
- `isExtensionEnabled` / `loadDisabledExtensionNames` — enable/disable
lifecycle
- Always-disabled hardcoded extensions (pysssss.Locking,
pysssss.SnapToGrid, pysssss.FaviconStatus, KJNodes.browserstatus)
- `enabledExtensions` — computed filter
- `isExtensionReadOnly` — hardcoded list check
- `inactiveDisabledExtensionNames` — ghost extension tracking
- Core extension capture and `hasThirdPartyExtensions` detection

### widgetStore (9 tests)
- Core widget availability via `ComfyWidgets`
- Custom widget registration and core/custom precedence
- `inputIsWidget` for both v1 array and v2 object InputSpec formats

## Part of
Test Coverage Q2 Overhaul — Phase 5 (Unit & Component Tests)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10647-test-add-unit-tests-for-commandStore-extensionStore-widgetStore-STORE-04-3316d73d365081e0b4f6ce913130e489)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Alexander Brown <448862+DrJKL@users.noreply.github.com>
2026-04-07 08:42:16 +00:00
Comfy Org PR Bot
5872885cc5 1.43.14 (#10928)
Patch version increment to 1.43.14

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10928-1-43-14-33b6d73d365081158ba6ec0814165770)
by [Unito](https://www.unito.io)

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
v1.43.14
2026-04-06 23:18:53 -07:00
jaeone94
a8d23275d9 fix: prevent subgraph node position corruption during graph transitions (#10828)
## Summary

Fix subgraph internal node positions being permanently corrupted when
entering a subgraph after a draft workflow reload. The corruption
accumulated across page refreshes, causing nodes to progressively drift
apart or compress together.

## Changes

- **What**: In the `ResizeObserver` callback
(`useVueNodeResizeTracking.ts`), node positions are now read from the
Layout Store (source of truth, initialized from LiteGraph) instead of
reverse-converting DOM screen coordinates via `getBoundingClientRect()`
+ `clientPosToCanvasPos()`. The fallback to DOM-based conversion is
retained only for nodes not yet present in the Layout Store.

## Root Cause

`ResizeObserver` was using `getBoundingClientRect()` to get DOM element
positions, then converting them to canvas coordinates via
`clientPosToCanvasPos()`. This conversion depends on the current
`canvas.ds.scale` and `canvas.ds.offset`.

During graph transitions (e.g., entering a subgraph from a draft-loaded
workflow), the canvas viewport was stale — it still had the **parent
graph's zoom level** because `fitView()` hadn't run yet (it's scheduled
via `requestAnimationFrame`). The `ResizeObserver` callback fired before
`fitView`, converting DOM positions using the wrong scale/offset, and
writing the corrupted positions to the Layout Store. The `useLayoutSync`
writeback then permanently overwrote the LiteGraph node positions.

The corruption accumulated across sessions:
1. Load workflow → enter subgraph → `ResizeObserver` writes corrupted
positions
2. Draft auto-saves the corrupted positions to localStorage
3. Page refresh → draft loads with corrupted positions → enter subgraph
→ positions corrupted further
4. Each cycle amplifies the drift based on the parent graph's zoom level

This is the same class of bug that PR #9121 fixed for **slot** positions
— the DOM→canvas coordinate conversion is inherently fragile during
viewport transitions. This PR applies the same principle to **node**
positions.

## Why This Only Affects `main` (No Backport Needed)

This bug requires two features that only exist on `main`, not on
`core/1.41` or `core/1.42`:

1. **PR #10247** changed `subgraphNavigationStore`'s watcher to `flush:
'sync'` and added `requestAnimationFrame(fitView)` on viewport cache
miss. This creates the timing window where `ResizeObserver` fires before
`fitView` corrects the canvas scale.
2. **PR #6811** added hash-based subgraph auto-entry on page load, which
triggers graph transitions during the draft reload flow.

On 1.41/1.42, `restoreViewport` does nothing on cache miss (no `fitView`
scheduling), and the watcher uses default async flush — so the
`ResizeObserver` never runs with a stale viewport.

## Review Focus

- The core change is small: use `nodeLayout.position` (already in the
Layout Store from `initializeFromLiteGraph`) instead of computing
position from `getBoundingClientRect()`. This eliminates the dependency
on canvas scale/offset being up-to-date during `ResizeObserver`
callbacks.
- The fallback path (`getBoundingClientRect` → `clientPosToCanvasPos`)
is retained for nodes not yet in the Layout Store (e.g., first render of
a newly created node). At that point the canvas transform is stable, so
the conversion is safe.
- Unit tests updated to reflect that position is no longer overwritten
from DOM when Layout Store already has the position.
- E2E test added: load subgraph workflow → enter subgraph → reload
(draft) → verify positions preserved.

## E2E Test Fixes

- `subgraphDraftPositions.spec.ts`: replaced `comfyPage.setup({
clearStorage: false })` with `page.reload()` + explicit draft
persistence polling. The `setup()` method performs a full navigation via
`goto()` which bypassed the draft auto-load flow.
- `SubgraphHelper.packAllInteriorNodes`: replaced `canvas.click()` with
`dispatchEvent('pointerdown'/'pointerup')`. The position fix places
subgraph nodes at their correct locations, which now overlap with DOM
widget textareas that intercept pointer events.

## Test Plan

- [x] Unit tests pass (`useVueNodeResizeTracking.test.ts`)
- [x] E2E test: `subgraphDraftPositions.spec.ts` — draft reload
preserves subgraph node positions
- [x] Manual: load workflow with subgraph, zoom in/out on root graph,
enter subgraph, verify no position drift
- [x] Manual: repeat with page refresh (draft reload) — positions should
be stable across reloads
- [x] Manual: drag nodes inside subgraph — positions should update
correctly
- [x] Manual: create new node inside subgraph — position should be set
correctly (fallback path)

## Screenshots
Before
<img width="1331" height="879" alt="스크린샷 2026-04-03 오전 3 56 48"
src="https://github.com/user-attachments/assets/377d1b2e-6d47-4884-8181-920e22fa6541"
/>

After
<img width="1282" height="715" alt="스크린샷 2026-04-03 오전 3 58 24"
src="https://github.com/user-attachments/assets/34528f6c-0225-4538-9383-227c849bccad"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10828-fix-prevent-subgraph-node-position-corruption-during-graph-transitions-3366d73d365081418502dbb78da54013)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-04-07 14:34:56 +09:00
Christian Byrne
84f401bbe9 docs: update backport-management skill with v1.43 session learnings (#10927)
## Summary

Update backport-management skill with learnings from the 2026-04-06
backport session (29 PRs across core/1.42, cloud/1.42, core/1.41,
cloud/1.41).

## Changes

- **What**: Captures operational learnings into the backport skill
reference docs
- Branch scope clarification: app mode and Firebase auth go to both
core+cloud branches, not cloud-only
- Accept-theirs regex warning: produces broken hybrids on component
rewrites (PrimeVue to Reka UI migrations); use `git show SHA:path`
instead
- Missing dependency pattern: cherry-picks can silently bring in code
referencing composables/components not on the target branch
- Fix PRs are expected: plan for 1 fix PR per branch after wave
verification
- `--no-verify` required for worktree commits/pushes (husky hooks fail
in /tmp/ worktrees)
- Automation success varies wildly by branch: core/1.42 got 69%
auto-PRs, cloud/1.42 got 4%
- Test-then-resolve batch pattern for efficient handling of
low-automation branches
- Slack-compatible final deliverables: plain text format replacing
mermaid diagrams (no emojis, tables, headers, or bold)
- Updated conflict triage table with component rewrite, import-only, and
locale/JSON conflict types
- Interactive approval flow replacing static decisions.md for human
review

## Review Focus

Documentation-only change to internal skill files. No production code
affected.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10927-docs-update-backport-management-skill-with-v1-43-session-learnings-33a6d73d3650811aa7cffed4b2d730b0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-06 18:23:21 -07:00
Christian Byrne
0b689a3c3c chore: remove pull-model registry type gen workflow (#9957)
## Summary

Removes `api-update-registry-api-types.yaml` — the workflow that cloned
the private `comfy-api` repo using a PAT to generate registry API types.
This is a security risk: a PAT with private repo access stored on a
public repo.

Type generation now happens in `comfy-api` and pushes PRs to this repo
instead.

### Action needed after merge

- Remove the `COMFY_API_PAT` secret from this repo's settings (Settings
→ Secrets → Actions)

### Depends on

- https://github.com/Comfy-Org/comfy-api/pull/937 (must merge first)
- Refs: COM-16785

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9957-chore-remove-pull-model-registry-type-gen-workflow-3246d73d365081cd9dbad03597817f05)
by [Unito](https://www.unito.io)
2026-04-06 18:21:38 -07:00
Christian Byrne
32ff1a5bdb feat(website): add SEO, sitemap, redirects, CI workflow, and Vercel config (#10156)
## Summary

<!-- One sentence describing what changed and why. -->

## Changes

- **What**: <!-- Core functionality added/modified -->
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->

## 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)

<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10156-feat-website-add-SEO-sitemap-redirects-CI-workflow-and-Vercel-config-3266d73d3650816ab9eaebd11072d481)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-06 18:20:28 -07:00
Christian Byrne
0a5f281291 refactor: reconcile workspaceAuthStore with authStore (#10485)
## Summary

Add dedicated auth token priority tests and reconcile workspace token
resolution between `authStore` and `workspaceAuthStore`.

## Changes

- **What**: Created `src/stores/__tests__/authTokenPriority.test.ts`
with 6 scenarios covering the full priority chain: workspace token →
Firebase token → API key → null. Added `getWorkspaceToken()` method to
`workspaceAuthStore`. Replaced raw `sessionStorage` reads in `authStore`
with proper store delegation.
- **Files**: 3 files changed (+ 1 new test file)

## Review Focus

- Token priority chain in `authTokenPriority.test.ts` — validates
workspace > Firebase > API key ordering
- The `getAuthToken()` and `getAuthHeader()` methods now delegate to
`workspaceAuthStore` instead of reading sessionStorage directly

## Stack

PR 3/5: #10483#10484 → **→ This PR** → #10486#10487

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-04-06 18:18:20 -07:00
Christian Byrne
61af482cd4 docs: document when to use page.evaluate vs user actions in browser tests (#10658)
## Summary

Document acceptable vs avoidable `page.evaluate` patterns in Playwright
E2E tests, with migration candidates for existing offenders.

## Changes

- **What**: Add "When to Use `page.evaluate`" section to
`docs/guidance/playwright.md` with acceptable/avoid/preferred guidance
and 8 migration candidates identified from audit

## Review Focus

Whether the migration candidate list covers the right tests and whether
the acceptable/avoid boundary is drawn correctly.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10658-docs-document-when-to-use-page-evaluate-vs-user-actions-in-browser-tests-3316d73d36508186be90f263f36daf75)
by [Unito](https://www.unito.io)
2026-04-06 16:56:43 -07:00
Christian Byrne
3a73ce72bb refactor: replace inline getByRole('dialog') with page objects in E2E tests (#10724)
## Summary

Replace remaining inline `getByRole('dialog')` calls in E2E tests with
page object accessors, following the pattern from PR #10586.

**Note:** The original scope covered 9 calls across 5 files. Three of
those refactors (ConfirmDialog, MediaLightbox, TemplatesDialog) were
already merged into main via other PRs, so this PR now covers only the
remaining 3 files.

## Changes

- **ComfyNodeSearchBox.ts**: Extract a `root` locator on
`ComfyNodeSearchFilterSelectionPanel` so `header` getter uses
`this.root` instead of an inline `page.getByRole('dialog')`
- **AppModeHelper.ts**: Add `imagePickerPopover` getter that locates the
PrimeVue Popover (`role="dialog"`) filtered by a button named "All"
- **appModeDropdownClipping.spec.ts**: Replace 4-line inline
`getByRole('dialog')` chain with `comfyPage.appMode.imagePickerPopover`

## Review Focus

Pure test infrastructure refactor — no production code changes. Each
page object follows existing conventions.

Fixes #10723
2026-04-06 16:42:09 -07:00
Benjamin Lu
f3cbbb8654 [codex] merge hashed auth user data into GTM auth events (#10778)
Merge email dataLayer push event into existing signup/login push. Hash
with SHA 256 and deduplicate with a util.

AI summary below

---

## Summary
- attach hashed auth email to the `login` / `sign_up` GTM event payload
instead of pushing a separate standalone `user_data` message
- add a shared telemetry email hashing utility and reuse it for both GTM
(`SHA-256`) and Impact (`SHA-1`)
- extend tests to cover merged auth payloads, both hash algorithms, and
fallback behavior when Web Crypto is unavailable

## Why
The current GTM auth flow was splitting `user_data` and the auth event
into separate dataLayer pushes, which makes GTM preview harder to
interpret and relies on carried-over dataLayer state instead of the
event payload that actually fired.

There was also an implementation gap: the previous GTM change was
intended to hash email client-side, but the provider was still sending
normalized plaintext email into `dataLayer`.

## Impact
Auth events now carry hashed email on the same event payload that GTM
tags consume, so the preview timeline is cleaner and downstream matching
can rely on the auth event directly.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10778-codex-merge-hashed-auth-user-data-into-GTM-auth-events-3346d73d36508197b57deada345dbeaa)
by [Unito](https://www.unito.io)
2026-04-06 16:39:49 -07:00
Christian Byrne
e2b07f3e9a feat(website): add Wave 4 secondary pages (#10145)
## Summary

<!-- One sentence describing what changed and why. -->

## Changes

- **What**: <!-- Core functionality added/modified -->
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->

## 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)

<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10145-feat-website-add-Wave-4-secondary-pages-3266d73d3650818c9101c7d2086c21ba)
by [Unito](https://www.unito.io)
2026-04-06 14:42:24 -07:00
Comfy Org PR Bot
2856a30b50 1.43.13 (#10857)
Patch version increment to 1.43.13

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10857-1-43-13-33a6d73d3650812598bec1f89696cfa5)
by [Unito](https://www.unito.io)

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
v1.43.13
2026-04-06 13:16:25 -07:00
Dante
64f75f0727 test(assets): add E2E tests for delete confirmation flow (#10785)
## Summary

Add E2E Playwright tests for the asset delete confirmation dialog flow.

## Changes

- **What**: New `Assets sidebar - delete confirmation` describe block in
`assets.spec.ts` covering right-click delete showing confirmation
dialog, confirming delete removes asset with success toast, and
cancelling delete preserves asset. Added `mockDeleteHistory()` to
`AssetsHelper` to intercept POST `/api/history` delete payloads and
update mock state.

## Review Focus

Tests use existing `ConfirmDialog` page object and `AssetsHelper` mock
infrastructure. The `mockDeleteHistory` handler removes jobs from the
in-memory mock array so subsequent `/api/jobs` fetches reflect the
deletion.

Fixes #10781

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10785-test-assets-add-E2E-tests-for-delete-confirmation-flow-3356d73d365081fb90c8e2a69de3a666)
by [Unito](https://www.unito.io)
2026-04-06 10:17:23 -07:00
Dante
0d535631a5 refactor(test): extract dialog page objects from inline getByRole usage (#10822)
## Summary

Extract inline `getByRole('dialog')` calls across E2E tests into
reusable page objects.

## Changes

- **What**: Extract `ConfirmDialog` class from `ComfyPage.ts` into
`browser_tests/fixtures/components/ConfirmDialog.ts` with new `save`
button locator. Add `MediaLightbox` and `TemplatesDialog` page objects.
Refactor 4 test files to use these page objects instead of raw dialog
locators.
- **Skipped**: `appModeDropdownClipping.spec.ts` uses
`getByRole('dialog')` for a PrimeVue Popover (not a true dialog), left
as-is.

## Review Focus

The `ConfirmDialog.click()` method now supports a `save` action used by
`workflowPersistence.spec.ts`, which also waits for the dialog mask to
disappear and workflow service to settle.

Fixes #10723

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10822-refactor-test-extract-dialog-page-objects-from-inline-getByRole-usage-3366d73d365081b3bc0ee7ef0ddce658)
by [Unito](https://www.unito.io)
2026-04-06 09:49:32 -07:00
Comfy Org PR Bot
6c1bf7a3cf 1.43.12 (#10782)
Patch version increment to 1.43.12

**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.43.12
2026-04-04 23:48:20 -07:00
Benjamin Lu
b61e15293c test: address review comments on new browser tests (#10852) 2026-04-04 19:26:55 -07:00
Dante
899660b135 test: add queue overlay and workflow search E2E tests (#10802)
## Summary
- Add queue overlay E2E tests: toggle, filter tabs, completed filter,
close (5 tests)
- Add workflow sidebar search E2E tests: search input, filter by name,
clear, no matches (4 tests)
- Fix AssetsHelper mock timestamps from seconds to milliseconds
(matching backend's `int(time.time() * 1000)`)
- Type AssetsHelper response pagination with `JobsListResponse` from
`@comfyorg/ingest-types`

## Test plan
- [ ] CI passes all Playwright shards
- [ ] `pnpm typecheck:browser` passes
- [ ] `pnpm lint` passes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10802-test-add-queue-overlay-and-workflow-search-E2E-tests-3356d73d365081018df8c7061c0854ee)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
2026-04-04 13:15:17 -07:00
Benjamin Lu
aeafff1ead fix: virtualize cloud job queue history list (#10592)
## Summary

Virtualize the shared job queue history list so opening the jobs panel
does not eagerly mount the full history on cloud.

## Changes

- **What**: Virtualize the shared queue history list used by the overlay
and sidebar, flatten date headers plus job rows into a single virtual
stream, and preserve hover/menu behavior with updated queue list tests.
- **Why `@tanstack/vue-virtual` instead of Reka virtualizers**: the
installed `reka-ui@2.5.0` does not expose a generic list virtualizer. It
only exposes `ListboxVirtualizer`, `ComboboxVirtualizer`, and
`TreeVirtualizer`, and those components inject `ListboxRoot`/`TreeRoot`
context and carry listbox or tree selection/keyboard semantics. The job
history UI is a flat grouped action list, not a selectable listbox or
navigable tree, so this uses the same TanStack virtualizer layer
directly without forcing the wrong semantics onto the component.

## Review Focus

Please verify the virtual row sizing and inter-group spacing behavior
across date headers and the last row in each group.

> [!TIP]
> Diff reads much cleaner through vscode's unified view with show
leading/trailing whitespace differences enabled

Linear: COM-304

https://tanstack.com/virtual/latest/docs/api/virtualizer

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10592-fix-virtualize-cloud-job-queue-history-list-3306d73d3650819d956bf4b2d8b59a40)
by [Unito](https://www.unito.io)
2026-04-04 12:33:47 -07:00
Johnpaul Chiwetelu
f4fb7a458e test: add browser tests for selection toolbox button actions (#10764) 2026-04-04 14:03:50 +01:00
Yourz
71a3bd92b4 fix: add delete/bookmark actions for blueprints in V2 node library sidebar (#10827)
## Summary

Add missing delete and bookmark actions for user blueprints in the V2
node library sidebar, fixing parity with the V1 sidebar.

## Changes

- **What**: 
- Add delete button (inline + context menu) for user blueprints in
`TreeExplorerV2Node` and `TreeExplorerV2`
- Extract `isUserBlueprint()` helper in `subgraphStore` for DRY usage
across V1/V2 sidebars


![Kapture 2026-04-03 at 00 12
09](https://github.com/user-attachments/assets/3f1f3f41-ed2b-4250-953f-511d39e54e45)

## Review Focus

- `isUserBlueprint` consolidates logic previously duplicated between
`NodeTreeLeaf` and the new V2 components
- Context menu guard `contextMenuNode?.data` prevents showing empty
menus
- Folder `@contextmenu` handler clears stale `contextMenuNode` to
prevent wrong actions

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10827-fix-add-delete-bookmark-actions-for-blueprints-in-V2-node-library-sidebar-3366d73d36508111afd2c2c7d8ff0220)
by [Unito](https://www.unito.io)
2026-04-04 20:14:32 +08:00
Dante
17d2870ef4 test(modelLibrary): add E2E tests for model library sidebar tab (#10789)
## Summary
- Add `ModelLibraryHelper` mock helper for `/experiment/models` and
`/view_metadata` endpoints
- Add `ModelLibrarySidebarTab` page object fixture with search, folder,
and leaf locators
- Add 11 E2E test scenarios covering tab open/close, folder display,
folder expansion, search with debounce, refresh, load all folders, and
empty state

## Test plan
- [ ] CI passes all Playwright shards
- [ ] `pnpm typecheck:browser` passes
- [ ] `pnpm lint` passes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10789-test-modelLibrary-add-E2E-tests-for-model-library-sidebar-tab-3356d73d365081b49a7ed752512164da)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 08:04:46 +09:00
Dante
7a68943839 test(assets): strengthen pagination E2E assertions (#10773)
## Summary

The existing pagination smoke test only asserts `count >= 1`, which
passes even if the sidebar eagerly loads all items or ignores page
boundaries entirely.

### What changed

**Before:**
- Created 30 mock jobs (less than BATCH_SIZE of 200) — all loaded in one
request, `has_more: false`
- Asserted `count >= 1` — redundant with the grid-render smoke test

**After — two targeted assertions:**

1. **Initial batch < total**: Mock 250 jobs (> BATCH_SIZE 200). First
`/api/jobs?limit=200&offset=0` returns 200 items with `has_more: true`.
Assert `initialCount < 250`.

2. **Scroll triggers second fetch**: Scroll `VirtualGrid` container to
bottom → `approach-end` event → `handleApproachEnd()` →
`assetsStore.loadMoreHistory()` → `/api/jobs?limit=200&offset=200`
fetches remaining 50. Assert `finalCount > initialCount` via
`expect.poll()`.

### Types
Mock data uses `RawJobListItem` from
`src/platform/remote/comfyui/jobs/jobTypes.ts` (Zod-inferred). This is
the correct source-of-truth per `docs/guidance/playwright.md` —
`/api/jobs` is a Python backend endpoint not covered by
`@comfyorg/ingest-types`.

## Test plan
- [ ] CI E2E tests pass
- [ ] `initial batch is smaller than total job count` validates
pagination boundary
- [ ] `scrolling to the end loads additional items` triggers actual
second API call

Fixes #10649

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 08:00:50 +09:00
Dante
8912f4159a test: add E2E tests for workflow tab operations (#10796)
## Summary

Add Playwright E2E tests for workflow tab interactions in the topbar.

## Changes

- **What**: New test file
`browser_tests/tests/topbar/workflowTabs.spec.ts` with 5 tests covering
default tab visibility, tab creation, switching, closing, and context
menu. Added `newWorkflowButton`, `getTab()`, and `getActiveTab()`
locators to `Topbar.ts` fixture.

## Review Focus

Tests are focused on tab UI interactions only (sidebar workflow
operations are already covered in `workflows.spec.ts`). Context menu
assertion uses Reka UI's `data-reka-context-menu-content` attribute.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10796-test-add-E2E-tests-for-workflow-tab-operations-3356d73d36508170a657ef816e23b71c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 07:07:31 +09:00
Dante
794b986954 test: add E2E tests for Node Library V2 sidebar (#10798)
## Summary

- Adds Playwright E2E tests for the Node Library V2 sidebar tab
(`Comfy.NodeLibrary.NewDesign: true`)
- Adds `NodeLibrarySidebarTabV2` fixture class with V2-specific locators
(search input, tab buttons, node cards)
- Exposes `menu.nodeLibraryTabV2` on `ComfyPage` for test access
- Tests cover: tab visibility, default tab selection, tab switching,
folder expansion, search filtering, and sort button presence

## Test plan

- [ ] Run `pnpm test:browser:local -- --grep "Node library sidebar V2"`
against a running ComfyUI server with the V2 node library
- [ ] Verify tests pass in CI

Fixes #9079

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10798-test-add-E2E-tests-for-Node-Library-V2-sidebar-3356d73d36508185a11feaf95e32225b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 07:04:56 +09:00
Terry Jia
a7b3515692 chore: add @jtydhr88 as code owner for GLSL renderer (#10742)
## Summary
add myself as glsl owner

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10742-chore-add-jtydhr88-as-code-owner-for-GLSL-renderer-3336d73d3650816f84deebf3161aee7a)
by [Unito](https://www.unito.io)
2026-04-02 13:09:58 -07:00
Johnpaul Chiwetelu
26f3f11a3e test: replace raw CSS selectors with TestIds in context menu spec (#10760)
## Summary
- Replace raw CSS selectors (`.lg-node-header`, `.p-contextmenu`,
`.node-title-editor input`, `.image-preview img`) with centralized
`TestIds` constants and existing fixtures in the context menu E2E spec
- Add `data-testid="title-editor-input"` to TitleEditor overlay for
stable selector targeting
- Use `NodeLibrarySidebarTab` fixture for node library sidebar
interaction

## Changes
- `browser_tests/fixtures/selectors.ts`: add `pinIndicator`,
`innerWrapper`, `titleEditorInput`, `mainImage` to `TestIds.node`
- `browser_tests/fixtures/utils/vueNodeFixtures.ts`: add `pinIndicator`
getter
- `src/components/graph/TitleEditor.vue`: add `data-testid` via
`input-attrs`
- `browser_tests/.../contextMenu.spec.ts`: replace all raw selectors
with TestIds/fixtures

## Test plan
- [x] All 23 context menu E2E tests pass locally
- [x] Typecheck passes
- [x] Lint passes

Fixes #10750
Fixes #10749

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10760-test-replace-raw-CSS-selectors-with-TestIds-in-context-menu-spec-3336d73d3650818790c0e32e0b6f1e98)
by [Unito](https://www.unito.io)
2026-04-02 21:04:50 +01:00
jaeone94
d9466947b2 feat: detect and resolve missing media inputs in error tab (#10309)
## Summary

Add detection and resolution UI for missing image/video/audio inputs
(LoadImage, LoadVideo, LoadAudio nodes) in the Errors tab, mirroring the
existing missing model pipeline.

## Changes

- **What**: New `src/platform/missingMedia/` module — scan pipeline
detects missing media files on workflow load (sync for OSS, async for
cloud), surfaces them in the error tab with upload dropzone, thumbnail
library select, and 2-step confirm flow
- **Detection**: `scanAllMediaCandidates()` checks combo widget values
against options; cloud path defers to `verifyCloudMediaCandidates()` via
`assetsStore.updateInputs()`
- **UI**: `MissingMediaCard` groups by media type; `MissingMediaRow`
shows node name (single) or filename+count (multiple), upload dropzone
with drag & drop, `MissingMediaLibrarySelect` with image/video
thumbnails
- **Resolution**: Upload via `/upload/image` API or select from library
→ status card → checkmark confirm → widget value applied, item removed
from error list
- **Integration**: `executionErrorStore` aggregates into
`hasAnyError`/`totalErrorCount`; `useNodeErrorFlagSync` flags nodes on
canvas; `useErrorGroups` renders in error tab
- **Shared**: Extract `ACCEPTED_IMAGE_TYPES`/`ACCEPTED_VIDEO_TYPES` to
`src/utils/mediaUploadUtil.ts`; extract `resolveComboValues` to
`src/utils/litegraphUtil.ts` (shared across missingMedia + missingModel
scan)
- **Reverse clearing**: Widget value changes on nodes auto-remove
corresponding missing media errors (via `clearWidgetRelatedErrors`)

## Testing

### Unit tests (22 tests)
- `missingMediaScan.test.ts` (12): groupCandidatesByName,
groupCandidatesByMediaType (ordering, multi-name),
verifyCloudMediaCandidates (missing/present, abort before/after
updateInputs, already resolved true/false, no-pending skip, updateInputs
spy)
- `missingMediaStore.test.ts` (10): setMissingMedia, clearMissingMedia
(full lifecycle with interaction state), missingMediaNodeIds,
hasMissingMediaOnNode, removeMissingMediaByWidget
(match/no-match/last-entry), createVerificationAbortController

### E2E tests (10 scenarios in `missingMedia.spec.ts`)
- Detection: error overlay shown, Missing Inputs group in errors tab,
correct row count, dropzone + library select visibility, no false
positive for valid media
- Upload flow: file picker → uploading status card → confirm → row
removed
- Library select: dropdown → selected status card → confirm → row
removed
- Cancel: pending selection → returns to upload/library UI
- All resolved: Missing Inputs group disappears
- Locate node: canvas pans to missing media node

## Review Focus

- Cloud verification path: `verifyCloudMediaCandidates` compares widget
value against `asset_hash` — implicit contract
- 2-step confirm mirrors missing model pattern (`pendingSelection` →
confirm/cancel)
- Event propagation guard on dropzone (`@drop.prevent.stop`) to prevent
canvas LoadImage node creation
- `clearAllErrors()` intentionally does NOT clear missing media (same as
missing models — preserves pending repairs)
- `runMissingMediaPipeline` is now `async` and `await`-ed, matching
model pipeline

## Test plan

- [x] OSS: load workflow with LoadImage referencing non-existent file →
error tab shows it
- [x] Upload file via dropzone → status card shows "Uploaded" → confirm
→ widget updated, error removed
- [x] Select from library with thumbnail preview → confirm → widget
updated, error removed
- [x] Cancel pending selection → returns to upload/library UI
- [x] Load workflow with valid images → no false positives
- [x] Click locate-node → canvas navigates to the node
- [x] Multiple nodes referencing different missing files → correct row
count
- [x] Widget value change on node → missing media error auto-removed

## Screenshots


https://github.com/user-attachments/assets/631c0cb0-9706-4db2-8615-f24a4c3fe27d
2026-04-01 17:59:02 +09:00
jaeone94
bb96e3c95c fix: resolve subgraph promoted widget panel regressions (#10648)
## Summary

Fix four bugs in the subgraph promoted widget panel where linked
promotions were not distinguished from independent ones, causing
incorrect UI state in both the SubgraphEditor (Settings) panel and the
Parameters tab WidgetActions menu.

## Changes

- **What**: Add `isLinkedPromotion` helper to correctly identify widgets
driven by subgraph input connections. Fix `disambiguatingSourceNodeId`
lookup mismatch that broke `isWidgetShownOnParents` and
`handleHideInput` for non-nested promoted widgets. Replace fragile CSS
icon selectors with `data-testid` attributes.

## Bugs fixed

Companion fix PR for #10502 (red-green test PR). All 4 E2E tests from
#10502 now pass:

| Bug | Root cause | Fix |
|-----|-----------|-----|
| Linked promoted widgets have hide toggle enabled | `SubgraphEditor`
only checked `node.id === -1` (physical) — linked promotions from
subgraph input connections were not detected | Added `isLinkedPromotion`
helper that checks `input._widget` bindings; `SubgraphNodeWidget`
`:is-physical` prop now covers both physical and linked cases |
| Linked promoted widgets show eye icon instead of link icon | Same root
cause as above — `isPhysical` prop was only true for `node.id === -1` |
Extended the `:is-physical` condition to include `isLinkedPromotion`
check |
| Widget labels show raw names instead of renamed values |
`SubgraphEditor` passed `widget.name` instead of `widget.label \|\|
widget.name` | Changed `:widget-name` binding to prefer `widget.label` |
| WidgetActions menu shows Hide/Show for linked promotions |
`v-if="hasParents"` didn't exclude linked promotions | Added
`canToggleVisibility` computed that combines `hasParents` with
`!isLinked` check via `isLinkedPromotion` |

### Additional bugs discovered and fixed

| Bug | Root cause | Fix |
|-----|-----------|-----|
| "Show input" always displayed instead of "Hide input" for promoted
widgets | `SectionWidgets.isWidgetShownOnParents` used
`getSourceNodeId(widget)` which falls back to `widget.sourceNodeId` when
`disambiguatingSourceNodeId` is undefined — this mismatches the
promotion store key (`undefined`) | Changed to
`widget.disambiguatingSourceNodeId` directly |
| "Hide input" click does nothing | `WidgetActions.handleHideInput` used
`getSourceNodeId(widget)` for the same reason — `demote()` couldn't find
the entry to remove | Same fix — use `widget.disambiguatingSourceNodeId`
directly |

## Tests added

### E2E (Playwright) —
`browser_tests/tests/subgraphPromotedWidgetPanel.spec.ts`

| Test | What it verifies |
|------|-----------------|
| linked promoted widgets have hide toggle disabled | All toggle buttons
in SubgraphEditor shown section are disabled for linked widgets (covers
1-level and 2-level nested promotions via `subgraph-nested-promotion`
fixture) |
| linked promoted widgets show link icon instead of eye icon | Link
icons appear for linked widgets, no eye icons present |
| widget labels display renamed values instead of raw names |
`widget.label` is displayed when set, not `widget.name` |
| linked promoted widget menu should not show Hide/Show input |
Three-dot menu on Parameters tab omits Hide/Show options for linked
promotions, Rename is still available |

### Unit (Vitest) — `src/core/graph/subgraph/promotionUtils.test.ts`

7 tests covering `isLinkedPromotion`: basic matching, negative cases,
nested subgraph with `disambiguatingSourceNodeId`, multiple inputs, and
mixed linked/independent state.

### Unit (Vitest) —
`src/components/rightSidePanel/parameters/WidgetActions.test.ts`

- Added `isSubgraphNode: () => false` to mock nodes to prevent crash
from new `isLinked` computed

## Review Focus

- `isLinkedPromotion` reads `input._widget` (WeakRef-backed,
non-reactive) directly in the template. This is intentional — `_widget`
bindings are set during subgraph initialization before the user opens
the panel, so stale reads don't occur in practice. A computed-based
approach was attempted but reverted because `_widget` changes cannot
trigger Vue reactivity.
- `getSourceNodeId` removal in `SectionWidgets` and `WidgetActions` is
intentional — the old fallback (`?? widget.sourceNodeId`) caused key
mismatches with the promotion store for non-nested widgets.

## Screenshots
Before
<img width="723" height="935" alt="image"
src="https://github.com/user-attachments/assets/09862578-a0d1-45b4-929c-f22f7494ebe2"
/>

After
<img width="999" height="952" alt="image"
src="https://github.com/user-attachments/assets/ed8fe604-6b44-46b9-a315-6da31d6b405a"
/>
2026-04-01 17:10:30 +09:00
jaeone94
df42b7a2a8 fix: collapsed node connection link positions (#10641)
## Summary

Fix connection links rendering at wrong positions when nodes are
collapsed in Vue nodes mode.

## Changes

- **What**: Fall back to `clientPosToCanvasPos` for collapsed node slot
positioning since DOM-relative scale derivation is invalid when layout
store preserves expanded size. Clear stale `cachedOffset` on collapse
and defer sync when canvas is not yet initialized.
- 3 unit tests for collapsed node slot sync fallback
(clientPosToCanvasPos, cachedOffset clearing, canvas-not-initialized
deferral)
- 3 E2E tests for collapsed node link positions (within bounds, after
position change, after expand recovery)

## Review Focus

- `clientPosToCanvasPos` fallback is safe for collapsed nodes because
collapse is user-initiated (no loading-time transform desync risk that
#9121 originally fixed)
- `cachedOffset` clearing prevents stale expanded-state offsets during
collapsed node drag
- Regression from #9121 (DOM-relative scale) combined with #9680
(collapsed node ResizeObserver skip)

## Screenshots 
Before
<img width="1030" height="434" alt="image"
src="https://github.com/user-attachments/assets/2f8b8a1f-ed22-4588-ab62-72b89880e53f"
/>

After
<img width="1029" height="476" alt="image"
src="https://github.com/user-attachments/assets/52dbbf7c-61ed-465b-ae19-a9781513e7e8"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10641-fix-collapsed-node-connection-link-positions-3316d73d365081f4aee3fecb92c83b91)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Alexander Brown <DrJKL0424@gmail.com>
2026-04-01 13:49:12 +09:00
Kelly Yang
4f3a5ae184 fix(load3d): fix squashed controls in 3D inspector side panel (#10768)
## Summary

Fixes squashed `input controls` (color picker, sliders, dropdowns) in
the 3D asset inspector side panel.

## Screenshots 

before
<img width="3012" height="1580" alt="image"
src="https://github.com/user-attachments/assets/edc6fadc-cdc5-4a4e-92e7-57faabfeb1a4"
/>

after
<img width="4172" height="2062" alt="image"
src="https://github.com/user-attachments/assets/766324ce-e8f7-43fc-899e-ae275f880e59"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10768-fix-load3d-fix-squashed-controls-in-3D-inspector-side-panel-3346d73d365081e8b438de8115180685)
by [Unito](https://www.unito.io)
2026-04-01 00:34:37 -04:00
Dante
c77c8a9476 test: migrate fromAny to fromPartial for type-checked test mocks (#10788)
## Summary
- Convert `fromAny` → `fromPartial` in 7 test files where object
literals or interfaces are passed
- `fromPartial` type-checks the provided fields, unlike `fromAny` which
bypasses all checking (same as `as unknown as`)
- Class-based types (`LGraphNode`, `LGraph`) remain `fromAny` due to
shoehorn's `PartialDeep` incompatibility with class constructors

## Changes
- **Pure conversions** (all `fromAny` → `fromPartial`):
`domWidgetZIndex`, `matchPromotedInput`, `promotionUtils`,
`subgraphNavigationStore`
- **Mixed** (some converted, some kept): `promotedWidgetView`,
`widgetUtil`
- **Cleanup**: `nodeOutputStore` type param normalization

Follows up on #10761.

## Test plan
- [x] `pnpm typecheck` passes
- [x] `pnpm vitest run` on all 7 changed files — 169 tests pass
- [x] `pnpm lint` passes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10788-test-migrate-fromAny-to-fromPartial-for-type-checked-test-mocks-3356d73d365081f7bf61d48a47af530c)
by [Unito](https://www.unito.io)
2026-03-31 21:11:50 -07:00
Dante
380fae9a0d chore(test): remove dead QueueHelper from browser tests (#10771)
## Summary
- Remove unused `QueueHelper` class and its `comfyPage.queue` property
- `QueueHelper` mocks the legacy `/api/queue` tuple format which the app
no longer uses (now `/api/jobs` via `fetchQueue()`)
- `comfyPage.queue.*` is never called in any test

Fixes #10670

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10771-chore-test-remove-dead-QueueHelper-from-browser-tests-3346d73d36508117bb19db9492bcbed3)
by [Unito](https://www.unito.io)
2026-03-31 19:55:52 +09:00
pythongosssss
515f234143 fix: Ensure all save/save as buttons are the same width (#10681)
## Summary

Makes the save/save as buttons in the builder footer toolbar all a fixed
size so when switching states the elements dont jump

## Changes

- **What**: 
- Apply widths from design to the buttons
- Add tests that measure the sizes of the buttons

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10681-fix-Ensure-all-save-save-as-buttons-are-the-same-width-3316d73d36508187bb74c5a977ea876f)
by [Unito](https://www.unito.io)
2026-03-31 02:47:27 -07:00
Dante
61049425a3 fix(DisplayCarousel): use back button in grid view and remove hover icons (#10655)
## Summary
- Grid view top-left icon changed from square to back arrow
(`arrow-left`) per Figma spec
- Back button is always visible in grid view (no longer
hover-dependent), uses sticky positioning
- Removed hover opacity effect on grid thumbnails

## Related
- Figma:
https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=6008-83034&m=dev
- Figma:
https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=6008-83069&m=dev

## Test plan
- [x] All 31 existing DisplayCarousel tests pass
- [ ] Visual check: grid view shows back arrow icon (top-left, always
visible)
- [ ] Visual check: hovering grid thumbnails shows no overlay icons
- [ ] Verify back button stays visible when scrolling through many grid
items

## Screenshot
### Before
<img width="492" height="364" alt="스크린샷 2026-03-28 오후 4 31 54"
src="https://github.com/user-attachments/assets/f9f36521-e993-45de-b692-59fba22a026d"
/>
<img width="457" height="400" alt="스크린샷 2026-03-28 오후 4 32 03"
src="https://github.com/user-attachments/assets/004f6380-8ad7-4167-b1f4-ebc4bdb559cc"
/>

### After
<img width="596" height="388" alt="스크린샷 2026-03-28 오후 4 31 43"
src="https://github.com/user-attachments/assets/e5585887-ad36-42e3-a6c0-e6eacb90dad7"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10655-fix-DisplayCarousel-use-back-button-in-grid-view-and-remove-hover-icons-3316d73d365081c5826afd63c50994ba)
by [Unito](https://www.unito.io)
2026-03-31 12:17:24 +09:00
Alexander Brown
661e3d7949 test: migrate as unknown as to @total-typescript/shoehorn (#10761)
*PR Created by the Glary-Bot Agent*

---

## Summary

- Replace all `as unknown as Type` assertions in 59 unit test files with
type-safe `@total-typescript/shoehorn` functions
- Use `fromPartial<Type>()` for partial mock objects where deep-partial
type-checks (21 files)
- Use `fromAny<Type>()` for fundamentally incompatible types: null,
undefined, primitives, variables, class expressions, and mocks with
test-specific extra properties that `PartialDeepObject` rejects
(remaining files)
- All explicit type parameters preserved so TypeScript return types are
correct
- Browser test `.spec.ts` files excluded (shoehorn unavailable in
`page.evaluate` browser context)

## Verification

- `pnpm typecheck` 
- `pnpm lint` 
- `pnpm format` 
- Pre-commit hooks passed (format + oxlint + eslint + typecheck)
- Migrated test files verified passing (ran representative subset)
- No test behavior changes — only type assertion syntax changed
- No UI changes — screenshots not applicable

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10761-test-migrate-as-unknown-as-to-total-typescript-shoehorn-3336d73d365081f6b8adc44db5dcc380)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-03-30 19:20:18 -07:00
Alexander Brown
1624750a02 fix(test): fix bulk context menu test using correct Playwright patterns (#10762)
*PR Created by the Glary-Bot Agent*

---

## Summary

Fixes the `Bulk context menu shows when multiple assets selected` test
that is failing on main.

**Root cause — two issues:**

1. `click({ modifiers: ['ControlOrMeta'] })` does not fire `keydown`
events that VueUse's `useKeyModifier('Control')` tracks (used in
`useAssetSelection.ts`). Multi-select silently fails because the
composable never sees the Control key pressed. Fix: use
`keyboard.down('Control')` / `keyboard.up('Control')` around the click.

2. `click({ button: 'right' })` can be intercepted by canvas overlays
(documented gotcha in `browser_tests/AGENTS.md`). Fix: use
`dispatchEvent('contextmenu', { bubbles: true, cancelable: true })`
which bypasses overlay interception.

Also removed the `toPass()` retry wrapper since the root causes are now
addressed directly.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10762-fix-test-fix-bulk-context-menu-test-using-correct-Playwright-patterns-3346d73d3650811c843ee4a39d3ab305)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
2026-03-30 18:38:25 -07:00