Commit Graph

7704 Commits

Author SHA1 Message Date
Kelly Yang
4a9001f675 test: add E2E tests for image crop widget Levels 4-7 (#11571)
## Summary

Adds 12 Playwright E2E tests for the `ImageCropV2` widget covering
aspect ratio selection, lock/unlock behavior, constrained resize, and
BoundingBox numeric input — all of which had zero test coverage.

## Changes

**Level 4 — Aspect Ratio Selection** (`with source image after
execution`)
- Selecting 16:9 preset adjusts crop height proportionally via
`applyLockedRatio`
- Selecting Custom unlocks the ratio and restores all 8 resize handles

**Level 5 — Lock/Unlock** (`without source image` + `with source image
after execution`)
- Selecting a preset auto-enables the lock (aria-label changes to
"Unlock aspect ratio")
- Unlocking after a preset reverts the dropdown display to "Custom"
- Full lock→unlock round-trip verifies handle count (4 → 8) and
aria-label on both transitions

**Level 6 — Constrained Resize** (`with source image after execution`)
- NW corner drag grows origin (x, y decrease) and dimensions while
maintaining ratio
- SE corner drag beyond image edge clamps to boundary
- NW corner drag beyond (0, 0) clamps x/y to image boundary
- Inward SE corner drag enforces `MIN_CROP_SIZE` (16px minimum)

**Level 7 — BoundingBox Numeric Input** (`with source image after
execution`)
- X increment button increments crop x by 1
- Width increment button increments crop width by 1
- BoundingBox inputs reflect updated position after a drag

No source code was modified.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: changes are limited to Playwright E2E coverage plus adding
`data-testid` attributes to BoundingBox inputs, with no behavioral logic
changes expected.
> 
> **Overview**
> **Expands E2E coverage for the `ImageCropV2` widget** by adding new
Playwright tests for ratio preset selection, lock/unlock behavior,
constrained resizing (including boundary clamping and min size), and
BoundingBox numeric input updates.
> 
> **Improves testability of BoundingBox controls** by adding
`data-testid` attributes to the `WidgetBoundingBox.vue`
`ScrubableNumberInput` fields (`x`, `y`, `width`, `height`) so E2E tests
can target increment/input elements reliably.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b008f42942. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11571-test-add-E2E-tests-for-image-crop-widget-Levels-4-7-34b6d73d365081c79118ca9ae08f291c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-25 00:17:12 -04:00
Dante
9cf035879f test: add WidgetCurve unit tests (#11469)
## Summary

Splits the WidgetCurve test coverage out of #11446 so this widget can be
reviewed independently.

## Changes

- **What**: Adds WidgetCurve unit tests covering point forwarding,
interpolation updates, disabled-state behavior, and upstream value
handling.

## Review Focus

Focused test-only PR extracted from #11446.
Validated with `pnpm test:unit -- --run
src/components/curve/WidgetCurve.test.ts`.

## Screenshots (if applicable)

N/A

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11469-test-add-WidgetCurve-unit-tests-3486d73d365081c2a68bc8403fa0265f)
by [Unito](https://www.unito.io)
2026-04-24 17:47:54 -07:00
Alexander Brown
d0e9984a73 feat: update BYOKeySection images to enterprise node WebPs (#11614)
Update BYOKeySection card images from placeholder API logos to dedicated
enterprise node WebP images hosted on media.comfy.org.

## Changes
- Replace `logo-purple.webp` and `logo-yellow.webp` with
`enterprise_node_1.webp` and `enterprise_node_2.webp`
- New images are near-lossless WebP (~70% smaller than source PNGs)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11614-feat-update-BYOKeySection-images-to-enterprise-node-WebPs-34c6d73d365081239d92c649eb563b7e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-04-24 20:03:21 +00:00
pythongosssss
125c11b3d0 fix: translate blueprint label (#11573)
## Summary

Fixes hardcoded "Blueprint" text and adds e2e coverage to test
visibility, resolving #11473

## Changes

- **What**: 
- add translation
- add test

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11573-fix-translate-blueprint-label-34b6d73d365081009215e06be6aa1fa0)
by [Unito](https://www.unito.io)
2026-04-24 19:27:47 +00:00
Alexander Brown
725ed120e8 fix: disable parallax on mobile to prevent enterprise section overlap (#11609)
## Summary

Disable parallax on mobile in the enterprise DataOwnershipSection to
prevent images from overlapping the next section.

## Changes

- **What**: Add `mediaQuery` option to `useParallax` composable, using
GSAP's `matchMedia()` to create/revert animations responsively.
DataOwnershipSection now only applies parallax at the `lg` (1024px+)
breakpoint.

## Review Focus

GSAP `matchMedia` automatically reverts animations (resetting
transforms) when the query stops matching, so no manual cleanup is
needed on resize.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11609-fix-disable-parallax-on-mobile-to-prevent-enterprise-section-overlap-34c6d73d365081a48d55e4cf880e3bab)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-04-24 12:13:00 -07:00
Alexander Brown
453a0edd1e chore: refresh Ashby careers snapshot (#11611)
## Changes

Refreshes the Ashby careers snapshot (`ashby-roles.snapshot.json`).

The "Business" department has been renamed to "Operations" on Ashby's
side. The same 3 roles (Senior Technical Recruiter, BizOps Strategist,
Founding Customer Success Manager) now appear under "Operations".

## Testing

- Snapshot was generated via `pnpm --filter @comfyorg/website
ashby:refresh-snapshot` which validates the API response through Zod
schemas before writing.
- Lint-staged checks (typecheck, eslint, oxlint, oxfmt) passed on
commit.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11611-chore-refresh-Ashby-careers-snapshot-34c6d73d3650813ea289c3c0371f882b)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-04-24 12:11:13 -07:00
Christian Byrne
5a8ded7959 Website: pull careers page listing from ashby API (#11590)
Careers

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-04-24 11:51:43 -07:00
Alexander Brown
bb23b9352c fix: update enterprise hero SVG to match updated design (#11608)
## Changes

Update the enterprise hero SVG in `HeroSection.vue` to match the updated
design export.

### Key changes

- **viewBox**: `600 -50 1000 1100` → `0 0 1600 1046` with `clip-path`
wrapper and background rects for proper clipping
- **Block pieces**: stroke/stroke-width moved from parent `<g>` to each
individual `<path>`, with duplicated paths for layered rendering
- **Block cluster**: wrapped in `.block-cluster` group with its own CSS
transform-origin
- **Ripple delay classes**: renamed `ripple-delay-*` → `delay-*`
- **Fade overlay**: added `pointer-events: none` to prevent blocking
interactions

Co-authored-by: Amp <amp@ampcode.com>
2026-04-24 10:35:24 -07:00
Alexander Brown
25f0b41f63 Remember what was forgotten (#11603)
## Summary

Every page has a story to tell.

## Changes

- **What**: Something was missing. Now it isn't.

## Review Focus

Look closely at what was lost.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11603-Remember-what-was-forgotten-34c6d73d36508184b3cef39d0be4a3bd)
by [Unito](https://www.unito.io)
2026-04-24 10:28:32 -07:00
Alexander Brown
e7673fcca7 update: API page links to keys and docs (#11606)
## Summary

Update API page CTA buttons to link directly to the API keys page and
API docs instead of generic platform/cloud/docs links.

## Changes

- **What**: Point "Get API Keys" buttons in HeroSection and StepsSection
to `platform.comfy.org/profile/api-keys`; point "Read the Docs" button
to `docsApi` route; add `apiKeys` entry to `externalLinks` config.

## Review Focus

Straightforward link target updates — verify the new URLs are correct.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11606-update-API-page-links-to-keys-and-docs-34c6d73d365081268bb5dd55c33646c8)
by [Unito](https://www.unito.io)
2026-04-24 10:28:03 -07:00
Yourz
7038cab926 update: replace placeholder images in API automation section (#11602)
## Changes

- Replace placeholder images in API automation section with final assets
  - Feature 1: `desert.webp` → `precision-tools.webp`
  - Feature 3: `free.webp` → `infrastructure-nodes.webp`


<img width="1000" height="552" alt="Kapture 2026-04-25 at 00 54 23"
src="https://github.com/user-attachments/assets/dd503d2f-56c3-4346-adfa-27b3b92a04a8"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11602-update-replace-placeholder-images-in-API-automation-section-34c6d73d365081319ca4db9b60099835)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-04-24 16:02:36 +00:00
Yourz
1f888de0f6 update: content in Price page (#11600)
## Summary

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

Update wrong content in Price page of new website

## Changes

- **What**: <!-- Core functionality added/modified -->
- update both English and Chinese content of translate key
`pricing.included.feature2.description`

## 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 -->
<img width="1046" height="147" alt="image"
src="https://github.com/user-attachments/assets/0f2dc66c-d384-4fb4-85cb-2d9f68469dbc"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11600-update-content-in-Price-page-34c6d73d365081bb9ae1e09842c77249)
by [Unito](https://www.unito.io)
2026-04-24 15:59:18 +00:00
Yourz
b7a8056ab0 update: local hero illustration stroke colors and fix overflow clipping (#11601)
## Changes

- Update SVG stroke color from `#7E7C78` to `#4D3762` to match design
spec
- Reduce hex node stroke width from 6 to 3
- Fix illustration bottom edge being hard-clipped by removing
`lg:overflow-y-clip` from section
- Adjust container aspect ratio to match SVG viewBox proportions


<img width="1000" height="1287" alt="Kapture 2026-04-25 at 00 41 16"
src="https://github.com/user-attachments/assets/a3a14fba-74b3-4051-ac12-c175c4b3bd61"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11601-update-local-hero-illustration-stroke-colors-and-fix-overflow-clipping-34c6d73d3650811e9f0bdc1a3274800e)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-04-24 15:57:11 +00:00
pythongosssss
40fec7882c chore: remove unused glslUtils helpers (#11597)
## Summary

These look to have been added as part of the initial phased
implementation to align with the backend code, however they are unused:
- `detectOutputCount` - The frontend only shows a single output preview
which imo is fine, we can add multi display in future if required
- `hasVersionDirective` - The backend needs this as it can execute other
version shaders, the web browser will automatically validate and throw
an error if it is not a valid shader, this gets surfaced to the user
already.

## Changes

- **What**:  
- remove unused functions & tests

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11597-chore-remove-unused-glslUtils-helpers-34c6d73d365081eab784d205b8a0053e)
by [Unito](https://www.unito.io)
2026-04-24 10:52:45 +00:00
jaeone94
17d980dbc8 feat: add inline-CTA nightly survey for error panel (#11591)
## Summary

Adds an inline-CTA Typeform survey for the redesigned error panel,
targeting nightly localhost users. Reuses the existing node-search
survey infrastructure rather than introducing a parallel stack.

## Changes

- **What**:
- `surveyRegistry` gains optional `presentation: 'floating' |
'inline-cta'` and a `getFloatingSurveys()` helper; controller filters by
it.
- `NightlySurveyPopover` accepts `mode` + `v-model:open`. Manual mode
skips the eligibility watcher, drops the "Not Now" button, and leaves
open/close/markSeen to the parent.
- New `ErrorPanelSurveyCta.vue` renders a CTA in the error tab footer
once `useExecutionErrorStore.hasAnyError` has transitioned `null →
value` at least 3 times.
- Popover lives in `NightlySurveyController` (session-persistent).
Shared state via module-level singleton (`useErrorSurveyPopoverState`)
so the iframe survives error-tab unmounts during workflow switches.
- `useTypeformEmbed` centralises script loading (singleton Promise, 10s
timeout, explicit `window.tf.load()` on each new container). Necessary
because the embed's DOMContentLoaded auto-scan only fires once; late
consumers need an explicit re-scan to render.
  - CTA and feedback-gate strings added under `errorPanelSurvey.*`.

## Review Focus

- Manual-mode flow in `NightlySurveyPopover.vue` — CTA click is routed
through the module singleton; popover stays mounted after the first open
(`hasOpenedOnce` + `v-show`) to preserve the iframe across repeated
open/close cycles and workflow switches.
- `useTypeformEmbed.ts` — the `window.tf.load()` trick (verified against
the CDN `embed.js`) is what lets two surveys coexist; without it
Typeform's one-shot DOM scan misses whichever element mounts second.
- `NightlySurveyController.vue` guards against double-mount by requiring
`presentation === 'inline-cta'` on `errorPanelConfig`.
- Scope is nightly+localhost only (`isNightlyLocalhost` in
`useSurveyEligibility`); async component gate in `TabErrors.vue` keeps
this out of Cloud/Desktop/stable bundles.

## Test plan

- [x] `IS_NIGHTLY=true pnpm dev`, clear `Comfy.SurveyState` +
`Comfy.FeatureUsage`, trigger 3 failed runs → CTA appears in error tab
footer.
- [x] Click "Give feedback" → Typeform popover opens and renders the
form.
- [x] Close popover, switch workflow (error tab unmounts), trigger a new
error → CTA reappears and reopening the popover shows the same iframe.
- [x] Open error-panel popover, close, then trigger 3 node searches →
node-search auto-popup renders its own iframe correctly (two surveys
coexist).
- [x] CTA × dismisses and persists (`seenSurveys['error-panel']`).
- [x] "Don't ask again" inside popover sets `optedOut: true` and hides
all nightly surveys.
- [x] Cloud/Desktop/stable builds: CTA never renders, controller's
manual popover doesn't mount.

## Screenshot


https://github.com/user-attachments/assets/91145f23-fd1e-4caf-b6cc-4b97d33ed6b7

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11591-feat-add-inline-CTA-nightly-survey-for-error-panel-34c6d73d3650817d9f95fddcf64633de)
by [Unito](https://www.unito.io)
2026-04-24 08:14:08 +00:00
Alexander Brown
9cd36c7f7d fix: website polish — prefetch, Safari video controls, border-spin perf (#11586)
## Summary

Website polish: enable Astro prefetch, fix mobile Safari video controls,
and optimize the ProductShowcase border-spin animation.

## Changes

- **What**:
- Enable `prefetch: { prefetchAll: true }` in Astro config for faster
navigation
- Hide native iOS Safari play-button overlays on autoplay background
videos (`BlobMedia`, global CSS)
- Refactor `ProductShowcaseSection` border-spin animation: use
`@property --border-angle` with `conic-gradient` directly on the
container (removes extra spinning `div`), gate animation on
`useIntersectionObserver` visibility, lazy-load non-active videos with
`preload="none"`, add `will-change-[opacity]` for smoother crossfade

## Review Focus

- `@property` CSS registered custom property — verify browser support is
acceptable for the website target audience
- `!important` on the webkit media controls rule — necessary to override
UA styles on iOS Safari

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11586-fix-website-polish-prefetch-Safari-video-controls-border-spin-perf-34c6d73d36508108a0e8f67df9b32a88)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-04-24 07:44:02 +00:00
Alexander Brown
8e3041aa2f ci: add e2e-status gate job for required checks (#11587)
## Summary

Follow-up to #11568. Fixes Playwright required checks hanging as
"Waiting for status to be reported" on PRs with no e2e-relevant file
changes.

## Problem

PR #11568 added a `changes` filter to skip E2E when only
docs/apps/storybook files are touched. The E2E workflow skips correctly,
but branch rulesets require the 11 matrix-expanded check names (e.g.
`playwright-tests-chromium-sharded (1, 8)`). When a matrix job is
skipped via dependency, GitHub only reports the parent job name — the
individual matrix entries are never reported, so required checks hang
forever.

## Fix

Add a single `e2e-status` gate job that:
- Uses `if: always()` so it always runs regardless of skipped
dependencies
- Passes when E2E was intentionally skipped (no relevant changes)
- Passes when all matrix jobs succeeded
- Fails when any matrix job failed

**After merging**, the ProtectMain and Core release branches rulesets
should be updated to require `e2e-status` instead of the 11 individual
matrix check names.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11587-ci-add-e2e-status-gate-job-for-required-checks-34c6d73d365081b59c01e8d61d0b808d)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-04-24 05:43:45 +00:00
Alexander Brown
0b2e605dee content: website copy and pricing updates (#11567)
## Summary

Update website copy: fix branding ("Comfy" → "ComfyUI"), correct pricing
runtime, remove "coming soon" seat features, and shorten use-case label.

## Changes

- **What**: Copy corrections in `translations.ts` (branding, runtime "60
min" → "30 min" for Standard plan, remove placeholder seat features for
Creator/Pro plans); trim feature arrays in `PriceSection.vue`

## Review Focus

Verify zh-CN translations still make sense after the English copy
changes (runtime string not updated in zh-CN).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11567-content-website-copy-and-pricing-updates-34b6d73d365081c29af8ee1469b08358)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
2026-04-24 04:25:15 +00:00
Christian Byrne
d038193d95 fix: add assetsPrefix to avoid /_astro collision with workflows site (#11584)
## Problem

The website and workflows sites both emit build assets under `/_astro/`.
The comfy-router sends all `/_astro/*` requests to the workflows Vercel
project, so when a page from the website references
`/_astro/SiteNav.xxx.js`, it 404s because that file only exists on the
website's Vercel deployment.

## Fix

Add `build.assetsPrefix: '/_website'` to the Astro config. This makes
Astro emit asset references as `/_website/SiteNav.xxx.js` instead of
`/_astro/SiteNav.xxx.js`.

The comfy-router already routes `/_website/*` to the website origin, so
these assets will resolve correctly.

## Why this matters

**This is blocking the comfy.org cutover from Framer → new site.**
Without it, the homepage loads but all CSS/JS 404s and nothing renders.

## Change

One line in `apps/website/astro.config.ts`:
```ts
build: {
  assetsPrefix: '/_website'
}
```

## Verification

After merge + Vercel deploy:
- Homepage HTML should reference `/_website/*.js` and `/_website/*.css`
instead of `/_astro/*`
- `/workflows` page should still reference `/_astro/*` (unaffected)
- Both sites render correctly through the proxy

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11584-fix-add-assetsPrefix-to-avoid-_astro-collision-with-workflows-site-34c6d73d36508101a932caa2905f0a2b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-04-23 21:05:21 -07:00
Benjamin Lu
04e430e006 fix: dedupe keybinding modifier display (#11570)
## Summary

Fix keybinding display so pressing a modifier key by itself shows that
modifier once instead of duplicated text like `Shift + Shift`.

## Changes

- **What**: Centralizes modifier key labels in `KeyComboImpl` and omits
the duplicated primary key when the pressed key is itself a modifier.
- **Breaking**: None.
- **Dependencies**: None.

## Review Focus

The keybinding model still includes active modifiers for normal
shortcuts like `Ctrl + Shift + k`, while modifier-only input now renders
as a single key. Regression coverage includes single modifier presses,
combined held modifiers, and a normal non-modifier shortcut.

Checks run: `pnpm exec vitest run
src/platform/keybindings/keyCombo.test.ts`; `pnpm lint:unstaged`; `pnpm
exec oxfmt --check src/platform/keybindings/keyCombo.ts
src/platform/keybindings/keyCombo.test.ts`; `pnpm exec vitest run
src/platform/keybindings/keyCombo.test.ts
src/platform/keybindings/keybindingStore.test.ts
src/platform/keybindings/keybindingService.escape.test.ts
src/platform/keybindings/keybindingService.canvas.test.ts`; `pnpm
typecheck`; pre-commit lint-staged checks; pre-push `pnpm knip --cache`.

Linear: FE-240

## Screenshots (if applicable)

Not applicable; covered by keybinding sequence unit tests.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11570-fix-dedupe-keybinding-modifier-display-34b6d73d365081968a88da4465c151de)
by [Unito](https://www.unito.io)
2026-04-24 03:41:42 +00:00
Comfy Org PR Bot
bab122ad6b 1.44.9 (#11581)
Patch version increment to 1.44.9

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11581-1-44-9-34c6d73d3650811a82d9fe7a039a7edc)
by [Unito](https://www.unito.io)

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.44.9
2026-04-24 03:24:41 +00:00
Kelly Yang
bba3c38ae1 test: E2E coverage for painter widget (Levels 1–5) (#11551)
## Summary

Adds 7 new E2E tests for the Painter widget covering the remaining
untested items from Levels 1–5 of the widget test plan. All new tests
pass typecheck, lint, and the full pre-commit hook suite.

## Changes

**`browser_tests/tests/painter.spec.ts`**

- **Level 1.2** — assert node size ≥ 450×550 via the graph API to verify
the painter extension enforces its minimum dimensions
- **Level 1.3** — assert `width`, `height`, and `bg_color` widgets have
`options.hidden = true` so they don't appear as standard widget controls
- **Level 2.4** — cursor element becomes visible on pointer enter, its
CSS transform updates as the mouse moves across the canvas, and it hides
on pointer leave; uses `expect.poll` on transform to avoid the Vue
microtask race
- **Level 4.2** — set brush color to `#ff0000` via input event, draw a
stroke, sample a 40×40 region at canvas center and assert red pixels (R
> 200, G < 50, B < 50)
- **Level 4.3** — set opacity to 50%, draw a stroke, sample pixel alpha
values and assert semi-transparency (50 < α < 230)
- **Level 4.4** — focus the hardness slider, press ArrowLeft ×10, assert
the display value changes from `100%` to `90%`
- **Level 5.4** — set background color input to `#ff0000` via input
event, assert the canvas container div has `background-color: rgb(255,
0, 0)`

**`src/components/painter/WidgetPainter.vue`**

- Added `data-testid="painter-canvas-container"` to the inner canvas
wrapper div
- Added `data-testid="painter-cursor"` to the brush cursor div
- Added `data-testid="painter-bg-color-row"` to the background color
control row
- Added `data-testid="painter-hardness-value"` to the hardness display
span (mirrors the existing `painter-size-value` pattern)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: changes are limited to Playwright E2E coverage plus a few
`data-testid` attributes to stabilize selectors, with no functional
logic changes to the painter behavior.
> 
> **Overview**
> Adds **7 new Playwright E2E tests** for the Painter widget, covering
minimum node sizing/hidden standard widgets, cursor
visibility/positioning behavior, brush hardness display updates,
color/opacity effects on rendered strokes, and background color
application.
> 
> Updates `WidgetPainter.vue` to add a handful of **`data-testid`
hooks** (canvas container, cursor, background color row, hardness value)
used by the new tests.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a6da0c3e39. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11551-test-E2E-coverage-for-painter-widget-Levels-1-5-34a6d73d36508154a90fd24ffb3adb5b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2026-04-23 23:13:52 -04:00
Kelly Yang
3737a32999 test: extend minimap e2e for level 1& level4 (#11192)
## Summary

Adds Playwright coverage for minimap **Level 1** gaps and **Level 4**
drag-to-pan behavior (`browser_tests/tests/minimap.spec.ts`).

## Changes

- [x] **Level 1.1** — After closing the minimap with the X button,
assert `Comfy.Minimap.Visible` is `false` (in addition to minimap hidden
and toolbar toggle still visible).
- [x] **Level 1.2** — New `@mobile` + `@canvas` suite: on a narrow
viewport (Pixel 5 project), assert default `Comfy.Minimap.Visible` is
`false`, minimap container is absent (`toHaveCount(0)`), and the toolbar
minimap toggle remains visible.
- [x] **Level 1.3** — New test: close via X, re-open via toolbar toggle;
assert minimap and viewport are visible and `Comfy.Minimap.Visible` is
`true`.
- [x] **Level 4.1** — Pointer drag on `minimap-interaction-overlay`
(`pointerdown` / `pointermove` / `pointerup`); use `expect.poll` on main
canvas offset so each move step updates pan; assert meaningful total pan
distance after drag.
- [x] **Level 4.2** — Same drag pattern; assert `.minimap-viewport`
`style.transform` updates mid-drag via `expect.poll` between move steps.
- [x] **Helpers** — `clientPointOnMinimapOverlay`,
`readMainCanvasOffset`, shared `MINIMAP_POINTER_OPTS` (no
`waitForTimeout`).

## Notes

- [x] Desktop minimap tests unchanged in intent; mobile-only cases live
in a separate `test.describe` so `beforeEach` does not force
`Comfy.Minimap.Visible` on small screens.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: changes are limited to Playwright e2e tests and shared test
utilities, with no production logic modifications. Main risk is
increased test flakiness due to pointer-event simulation and
timing/polling on canvas state.
> 
> **Overview**
> Expands minimap Playwright coverage by adding shared helpers
(`minimapUtils.ts`) for overlay-relative pointer coordinates, consistent
pointer event options, and reading the main canvas offset.
> 
> Updates `minimap.spec.ts` to assert the `Comfy.Minimap.Visible`
setting toggles correctly when closing/reopening, adds
FitView+minimap-center regression coverage, and replaces mouse-drag with
explicit `pointerdown`/`pointermove`/`pointerup` overlay interactions
that assert progressive canvas panning and viewport `transform` updates.
Adds a new `@mobile` suite verifying the minimap is hidden by default on
small viewports while the toolbar toggle remains visible.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7fad2a7dd3. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11192-test-extend-minimap-e2e-for-level-1-gaps-and-drag-panning-3416d73d36508194bc81f81a6d4536b8)
by [Unito](https://www.unito.io)
2026-04-23 22:17:16 -04:00
Alexander Brown
39e1877065 ci: filter e2e workflow on PRs to skip unrelated changes (#11568)
## Summary

Skip the main e2e test suite on PRs that only touch unrelated paths
(website, docs, storybook, markdown).

## Changes

- **What**: Replace the broad `paths-ignore: ['**/*.md']` on the
`pull_request` trigger with a more targeted `paths-ignore` list covering
`apps/**`, `docs/**`, `**/*.md`, and `.storybook/**`. The `push` (to
main), `merge_group`, and `workflow_dispatch` triggers remain
unconditional.

## Review Focus

- The `merge_group` trigger has no path filter, so the merge queue
always runs e2e as a safety net before merge.
- Using `paths-ignore` (denylist) rather than `paths` (allowlist) so new
top-level directories trigger e2e by default.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11568-ci-filter-e2e-workflow-on-PRs-to-skip-unrelated-changes-34b6d73d365081ea8603ef94bc86b6e6)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-04-23 23:02:03 +00:00
Dr.Lt.Data
bd96bdf4cc fix(manager): migrate 4 endpoints GET→POST for CSRF hardening (#11520)
## Summary

Align `comfyManagerService` and Manager UI state with CSRF hardening in
[Comfy-Org/ComfyUI-Manager#2818](https://github.com/Comfy-Org/ComfyUI-Manager/pull/2818)
(4.2.0, Content-Type gate + GET→POST migration) and
[Comfy-Org/ComfyUI-Manager#2823](https://github.com/Comfy-Org/ComfyUI-Manager/pull/2823)
(4.2.1, `extension.manager.supports_csrf_post` feature flag).

## Changes

- **Service layer**: Convert 4 state-mutation endpoints (`START_QUEUE`,
`UPDATE_ALL`, `UPDATE_COMFYUI`, `REBOOT`) from GET to POST. `body=null`
+ axios default `Content-Type: application/json` is allowed by the
backend's `reject_simple_form_post` gate (only the three CORS
simple-form types are rejected).
- **UI/state layer**: Add `ManagerUIState.INCOMPATIBLE` triggered when
the backend advertises `supports_manager_v4` but not
`supports_csrf_post`. Manager UI is treated as "not installed" — buttons
hide via `shouldShowManagerButtons` with zero call-site changes across
`TopMenuSection`, `MissingNodeCard`, `MissingPackGroupRow`, `TabErrors`.
- **Graceful degraded mode**: One-shot upgrade toast (warn, 15s)
dispatched via `watch(immediate:true)` with a module-level guard that
survives multiple composable instances. `openManager()` re-emits on
explicit user action so stale shortcuts still surface guidance. i18n
(en/ko) covering Desktop / standalone pip / Manager UI self-update
paths.
- **Breaking**: None. Existing policies preserved (`--enable-manager`
absent → `DISABLED`; `--enable-manager-legacy-ui` → `LEGACY_UI`; feature
flags not yet loaded → `NEW_UI` transient fallback).

## Review Focus

- Decision-tree ordering in `useManagerState.ts`: `supports_csrf_post`
check evaluates before `NEW_UI`/`LEGACY_UI` branches so stale Manager
backends never reach the enabled paths.
- Toast guard: module-level `incompatibleToastShown` survives multiple
composable instances (tests verify 3× `useManagerState()` = 1 toast
call).
- `generatedManagerTypes.ts` still declares the 4 endpoints as GET;
regeneration follows once Manager 4.2.1 OpenAPI is published. Runtime is
unaffected since axios operates on the route string.

## References

-
[Comfy-Org/ComfyUI-Manager#2818](https://github.com/Comfy-Org/ComfyUI-Manager/pull/2818)
— CSRF Content-Type gate + GET→POST migration (4.2.0)
-
[Comfy-Org/ComfyUI-Manager#2823](https://github.com/Comfy-Org/ComfyUI-Manager/pull/2823)
— `supports_csrf_post` feature flag (4.2.1)
- [comfyui-manager 4.2.1 on
PyPI](https://pypi.org/project/comfyui-manager/4.2.1) — release package
2026-04-23 15:53:19 -07:00
Dante
559922eaa5 feat: add bug-dump-ingest skill (#11460)
## Summary

Adds a new Claude Code skill at `.claude/skills/bug-dump-ingest/` that
syncs the `#bug-dump` Slack channel into Linear as the system of record,
per discussion in [this
thread](https://comfy-organization.slack.com/archives/C075ANWQ8KS/p1776510375473579).

- Primary mode is bulk sync — every ingestable top-level message becomes
a Linear issue in the Frontend Engineering team's Triage state with
labels for area / env / severity / reporter.
- Marks handled messages via the team emoji scheme:
  - `` — ticket created
  - `:pr-open:` — fix PR open
  - `` — needs more context
  - `🔁` — duplicate
- Since the Slack reactions API isn't exposed to the skill, the
machine-readable marker is a thread reply carrying the Linear URL; the
human is prompted to add the visible parent reaction from a batch list
printed at session end.
- Secondary opt-in per-row mode delegates to `red-green-fix` to author a
failing unit + e2e test, then a minimal fix, then a PR.

## Files

- `SKILL.md` — entry point: workflow, classification, verification,
approval flow, Linear integration (MCP / GraphQL / draft fallback), fix
workflow
- `reference/linear-api.md` — GraphQL snippets for teams / states /
labels / issues
- `reference/schema.md` — field-by-field extraction rules
- `reference/examples.md` — seven worked examples from real recent
`#bug-dump` messages
- `reference/verify-commands.md` — cookbook of false-defect verification
commands

## Linear MCP setup

Two supported paths documented in `SKILL.md`:

- Option A: official hosted Linear MCP at `https://mcp.linear.app/sse`
via `claude mcp add`, OAuth-based, no API key.
- Option B: community self-hosted MCP with `LINEAR_API_KEY` in env.

## Test plan

- [ ] Restart Claude Code session after merging so the Linear MCP tools
register
- [ ] Authorize Linear OAuth on first tool call
- [ ] Dry-run the skill against a 48h window of `#bug-dump`, confirm the
approval table renders with all 9 columns
- [ ] File one real ticket end-to-end; verify labels, Triage state,
Slack thread reply, and permalink attachment
- [ ] Add a `` reaction on that parent; re-run and
confirm the message is skipped

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11460-feat-add-bug-dump-ingest-skill-3486d73d3650810094aee3e4ee79eb86)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: bymyself <cbyrne@comfy.org>
2026-04-24 07:20:35 +09:00
Christian Byrne
4b7a027946 fix: route progress_text feature flag check through getDevOverride (#11384)
## Summary

Route the `progress_text` binary parser's feature-flag check through
`serverSupportsFeature()` so dev overrides via `localStorage` take
effect.

## Changes

- **What**: Replace
`this.getClientFeatureFlags()?.supports_progress_text_metadata` with
`this.serverSupportsFeature('supports_progress_text_metadata')` in the
`case 3` binary message handler, consistent with all other feature-flag
checks in the class.

## Review Focus

Minimal one-line change. The key consideration is that
`serverSupportsFeature()` routes through `getDevOverride()` first,
enabling `localStorage` overrides (`ff:supports_progress_text_metadata`)
for dev testing of the binary wire format.

Fixes #11187

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11384-fix-route-progress_text-feature-flag-check-through-getDevOverride-3476d73d36508161bca0d6c2ea7c3c55)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-23 15:04:18 -07:00
Christian Byrne
cc1338e51e perf: exclude canvas nodes from PostHog session recording (#10494)
## Summary

Add `ph-no-capture` class to TransformPane to block PostHog session
recording of canvas node DOM mutations, eliminating 226ms CPU overhead
in the viewport scenario.

## Changes

- **What**: Add `ph-no-capture` CSS class to the TransformPane root div
and a unit test guarding it
- **Why**: Profiling showed PostHog session recording (via rrweb
mutation observer) consuming **226ms CPU** in the viewport scenario —
**9× more** than the entire Vue reactivity system (25ms). The 150+
LGraphNode Vue components that mount/unmount during pan/zoom each
trigger rrweb to snapshot new DOM subtrees.

### How it works

PostHog uses rrweb under the hood. The `ph-no-capture` class maps to
rrweb's `blockClass`, which causes:
1. `processMutation` to **early-return** for `childList` mutations when
the parent is blocked
2. `genAdds` to **skip child traversal** (`dom.childNodes()`) for
blocked elements
3. The element to be replaced with a **same-size placeholder** in replay

This means all 150+ node components inside TransformPane produce **zero
mutation processing cost**.

### Scope

- **TransformPane**: blocked — wraps all Vue-rendered graph nodes
- **LinkOverlayCanvas**: evaluated but not blocked — contains only a
single `<canvas>` element with no DOM children, so no mutation overhead
- **All other UI** (sidebar, menus, dialogs, toolbar, bottom panel):
continues recording normally

### Trade-off

The graph canvas area appears as a blank placeholder in session replays.
This is acceptable because canvas interaction is better captured via
workflow JSON, and the performance gain far outweighs the replay
fidelity loss.

## Review Focus

- Correctness of the `ph-no-capture` placement on TransformPane as the
optimal DOM boundary
- Whether any other high-mutation DOM subtrees should also be blocked

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10494-perf-exclude-canvas-nodes-from-PostHog-session-recording-32e6d73d36508169ab07f1b193860fb0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Dante <bunggl@naver.com>
2026-04-23 15:03:41 -07:00
pythongosssss
0052cdadd4 test: e2e coverage for node templates (#11564)
## Summary

Add E2E tests for node templates

## Changes

- **What**: 
- add tests for save, insert, delete, import export
- vue and litegraph
- add testid to dialog
- update `clickLitegraphMenuItem` to enable clicking children with the
same name as parent

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11564-test-e2e-coverage-for-node-templates-34b6d73d365081a39ce5c713f05a2a92)
by [Unito](https://www.unito.io)
2026-04-23 19:33:18 +00:00
Christian Byrne
aa40dd8a65 [chore] Update Comfy Registry API types from comfy-api@315429a (#11566)
## Automated API Type Update

This PR updates the Comfy Registry API types from the latest comfy-api
OpenAPI specification.

- API commit: 315429a
- Generated on: 2026-04-22T21:35:59Z

These types are automatically generated using openapi-typescript.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11566-chore-Update-Comfy-Registry-API-types-from-comfy-api-315429a-34b6d73d3650815dbe21f1960115dc47)
by [Unito](https://www.unito.io)

Co-authored-by: coderfromthenorth93 <213232275+coderfromthenorth93@users.noreply.github.com>
2026-04-23 18:21:03 +00:00
Alexander Brown
68798d8e37 feat(website): website mise en place (#11552)
## Summary

Assorted website copy and content refinements — tidying up loose ends
across the site.

## Changes

- **What**: Remove placeholder doc links from custom nodes feature
description on pricing page

## Review Focus

Low-risk copy changes only; no logic or layout modifications.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11552-feat-website-website-mise-en-place-34b6d73d3650813b954afbc965e4dc74)
by [Unito](https://www.unito.io)

> **Note:** The `PR: Vercel Website Preview` workflow is
`workflow_run`-triggered, so it always runs the **main branch version**
of the workflow file. Until this PR is merged, the preview workflow will
continue posting standalone comments using the old `<!--
VERCEL_WEBSITE_PREVIEW -->` marker instead of writing to the
consolidated `<!-- WEBSITE_CI_REPORT -->` comment. This is expected and
resolves itself on merge.

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Yourz <crazilou@vip.qq.com>
2026-04-23 17:26:06 +00:00
pythongosssss
a6c3ff1a54 fix: load3d used wrong i18n key, add test (#11546)
## Summary

Toast for Load3D initialization failure was using the wrong key and so
showed an untranslated key to the user.

## Changes

- **What**: 
- Update to use correct existing key
- Add test that forces init failure

## Screenshots (if applicable)
Fixed
<img width="482" height="121" alt="image"
src="https://github.com/user-attachments/assets/f89eef99-c1a6-463a-a711-7e9c16d0e89a"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11546-fix-load3d-used-wrong-i18n-key-add-test-34a6d73d36508159aab9f042d3e9c4f0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-23 11:52:42 -04:00
Dante
9599a4e00a fix cloud frontend runtime guard regressions (#11180)
## Summary
- harden cloud frontend runtime paths that were throwing on
`cloud.comfy.org`
- guard widget value propagation when the source widget is missing
- treat nullish executed outputs as empty output during flatten/parsing
- ignore stale autogrow disconnect callbacks after an autogrow group is
removed

## Root cause
This PR bundles three small runtime guard fixes from
`cloud-frontend-staging` issues that reproduce on
`https://cloud.comfy.org/`:

- `CLOUD-FRONTEND-STAGING-429`: widget propagation assumed
`this.widgets[0]` always existed and crashed during group-node/widget
lifecycle transitions
- `CLOUD-FRONTEND-STAGING-3QA` and sibling `3QB`: executed-event parsing
assumed `detail.output` was always an object and crashed on nullish
output payloads
- `CLOUD-FRONTEND-STAGING-42B`: `autogrowInputDisconnected()` could run
from a stale `requestAnimationFrame()` callback after its autogrow group
had already been removed

## User impact
- prevents unhandled frontend exceptions on `cloud.comfy.org`
- keeps node output rendering and linear-mode flattening resilient to
sparse executed payloads
- avoids autogrow disconnect crashes during graph/widget churn

## Changes
- extracted shared widget propagation logic into
`widgetValuePropagation.ts`
- added source-widget guards in custom widget / primitive widget
propagation paths
- added null guards in result parsing and linear-mode output flattening
- added an autogrow-group existence guard in `dynamicWidgets.ts`
- added focused regression tests for all three bug shapes

## Red / Green Verification
### Red
I ran the new targeted regression suite in a temporary pre-fix worktree
with the runtime guards reverted while keeping the new tests.

Failing tests in that state:
- `src/extensions/core/widgetValuePropagation.test.ts`
  - `returns early when the source widget is missing`
- `src/stores/resultItemParsing.test.ts`
  - `returns empty array for nullish node output`
  - `ignores nullish node outputs`
- `src/renderer/extensions/linearMode/flattenNodeOutput.test.ts`
  - `returns empty array for nullish node output`

Representative pre-fix errors:
- `TypeError: Cannot read properties of undefined (reading 'value')`
- `TypeError: Cannot convert undefined or null to object`

### Green
On the draft PR branch, the targeted regression suite passes:
- `pnpm exec vitest run src/core/graph/widgets/dynamicWidgets.test.ts
src/stores/resultItemParsing.test.ts
src/renderer/extensions/linearMode/flattenNodeOutput.test.ts
src/extensions/core/widgetValuePropagation.test.ts
src/extensions/core/customWidgets.test.ts`

Result:
- `5` test files passed
- `49` tests passed

## Validation
- `pnpm exec vitest run src/core/graph/widgets/dynamicWidgets.test.ts
src/stores/resultItemParsing.test.ts
src/renderer/extensions/linearMode/flattenNodeOutput.test.ts
src/extensions/core/widgetValuePropagation.test.ts
src/extensions/core/customWidgets.test.ts`
- `pnpm exec eslint --no-ignore src/core/graph/widgets/dynamicWidgets.ts
src/core/graph/widgets/dynamicWidgets.test.ts
src/stores/resultItemParsing.ts src/stores/resultItemParsing.test.ts
src/renderer/extensions/linearMode/flattenNodeOutput.ts
src/renderer/extensions/linearMode/flattenNodeOutput.test.ts
src/extensions/core/customWidgets.ts src/extensions/core/widgetInputs.ts
src/extensions/core/widgetValuePropagation.ts
src/extensions/core/widgetValuePropagation.test.ts`
- `pnpm exec oxfmt --check src/core/graph/widgets/dynamicWidgets.ts
src/core/graph/widgets/dynamicWidgets.test.ts
src/stores/resultItemParsing.ts src/stores/resultItemParsing.test.ts
src/renderer/extensions/linearMode/flattenNodeOutput.ts
src/renderer/extensions/linearMode/flattenNodeOutput.test.ts
src/extensions/core/customWidgets.ts src/extensions/core/widgetInputs.ts
src/extensions/core/widgetValuePropagation.ts
src/extensions/core/widgetValuePropagation.test.ts`

## Notes
- I explicitly skipped the `getCanvas: canvas is null` issue because it
is already covered by open PRs `#11173` / `#11174`.
- `pnpm typecheck` was not included in validation because the temporary
PR worktree used for publication hits local path-resolution issues
through the shared dependency install, which is unrelated to the changes
in this PR.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11180-codex-fix-cloud-frontend-runtime-guard-regressions-3416d73d365081e0af6ec612c9d0d8aa)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-23 12:02:46 +00:00
pythongosssss
cc73baaf57 test: Test subgraph breadcrumbs (#11472)
## Summary

Add test coverage to subgraph breadcrumbs

## Changes

- **What**: 
- Add subgraph breadcrumb helpers
- Add tests for entering/navigating/menu/collapsing

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11472-test-Test-subgraph-breadcrumbs-3486d73d365081e1a75bf57404eaa63b)
by [Unito](https://www.unito.io)
2026-04-23 01:49:29 -07:00
Dante
700fcb6bda test: add unit tests for FormDropdownMenuActions (#11443)
## Summary

Adds 14 unit tests for \`FormDropdownMenuActions\` — isolated into its
own PR because the component is denser (three PrimeVue Popovers,
multiple filter models) than the sibling components in the form-dropdown
PR. Part of a widget-test-coverage sequence.

## Changes

- **What**: \`FormDropdownMenuActions.test.ts\` — search v-model, sort
popover options + selection, ownership popover visibility gated by
\`showOwnershipFilter\` + options present, base-model multi-select
toggle (add/remove/multiple), Clear Filters, list/grid layout-mode
v-model.

## Review Focus

- PrimeVue \`Popover\` stubbed as an always-slotted \`<div>\` with
\`toggle\`/\`hide\` methods on the Options-API \`methods\` (stub
\`expose\` did not satisfy template-ref access).
- Sort/ownership/base-model option discovery uses
\`within(popover-body)\` to disambiguate buttons across the three
popovers.
- Layout-switch locator uses an \`.icon-*\` class probe since the button
has no accessible name; covered by an \`eslint-disable-next-line
testing-library/no-node-access\`.
- No changes to the component source.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11443-test-add-unit-tests-for-FormDropdownMenuActions-3486d73d365081ed80dcfdf5d83655e1)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-23 08:00:54 +00:00
Dante
30c07dc9ec fix: cancel-subscription dialog renders Invalid Date for ISO fractional seconds (#11539)
## Summary

`CancelSubscriptionDialogContent` calls `new Date(dateStr)` directly on
both `cancelAt` and `subscription.value?.endDate`. Strict ISO 8601
parsers (Safari and some WebViews) reject fractional seconds whose
length is anything other than 3 digits, so a Go-style backend
timestamp such as `2026-04-18T10:04:55.6513Z` rendered as
`Your access continues until Invalid Date.` in a destructive billing
flow.

PR #11358 already added the tolerant `parseIsoDateSafe` helper and
applied it to the Secrets panel. This PR closes the same gap in the
cancellation dialog and adds regression coverage that exercises the
strict-parser code path (V8 alone is too lenient to fail without it).

## Changes

- `CancelSubscriptionDialogContent.vue` — pipe both date sources through
  `parseIsoDateSafe`; collapse the two-step null check into one. When
  the value is missing OR unparseable, fall back to the existing
  `subscription.cancelDialog.endOfBillingPeriod` translation instead of
  emitting `Invalid Date`.
- `CancelSubscriptionDialogContent.test.ts` (new) — wraps the assertions
  in the same `withStrictMillisecondParser` shim used by
  `dateTimeUtil.test.ts`, so 1-, 4-, and 9-digit fractional inputs
  actually exercise the broken path. Also covers the missing/unparseable
  fallbacks and the `cancelAt`-takes-precedence ordering.

## Red-green proof (local)

Confirmed locally before splitting the commits:

| Commit | `pnpm exec vitest run
…CancelSubscriptionDialogContent.test.ts` |
|---|---|
| `c8aecd07f test: regression cover …` (test-only) | 5 failed / 1 passed
— DOM rendered `"Your access continues until Invalid Date."` |
| `f24cb903f fix: parse cancel-subscription dialog ISO timestamps …` | 6
passed |

CI on this branch only runs on PR HEAD; happy to force-push a transient
red commit if you want a recorded red CI run alongside the green one.

## Test plan

- [x] `pnpm exec vitest run
src/components/dialog/content/subscription/CancelSubscriptionDialogContent.test.ts`
(6 passing on green)
- [x] `pnpm typecheck`
- [x] `pnpm exec eslint
src/components/dialog/content/subscription/CancelSubscriptionDialogContent.{vue,test.ts}`
- [x] `pnpm exec oxfmt --check` on changed files
- [ ] Manual repro on Safari with a Go-emitted cancel timestamp (out of
scope here; the unit test asserts the equivalent strict-parser behavior)

## Origin

Surfaced by `/codex:adversarial-review` as the gap left after PR #11358
(Secrets panel) — the same `new Date(...)` hazard survived in a
destructive billing flow with no regression coverage.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11539-fix-cancel-subscription-dialog-renders-Invalid-Date-for-ISO-fractional-seconds-34a6d73d365081e4bdd6c941afd8cef3)
by [Unito](https://www.unito.io)
2026-04-23 03:30:31 +00:00
Comfy Org PR Bot
d81f3a9278 1.44.8 (#11514)
Patch version increment to 1.44.8

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
v1.44.8
2026-04-22 20:28:18 -07:00
Dante
bab2870131 test: harden strict parser coverage for long ISO fractions (#11529)
## Summary

- run every `parseIsoDateSafe` case with more than 3 fractional-second
digits through `withStrictMillisecondParser`
- assert the normalized 3-digit timestamp string passed into `Date` for
each long-fraction variant
- keep the follow-up scoped to test coverage only

## Root cause

V8 already accepts and truncates ISO timestamps with more than 3
fractional-second digits, so the existing tests could stay green even if
`parseIsoDateSafe` failed to normalize those values before constructing
`Date`. Wrapping the long-fraction cases in the strict parser shim makes
CI exercise the Safari/WebView-sensitive path the feature is meant to
protect.

## Testing

- `pnpm exec vitest run src/utils/dateTimeUtil.test.ts`
- `pnpm exec eslint src/utils/dateTimeUtil.test.ts`

Fixes #11528

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11529-codex-test-harden-strict-parser-coverage-for-long-ISO-fractions-34a6d73d365081119577eb5fb6d4992c)
by [Unito](https://www.unito.io)
2026-04-23 03:05:03 +00:00
Christian Byrne
e5cb244a2d test: add E2E tests for keyboard shortcut actions (#11210)
## Summary

Add Playwright E2E tests covering core keyboard shortcut actions.

## Changes

- **What**: 7 new E2E tests in
`browser_tests/tests/keyboardShortcutActions.spec.ts` covering:
  - Ctrl+Z undoes the last graph change
  - Ctrl+Shift+Z redoes after undo
  - Ctrl+S opens save dialog
  - Ctrl+, opens settings dialog
  - Escape closes settings dialog
  - Delete key removes selected nodes
  - Ctrl+A selects all nodes

## Review Focus

- Tests use `expect.poll()` for async graph state assertions
- `@keyboard` tag for selective test runs
- Uses `comfyPage.nodeOps` and `comfyPage.menu.topbar` helpers

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11210-test-add-E2E-tests-for-keyboard-shortcut-actions-3416d73d3650812eafd5e789cf8463a6)
by [Unito](https://www.unito.io)
2026-04-23 02:43:57 +00:00
Christian Byrne
c5b6fd9c40 test: clarify inert getClientFeatureFlags mock in progress_text binary parsing tests (#11385)
## Summary

Adds inline comments to three
`vi.mocked(api.getClientFeatureFlags).mockReturnValue()` calls in the
`progress_text binary message parsing` describe block, clarifying they
are intentionally inert — the parser checks `serverFeatureFlags` only.

This prevents future readers from being confused about whether the mock
has any effect.

- Fixes #11186

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11385-test-clarify-inert-getClientFeatureFlags-mock-in-progress_text-binary-parsing-tests-3476d73d365081a98c06c43c4737fdd9)
by [Unito](https://www.unito.io)
2026-04-23 02:43:17 +00:00
Christian Byrne
0f0210e482 test: add unit tests for workspaceApi (#11393)
## Summary
Adds 27 unit tests for `src/platform/workspace/api/workspaceApi.ts`,
increasing line coverage from 2.9% to 83.2%.

## Test Coverage
- Authentication: auth header null checks (workspace auth + firebase
auth)
- Error handling: axios error wrapping, fallback messages, non-axios
rethrow
- Workspace CRUD: list, create, update, delete, leave
- Member management: listMembers, removeMember
- Invite management: listInvites, createInvite, revokeInvite,
acceptInvite
- Billing: getBillingStatus, getBillingBalance, getBillingPlans
- Subscription: previewSubscribe, subscribe, cancelSubscription,
resubscribe
- Payment: getPaymentPortalUrl, createTopup
- Billing events: getBillingEvents, getBillingOpStatus

## Testing
```bash
pnpm vitest run src/platform/workspace/api/workspaceApi.test.ts
# 27 tests pass
```

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11393-test-add-unit-tests-for-workspaceApi-3476d73d36508192b8f6d1af6aa543f4)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-04-23 02:43:10 +00:00
Christian Byrne
7d1d7c8315 fix: remove deleted workflow from search results in sidebar (#11425)
*PR Created by the Glary-Bot Agent*

---

## Summary

Deleting a workflow in the sidebar while search is active left the
deleted workflow visible in the search results. Interacting with the
stale entry (e.g. duplicate) caused undefined behavior.

## Root Cause

`filteredWorkflows` in `BaseWorkflowsSidebarTab.vue` was a `ref`
populated once per search event — a static snapshot that never reacted
to store mutations. When `workflowStore.deleteWorkflow()` removed a
workflow from `workflowLookup`, the search-panel's `filteredWorkflows`
still held a stale reference.

## Fix

Convert `filteredWorkflows` from a `ref` to a `computed` that reactively
derives from `searchQuery` and `workflowStore.workflows`. This follows
the same pattern already used by `filteredPersistedWorkflows` and
`filteredBookmarkedWorkflows` in the same component. `handleSearch` is
simplified to only manage tree expansion (its only remaining side
effect).

## Test Plan

Three e2e regression tests added to `workflowSearch.spec.ts`:
- **Delete during search removes from results** — deletes a workflow
while search is active, asserts it disappears
- **Delete during search preserves siblings** — asserts all other
matched workflows remain visible after one is deleted
- **Clear search after delete shows correct browse view** — deletes
during search, clears search, verifies browse view is consistent

## Screenshots

![Search active with 3 workflows
visible](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/3a9cc4daaaf582a87638fc62ec96258cea63981a28bcb282dd625956286c582c/pr-images/1776596412591-c8cc80fb-e57e-4c76-b574-8ab776076105.png)

![After deleting test-alpha during search — removed from results
correctly](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/3a9cc4daaaf582a87638fc62ec96258cea63981a28bcb282dd625956286c582c/pr-images/1776596412913-23eebf62-aac8-4848-8468-8ecb56c0dc8f.png)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11425-fix-remove-deleted-workflow-from-search-results-in-sidebar-3476d73d365081f19ef6c6b9261a1ee9)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
2026-04-23 02:42:21 +00:00
Christian Byrne
739d4b6136 fix: move template distribution filter from v-show to data pipeline (#11418)
*PR Created by the Glary-Bot Agent*

---

## Summary

- Moves distribution-based template filtering from a CSS-level `v-show`
gate into the `useTemplateFiltering` composable's data pipeline,
guaranteeing that templates not meant for the current distribution never
reach the view layer
- Fixes "Showing 19 of 419" count mismatch when only 2 templates are
visible on Cloud with "Wan 2.2" filter active
- Derives `availableModels` and `availableUseCases` from
distribution-visible templates so filter dropdowns don't show options
that only exist on other distributions
- Always prunes `activeModels`/`activeUseCases` against available
options to prevent stale persisted selections from causing zero-result
filtering

## Root Cause

The template selector dialog used
`v-show="isTemplateVisibleOnDistribution(template)"` to hide templates
that don't match the current distribution (cloud/desktop/local). But
`filteredCount` and `totalCount` were computed upstream in the pipeline
before this visual filter, so the count text showed all matching
templates regardless of distribution visibility.

## Changes

- **`useTemplateFiltering.ts`**: Added `visibleTemplates` computed that
applies distribution filter at the top of the pipeline. All downstream
computeds (`fuse`, `availableModels`, `availableUseCases`,
`filteredBySearch`, counts) now operate on this distribution-filtered
set. `activeModels`/`activeUseCases` always prune against available
options.
- **`WorkflowTemplateSelectorDialog.vue`**: Passes `distributions` ref
to composable, removes `v-show` gate and
`isTemplateVisibleOnDistribution` function.
- **`useTemplateFiltering.test.ts`**: 10 new unit tests covering
distribution filtering, filter composition (search + model + use case +
runsOn), stale persisted selections, multi-distribution templates, and
Mac distribution.
- **`templateFilteringCount.spec.ts`**: 5 new `@cloud` e2e tests
verifying count/card consistency, DOM leak prevention, and filter reset
behavior with mocked template data.

## Verification

- 22 unit tests passing (12 existing + 10 new)
- `pnpm typecheck` clean
- `pnpm typecheck:browser` clean
- `oxlint` + `eslint` clean on all changed files
- E2E tests tagged `@cloud` — designed for CI cloud build execution

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11418-fix-move-template-distribution-filter-from-v-show-to-data-pipeline-3476d73d365081c3ba09fc8a42eb4c9b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-23 02:42:04 +00:00
Christian Byrne
0d5f8161f3 test: improve promotedWidgetView coverage to 100% (+9.2%) (#11403)
## Summary

Adds 11 unit tests covering all previously uncovered branches in
`src/core/graph/subgraph/promotedWidgetView.ts`, bringing line coverage
from 90.8% to 100.0%.

## Coverage Delta

| File | Before | After | Delta | Missed |
|------|--------|-------|-------|--------|
| promotedWidgetView.ts | 90.8% | 100.0% | +9.2% | 0 |

## What is covered

- `tooltip` getter
- `hidden` getter
- `label` setter
- `isWidgetValue` — number, boolean, and object branches
- `onPointerDown` — interior handler, concrete widget, and fallthrough
paths
- `bindConcretePointerHandlers` — onClick and onDrag lambdas
- `getProjectedWidget` — null concrete widget reset
- `resolveDeepest` — frame cache path
- `draw` — unknown widget type branch

## Testing

```bash
npx vitest run src/core/graph/subgraph/promotedWidgetView.test.ts
```

75/75 tests pass. All quality gates verified: `pnpm typecheck`, `pnpm
lint`, `pnpm format:check`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11403-test-improve-promotedWidgetView-coverage-to-100-9-2-3476d73d3650818a9558e70de3a92432)
by [Unito](https://www.unito.io)
2026-04-23 02:41:40 +00:00
Christian Byrne
7fc0c357da test: cover error branch in useWorkflowThumbnail (+6.1%) (#11404)
## Summary

Cover the remaining uncovered error-handling branch in
`useWorkflowThumbnail`, bringing unit test coverage from 93.9% to 100%.

## Changes

- **What**: Add 2 unit tests for `createMinimapPreview` error path and
`storeThumbnail` null-thumbnail branch

## Review Focus

Tests verify that when `createGraphThumbnail` throws,
`createMinimapPreview` returns `null` and `storeThumbnail` does not
persist anything.

## Coverage Delta

| File | Before | After | Delta | Missed |
|------|--------|-------|-------|--------|
| `useWorkflowThumbnail.ts` | 93.9% | 100.0% | 🟢 +6.1% | 0 |

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11404-test-cover-error-branch-in-useWorkflowThumbnail-6-1-3476d73d36508148a135ee29f7abf22e)
by [Unito](https://www.unito.io)
2026-04-22 19:47:13 -07:00
Christian Byrne
25ff147176 test: add unit tests for CanvasPathRenderer (#11387)
## Summary

Add 51 unit tests for `CanvasPathRenderer`, improving line coverage from
23.2% to 84.19% (100% function coverage).

## Changes

- **What**: New test file
`src/renderer/core/canvas/pathRenderer.test.ts` covering color
determination, border rendering, linear/straight/spline path modes,
`findPointOnBezier`, center point calculation, arrows, flow animation,
center markers, disabled patterns, and `drawDraggingLink`.

## Review Focus

Pure test addition — no production code changes. Tests mock `Path2D` via
`vi.stubGlobal` and `CanvasRenderingContext2D` via a plain object with
`vi.fn()` methods.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11387-test-add-unit-tests-for-CanvasPathRenderer-3476d73d365081bbb526d584cc41b723)
by [Unito](https://www.unito.io)
2026-04-22 19:46:57 -07:00
Christian Byrne
0efc0c4d72 test: exclude legacy UI component library from e2e coverage (#11377)
*PR Created by the Glary-Bot Agent*

---

## Summary

- Excludes `src/scripts/ui/**` (legacy DOM component library) from
Playwright e2e coverage reports — this code is kept solely for extension
backwards-compatibility and shouldn't count toward coverage metrics
- Extracts Monocart coverage config (`outputDir`, `sourceFilter`) into
`browser_tests/coverageConfig.ts` so coverage exclusions are
discoverable and centralized instead of buried in `globalTeardown.ts`

## Details

Monocart does support external config files (`mcr.config.ts`
auto-discovery), but since MCR is instantiated in two places with
different configs (per-worker collection in `ComfyPage.ts` vs final
report in `globalTeardown.ts`), auto-discovery would affect both
instances. A shared TypeScript constant is safer and more explicit.

## Changes
- **New**: `browser_tests/coverageConfig.ts` — shared
`COVERAGE_OUTPUT_DIR` and `coverageSourceFilter`
- **Modified**: `browser_tests/globalTeardown.ts` — imports from shared
config
- **Modified**: `browser_tests/fixtures/ComfyPage.ts` — imports
`COVERAGE_OUTPUT_DIR`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11377-test-exclude-legacy-UI-component-library-from-e2e-coverage-3466d73d365081b78dc9e4e14d913295)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
2026-04-22 19:46:34 -07:00
Christian Byrne
d0e3b7ebf0 fix: handle EPERM/EBUSY in global teardown restorePath (#11013)
Fixes #11009

## Summary

On Windows, Chromium may still hold file handles on the user-data
directory when global teardown runs `restorePath`. The `fs.moveSync(...,
{ overwrite: true })` call fails with EPERM because it can't remove the
target while handles are held.

## Changes

- Split `restorePath` into explicit remove-then-move
- Added `removeWithRetry` that retries up to 3× on EPERM/EBUSY with
500ms delay between attempts
- Downgraded the catch from `console.error` (which looks like a test
failure) to `console.warn` so teardown noise doesn't mask real failures

No E2E regression test added: this is a test-infrastructure fix for a
Windows-specific race condition in teardown that cannot be reliably
reproduced in CI.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11013-fix-handle-EPERM-EBUSY-in-global-teardown-restorePath-33e6d73d3650815ebe0cd42af23e6c0e)
by [Unito](https://www.unito.io)
2026-04-22 19:44:43 -07:00
Christian Byrne
f2677aa598 fix: include actual slot index in InputSlot/OutputSlot keys to prevent stale indices after autogrow (#11423)
*PR Created by the Glary-Bot Agent*

---

## Summary

Fixes auto-grow input slot connections breaking in Nodes 2.0 when a node
has multiple auto-grow groups (e.g., `Wan 2.7 Reference to Video` with
both image and video auto-grow inputs).

## Problem

When auto-grow adds inputs to one group, it splices new entries into
`node.inputs`, shifting the indices of all subsequent groups. The data
layer handles this correctly via `spliceInputs()`, but Nodes 2.0 Vue
components retained stale slot indices because:

1. **`NodeSlots.vue`** keyed `InputSlot`/`OutputSlot` by name only — Vue
reused components without remounting when indices shifted
2. **`useSlotElementTracking`** registered `data-slot-key` once at mount
and stopped its watcher — stale keys persisted in the DOM
3. **`useSlotLinkInteraction`** captured `index` in closures at mount —
stale closures targeted wrong slots

This caused connections to land on wrong inputs, incorrect hover
indicators, and some slot types becoming unreachable.

## Fix

Include the actual slot index in the component key for `InputSlot` (both
in `NodeSlots.vue` and `NodeWidgets.vue`) and `OutputSlot`. When
autogrow shifts a slot's position or an output is removed, the key
changes, forcing Vue to remount — which re-registers `data-slot-key` and
refreshes all interaction closures with the correct index.

## Testing

- **Remount verification**: Tests use setup() invocation counting to
prove components are actually remounted (not just prop-patched) when
indices shift — directly validating that `useSlotElementTracking` and
`useSlotLinkInteraction` are re-initialized
- **Multi-group autogrow**: Verifies data-layer index correctness when
first group growth shifts second group
- **Output removal**: Verifies OutputSlot remount when earlier output
removal shifts later output indices
- All existing tests pass, lint/typecheck/format clean

## Screenshots

![ComfyUI frontend loads correctly with Nodes 2.0 after the
fix](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/ed95fd14fd7d0e7797f7fa7a2737ed737829303e39a30afe453eb05e19218a2d/pr-images/1776594420504-a5c1b967-626d-4463-b1e3-ae800535c57b.png)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11423-fix-include-actual-slot-index-in-InputSlot-OutputSlot-keys-to-prevent-stale-indices-aft-3476d73d365081859da6c450a840a625)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
2026-04-22 19:42:45 -07:00
AustinMroz
fc9a1d6bfb Add audio/video preview tests (#11523)
Adds tests for the vue audio preview widget and vue video previews
(which are not widgets).

Also
- Fixes a bug where muted audio previews would incorrectly display a
'low volume' indicator instead of a muted indicator.
- Add test helper for deleting uploaded files after a test completes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11523-Add-audio-preview-tests-3496d73d365081be8630ede6dae1726a)
by [Unito](https://www.unito.io)
2026-04-23 02:14:48 +00:00