Commit Graph

7348 Commits

Author SHA1 Message Date
Comfy Org PR Bot
668f7e48e7 1.43.7 (#10583)
Patch version increment to 1.43.7

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10583-1-43-7-3306d73d3650810f921bf97fc447e402)
by [Unito](https://www.unito.io)

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
v1.43.7
2026-03-26 19:56:32 -07:00
Alexander Brown
3de387429a test: migrate 11 interactive component tests from VTU to VTL (Phase 2) (#10490)
## Summary

Phase 2 of the VTL migration: migrate 11 interactive component tests
from @vue/test-utils to @testing-library/vue (69 tests).

Stacked on #10471.

## Changes

- **What**: Migrate BatchCountEdit, BypassButton, BuilderFooterToolbar,
ComfyActionbar, SidebarIcon, EditableText, UrlInput, SearchInput,
TagsInput, TreeExplorerTreeNode, ColorCustomizationSelector from VTU to
VTL
- **Pattern transforms**: `trigger('click')` → `userEvent.click()`,
`setValue()` → `userEvent.type()`, `findComponent().props()` →
`getByRole/getByText/getByTestId`, `emitted()` → callback props
- **Removed**: 4 `@ts-expect-error` annotations, 1 change-detector test
(SearchInput `vm.focus`)
- **PrimeVue**: `data-pc-name` selectors + `aria-pressed` for
SelectButton, container queries for ColorPicker/InputIcon

## Review Focus

- PrimeVue escape hatches in ColorCustomizationSelector
(SelectButton/ColorPicker lack standard ARIA roles)
- Teleport test in ComfyActionbar uses `container.querySelector`
intentionally (scoped to teleport target)
- SearchInput debounce tests use `fireEvent.update` instead of
`userEvent.type` due to fake timer interaction
- EditableText escape-then-blur test simplified:
`userEvent.keyboard('{Escape}')` already triggers blur internally

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10490-test-migrate-11-interactive-component-tests-from-VTU-to-VTL-Phase-2-32e6d73d3650817ca40fd61395499e3f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-03-26 19:46:31 -07:00
Alexander Brown
08b1199265 test: migrate 13 component tests from VTU to VTL (Phase 1) (#10471)
## Summary

Migrate 13 component test files from @vue/test-utils to
@testing-library/vue as Phase 1 of incremental VTL adoption.

## Changes

- **What**: Rewrite 13 test files (88 tests) to use `render`/`screen`
queries, `userEvent` interactions, and `jest-dom` assertions. Add
`data-testid` attributes to 6 components for lint-clean icon/element
queries. Delete unused `src/utils/test-utils.ts`.
- **Dependencies**: `@testing-library/vue`,
`@testing-library/user-event`, `@testing-library/jest-dom` (installed in
Phase 0)

## Review Focus

- `data-testid` additions to component templates are minimal and
non-behavioral
- PrimeVue passthrough (`pt`) usage in UserAvatar.vue for icon testid
- 2 targeted `eslint-disable` in FormRadioGroup.test.ts where PrimeVue
places `aria-describedby` on wrapper div, not input

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10471-test-migrate-13-component-tests-from-VTU-to-VTL-Phase-1-32d6d73d36508159a33ffa285afb4c38)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-03-26 18:15:11 -07:00
Dante
98a9facc7d tool: add CSS containment audit skill and Playwright diagnostic (#10026)
## Summary

- Add a Playwright-based diagnostic tool (`@audit` tagged) that
automatically detects DOM elements where CSS `contain: layout style`
would improve rendering performance
- Extend `ComfyPage` fixture and `playwright.config.ts` to support
`@audit` tag (excluded from CI, perf infra enabled)
- Add `/contain-audit` skill definition documenting the workflow

## How it works

1. Loads the 245-node workflow in a real browser
2. Walks the DOM tree and scores every element by subtree size and
sizing constraints
3. For each high-scoring candidate, applies `contain: layout style` via
JS
4. Measures rendering performance (style recalcs, layouts, task
duration) before and after
5. Takes before/after screenshots to detect visual breakage
6. Outputs a ranked report to console

## Test plan

- [ ] `pnpm typecheck` passes
- [ ] `pnpm typecheck:browser` passes
- [ ] `pnpm lint` passes
- [ ] Existing Playwright tests unaffected (`@audit` excluded from CI
via `grepInvert`)
- [ ] Run `pnpm exec playwright test
browser_tests/tests/containAudit.spec.ts --project=chromium` locally
with dev server

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10026-tool-add-CSS-containment-audit-skill-and-Playwright-diagnostic-3256d73d365081b29470df164f798f7d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 10:08:41 +09:00
jaeone94
8b1cf594d1 fix: improve error overlay design and indicator placement (#10564)
## Summary

- Move error red border from TopMenuSection/ComfyActionbar to
ErrorOverlay
- Add error indicator (outline + StatusBadge dot) on right side panel
toggle button when errors are present, the panel/overlay are closed, and
the errors tab setting is enabled
- Replace technical group titles (e.g. "Missing Node Packs") with
user-friendly i18n messages in ErrorOverlay
- Dynamically change action button label based on single error type
(e.g. "Show missing nodes" instead of "See Errors")
- Remove unused `hasAnyError` prop from ComfyActionbar
- Fix `type="secondary"` → `variant="secondary"` on panel toggle button
- Pre-wire `missing_media` error type support for #10309
- Migrate ErrorOverlay E2E selectors from `getByText`/`getByRole` to
`data-testid`
- Update E2E screenshot snapshots affected by TopMenuSection error state
design changes

## Test plan

- [x] Trigger execution error → verify red border on ErrorOverlay, no
red border on TopMenuSection/ComfyActionbar
- [x] With errors and right side panel closed → verify red outline + dot
on panel toggle button
- [x] Open right side panel or error overlay → verify indicator
disappears
- [x] Disable `Comfy.RightSidePanel.ShowErrorsTab` → verify no indicator
even with errors
- [x] Load workflow with only missing nodes → verify "Show missing
nodes" button label and friendly message
- [x] Load workflow with only missing models → verify "Show missing
models" button label and count message
- [x] Load workflow with mixed errors → verify "See Errors" fallback
label
- [x] E2E: `pnpm test:browser:local -- --grep "Error overlay"`

## Screenshots
<img width="498" height="381" alt="스크린샷 2026-03-26 230252"
src="https://github.com/user-attachments/assets/034f0f3f-e6a1-4617-b8f6-cd4c145e3a47"
/>
<img width="550" height="303" alt="스크린샷 2026-03-26 230525"
src="https://github.com/user-attachments/assets/2958914b-0ff0-461b-a6ea-7f2811bf33c2"
/>
<img width="551" height="87" alt="스크린샷 2026-03-26 230318"
src="https://github.com/user-attachments/assets/396e9cb1-667e-44c4-83fe-ab113b313d16"
/>

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Dante <bunggl@naver.com>
2026-03-27 09:11:26 +09:00
Dante
ff263fced0 fix: make splitter state key position-aware to prevent shared panel widths (#9525)
## Summary

Fix right-side sidebar panels and left-side panels sharing the same
PrimeVue Splitter state key, causing them to incorrectly apply each
other's saved widths.

## Changes

- **What**: Make `sidebarStateKey` position-aware by including
`sidebarLocation` and offside panel visibility in the localStorage key

## Problem

When sidebar location is set to **right**, all panels (both the
right-side sidebar like Job History and left-side panels like Workflow
overview) share a single PrimeVue Splitter `state-key`
(`unified-sidebar`). PrimeVue persists panel widths to localStorage
using this key, so any resize on one side gets applied to the other.

### AS-IS (before fix)

The `sidebarStateKey` is computed without any awareness of panel
position:

```typescript
// Always returns 'unified-sidebar' (when unified width enabled)
// or the active tab id — regardless of sidebar location or offside panel state
const sidebarStateKey = computed(() => {
  return unifiedWidth.value
    ? 'unified-sidebar'
    : (activeSidebarTabId.value ?? 'default-sidebar')
})
```

This produces a **single localStorage key** for all layout
configurations. The result:

1. Set sidebar to **right**, open **Job History** → resize it smaller →
saved to `unified-sidebar`
2. Open **Workflow overview** (appears on the left as an offside panel)
→ loads the same `unified-sidebar` key → gets the Job History width
applied to a completely different panel position
3. Both panels open simultaneously share the same persisted width, even
though they are on opposite sides of the screen

This is exactly the behavior shown in the [issue
screenshots](https://github.com/Comfy-Org/ComfyUI_frontend/issues/9440):
pulling the Workflow overview smaller also changes Job History to that
same size, and vice versa.

### TO-BE (after fix)

The `sidebarStateKey` now includes `sidebarLocation` (`left`/`right`)
and whether the offside panel is visible:

```typescript
const sidebarTabKey = computed(() => {
  return unifiedWidth.value
    ? 'unified-sidebar'
    : (activeSidebarTabId.value ?? 'default-sidebar')
})

const sidebarStateKey = computed(() => {
  const base = sidebarTabKey.value
  const suffix = showOffsideSplitter.value ? '-with-offside' : ''
  return `${base}-${sidebarLocation.value}${suffix}`
})
```

This produces **distinct localStorage keys** per layout configuration:
| Layout | Key |
|--------|-----|
| Sidebar left, no offside | `unified-sidebar-left` |
| Sidebar left, right panel open | `unified-sidebar-left-with-offside` |
| Sidebar right, no offside | `unified-sidebar-right` |
| Sidebar right, left panel open | `unified-sidebar-right-with-offside`
|

Each configuration now persists and restores its own panel sizes
independently, so resizing Job History on the right no longer affects
Workflow overview on the left.

## Review Focus

- The offside suffix (`-with-offside`) is necessary because the Splitter
transitions from a 2-panel layout (sidebar + center) to a 3-panel layout
(sidebar + center + offside) — these are fundamentally different panel
configurations and should not share persisted sizes.

Fixes #9440

## Screenshots (if applicable)

See issue for reproduction screenshots:
https://github.com/Comfy-Org/ComfyUI_frontend/issues/9440

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 08:31:56 +09:00
Alexander Brown
3e197b5c57 docs: ADR 0008 — Entity Component System (#10420)
## Summary

Architecture documentation proposing an Entity Component System for the
litegraph layer.

```mermaid
graph LR
    subgraph Today["Today: Spaghetti"]
        God["🍝 God Objects"]
        Circ["🔄 Circular Deps"]
        Mut["💥 Render Mutations"]
    end

    subgraph Tomorrow["Tomorrow: ECS"]
        ID["🏷️ Branded IDs"]
        Comp["📦 Components"]
        Sys["⚙️ Systems"]
        World["🌍 World"]
    end

    God -->|"decompose"| Comp
    Circ -->|"flatten"| ID
    Mut -->|"separate"| Sys
    Comp --> World
    ID --> World
    Sys -->|"query"| World
```

## Changes

- **What**: ADR 0008 + 4 architecture docs (no code changes)
- `docs/adr/0008-entity-component-system.md` — entity taxonomy, branded
IDs, component decomposition, migration strategy
- `docs/architecture/entity-interactions.md` — as-is Mermaid diagrams of
all entity relationships
- `docs/architecture/entity-problems.md` — structural problems with
file:line evidence
- `docs/architecture/ecs-target-architecture.md` — target architecture
diagrams
- `docs/architecture/proto-ecs-stores.md` — analysis of existing Pinia
stores as proto-ECS patterns

## Review Focus

- Does the entity taxonomy (Node, Link, Subgraph, Widget, Slot, Reroute,
Group) cover all cases?
- Are the component decompositions reasonable starting points?
- Is the migration strategy (bridge layer, incremental extraction)
feasible?
- Are there entity interactions or problems we missed?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10420-docs-ADR-0008-Entity-Component-System-32d6d73d365081feb048d16a5231d350)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
2026-03-26 16:14:44 -07:00
Deep Mehta
9573074ea6 feat: add model-to-node mappings for 10 node packs (#10560)
## Summary
Add `MODEL_NODE_MAPPINGS` entries for 10 model directories missing UI
backlinks.

### New mappings
- `BiRefNet` → `LayerMask: LoadBiRefNetModelV2` (`version`)
- `EVF-SAM` → `LayerMask: EVFSAMUltra` (`model`)
- `florence2` → `DownloadAndLoadFlorence2Model` (`model`)
- `interpolation` → `DownloadAndLoadGIMMVFIModel` (`model`)
- `rmbg` → `RMBG` (`model`)
- `smol` → `LayerUtility: LoadSmolLM2Model` (`model`)
- `transparent-background` → `LayerMask: TransparentBackgroundUltra`
(`model`)
- `yolo` → `LayerMask: ObjectDetectorYOLO8` (`yolo_model`)
- `mediapipe` → `LivePortraitLoadMediaPipeCropper` (auto-load)
- `superprompt-v1` → `Superprompt` (auto-load)

These mappings ensure the "Use" button in the model browser correctly
creates the appropriate loader node.

## Test plan
- [ ] Verify "Use" button works for models in each new directory

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10560-feat-add-model-to-node-mappings-for-10-node-packs-32f6d73d365081d18b6acda078e7fe0b)
by [Unito](https://www.unito.io)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:01:08 -07:00
Alexander Brown
68d47af075 fix: normalize legacy prefixed proxyWidget entries on configure (#10573)
## Summary

Normalize legacy prefixed proxyWidget entries during subgraph configure
so nested subgraph widgets resolve correctly.

## Changes

- **What**: Extract `normalizeLegacyProxyWidgetEntry` to strip legacy
`nodeId: innerNodeId: widgetName` prefixes from serialized proxyWidgets
and resolve the correct `disambiguatingSourceNodeId`. Write-back
comparison now checks serialized content (not just array length) so
stale formats are cleaned up even when the entry count is unchanged.

## Review Focus

- The iterative prefix-stripping loop in `resolveLegacyPrefixedEntry` —
it peels one `N: ` prefix per iteration and tries all disambiguator
candidates at each level.
- The write-back condition change from length comparison to
`JSON.stringify` equality.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10573-fix-normalize-legacy-prefixed-proxyWidget-entries-on-configure-32f6d73d365081e886e1c9b3939e3b9f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-03-26 15:53:10 -07:00
Alexander Brown
897cf9cb8f Mark failing test as in need of fixing (#10572)
CC @christian-byrne

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10572-Mark-failing-test-as-in-need-of-fixing-32f6d73d3650815cba72d19ebc54b0b3)
by [Unito](https://www.unito.io)
2026-03-26 12:23:44 -07:00
jaeone94
e9b01cf479 fix: create initial workflow tab when persistence is disabled (#10565)
## Summary

- When `Comfy.Workflow.Persist` is OFF and storage is empty,
`initializeWorkflow()` returned without creating any workflow tab —
leaving users with no tab and no way to save
- Now falls through to `loadDefaultWorkflow()` so a default temporary
workflow is always created

## Root Cause

In `useWorkflowPersistenceV2.ts`, `initializeWorkflow()` had an early
return when persistence was disabled:

```ts
if (!workflowPersistenceEnabled.value) return
```

This skipped `loadDefaultWorkflow()`, which is responsible for creating
the initial temporary workflow tab via `comfyApp.loadGraphData()` →
`afterLoadNewGraph()` → `workflowStore.createNewTemporary()`.

## Fix

One-line change: `return` → `return loadDefaultWorkflow()`.

## Test plan

- [x] E2E test: verifies `openWorkflows.length >= 1` after reload with
persistence OFF

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10565-fix-create-initial-workflow-tab-when-persistence-is-disabled-32f6d73d365081d5a681c3e019d373c3)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-26 11:16:50 -07:00
Terry Jia
bcb39b1bf6 feat: support histogram display in curve widget (#10365)
## Summary
- WidgetCurve reads histogram data from nodeOutputStore (sent by backend
CurveEditor node via ui output) and passes it to CurveEditor
- histogramToPath now supports arbitrary-length bin arrays instead
ofhardcoded 256

need BE changes

## Screenshots (if applicable)
<img width="2431" height="1022" alt="image"
src="https://github.com/user-attachments/assets/8421d4a7-1bff-4269-8b55-649838f9d619"
/>

<img width="2462" height="979" alt="image"
src="https://github.com/user-attachments/assets/191c9163-82ab-4eb2-bb74-0037b3ccd383"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10365-feat-support-histogram-display-in-curve-widget-32a6d73d3650816b9852d73309a0b35f)
by [Unito](https://www.unito.io)
2026-03-26 05:29:08 -04:00
Alexander Brown
d940ea76ee fix: repoint ancestor promoted widget bindings when packing nested subgraphs (#10532)
## Summary

Packing nodes inside a subgraph into a nested subgraph no longer blanks
the parent subgraph node's promoted widget values.

## Changes

- **What**: After `convertToSubgraph` moves interior nodes into a nested
subgraph, `_repointAncestorPromotions` rewrites the promotion store
entries on all host SubgraphNodes so they chain through the new nested
node. `rebuildInputWidgetBindings()` then clears the stale
`input._widget` PromotedWidgetView cache and re-resolves bindings from
current connections.
- The root cause was two separate sets of PromotedWidgetView references:
`node.widgets` (rebuilt from the store — correct) vs `input._widget`
(cached at promotion time — stale). `SubgraphNode.serialize()` reads
`input._widget.value`, which resolved against removed node IDs →
`missing-node` → blank values on the next `checkState` cycle.

## Review Focus

- `_repointAncestorPromotions` iterates all graphs to find host nodes of
the current subgraph type — verify this covers all cases (multiple
instances of the same subgraph type).
- `rebuildInputWidgetBindings()` clears `_promotedViewManager` and
re-resolves — confirm no side effects on event listeners or pending
promotions.
- The nested node gets duplicate promotion entries (from both
`_repointAncestorPromotions` and `promoteRecommendedWidgets` via the
`subgraph-converted` event). `store.promote()` deduplicates via
`isPromoted`, but worth verifying.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10532-fix-repoint-ancestor-promoted-widget-bindings-when-packing-nested-subgraphs-32e6d73d365081109d5aea0660434082)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com>
Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: pythongosssss <125205205+pythongosssss@users.noreply.github.com>
Co-authored-by: Yourz <crazilou@vip.qq.com>
2026-03-25 22:43:52 -07:00
Alexander Brown
d397318ad8 Update README for browser testing commands (#10541)
## Summary

Update recommended commands in browser_tests README

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10541-Update-README-for-browser-testing-commands-32f6d73d36508175a675e865a990caed)
by [Unito](https://www.unito.io)

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-03-25 21:50:02 -07:00
Christian Byrne
d860d54366 test: add large graph zoom perf test for ResizeObserver baseline (#10478)
## Summary

Adds a `@perf` test that establishes a baseline for ResizeObserver
layout cost during zoom on a large graph (245 nodes).

## Changes

- **What**: New `large graph zoom interaction` perf test that zooms
in/out 30 steps on `large-graph-workflow`, measuring `layouts`,
`layoutDurationMs`, `frameDurationMs`, and `TBT`. Each zoom step
triggers ResizeObserver for all node elements due to CSS scale changes.

## Review Focus

This is **PR 1 of 2** for throttling the ResizeObserver during zoom/pan.
Once this merges and establishes a baseline on main, the fix PR (#10473)
will show a CI-proven delta demonstrating the improvement.

The test follows the same patterns as `large graph pan interaction` and
`canvas zoom sweep`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10478-test-add-large-graph-zoom-perf-test-for-ResizeObserver-baseline-32d6d73d365081169537e557c14d8c51)
by [Unito](https://www.unito.io)
2026-03-25 21:05:36 -07:00
Comfy Org PR Bot
86d202bcc1 1.43.6 (#10540)
Patch version increment to 1.43.6

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10540-1-43-6-32f6d73d3650810fb3f5c6d4c59130f2)
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>
Co-authored-by: github-actions <github-actions@github.com>
v1.43.6
2026-03-25 20:31:24 -07:00
Johnpaul Chiwetelu
60b6f78397 chore: bump CI container to 0.0.16 (#10527) 2026-03-26 02:34:27 +01:00
Yourz
6c7c3ea006 fix: tree explorer row height and width overflow (#10501)
## Summary

Fix tree explorer row sizing: consistent row height and prevent
horizontal overflow.

## Changes

- **What**:
1. Reduce node bookmark button from `size-6` (24px) to `size-5` (20px)
so node and folder rows both have 36px height, matching
`TreeVirtualizer` estimate-size and fixing tree list overlap.
2. Change row width from `w-full` to `w-[calc(100%-var(--spacing)*4)]`
to prevent horizontal overflow while keeping `mx-2` margin.

## Review Focus

Pure UI change — no test coverage needed. Verify tree rows render at
consistent height and no horizontal overflow occurs.

## Screenshots (if applicable)

| Before    | After |
| -------- | ------- |
|<img width="1218" height="1662" alt="image"
src="https://github.com/user-attachments/assets/89c799ab-cef3-40ee-88ca-900f5d3c7890"
/>|<img width="407" height="758" alt="image"
src="https://github.com/user-attachments/assets/f9aa4569-aaf8-467f-9dde-a187151af9aa"
/>|

N/A

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10501-fix-tree-explorer-row-height-and-width-overflow-32e6d73d3650819aa645c2262693ec62)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-03-26 09:20:42 +09:00
pythongosssss
771f68f92a fix: App mode - workaround for alt+m producing alt+μ on mac (#10528)
## Summary

Adds a second keybinding for app mode as on Mac Alt+M produces Alt+μ

## Changes

- **What**: add extra binding entry

## Screenshots (if applicable)

Still shows alt + m in the keybinding panel
<img width="840" height="206" alt="image"
src="https://github.com/user-attachments/assets/f1906bc2-e7c2-4eac-b3ca-5a8a207cc93c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10528-fix-App-mode-workaround-for-alt-m-producing-alt-on-mac-32e6d73d36508176b7d6d918c5bb88f3)
by [Unito](https://www.unito.io)
2026-03-25 16:15:41 -07:00
Comfy Org PR Bot
c5e9e52e5f 1.43.5 (#10489)
Patch version increment to 1.43.5

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10489-1-43-5-32e6d73d365081c8aed2d65a4823a0c0)
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>
Co-authored-by: github-actions <github-actions@github.com>
v1.43.5
2026-03-25 14:55:05 -07:00
Christian Byrne
fa1ffcba01 fix: handle clipboard errors in Copy Image and useCopyToClipboard (#9299)
## Summary

Fix unhandled promise rejection ("Document is not focused") in Copy
Image and improve clipboard fallback reliability.

## Changes

- **What**: Two clipboard fixes:
1. `litegraphService.ts`: The "Copy Image" context menu passed async
`writeImage` as a callback to `canvas.toBlob()` without awaiting —
errors became unhandled promise rejections reported in [Sentry
CLOUD-FRONTEND-STAGING-AQ](https://comfy-org.sentry.io/issues/6948073569/).
Extracted `convertToPngBlob` helper that wraps `toBlob` in a proper
Promise so errors propagate to the existing outer try/catch and surface
as a user-facing toast instead of a silent Sentry error.
2. `useCopyToClipboard.ts`: Replaced `useClipboard({ legacy: true })`
with explicit modern→legacy fallback that checks
`document.execCommand('copy')` return value. VueUse's `legacyCopy` sets
`copied.value = true` regardless of whether `execCommand` succeeded,
causing false success toasts.

## Review Focus

- The `convertToPngBlob` helper does the same canvas→PNG work as the old
inline code but properly awaited
- The happy path (PNG clipboard write succeeds first try) is unchanged
- No public API surface changes — verified zero custom node dependencies
via ecosystem code search

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9299-fix-handle-clipboard-errors-in-Copy-Image-and-useCopyToClipboard-3156d73d3650817c8608cba861ee64a9)
by [Unito](https://www.unito.io)
2026-03-25 12:20:33 -07:00
Christian Byrne
f1db1122f3 fix: allow URI drops to bubble from Vue nodes to document handler (#9463)
## Summary

Fix URI drops (e.g. dragging `<img>` thumbnails) onto Vue-rendered nodes
by letting unhandled drops bubble to the document-level `text/uri-list`
fallback in `app.ts`.

## Changes

- **What**: Removed unconditional `.stop` modifier from `@drop` in
`LGraphNode.vue`. `stopPropagation()` is now called conditionally — only
when `onDragDrop` returns `true` (file drop handled). Made `handleDrop`
synchronous since `onDragDrop` returns a plain boolean.

## Review Focus

The key insight is that `onDragDrop` (from `useNodeDragAndDrop`) returns
`false` synchronously for URI drags (no files in `DataTransfer`), so the
event must bubble to reach the document handler that fetches the URI.
The original `async` + `await` pattern would have deferred
`stopPropagation` past the synchronous propagation phase, so
`handleDrop` is now synchronous.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9463-fix-allow-URI-drops-to-bubble-from-Vue-nodes-to-document-handler-31b6d73d36508196a1b3f17e7e4837a9)
by [Unito](https://www.unito.io)
2026-03-25 12:19:38 -07:00
Christian Byrne
f56abb3ecf test: add regression tests for subgraph slot label propagation (#10013)
## Summary

Add regression tests for subgraph slot label propagation. The
OutputSlot.vue fix (adding `slotData.label` to the display template) was
already merged via another PR — this adds tests to prevent future
regressions.

## Changes

- **What**: Two new test files covering the label/localized_name
fallback chain in OutputSlot.vue and SubgraphNode label propagation
through configure() and rename event paths.

## Review Focus

Tests only — no production code changes. Verifies that renamed subgraph
inputs/outputs display correctly in Nodes 2.0 mode.

Fixes #9998

<!-- Pipeline-Ticket: 7d887122-eea5-45f1-b6eb-aed94f708555 -->
2026-03-25 12:19:08 -07:00
Benjamin Lu
95c6811f59 fix: remove unused Playwright hook config args (#10513)
## Summary

Remove the unused `_config` parameter from the Playwright global
setup/teardown hooks and drop the now-unused `FullConfig` imports.

## Changes

- **What**: Simplified `browser_tests/globalSetup.ts` and
`browser_tests/globalTeardown.ts` to match actual usage.

## Review Focus

Verify that removing the unused hook argument does not change Playwright
behavior.

## Screenshots (if applicable)

N/A

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10513-fix-remove-unused-Playwright-hook-config-args-32e6d73d365081d59b63dbbca0596025)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-25 19:18:15 +00:00
Christian Byrne
88079250eb chore: add CI safety rules to backport-management skill (#10164)
Adds lessons learned from a bulk backport session where 69 PRs were
admin-merged without CI checks, shipping 3 test failures to core/1.41.

**Changes:**
- **SKILL.md**: CI Safety Rules section, wave verification with `pnpm
test:unit`, continuous backporting recommendation, Never Admin-Merge
Without CI lesson
- **execution.md**: Wait-for-CI step after automation, `gh pr checks
--watch` for manual cherry-picks, CI Failure Triage section with common
failure categories
- **logging.md**: Wave verification log template, CI failure report
table in session report

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10164-chore-add-CI-safety-rules-to-backport-management-skill-3266d73d365081aa856de1fb85a31887)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-03-25 12:17:43 -07:00
Alexander Brown
08ea013c51 fix: prune stale proxyWidgets referencing nodes removed by nested subgraph packing (#10390)
## Summary

Prune stale proxyWidgets entries that reference grandchild nodes no
longer present in the outer subgraph after nested packing.

## Changes

- **What**: Filter out proxyWidgets entries during hydration when the
source node doesn't exist in the subgraph. Also skip missing-node
entries in `_pruneStaleAliasFallbackEntries` as defense-in-depth. Write
back cleaned entries so stale data doesn't persist.

## Review Focus

The fix touches two codepaths in `SubgraphNode.ts`:
1. **Hydration** (`_internalConfigureAfterSlots`): Added `getNodeById`
guard before accepting a proxyWidget entry, and broadened the write-back
condition from legacy-only to any filtered entries.
2. **Runtime pruning** (`_pruneStaleAliasFallbackEntries`): Added
early-exit for entries whose source node no longer exists — previously
these survived because failed resolution returned `undefined` which
bypassed the concrete-key comparison.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10390-fix-prune-stale-proxyWidgets-referencing-nodes-removed-by-nested-subgraph-packing-32b6d73d365081e69eedcb2b67d7043d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-03-25 12:15:52 -07:00
Benjamin Lu
4aae52c2fc test: move getNodeDefs spec into src/scripts (#10503)
## Summary

Move the `getNodeDefs` unit test out of deprecated `tests-ui` and into
`src/scripts` so Vitest discovers and runs it.

## Changes

- **What**: Renamed `tests-ui/tests/scripts/app.getNodeDefs.test.ts` to
`src/scripts/app.getNodeDefs.test.ts`

## Review Focus

Confirm the spec now follows the colocated test convention and is
included by the existing Vitest `include` globs.

## Screenshots (if applicable)

N/A

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10503-test-move-getNodeDefs-spec-into-src-scripts-32e6d73d3650816f9211dc4c20daba4b)
by [Unito](https://www.unito.io)
2026-03-25 12:15:18 -07:00
Benjamin Lu
6b7691422b fix: use named dotenv config imports (#10514)
## Summary

Use named `dotenv` config imports where we were calling
`dotenv.config()` so ESLint and IDEs stop flagging
`import-x/no-named-as-default-member`.

## Changes

- **What**: Replace default `dotenv` imports plus `.config()` member
access with `import { config as dotenvConfig } from 'dotenv'` in browser
test setup/fixture files and the desktop Vite config.
- **What**: Keep behavior unchanged while aligning those files with the
cleaner import form already used elsewhere in the repo.

## Review Focus

This is a no-behavior-change cleanup. The issue was that `dotenv`
exposes `config` both as a named export and as a property on the
default-exported module object, so `import dotenv from 'dotenv';
dotenv.config()` triggers `import-x/no-named-as-default-member` even
though it works at runtime.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10514-fix-use-named-dotenv-config-imports-32e6d73d36508195b346dbcab764a6b8)
by [Unito](https://www.unito.io)
2026-03-25 12:10:20 -07:00
Christian Byrne
437f41c553 perf: add layout/GC metrics + reduce false positives in regression detection (#10477)
## Summary

Add layout duration, style recalc duration, and heap usage metrics to CI
perf reports, while improving statistical reliability to reduce false
positive regressions.

## Changes

- **What**:
- Collect `layoutDurationMs`, `styleRecalcDurationMs`, `heapUsedBytes`
(absolute snapshot) alongside existing metrics
- Add effect size gate (`minAbsDelta`) for integer-quantized count
metrics (style recalcs, layouts, DOM nodes, event listeners) — prevents
z=7.2 false positives from e.g. 11→12 style recalcs
- Switch from mean to **median** for PR metric aggregation — robust to
outlier CI runs that dominate n=3 mean
- Increase historical baseline window from **5 to 15 runs** for more
stable σ estimates
- Reorder reported metrics: layout/style duration first (actionable),
counts and heap after (informational)

## Review Focus

The effect size gate in `classifyChange()` — it now requires both z > 2
AND absolute delta ≥ `minAbsDelta` (when configured) to flag a
regression. This addresses the core false positive issue where integer
metrics with near-zero historical variance produce extreme z-scores for
trivial changes.

Median vs mean tradeoff: median is more robust to outliers but less
sensitive to real shifts — acceptable given n=3 and CI noise levels.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10477-perf-add-layout-GC-metrics-reduce-false-positives-in-regression-detection-32d6d73d365081daa72cec96d8a07b90)
by [Unito](https://www.unito.io)
2026-03-25 10:16:56 -07:00
ComfyUI Wiki
975393b48b fix: restore is_template tracking for app mode templates (#10252)
## Summary

App mode templates (names ending in `.app`, e.g.
`templates-qwen_multiangle.app`) were never counted as template
executions in Mixpanel because `getExecutionContext` used
`activeWorkflow.filename` for the `knownTemplateNames` lookup — but
`getFilenameDetails` treats `.app.json` as a compound extension and
strips it entirely, leaving `"templates-qwen_multiangle"` instead of
`"templates-qwen_multiangle.app"`. The set lookup always returned
`false`, so every execution was sent with `is_template: false`.

## Changes

- **Fix**: derive the template lookup key from
`fullFilename.replace(/\.json$/i, '')` instead of `filename`, which
preserves the `.app` suffix and correctly matches `knownTemplateNames`
- **Also fixes**: `workflow_name`, `getTemplateByName`, and
`getEnglishMetadata` calls in the same branch now use the corrected name
- **Tests**: three new cases in `MixpanelTelemetryProvider.test.ts` —
regular template, `.app` template (regression), and non-template

## Before / After

| Template name in index | `activeWorkflow.filename` | `fullFilename` →
stripped | `is_template` |
|---|---|---|---|
| `flux-dev` | `flux-dev` | `flux-dev` |  true |
| `templates-qwen_multiangle.app` | `templates-qwen_multiangle`  |
`templates-qwen_multiangle.app`  | fixed: true |

## Review Focus

The change is confined to `getExecutionContext.ts`. `fullFilename` is
always set (it is assigned in `UserFile` constructor from
`getPathDetails`), so no null-safety issue.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10252-fix-restore-is_template-tracking-for-app-mode-templates-3276d73d365081d4b998edc62ad010dc)
by [Unito](https://www.unito.io)
2026-03-25 21:31:09 +08:00
Luke Mino-Altherr
a44fa1fdd5 fix: tighten date detection regex in formatJsonValue() (#10110)
`formatJsonValue()` uses a loose regex `^\d{4}-\d{2}-\d{2}` to detect
date-like strings, which matches non-date strings like
`"2024-01-01-beta"`.

Changes:
- Require ISO 8601 `T` separator: `/^\d{4}-\d{2}-\d{2}T/`
- Validate parse result with `!Number.isNaN(date.getTime())`
- Use `d()` i18n formatter for consistency with `formatDate()` in the
same file
2026-03-24 19:46:58 -07:00
Christian Byrne
cc3acebceb feat: scaffold Astro 5 website app + design-system base.css [1/3] (#10140)
## Summary
Scaffolds the new apps/website/ Astro 5 + Vue 3 marketing site inside
the monorepo.

## Changes
- apps/website/ with package.json, astro.config.mjs, tsconfig, Nx
targets
- @comfyorg/design-system/css/base.css — brand tokens + fonts (no
PrimeVue)
- pnpm-workspace.yaml catalog entries for Astro deps
- .gitignore and env.d.ts for Astro

## Stack (via Graphite)
- **[1/3] Scaffold** ← this PR
- #10141 [2/3] Layout Shell
- #10142 [3/3] Homepage Sections

Part of the comfy.org website refresh (replacing Framer).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10140-feat-scaffold-Astro-5-website-app-design-system-base-css-1-3-3266d73d365081688dcee0220a03eca4)
by [Unito](https://www.unito.io)
2026-03-24 19:02:10 -07:00
Alexander Brown
23c22e4c52 🧙 feat: wire ComfyHub publish wizard with profile gate, asset upload, and submission (#10128)
## Summary 🎯

Wire the ComfyHub publish flow end-to-end: profile gate, multi-step
wizard (describe, examples, finish), asset upload, and workflow
submission via hub API.

> *A wizard of steps, from describe to the end,*
> *Upload your assets, your workflows you'll send!*
> *With tags neatly slugged and thumbnails in place,*
> *Your ComfyHub publish is ready to race!* 🏁

## Changes 🔧

- 🌐 **Hub Service** — `comfyHubService` for profile CRUD, presigned
asset uploads, and workflow publish
- 📦 **Submission** — `useComfyHubPublishSubmission` orchestrates file
uploads → publish in one flow
- 🧙 **Wizard Steps** — Describe (name/description/tags) → Examples
(drag-drop reorderable images) → Thumbnail → Finish (profile card +
private-asset warnings)
- 🖼️ **ReorderableExampleImage** — Drag-drop *and* keyboard reordering,
accessible and fun
- 🏷️ **Tag Normalization** — `normalizeTags` slugifies before publishing
- 🔄 **Re-publish Prefill** — Fetches hub workflow metadata on
re-publish, with in-memory cache fallback
- 📐 **Schema Split** — Publish-record schema separated from
hub-workflow-metadata schema
- 🙈 **Unlisted Skip** — No hub-detail prefill fetch for unlisted records
- 👤 **Profile Gate** — Username validation in `useComfyHubProfileGate`
- 🧪 **Tests Galore** — New suites for DescribeStep, ExamplesStep,
WizardContent, PublishSubmission, comfyHubService, normalizeTags, plus
expanded PublishDialog & workflowShareService coverage

## Review Focus 🔍

> *Check the service, the schema, the Zod validation too,*
> *The upload orchestration — does it carry things through?*
> *The prefill fetch strategy: status → detail → cache,*
> *And drag-drop reordering — is it keeping its place?* 🤔

- 🌐 `comfyHubService.ts` — API contract shape, error handling, Zod
parsing
- 📦 `useComfyHubPublishSubmission.ts` — Upload-then-publish flow, edge
cases (no profile, no workflow)
- 🗂️ `ComfyHubPublishDialog.vue` — Prefill fetch strategy (publish
status → hub detail → cache)
- 🖼️ `ReorderableExampleImage.vue` — Drag-drop + keyboard a11y

## Testing 🧪

```bash
pnpm test:unit -- src/platform/workflow/sharing/
pnpm typecheck
```

> *If the tests all turn green and the types all align,*
> *Then merge it on in — this publish flow's fine!* 

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Dante <bunggl@naver.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: bymyself <cbyrne@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2026-03-25 09:30:25 +09:00
Comfy Org PR Bot
88b54c6775 1.43.4 (#10411)
Patch version increment to 1.43.4

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10411-1-43-4-32d6d73d365081659c13eb6896c5ed96)
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.4
2026-03-24 16:34:02 -07:00
Christian Byrne
e40995fb6c fix: restore Firebase getAdditionalUserInfo for sign-up telemetry OR logic (#10453)
## Summary

Restores `getAdditionalUserInfo` from Firebase Auth so sign-up telemetry
fires when *either* Firebase or the UI context identifies a new user,
fixing a regression from #10388.

## Changes

- **What**: In `loginWithGoogle` and `loginWithGithub`, call
`getAdditionalUserInfo(result)` and OR it with the UI-provided
`options?.isNewUser` flag: `is_new_user: options?.isNewUser ||
additionalUserInfo?.isNewUser || false`. Added 8 parameterized unit
tests covering the OR truth table (Firebase true, UI true, both false,
null result).

## Review Focus

The OR semantics: if either source says new user, we send `sign_up`
telemetry. Previously only the UI flag was checked, which missed cases
where the user lands directly on the OAuth provider without going
through the sign-up view.

## Testing

Unit tests cover all branches of the OR logic. An e2e test is not
feasible here because it would require completing a real OAuth flow with
Google/GitHub (interactive popup, valid credentials, CAPTCHA) and
intercepting the resulting `getAdditionalUserInfo` response from
Firebase — none of which can be reliably automated in a headless
Playwright environment without a live Firebase project seeded with
disposable accounts.

Fixes #10447
2026-03-24 12:39:48 -07:00
Christian Byrne
908a3ea418 fix: resolve subgraph node slot link misalignment during workflow load (#9121)
## Summary

Fix subgraph node slot connector links appearing misaligned after
workflow load, caused by a transform desync between LiteGraph's internal
canvas transform and the Vue TransformPane's CSS transform.

## Changes

- **What**: Changed `syncNodeSlotLayoutsFromDOM` to use DOM-relative
measurement (slot position relative to its parent `[data-node-id]`
element) instead of absolute canvas-space conversion via
`clientPosToCanvasPos`. This makes the slot offset calculation
independent of the global canvas transform, eliminating the frame-lag
desync that occurred when `fitView()` updated `lgCanvas.ds` before the
Vue CSS transform caught up.
- **Cleanup**: Removed the unreachable fallback path that still used
`clientPosToCanvasPos` when the parent node element wasn't found (every
slot element is necessarily a child of a `[data-node-id]` element — if
`closest()` fails the element is detached and measuring is meaningless).
This also removed the `conv` parameter from `syncNodeSlotLayoutsFromDOM`
and `flushScheduledSlotLayoutSync`, and the
`useSharedCanvasPositionConversion` import.
- **Test**: Added a Playwright browser test that loads a subgraph
workflow with `workflowRendererVersion: "LG"` (triggering the 1.2x scale
in `ensureCorrectLayoutScale`) as a template (triggering `fitView`), and
verifies all slot connector positions are within bounds of their parent
node element.

## Review Focus

- The core change is in `useSlotElementTracking.ts` — the new
measurement approach uses `getBoundingClientRect()` on both the slot and
its parent node element, then divides by `currentScale` to get
canvas-space offsets. This is simpler and more robust than the previous
approach.
- SubgraphNodes were disproportionately affected because they are
relatively static and don't often trigger `ResizeObserver`-based
re-syncs that would eventually correct stale offsets.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9121-fix-resolve-subgraph-node-slot-link-misalignment-during-workflow-load-3106d73d365081eca413c84f2e0571d6)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <448862+DrJKL@users.noreply.github.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-24 12:28:12 -07:00
AustinMroz
0ae4b78cbc Use native context menu for focused textareas (#10454)
The custom context menu provided by the frontend exposes widget specific
options. In order to support renaming, promotion, and favoriting, there
needs to be a way to access this context menu when targeting a textarea.
However, always displaying this custom context menu will cause the user
to lose access to browser specific functionality like spell checking,
translation, and the ability to copy paste text.

This PR updates the behaviour so that the native browser context menu
will display when the text area already has focus. Our custom frontend
context menu will continue to display when it does not.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10454-Use-native-context-menu-for-focused-textareas-32d6d73d365081909673d81d6a6ba054)
by [Unito](https://www.unito.io)
2026-03-24 12:25:35 -07:00
Christian Byrne
5f14276159 [feat] Improve group title layout (#9839)
Rebased and adopted from #5774 by @felixturner.

## Changes
- Remove unused font-size properties (`NODE_TEXT_SIZE`,
`NODE_SUBTEXT_SIZE`, `DEFAULT_GROUP_FONT`) from theme palettes and color
palette schema
- Replace `DEFAULT_GROUP_FONT`/`DEFAULT_GROUP_FONT_SIZE` with a single
`GROUP_TEXT_SIZE = 20` constant (reduced from 24px)
- Use `NODE_TITLE_HEIGHT` for group header height instead of `font_size
* 1.4`
- Vertically center group title text using `textBaseline = 'middle'`
- Use `GROUP_TEXT_SIZE` directly in TitleEditor instead of per-group
`font_size`
- Remove `font_size` from group serialization (no longer per-group
configurable)

## Original PR
Closes #5774

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9839-feat-Improve-group-title-layout-3216d73d36508112a0edc2a370af20ba)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Felix Turner <felixturner@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-24 12:25:18 -07:00
Christian Byrne
df81c66725 fix: prevent blueprint cache corruption on repeated placement (#9897)
Add `structuredClone()` in `getBlueprint()` so `_deserializeItems()`
mutations (node IDs, subgraph UUIDs) don't corrupt cached data.

Includes regression test verifying cache immutability.

Fixes COM-16026

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9897-fix-prevent-blueprint-cache-corruption-on-repeated-placement-3226d73d3650815c8594fa8d266e680f)
by [Unito](https://www.unito.io)
2026-03-24 12:24:41 -07:00
Christian Byrne
c5ad6cf1aa fix: migrate V1 tab state pointers during V1→V2 draft migration (#10007)
## Problem

When upgrading from V1 to V2 draft persistence, users' open workflow
tabs were lost. V1 stored tab state (open paths + active index) in
localStorage via `setStorageValue` fallback, but the V1→V2 migration
only migrated draft payloads — not these tab state pointers.

This meant that after upgrading, all previously open tabs disappeared
and users had to manually reopen their workflows.

## Solution

Add `migrateV1TabState()` to the V1→V2 migration path. After draft
payloads are migrated, the function reads the V1 localStorage keys
(`Comfy.OpenWorkflowsPaths` and `Comfy.ActiveWorkflowIndex`) and writes
them to V2's sessionStorage format via `writeOpenPaths()`.

The `clientId` is threaded from `useWorkflowPersistenceV2` (which has
access to `api.clientId`) through to `migrateV1toV2()`.

## Changes

- **`migrateV1toV2.ts`**: Added `migrateV1TabState()` + V1 key constants
for tab state
- **`useWorkflowPersistenceV2.ts`**: Pass `api.clientId` to migration
call
- **`migrateV1toV2.test.ts`**: Two new tests proving tab state migration
works

## Testing

TDD approach — RED commit shows the test failing, GREEN commit shows it
passing.

All 123 persistence tests pass.

- Fixes #9974

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10007-fix-migrate-V1-tab-state-pointers-during-V1-V2-draft-migration-3256d73d36508103b619e521c1b603f5)
by [Unito](https://www.unito.io)
2026-03-24 12:23:59 -07:00
Terry Jia
fe1fae4de1 fix: disable preload error toast triggered by third-party plugin failures (#10445)
## Summary
The preload error toast fires whenever any custom node extension fails
to load via dynamic `import()`. In practice, this is almost always
caused by third-party plugin bugs rather than ComfyUI core issues.
Common triggers include:

- Bare module specifiers (e.g., `import from "vue"`) that the browser
cannot resolve without an import map
- Incorrect relative paths to `scripts/app.js` due to nested web
directory structures
  - Missing dependencies on other extensions (e.g., `clipspace.js`)

Since many users have multiple custom nodes installed, the toast
frequently appears on startup — sometimes multiple times — with a
generic message that offers no actionable guidance. This creates
unnecessary alarm and support burden.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10445-fix-disable-preload-error-toast-triggered-by-third-party-plugin-failures-32d6d73d365081f281efcd6fe90642a5)
by [Unito](https://www.unito.io)
2026-03-24 13:34:34 -04:00
Johnpaul Chiwetelu
38aad02fb9 test: add e2e tests for all default keybindings (#10440)
## Summary
- Add 21 Playwright E2E tests covering all previously untested default
keybindings
- Add `isReadOnly()` and `getOffset()` helpers to `CanvasHelper`

### Tests added
| Group | Keybindings | Count |
|-------|------------|-------|
| Sidebar toggles | `w`, `n`, `m`, `a` | 4 |
| Canvas controls | `Alt+=`, `Alt+-`, `.`, `h`, `v` | 5 |
| Node state | `Alt+c`, `Ctrl+m` | 2 |
| Mode/panel toggles | `Alt+m`, `Alt+Shift+m`, `` Ctrl+` `` | 3 |
| Queue/execution | `Ctrl+Enter`, `Ctrl+Shift+Enter`, `Ctrl+Alt+Enter` |
3 |
| File operations | `Ctrl+s`, `Ctrl+o` | 2 |
| Graph operations | `Ctrl+Shift+e`, `r` | 2 |

## Test plan
- [x] `pnpm exec playwright test defaultKeybindings.spec.ts` — 21/21
pass
- [x] `pnpm typecheck:browser` — clean
- [x] `pnpm lint` — clean

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10440-test-add-e2e-tests-for-all-default-keybindings-32d6d73d36508124a510d4742f9fbdbb)
by [Unito](https://www.unito.io)
2026-03-24 16:12:59 +01:00
pythongosssss
7c4234cf93 feat: App mode - add execution status messages (#10369)
## Summary

Adds custom status messages that are shown under the previews in order
to provide additional progress feedback to the user

Nodes matching the words:

Save, Preview -> Saving
Load, Loader -> Loading
Encode -> Encoding
Decode -> Decoding
Compile, Conditioning, Merge, -> Processing
Upscale, Resize -> Resizing
ToVideo -> Generating video

Specific nodes:
KSampler, KSamplerAdvanced, SamplerCustom, SamplerCustomAdvanced ->
Generating
Video Slice, GetVideoComponents, CreateVideo -> Processing video
TrainLoraNode -> Training


## Changes

- **What**: 
- add specific node lookups for non-easily matchable patterns
- add regex based matching for common patterns
- show on both latent preview & skeleton preview 
- allow app mode workflow authors to override status with custom
property `Execution Message` (no UI for doing this)

## Review Focus

This is purely pattern/lookup based, in future we could update the
backend node schema to allow nodes to define their own status key.

## Screenshots (if applicable)

<img width="757" height="461" alt="image"
src="https://github.com/user-attachments/assets/2b32cc54-c4e7-4aeb-912d-b39ac8428be7"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10369-feat-App-mode-add-execution-status-messages-32a6d73d3650814e8ca2da5eb33f3b65)
by [Unito](https://www.unito.io)

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-03-24 07:58:35 -07:00
jaeone94
4bfc730a0e fix: restore workflow tabs on browser restart (#10336)
## Problem

Since PR #8520 (`feat(persistence): fix QuotaExceededError and
cross-workspace draft leakage`), all workflow tabs are lost when the
browser is closed and reopened.

PR #8520 moved tab pointers (`ActivePath`, `OpenPaths`) from
`localStorage` to `sessionStorage` for per-tab isolation. However,
`sessionStorage` is cleared when the browser closes, so the open tab
list is lost on restart. The draft data itself survives in
`localStorage` — only the pointers to which tabs were open are lost.

Reported in
[Comfy-Org/ComfyUI#12984](https://github.com/Comfy-Org/ComfyUI/issues/12984).
Confirmed via binary search: v1.40.9 (last good) → v1.40.10 (first bad).

## Changes

Dual-write tab pointers to both storage layers:

- **sessionStorage** (scoped by `clientId`) — used for in-session
refresh, preserves per-tab isolation
- **localStorage** (scoped by `workspaceId`) — fallback for browser
restart when sessionStorage is empty

Also adds:
- `storageAvailable` guard on write functions for consistency with
`writeIndex`/`writePayload`
- `isValidPointer` validation on localStorage reads to reject stale or
malformed data

## Benefits

- Workflow tabs survive browser restart (restores V1 behavior)
- Per-tab isolation is preserved for in-session use (sessionStorage is
still preferred when available)

## Trade-offs

- On browser restart, the restored tabs come from whichever browser tab
wrote last to localStorage. If Tab A had workflows 1,2,3 and Tab B had
4,5 — the user gets whichever tab wrote most recently. This is the same
limitation V1 had with `Comfy.OpenWorkflowsPaths` in localStorage.
- Previously (post-#8520), opening a new browser tab would only restore
the single most recent draft. With this fix, a new tab restores the full
set of open tabs from the last session. This may be surprising for
multi-tab users who expect a clean slate in new tabs.

## Test plan

- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] All 121 persistence tests pass
- [x] Manual: open multiple workflow tabs → close browser → reopen →
tabs restored
- [x] Manual: open two browser tabs with different workflows → refresh
each → correct tabs in each

Fixes Comfy-Org/ComfyUI#12984

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10336-fix-restore-workflow-tabs-on-browser-restart-3296d73d365081b7a7d3e91427d08d17)
by [Unito](https://www.unito.io)
<!-- QA_REPORT_SECTION -->
---
## 🔍 Automated QA Report

| | |
|---|---|
| **Status** |  Complete |
| **Report** |
[sno-qa-10336.comfy-qa.pages.dev](https://sno-qa-10336.comfy-qa.pages.dev/)
|
| **CI Run** | [View
workflow](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/23373697656)
|

Before/after video recordings with **Behavior Changes** and **Timeline
Comparison** tables.
2026-03-24 17:30:36 +09:00
Yourz
001916edf6 refactor: clean up essentials node organization logic (#10433)
## Summary

Refactor essentials tab node organization to eliminate duplicated logic
and restrict essentials to core nodes only.

## Changes

- **What**: 
- Extract `resolveEssentialsCategory` to centralize category resolution
(was duplicated between filter and pathExtractor).
- Add `isCoreNode` guard so third-party nodes never appear in
essentials.
- Replace `indexOf`-based sorting with precomputed rank maps
(`ESSENTIALS_CATEGORY_RANK`, `ESSENTIALS_NODE_RANK`).

<img width="589" height="769" alt="image"
src="https://github.com/user-attachments/assets/66f41f35-aef5-4e12-97d5-0f33baf0ac45"
/>


## Review Focus

- The `isCoreNode` guard in `resolveEssentialsCategory` — ensures only
core nodes can appear in essentials even if a custom node sets
`essentials_category`.
- Rank map precomputation vs previous `indexOf` — functionally
equivalent but O(1) lookup.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10433-refactor-clean-up-essentials-node-organization-logic-32d6d73d36508193a4d1f7f9c18fcef7)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-03-24 16:22:09 +08:00
jaeone94
66daa6d645 refactor: error system cleanup — store separation, DDD fix, test improvements (#10302)
## Summary

Refactors the error system to improve separation of concerns, fix DDD
layer violations, and address code quality issues.

- Extract `missingNodesErrorStore` from `executionErrorStore`, removing
the delegation pattern that coupled missing-node logic into the
execution error store
- Extract `useNodeErrorFlagSync` composable for node error flag
reconciliation (previously inlined)
- Extract `useErrorClearingHooks` composable with explicit callback
cleanup on node removal
- Extract `useErrorActions` composable to deduplicate telemetry+command
patterns across error card components
- Move `getCnrIdFromNode`/`getCnrIdFromProperties` to
`platform/nodeReplacement` layer (DDD fix)
- Move `missingNodesErrorStore` to `platform/nodeReplacement` (DDD
alignment)
- Add unmount cancellation guard to `useErrorReport` async `onMounted`
- Return watch stop handle from `useNodeErrorFlagSync`
- Add `asyncResolvedIds` eviction on `missingNodesError` reset
- Add `console.warn` to silent catch blocks and empty array guard
- Hoist `useCommandStore` to setup scope, fix floating promises
- Add `data-testid` to error groups, image/video error spans, copy
button
- Update E2E tests to use scoped locators and testids
- Add unit tests for `onNodeRemoved` restoration and double-install
guard

Fixes #9875, Fixes #10027, Fixes #10033, Fixes #10085

## Test plan

- [x] Existing unit tests pass with updated imports and mocks
- [x] New unit tests for `useErrorClearingHooks` (callback restoration,
double-install guard)
- [x] E2E tests updated to use scoped locators and `data-testid`
- [ ] Manual: verify error tab shows runtime errors and missing nodes
correctly
- [ ] Manual: verify "Find on GitHub", "Copy", and "Get Help" buttons
work in error cards

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10302-refactor-error-system-cleanup-store-separation-DDD-fix-test-improvements-3286d73d365081838279d045b8dd957a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-03-24 16:43:22 +09:00
jaeone94
32a53eeaee fix: sync advanced inputs button color with node header (#10427)
## Summary
- The "Show Advanced Inputs" footer button was missing `headerColor`
style binding, causing it to not sync with the node header color (unlike
the "Enter Subgraph" button which already had it)
- Extracted the repeated `{ backgroundColor: headerColor }` inline style
(4 occurrences) into a `headerColorStyle` computed

## Screenshots 
before 
<img width="211" height="286" alt="스크린샷 2026-03-24 154312"
src="https://github.com/user-attachments/assets/edfd9480-04fa-4cd4-813d-a95adffbe2d3"
/>

after 
<img width="261" height="333" alt="스크린샷 2026-03-24 154622"
src="https://github.com/user-attachments/assets/eab28717-889e-4a6b-8775-bfc08fa727ff"
/>

## Test plan
- [x] Set a custom color on a node with advanced inputs and verify the
footer button matches the header color
- [x] Verify subgraph enter button still syncs correctly
- [x] Verify dual-tab layouts (error + advanced, error + subgraph) both
show correct colors

### Why no E2E test
Node header color is applied as an inline style via `headerColor` prop,
which is already passed and tested through the existing subgraph enter
button path. This change simply extends the same binding to the advanced
inputs buttons — no new data flow or interaction is introduced, so a
screenshot-based E2E test would add maintenance cost without meaningful
regression coverage.
2026-03-24 16:00:55 +09:00
Jin Yi
7c6ab19484 fix: manager progress toast and install button UX issues (#10423)
## Changes

- Reset `isRestartCompleted` in `closeToast()` so the "Apply Changes"
button appears correctly on subsequent installs instead of skipping to
the success message
- Add `@click.stop` on `PackInstallButton` to prevent click from
bubbling up to card selection, which was unintentionally opening the
right info panel

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10423-fix-manager-progress-toast-and-install-button-UX-issues-32d6d73d365081eba2c6d5b9b35ab26e)
by [Unito](https://www.unito.io)
2026-03-23 21:57:16 -07:00
Dante
8f9fe3b21e refactor: rebuild SingleSelect and MultiSelect with Reka UI (#9742)
## Summary
- Rebuild `SingleSelect` with Reka UI Select primitives (SelectRoot,
SelectTrigger, SelectContent, SelectItem)
- Rebuild `MultiSelect` with Reka UI Popover + Listbox (PopoverRoot,
ListboxRoot with `multiple`)
- Remove PrimeVue dependency from both components
- Preserve existing API contract for all consumers

## TODO
- [x] Re-evaluate MultiSelect implementation (ComboboxRoot with
`multiple` may be cleaner than Popover+Listbox)
- [x] E2E verification in actual app (AssetFilterBar,
WorkflowTemplateSelectorDialog, etc.)

## Test plan
- [x] Storybook visual verification for all SingleSelect/MultiSelect
stories
- [x] Keyboard navigation (arrow keys, Enter, Escape, typeahead)
- [x] Multi-selection with badge count
- [x] Search filtering in MultiSelect
- [x] Clear all functionality
- [x] Disabled state
- [x] Invalid state (SingleSelect)
- [x] Loading state (SingleSelect)

## screenshot
<img width="519" height="475" alt="스크린샷 2026-03-20 오전 12 12 37"
src="https://github.com/user-attachments/assets/ffc7f0b0-c88c-486b-a253-73a4da73c1de"
/>
<img width="842" height="554" alt="스크린샷 2026-03-20 오전 12 23 51"
src="https://github.com/user-attachments/assets/410551d4-c843-4898-b305-13a6ad6978ca"
/>

## video


https://github.com/user-attachments/assets/2fc3a9b9-2671-4c2c-9f54-4f83598afb53



Fixes #9700

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9742-refactor-rebuild-SingleSelect-and-MultiSelect-with-Reka-UI-3206d73d36508113bee2cf160c8f2d50)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-24 04:39:20 +00:00
Dante
4411d9a417 refactor: extract shared click-vs-drag guard utility (#10357)
## Summary

Extract duplicated click-vs-drag detection logic into a shared
`useClickDragGuard` composable and `exceedsClickThreshold` pure utility
function.

## Changes

- **What**: New `useClickDragGuard(threshold)` composable in
`src/composables/useClickDragGuard.ts` that stores pointer start
position and checks squared distance against a threshold. Also exports
`exceedsClickThreshold` for non-Vue contexts.
- Migrated `DropZone.vue`, `useNodePointerInteractions.ts`, and
`Load3d.ts` to use the shared utility
- `CanvasPointer.ts` left as-is (LiteGraph internal)
- All consumers now use squared-distance comparison (no `Math.sqrt` or
per-axis `Math.abs`)

## Review Focus

- The composable uses plain `let` state instead of `ref` since
reactivity is not needed for the start position
- `Load3d.ts` uses the pure `exceedsClickThreshold` function directly
since it is a class, not a Vue component
- Threshold values preserved per-consumer: DropZone=5,
useNodePointerInteractions=3, Load3d=5

Fixes #10356

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10357-refactor-extract-shared-click-vs-drag-guard-utility-32a6d73d3650816e83f5cb89872fb184)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-24 12:27:42 +09:00