Commit Graph

8338 Commits

Author SHA1 Message Date
dante01yoon
8e0970e6d3 Merge branch 'main' into jaewon/fe-729-delete-is-asset-api-enabled 2026-06-20 08:34:15 +09:00
pythongosssss
c8b5589768 feat: remove subscript prompt dialog on load (#13012)
## Summary

The first thing a user sees when they log in is the paywall prompt which
is a poor UX. This removes the dialog that pops up as the run buttons
are already gated to prompt to subscribe.

## Changes

- **What**: 
- Remove extension & invocation
2026-06-19 21:31:17 +00:00
Christian Byrne
b4b95980da fix: publish core/* releases immediately to create tags (#12995)
## Summary
- Fix release pipeline where core/* releases hang indefinitely waiting
for tags

## Problem
Draft releases don't create git tags, but `publish-pypi` workflow waits
for the tag to exist. For `core/*` branches, releases were always
created as drafts, causing the pipeline to wait forever.

## Solution
Only use draft releases for prereleases (alpha/beta/rc). Publish all
stable releases (main and core/*) immediately so tags are created.

## Test plan
- [x] Verify workflow change logic is correct
- [ ] Test on next core/1.45 release

Co-authored-by: Connor Byrne <c.byrne@comfy.org>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-06-19 14:28:49 -07:00
Deep Mehta
8c0af36c4a fix: re-disable ultralytics asset-picker registration (#13020)
## Summary

Re-comment the `ultralytics/bbox` and `ultralytics/segm` entries in
`MODEL_NODE_MAPPINGS` so `UltralyticsDetectorProvider` falls back to the
static combo from `/api/object_info`, restoring the working post-#12075
behavior. Companion to
[Comfy-Org/cloud#4395](https://github.com/Comfy-Org/cloud/pull/4395)
(ONNX dummy-placeholder fix).

## Why

PR #12151 (2026-05-12) re-added these entries before BE-689 (the
cloud-side asset-ingestion fix) landed, reintroducing both halves of the
regression that #12075 originally cited:

1. **Tag lookup mismatch.** Cloud stores model tags as combined values
like `ultralytics/bbox`, but the asset query splits them into `(models,
ultralytics)` with exact-match filtering, so the picker returns no
results.
2. **Submitted value mismatch.** Picker returns basenames like
`face_yolov8m.pt`, but the ingest service validates against the
subdirectory-prefixed form (`bbox/face_yolov8m.pt`) that the static
combo path produces. The combo rejects the basename with `Value not in
list` (the exact error users hit in BE-1116).

Until BE-689 lands both halves of the cloud fix, the asset-picker
registration cannot ship without breaking the node.

## Changes

- **What**: comment out the two ultralytics entries in
`src/platform/assets/mappings/modelNodeMappings.ts` with an inline note
pointing at BE-689 and PR #12075 / #12151 for context.
- **Breaking**: none. The node returns to the same static-combo behavior
it had between #12075 and #12151.
- **Dependencies**: none.

## Review Focus

- The existing assertion in `src/stores/modelToNodeStore.test.ts:278`
already expects `ultralytics`, `ultralytics/bbox`, and
`ultralytics/segm` to NOT resolve to a default provider — that test was
added with #12075 and was left in place when #12151 re-added the
mappings. After this change the mappings file is consistent with the
test again. 55/55 in `modelToNodeStore.test.ts` pass locally.
- This is a targeted revert of the ultralytics half of #12151. The other
entries that PR added (`background_removal`, `frame_interpolation`,
`film`) are unaffected.

## Companion PRs

- Comfy-Org/cloud#4395 — fixes the `ONNXDetectorProvider` half of the
FaceDetailer-broken report (BE-1116) by repairing the dummy-placeholder
mechanism so the static combo carries the right model list. ONNX uses
the static-combo path (no `MODEL_NODE_MAPPINGS` entry), so the
cloud-side fix is sufficient there.

<!-- Fixes BE-689 -->
2026-06-19 20:59:56 +00:00
jaeone94
78a8d6f8fc test: stabilize node help locale e2e (#12998)
## Summary

Stabilizes the locale-specific Node Help E2E by setting the locale
through the existing Playwright settings fixture before app bootstrap
instead of racing a workflow reload pulse.

## Changes

- **What**: Removed the brittle in-page locale mutation helper and uses
`test.use({ initialSettings: { 'Comfy.Locale': 'ja' } })` for the
locale-specific documentation case.
- **What**: Keeps the Japanese and English doc routes local to the test,
then verifies the Japanese help content after loading the default
workflow.
- **Dependencies**: None.

## Review Focus

Please focus on whether the E2E now waits on the correct setup boundary.
The previous helper watched `ChangeTracker.isLoadingGraph` after
changing `Comfy.Locale`; once unrelated workflow-load work became
faster, that loading pulse could complete before the helper observed it.
Pre-boot `initialSettings` avoids that timing dependency and uses
existing test infrastructure.

Verification:

- `pnpm exec oxfmt --check browser_tests/tests/nodeHelp.spec.ts
browser_tests/fixtures/helpers/WorkflowHelper.ts`
- `pnpm exec eslint browser_tests/tests/nodeHelp.spec.ts
browser_tests/fixtures/helpers/WorkflowHelper.ts`
- `pnpm typecheck:browser`
- `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:5176 pnpm
exec playwright test browser_tests/tests/nodeHelp.spec.ts --grep "Should
handle locale-specific documentation" --project=chromium
--repeat-each=10`

## Screenshots (if applicable)

N/A
2026-06-19 19:50:04 +00:00
jaeone94
cc41e3e1ac test: stabilize cloud template filtering e2e (#12999)
## Summary

Stabilizes the cloud template filtering E2E by making the test own both
startup asset API responses and the complete template universe it
asserts against.

## Changes

- **What**: Uses the existing `createCloudAssetsFixture([])` fixture so
startup `/api/assets` calls do not create an unrelated error toast that
can intercept the Clear Filters click.
- **What**: Extends `TemplateHelper.mockIndex()` to also mock
`/api/workflow_templates` as an empty custom-template map, so tests that
configure a core template index do not accidentally include custom
templates from the local backend.
- **Dependencies**: None.

## Review Focus

Please focus on fixture ownership. This spec asserts exact template
counts, so `templateApi.mockIndex()` should isolate the core template
index and the custom workflow-template endpoint together. The asset
fixture change is intentionally scoped to this cloud spec and reuses
existing infrastructure instead of dismissing arbitrary toasts or
forcing clicks.

Verification:

- `pnpm exec oxfmt --check
browser_tests/tests/templateFilteringCount.spec.ts
browser_tests/fixtures/helpers/TemplateHelper.ts
browser_tests/fixtures/helpers/WorkflowHelper.ts`
- `pnpm exec eslint browser_tests/tests/templateFilteringCount.spec.ts
browser_tests/fixtures/helpers/TemplateHelper.ts
browser_tests/fixtures/helpers/WorkflowHelper.ts`
- `pnpm typecheck:browser`
- `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188
PLAYWRIGHT_SETUP_API_URL=http://localhost:8188 pnpm exec playwright test
browser_tests/tests/templateFilteringCount.spec.ts --grep "clear filters
button resets" --project=cloud --repeat-each=5`
- `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188
PLAYWRIGHT_SETUP_API_URL=http://localhost:8188 pnpm exec playwright test
browser_tests/tests/templateFilteringCount.spec.ts --project=cloud`

## Screenshots (if applicable)

N/A
2026-06-19 17:34:23 +00:00
Dante
f994673dd1 fix(test): import AppMode from utils/appMode in WorkflowHelper (#13003)
## Summary

`pnpm typecheck:browser` (`vue-tsc --project
browser_tests/tsconfig.json`) currently fails on `main`, turning
`lint-and-format` red for every PR built on it:

```
browser_tests/fixtures/helpers/WorkflowHelper.ts(5,15): error TS2459:
Module '"@/composables/useAppMode"' declares 'AppMode' locally, but it is not exported.
```

## Cause

#12925 (`bc885f383c`, "Decouple run telemetry context from providers")
moved the `AppMode` type from `@/composables/useAppMode` to
`@/utils/appMode`. `useAppMode` now only `import type`s `AppMode` (it no
longer re-exports it), so this fixture's `import type { AppMode } from
'@/composables/useAppMode'` became invalid. The other `useAppMode`
import sites are unaffected — they import the `useAppMode` composable
(still exported), not the type.

## Fix

Import `AppMode` from its actual source, `@/utils/appMode`. One line, no
behavior change.

Verified `vue-tsc --project browser_tests/tsconfig.json` no longer
reports the `WorkflowHelper.ts` error.
2026-06-19 15:17:57 +00:00
jaeone94
444dc3fccd test: stabilize mask editor screenshot e2e (#13011)
<img width="1155" height="648" alt="스크린샷 2026-06-19 오후 11 24 27"
src="https://github.com/user-attachments/assets/01ed2607-662f-4735-b0c2-2f1a2c8a8811"
/>

## Summary

Stabilizes the Mask Editor screenshot E2E by hiding the transient brush
cursor before capturing the dialog.

## Changes

- **What**: Moves the pointer from the mask editor pointer zone to the
Brush Settings panel before the screenshot and asserts that the brush
cursor is hidden.
- **Dependencies**: None.

## Review Focus

Please check that the test still exercises the dialog UI while excluding
only cursor-position noise from the screenshot. The PR also includes the
existing browser-test `AppMode` type import fix needed for
`typecheck:browser` on branches that touch `browser_tests/**`.


Validation:
- `pnpm exec oxfmt --check browser_tests/tests/maskEditor.spec.ts
browser_tests/fixtures/helpers/WorkflowHelper.ts`
- `pnpm exec eslint browser_tests/tests/maskEditor.spec.ts
browser_tests/fixtures/helpers/WorkflowHelper.ts`
- `pnpm typecheck:browser`
- `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:5173 pnpm
exec playwright test browser_tests/tests/maskEditor.spec.ts --grep
"opens mask editor from image preview button" --project=chromium
--repeat-each=10`

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-06-19 14:57:31 +00:00
jaeone94
ed028a88be Fix LiteGraph hidden widget metadata handling (FE-1014) (#12916)
## Summary

Fixes FE-1014 by making legacy LiteGraph honor backend-provided hidden
widget metadata.

This PR adds a regression test for the Painter node and updates
LiteGraph widget construction so that a backend input spec with `hidden:
true` is mirrored onto the top-level `widget.hidden` property that the
legacy canvas renderer actually reads.

## Problem

Backend node definitions can mark inputs as hidden, for example with
`extra_dict={"hidden": True}`. That metadata already flows into the
frontend widget options as `widget.options.hidden`, which is why Vue
nodes correctly hide those fields.

Legacy LiteGraph, however, does not use `widget.options.hidden` for
canvas visibility. Its rendering, layout, and hit-testing paths check
top-level `widget.hidden` instead. As a result, a field could be hidden
in Vue nodes while still appearing as an editable control in the legacy
LiteGraph canvas.

For affected nodes, this exposes fields that are intended to be
implementation details, schema/version values, or other non-user-facing
inputs.

## Root Cause

The frontend widget construction path copied backend display metadata
into `widget.options`, including:

- `advanced`
- `hidden`

But it did not mirror backend `hidden` metadata into `widget.hidden`.

That created a renderer split:

- Vue nodes and the right panel use `widget.options.hidden`.
- Legacy LiteGraph uses top-level `widget.hidden`.

So backend-hidden widgets were hidden in Vue mode but still visible and
clickable in legacy LiteGraph mode.

## Implementation

The production change is intentionally small and scoped to
backend-provided hidden metadata:

- Continue assigning `inputSpec.hidden` to `widget.options.hidden` as
before.
- When `inputSpec.hidden` is explicitly defined, also assign it to
top-level `widget.hidden`.

This keeps Vue behavior unchanged while making the legacy LiteGraph
renderer receive the same backend hidden signal through the field it
already uses for visibility.

The fix deliberately does not mirror `advanced` into top-level
`widget.advanced`. While investigating this area, I found that many
backend inputs define `advanced`, and changing legacy advanced-widget
behavior would be a much broader behavioral change than FE-1014
requires. This PR only addresses hidden metadata.

## Test Coverage

This PR adds and tightens Painter regression coverage because Painter
currently provides a concrete backend-hidden widget case:

- In Vue mode, the test verifies hidden Painter widgets are not rendered
to the user.
- In legacy LiteGraph mode, the test disables Vue nodes, loads the
Painter workflow, clicks the rows where backend-hidden number widgets
used to be exposed, and verifies the legacy graph editor dialog does not
open.

The legacy test specifically covers the backend-hidden number widgets
`width` and `height`. It uses user-observable behavior rather than
asserting internal widget flags directly.

A follow-up discussion is ongoing about the broader contract between
`widget.options.hidden` and top-level `widget.hidden`, especially for
frontend-extension-only hiding such as Painter `bg_color`. This PR
intentionally keeps that broader renderer-contract question out of scope
and focuses on backend `hidden` metadata from FE-1014.

## Validation

Validated locally with targeted Playwright coverage:

```bash
PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:5173 pnpm exec playwright test browser_tests/tests/painter.spec.ts --project=chromium -g "Does not render hidden standard widgets|Does not open editors for backend-hidden number widget rows"
```

Result:

```text
2 passed
```

Also validated with linting:

```bash
pnpm eslint src/services/litegraphService.ts browser_tests/tests/painter.spec.ts
pnpm eslint browser_tests/tests/painter.spec.ts
```

The commit hooks also passed:

- `oxfmt`
- `oxlint`
- `eslint`
- `pnpm typecheck`
- `pnpm typecheck:browser`

## Notes

The new legacy test was confirmed red before the production fix and
green after the production fix, so it is not a vacuous assertion. The
final cleanup commit only tightens test naming and coordinate handling
while preserving the same regression intent.
2026-06-19 14:05:49 +00:00
Terry Jia
26cd975c1d refactor(load3d): extract Viewport3d base + SceneOverlay protocol (#12987)
## Summary
Split Load3d into Viewport3d (model-agnostic viewport scaffolding) and
Load3d (extends Viewport3d, adds
loader/model/animation/HDRI/recording/gizmo).
Viewport3d exposes only render-loop plumbing, layout, mouse status,
camera orchestration, and the SceneOverlay protocol so future 3D node
viewports can compose it without inheriting model machinery.

- SceneOverlay protocol
(attach/detach/update/onActiveCameraChange/dispose) gives any 3D node a
managed lifecycle slot for plugging scene content into the viewport.
- Viewport3d.setExternalActiveCamera(cam | null) for POV swap: renders
from an externally-owned camera (e.g. a subject camera authored by an
overlay), with OrbitControls detached and the view helper hidden.
- ControlsManager.detach()/attach() back the POV control gating.
- Two-phase init via Viewport3d.start() so subclass field assignments
finish before any render-path code dispatches through overridden
tickPerFrame / isActive.

Behavior preserved for all 5 Load3d consumers (Load3D, Preview3D,
PreviewGaussianSplat, PreviewPointCloud, SaveGLB).
Sets up the upcoming CreateCameraInfo preview and other future
Three.js-based 3D nodes (Pose Editor, Animation Director) to compose
Viewport3d plus their own SceneOverlay implementation.
2026-06-19 07:00:33 -04:00
Dante
c2968422e6 fix(billing): refresh workspace billing status after completed top-up (FE-932) (#12787)
## Summary

A completed workspace top-up refreshed only the balance, leaving billing
status — and `subscription.hasFunds` (derived from
`statusData.has_funds`) — stale until the next status fetch. The
completed handler now refreshes both.

## Changes

- **What**: `TopUpCreditsDialogContentWorkspace.vue` completed branch —
`await fetchBalance()` → `await Promise.all([fetchBalance(),
fetchStatus()])` (both already exposed on `useBillingContext()`).
- **Breaking**: none.

## Review Focus

- Pre-existing bug (predates the B2 facade; `main`'s top-up already
called `fetchBalance` only). Test validity proven by reverting to
balance-only → the completed case goes red on the `fetchStatus`
assertion.
- Tests: completed → both refresh; pending / failed → neither (3 cases).
typecheck / oxlint / eslint / stylelint / oxfmt / knip clean.

Fixes FE-932
2026-06-19 06:39:10 +00:00
Benjamin Lu
5acd76cb6d Add Desktop telemetry event sink (#12802)
## Summary

- initialize a Desktop-only telemetry provider in ComfyUI_frontend
- forward existing typed telemetry events through
`window.__comfyDesktop2.Telemetry.capture` using the existing event
names
- move the Desktop 2 bridge typing to the shared ambient types and let
run/execution telemetry fire when any provider is registered

## Paired change

- Desktop PR: https://github.com/Comfy-Org/Comfy-Desktop/pull/1069

## Validation

- `pnpm typecheck`
- `pnpm format:check`
- `pnpm lint`
- `pnpm knip`
- `pnpm test:unit src/platform/missingModel/missingModelDownload.test.ts
src/platform/telemetry/initDesktopTelemetry.test.ts
src/platform/telemetry/providers/desktop/DesktopTelemetryProvider.test.ts`
- YAML lint over tracked YAML files with `.yamllint`


[MAR-240](https://linear.app/comfyorg/issue/MAR-240/frontend-telemetry-pipeline-for-desktop-app-eventsink-refactor)
2026-06-18 20:19:35 -07:00
Benjamin Lu
bc885f383c Decouple run telemetry context from providers (#12925)
## Summary

Move run-button context assembly out of telemetry providers so telemetry
can initialize without importing app-mode/workspace state.

## Changes

- **What**: Providers now accept completed `RunButtonProperties`;
run-button call sites use a workspace composable to build that payload.
- **Dependencies**: None.
2026-06-18 19:26:17 -07:00
imick-io
fc4d44c3db [feat] migrate website navbar to shadcn-vue + mobile sheet drill-down (#12861)
## Summary
- Migrate the website's top nav from bespoke components (`SiteNav`,
`MobileMenu`, `NavDesktopLink`, `PillButton`, `MaskRevealButton`) to
shadcn-vue primitives (`NavigationMenu`, `Sheet`, `Button`), split into
`HeaderMain` → `HeaderMainDesktop` + `HeaderMainMobile`.
- Mobile nav becomes a `Sheet` with drill-down sub-navigation, scroll
lock, sticky CTAs, sr-only i18n title/description, and a back-to-home
logo.
- Desktop nav uses `NavigationMenu` with shared viewport, featured
cards, `NavColumn` extraction, and centralized nav data in
`data/mainNavigation.ts`.
- Adds i18n strings for nav labels, dropdown column headers, close/back
affordances, and the mobile menu description.

## Test plan
- [ ] Desktop: hover PRODUCTS / COMMUNITY / COMPANY — dropdowns open
with featured card + columns, NEW badges render, external links show the
arrow-up-right icon, viewport is shared between triggers.
- [ ] Mobile (<lg): open hamburger sheet — verify logo + close, body
scroll is locked while open, top-level nav scrolls if it overflows, CTAs
stay pinned at bottom.
- [ ] Tap COMMUNITY / PRODUCTS / COMPANY — sub-panel slides over root
nav, in-sheet BACK returns to root, links navigate correctly.
- [ ] Reload `?locale=zh-CN`: dropdown labels, sheet title/description,
BACK / close affordances all localized.
- [ ] No regressions on `/cloud`, `/cloud/pricing`, `/customers`, etc. —
pages that swap CTAs (`BrandButton` → shadcn `Button`) still render at
every breakpoint.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-06-19 01:55:02 +00:00
AustinMroz
7f25d28b71 Filter canvasOnly non-preview widgets in editor (#12957)
Non preview, `canvasOnly` widgets like `control_after_generate` could be
displayed in the subgraph editor even though promoting them would have
no visual or functional effect when in vue mode.

In vue mode, these entries are now hidden from the list of candidate
items for promotion to reduce confusion.
2026-06-19 00:59:00 +00:00
Dante
67b884d0f7 fix(billing): route subscription/sign-in/credit preconditions to modal, out of error panel (FE-878) (#12785)
## Summary

Account preconditions (sign-in / subscription / credits) on running a
workflow now open their modal directly and stay out of the error panel +
error count — previously `subscription_required` fell through to a red
"1 ERROR — Subscription required to queue workflows" banner. This covers
**both** paths: the `execution_error` websocket event and the `POST
/prompt` 402 queue paywall (`{ type: "PAYMENT_REQUIRED", message:
"Subscription required to queue workflows" }`), which is the exact
payload reported in #12840.

## Changes

- **What**: `execution_error` is classified by a pure
`accountPreconditionRouting` resolver (precedence sign-in > subscription
> credits) and routed to the existing modal via
`useAccountPreconditionDialog`; `executionStore` returns early for
preconditions so they never populate `lastExecutionError` /
`lastPromptError` / `lastNodeErrors` → fully excluded from the panel and
`totalErrorCount`. Runtime credit error at a node → credits modal (out
of panel; can name the node).
- **Queue paywall**: the `queuePrompt` catch resolves the same
precondition from the `POST /prompt` 402 response and opens the modal,
short-circuiting before `lastPromptError`, so the queue paywall stays
out of the panel too. The runtime matcher learns the `"Subscription
required to queue workflows"` message.
- **Breaking**: none.

## Before / After

Free-tier queue paywall (`POST /prompt` → 402) on a cloud build:

**Before** — raw `Subscription required to queue workflows` surfaced in
the error panel (no actionable upgrade):

<img width="1600" height="873" alt="before-error-panel"
src="https://github.com/user-attachments/assets/1b76b742-16bf-47e3-9245-17e35f8f1e70"
/>

**After** — clean subscription modal opens; nothing in the error panel
or error count:

<img width="1600" height="873" alt="after-subscription-modal"
src="https://github.com/user-attachments/assets/13d238cb-20bf-4795-a530-5abcf9968dc7"
/>

## Review Focus

- **Routing-only — the run button is intentionally untouched.** The
original AC#3 ("no Subscribe-to-Run button") is superseded by the FE-978
run-lock decision (pre-emptive role-aware lock, Figma 3253-18671).
Complements #12786 (FE-978 run-lock); disjoint file sets.
- Tests: `accountPreconditionRouting` / `useAccountPreconditionDialog` /
`executionStore` — each precondition routes to its modal and is excluded
from the panel/count; precedence resolves on co-occurrence. Plus
Playwright `browser_tests/tests/subscriptionPaywallError.spec.ts` — the
queue paywall (402) stays out of the error panel, with a control
asserting ordinary queue errors still surface. typecheck / oxlint /
eslint / stylelint / oxfmt / knip clean.

Fixes FE-878
Fixes #12840
2026-06-19 00:51:52 +00:00
Benjamin Lu
05efee07ce Move Comfy Desktop bridge types into frontend (#12857)
## Summary

Adds `@comfyorg/comfyui-desktop-bridge-types` as a workspace package in
the frontend monorepo and changes the frontend app dependency to
`workspace:*`.

Adds a dedicated `Publish Desktop Bridge Types` workflow for publishing
`packages/comfyui-desktop-bridge-types` by its own package version,
without coupling it to the generated `@comfyorg/comfyui-frontend-types`
release. The generated frontend types package still emits a concrete
`@comfyorg/comfyui-desktop-bridge-types@0.1.2` dependency instead of
leaking workspace/catalog protocol references.

The Desktop2 missing-model path uses `window.__comfyDesktop2.isRemote()`
when available, but falls back to the legacy
`window.__comfyDesktop2Remote` marker so frontend rollout stays
compatible with older Desktop builds.

Paired Desktop PR: https://github.com/Comfy-Org/Comfy-Desktop/pull/1112
2026-06-18 18:02:13 -07:00
Christian Byrne
bc212e8a19 fix: remove unused export from ExportFormatOption interface (#12973)
## Summary

Remove unused `export` keyword from `ExportFormatOption` interface. The
interface is only used internally in `constants.ts` and is not imported
elsewhere.

Fixes knip "unused exported types" error.

Co-authored-by: Connor Byrne <c.byrne@comfy.org>
2026-06-18 17:57:44 -07:00
github-actions
4066b2aa61 [automated] Update test expectations 2026-06-19 00:48:40 +00:00
AustinMroz
4199ab2f01 Support opus format in assets panel (#12956)
| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/aa6ff824-ee90-49db-a059-0da408ba10bf"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/98717dfd-4fee-474a-bd5c-03e7b7e23a05"
/>|
2026-06-19 00:23:22 +00:00
Dante
b0e0423c1b Merge branch 'main' into jaewon/fe-729-delete-is-asset-api-enabled 2026-06-19 09:22:09 +09:00
dante01yoon
bc5d7a3249 fix(assets): fall back to sidebar toggle when BrowseModelAssets unregistered (FE-729)
If Comfy.BrowseModelAssets is not yet registered (load-ordering race), the
model-library tab click was a silent no-op via optional chaining. Fall back to
toggleSidebarTab so the click is never dead, and add regression coverage.
2026-06-19 08:42:20 +09:00
Alexander Brown
ab6c44aabf feat: remove deprecated group nodes, auto-convert to subgraphs on load (#12931)
## Summary

Removes the deprecated Group Nodes feature and replaces it with a
load-time migration that auto-converts any group nodes in a loaded
workflow into Subgraphs (with accepted lossiness).

## Changes

- **What**:
- `groupNode.ts` is now a migration-only extension.
`beforeConfigureGraph` registers temporary node types from
`extra.groupNodes` so instances are created during `configure`; a new
`afterConfigureGraph` hook converts every group node in the root graph
to a subgraph (via `LGraph.convertToSubgraph`), re-scanning until none
remain, then deletes `extra.groupNodes`. A failed conversion removes the
offending node so loading never hangs or breaks.
- Kept the minimum needed: `GroupNodeConfig` (builds the
input/output/widget maps), a slimmed `GroupNodeHandler` exposing a
rewritten `convertToNodes()` that no longer depends on the execution
DTOs, the `globalDefs`/`addCustomNodeDefs` path, and the `nodeDefStore`
`Object.assign` shim the migration relies on to detect group nodes.
- Deleted: the Manage Group Nodes dialog (`groupNodeManage.ts`/`.css`),
execution DTOs (`executableGroupNodeDto.ts`,
`executableGroupNodeChildDTO.ts`), the create/builder flow, recreate,
commands, keybindings, menus, the `isGroupNode` branches in the
right-side panel / error grouping / focus composable, the group-node
branches in node templates, dead i18n keys, and the now-unused
`serialise` clipboard helper.
- Rewrote `browser_tests/tests/groupNode.spec.ts` to assert
auto-conversion; deleted the `ManageGroupNode` page object and
`manageGroupNode()` helper.
  - Net: ~2,700 lines removed across 23 files (7 files deleted).
- **Breaking**: Group nodes can no longer be created, managed, or
executed. Existing workflows still load — their group nodes are
converted to subgraphs on open.

## Review Focus

- The load-time migration in `afterConfigureGraph` and the rewritten
`GroupNodeHandler.convertToNodes()` (no longer uses the execution
`getInnerNodes()` / DTOs; derives inner node type/index from
`groupData.nodeData.nodes` and relies on `deserialiseAndCreate` +
selection ordering).
- Kept `nodeDefStore`'s `Object.assign(this, obj)` shim: the migration
depends on it to propagate the group-node marker symbol onto the
registered node definition.

### Accepted lossiness
- Group nodes nested inside subgraphs (or inside other group nodes)
convert into the root graph rather than their original container —
essentially nonexistent in real legacy workflows since group nodes
predate subgraphs.
- Temporary `workflow>name` node types stay registered for the session;
instantiating one auto-converts it to a subgraph.

## Verification

`pnpm typecheck`, `typecheck:browser`, `knip`, `oxlint`, `eslint`, and
`oxfmt` are green (also enforced by pre-commit hooks). Unit tests for
the touched files could not be run locally due to a pre-existing
environment error (`file:///assets/images/*.svg` passed to a Node
filename API at import time, which also fails on unmodified test files);
the browser spec requires a live server.

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2026-06-18 21:16:24 +00:00
Dante
2cdaead000 fix(cloud): stop bouncing working users to /cloud/survey mid-session (FE-739) (#12621)
## Summary

Cloud users get yanked to `/cloud/survey` mid-workflow with no user
action. The redirect is **downstream of auth**: when the Cloud token is
briefly stale (token rotation / auth-refresh / reconnect window), the
authenticated survey-status check 401s, and the gate turned that
transient 401 into "survey not completed" → redirect.

Surveys are currently disabled for everyone on cloud via dynamicconfig
as the live mitigation. This PR lets us re-enable them safely
**without** waiting on the auth rework.

## Root cause

`getSurveyCompletedStatus()` returned `false` ("not completed") on
**any** non-200 — including a transient 401/403/5xx or network error —
and consumers treat `false` as a redirect to the survey. So a
stale-token 401 (or the page force-reload a 401 triggers in
`GraphCanvas`) bounced a working, already-onboarded user to the survey.

The real root cause of the transient 401s is a separate, still-open
effort: **FE-963** (reactive 401 re-mint + single retry), **FE-950/951**
(unified Cloud JWT), **BE-1125**. This PR does **not** fix those; it
stops the survey from being their user-visible casualty.

## The fix

`getSurveyCompletedStatus` now distinguishes the responses instead of
failing closed on all of them:

- **404** → not completed (show survey). This is the genuine signal: the
cloud backend (`GetSettingById`) returns 404 for a survey key that was
never stored, and a 404 is only reachable after a successful
authenticated read (a stale token 401s, never 404s), so it can't be a
transient false signal.
- **transient 401/403/5xx/network** → treat as completed (fail-safe), so
a working user is never bounced.
- **200** → completed iff `value` is non-empty (unchanged).

**No router change.** The `/` onboarding guard is untouched (router.ts
matches main), so the existing UX is preserved — a not-completed user is
still gated to the survey on load; only the spurious transient-failure
bounce is removed.

## Why #12301 was reverted, and how this differs

#12301 shipped a **blanket** fail-safe (`!response.ok → true`, 404
included), which made the survey unreachable for genuinely-not-completed
users (404 → "completed") and was reverted in #12344. This PR
special-cases **404 as the real not-completed signal** and fails safe
only on transient/ambiguous responses, so onboarding still works.

## Tests

- **Unit** (`auth.test.ts`): 200 non-empty → true; 200 empty / `null` /
missing `value` key → false; **404 → false**; 401/403/500/network →
true.
- **E2E** (`browser_tests/tests/cloudSurveyGate.spec.ts`, `@cloud`): a
transient 401 on `/` does **not** bounce a working user; a genuine 404
on `/` **does** route to the survey.

Linear: FE-739. Root cause (separate): FE-963 / FE-950 / FE-951 /
BE-1125.
2026-06-18 20:52:41 +00:00
imick-io
003303d8ac feat(website): pricing API copy + tutorial video captions (#12929)
## Summary

- **Pricing copy** — replace "concurrent API jobs" wording with
"workflows via API" on Creator/Pro plans, add a third feature line to
Standard, and add a new "Run Workflows via API" entry to the "What's
Included" section (existing feature11 → feature12).
- **Tutorial captions** — wire per-locale VTT caption tracks into
`TutorialDetailDialog`, so the CC button in `VideoPlayer` is now
functional for the learning tutorials. `LearningTutorial.caption` is
typed as `readonly VideoTrack[]`; `VideoTrack` is now exported from
`VideoPlayer.vue`.
- Tailwind class ordering in `PriceSection.vue` shifted due to the
formatter on touched lines.

## Test plan

- [ ] `/pricing` page (EN + zh-CN): Standard plan shows the new "1
workflow via API" line; Creator/Pro show "3/5 workflows via API";
"What's Included" lists "Run Workflows via API" above "Parallel job
execution".
- [ ] Open a learning tutorial → CC button is visible in the player →
toggling CC shows English captions in sync with playback.
- [ ] Tutorials without a `caption` entry hide the CC button.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions <github-actions@github.com>
2026-06-18 20:51:40 +00:00
Benjamin Lu
ca2ead3c4a fix: guard workspace auth refresh races (#11726)
## Summary

Fixes FE-485.

This updates workspace auth refresh handling so stale in-flight refresh
responses cannot overwrite a newer workspace context, and exhausted
transient token exchange failures preserve the existing workspace
context while its token is still valid.

## Changes

- Add commit-time request-id guards before `switchWorkspace` writes
workspace state, workspace token, `error`, or `sessionStorage`.
- Track the current workspace token expiry in memory and use it to
distinguish safe transient refresh failures from failures that must
clear context.
- Convert the stale refresh race coverage from expected-failing to a
normal passing regression test.
- Update transient retry coverage to assert valid context and
`sessionStorage` preservation.

## Browser / E2E coverage

No Playwright test was added because this bug is in the Pinia store race
between mocked token-exchange promises, request IDs, token expiry, and
`sessionStorage` commits. The deterministic unit spec directly controls
the ordering that is not practical to force through the browser without
real auth/session infrastructure and artificial network timing hooks.

## Validation

- `pnpm format -- src/platform/workspace/stores/workspaceAuthStore.ts
src/platform/workspace/stores/useWorkspaceAuth.test.ts`
- `pnpm exec vitest run
src/platform/workspace/stores/useWorkspaceAuth.test.ts`
- `pnpm exec eslint src/platform/workspace/stores/workspaceAuthStore.ts
src/platform/workspace/stores/useWorkspaceAuth.test.ts`
- `pnpm exec oxlint src/platform/workspace/stores/workspaceAuthStore.ts
src/platform/workspace/stores/useWorkspaceAuth.test.ts --type-aware`
- `pnpm exec vue-tsc --noEmit --pretty false`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11726-fix-guard-workspace-auth-refresh-races-3506d73d365081b99df3c1bf3d0e008a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2026-06-18 13:49:40 -07:00
AustinMroz
1a27372e44 Fix 'insert as node' in sidebar tab (#12900)
When right clicking an output asset from the assets sidebar panel, the
'insert as node in workflow' action was twice bugged
- The default type, as used for determining filename annotation, was set
to the type of the file. This meant that annotations would never be
applied to the filename
- `temp` outputs would incorrectly be assigned the `output` type.
- My fix for this one gives me a slightly bad taste in my mouth. Parsing
URLs isn't great, but it's cleaner than needing to scan the (potentially
sparse) full outputs to try and find the corresponding output.
2026-06-18 13:48:02 -07:00
Yourz
443e201684 feat: use Claude glyph for Anthropic partner icon (#12292)
*PR Created by the Glary-Bot Agent*

---

Follow-up to #12216.

The backend Anthropic node (Comfy-Org/ComfyUI#13867) is the Claude node,
so swap the brand-mark from Anthropic's "A" wordmark to Claude's
sunburst glyph (sourced from
[lobehub/lobe-icons](https://github.com/lobehub/lobe-icons/blob/master/packages/static-svg/icons/claude.svg)).

Filename stays `anthropic.svg` — the frontend resolves `category="api
node/text/Anthropic"` to provider `Anthropic` and looks up
`icon-[comfy--anthropic]`, so renaming the file would break the badge.
Brand color stays `#D97757` (Claude shares Anthropic's coral).

Verification:
- `pnpm typecheck` clean
- Playwright check confirms the Claude sunburst renders in the badge
with coral border, and at 48 px the glyph correctly inherits
`text-foreground` (white in dark / charcoal in light) via `currentColor`
— matching the pattern from #12216.

Related: Comfy-Org/ComfyUI#13867

## Screenshots

![Anthropic-keyed badge now showing Claude sunburst glyph with coral
border, alongside OpenAI and
BFL](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/c0076decedd8863eec6253b44e583da6b3eaacc20081d126aaf5267c72c8cc84/pr-images/1778857361305-00b38f81-8742-4017-a1e7-12f4a60a38d6.png)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12292-feat-use-Claude-glyph-for-Anthropic-partner-icon-3616d73d365081f88c5be2e413f95799)
by [Unito](https://www.unito.io)

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
2026-06-18 13:46:39 -07:00
Alexis Rolland
cf7c68cd50 Update default workflow (#12804)
## Summary

Update the default workflow to use a more modern model than SD1.5. This
new workflow uses Z-Image Turbo and is the same workflow as the one in
the README for consistency.

## Changes

- **What**: `src/scripts/defaultGraph.ts`

## Screenshots (if applicable)

<img width="1920" height="1152"
alt="{2DD28B9F-A9E7-4DD7-8F07-AF7241F5702E}"
src="https://github.com/user-attachments/assets/6e6ee298-a786-4a8c-adf3-6452df08a995"
/>

---------

Co-authored-by: Connor Byrne <c.byrne@comfy.org>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-06-18 13:46:02 -07:00
dante01yoon
aaf2177ec7 Merge remote-tracking branch 'origin/main' into HEAD 2026-06-18 12:10:53 +09:00
Dante
a738f75f13 Merge branch 'main' into jaewon/fe-729-delete-is-asset-api-enabled 2026-06-18 10:23:16 +09:00
AustinMroz
eafe2af91d Indicate in progress upload with spinner (#12673)
While a file is uploading to any of the file picker nodes (ie "Load
Image"), the 'select folder' icon is replaced with a loading spinner.

I had previously implemented this with a full progress bar indicating
the rate of upload, but found it particularly uninformative when used on
cloud.

<img width="659" height="494" alt="image"
src="https://github.com/user-attachments/assets/6d3ca82b-360b-44bc-b123-b276aae2c4d6"
/>
2026-06-17 18:09:49 -07:00
AustinMroz
4c870d84ed Fix disabling of linked widgets in props panel (#12896)
| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/9d602ee3-ff10-48b9-95ca-4c7f5ca57a45"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/36e96aff-60ec-4f8c-b7c9-b4d68e03884c"
/>|

Making reactivity function is a little bit clunkier than I would like,
but it'll get simplified in the future by east coast swing
2026-06-17 18:05:57 -07:00
Comfy Org PR Bot
0a4021df99 1.47.2 (#12893)
Patch version increment to 1.47.2

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.47.2
2026-06-18 01:04:08 +00:00
Christian Byrne
4f60d09420 ci: consolidate PR bot comments into one unified comment (#11935)
## Summary

- Merges the separate Playwright and Storybook bot comments into the
existing `<!-- COMFYUI_FRONTEND_PR_REPORT -->` unified comment, using
named sections via `upsert-comment-section`
- Each workflow independently upserts its own section without clobbering
others
- `pr-report.yaml` cleans up legacy `<!-- PLAYWRIGHT_TEST_STATUS -->`
and `<!-- STORYBOOK_BUILD_STATUS -->` comments on first run
- Perf report: collapses detailed metric tables in `<details>` by
default — only headline FPS/TBT/heap summary and a brief regression
count are visible inline

## Before → After

**Before:** 4–5 separate bot comments per PR (Playwright, Storybook,
Bundle/Perf/Coverage, Chromatic URLs) + CodeRabbit + Codecov

**After:** 1 unified comment with collapsible sections:
```
<!-- COMFYUI_FRONTEND_PR_REPORT -->
<!-- section:playwright:start --> ... <!-- section:playwright:end -->
<!-- section:storybook:start --> ... <!-- section:storybook:end -->
<!-- section:ci-metrics:start --> ... <!-- section:ci-metrics:end -->
<!-- section:chromatic:start --> ... <!-- section:chromatic:end -->
```
(CodeRabbit and Codecov are external and can't be merged)

## How it works

`upsert-comment-section` already existed for the website CI comment.
This PR extends that pattern to the main PR report comment:

1. **Playwright** — shell script writes to `SUMMARY_FILE` when set, CI
workflow uses `upsert-comment-section` with `section-name: playwright`
2. **Storybook** — same pattern, `section-name: storybook`
3. **Bundle/Perf/Coverage** — `pr-report.yaml` now also uses
`upsert-comment-section` instead of replacing the whole comment,
`section-name: ci-metrics`
4. **Chromatic** — `section-name: chromatic`, no script change needed

Both fork and non-fork PR paths updated.

## Perf report noise fix

Previously the full per-metric regression table was shown inline. Now:
- **Visible:** headline summary (avg FPS, P5 FPS, TBT, heap per test) +
"⚠️ N regressions detected" count
- **Collapsed by default:** regression detail table, all-metrics table,
historical variance, trend data

## Test plan

- [ ] Open a PR with frontend changes — confirm single unified bot
comment appears
- [ ] Confirm Playwright section appears with  then updates to results
- [ ] Confirm Storybook section appears only when storybook-relevant
files change
- [ ] Confirm legacy standalone Playwright/Storybook comments are
deleted by `pr-report.yaml`
- [ ] Confirm perf report shows headline only, details collapsed
- [ ] Test with a fork PR — confirm fork path also works

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11935-ci-consolidate-PR-bot-comments-into-one-unified-comment-3566d73d36508173964cdf596cfae1f8)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Connor Byrne <c.byrne@comfy.org>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-06-17 18:02:28 -07:00
Alexander Brown
edf3e1a682 refactor(litegraph): remove vestigial use_uuids node-id mode (#12930)
## Summary

Removes LiteGraph's vestigial `use_uuids` node-id mode, which was never
enabled anywhere in the codebase.

## Changes

- **What**: Deletes the `LiteGraph.use_uuids` flag and the node-id
branches that read it. Newly created/added/cloned nodes now always
receive integer ids. The `asSerialisable` sort guard drops its
`@ts-expect-error` in favor of an explicit `Number(a.id) - Number(b.id)`
comparison.
- **Breaking**: `LiteGraph.use_uuids` is removed from the public
`LiteGraph` global. It defaulted to `false` and nothing ever set it to
`true`, so behavior is unchanged for all real usage — but the property
no longer exists on the global surface. Litegraph changes can affect
downstream custom-node repos.

`createUuidv4` / `LiteGraph.uuidv4` are intentionally kept — still used
for subgraph ids, slot ids, and clipboard remapping.

## Review Focus

- Confirm hard removal (vs. deprecate-then-remove no-op getter) is
acceptable for the public `LiteGraph` surface.
- `asSerialisable` sort now coerces ids via `Number()`; all serialised
node ids are numeric, so ordering is unchanged.

## Notes

- 2 litegraph test suites (`LGraphCanvas.clipboard.test.ts`,
`SubgraphWidgetPromotion.test.ts`) fail at import with a pre-existing
asset-URL error unrelated to this change (confirmed they fail with these
changes stashed). All 909 tests that ran pass.

Co-authored-by: Amp <amp@ampcode.com>
2026-06-17 23:02:35 +00:00
Alexander Brown
968598d9e6 fix: prevent Vitest createRequire crash on Windows public-asset URLs (#12927)
## Summary

Stop Vitest from crashing on Windows when a unit test imports a Vue
component whose template references an absolute public-asset URL (e.g.
`<img src="/assets/images/default-template.png">`).

## Changes

- **What**: Pass `template.transformAssetUrls.includeAbsolute: false` to
`@vitejs/plugin-vue` under Vitest only. With no dev server, the plugin
forces `includeAbsolute: true`, compiling absolute public-asset URLs
into module imports. Vite's module runner then derives a rootless
`import.meta.url` of `file:///assets/...`, which has no drive letter on
Windows, so Vitest's `createRequire(import.meta.url)` throws
`ERR_INVALID_ARG_VALUE` and aborts collection of any test transitively
importing such a component. Disabling the absolute transform under tests
keeps these as plain string literals (identical rendered output).
- **Breaking**: None — change is scoped to `process.env.VITEST`; dev and
production builds are unchanged.

## Review Focus

`vite.config.mts` — the `vuePluginOptions` const is `undefined` outside
Vitest, so only the test runner is affected.

Co-authored-by: Amp <amp@ampcode.com>
2026-06-17 22:58:40 +00:00
Christian Byrne
305d209f6f fix: remove unused export from Load3dSerializedBase type (#12928)
## Summary

Remove unused `export` keyword from `Load3dSerializedBase` type. The
type is only used internally as the return type of `snapshotLoad3dState`
and is not imported elsewhere.

Fixes knip "unused exported types" error.

Co-authored-by: Connor Byrne <c.byrne@comfy.org>
2026-06-17 21:15:59 +00:00
Maanil Verma
0c23e8305f fix: skip templates modal when opening a template from the URL (#12835)
## Summary

On first launch, the templates modal flashed open for a split second
before a deeplinked template (`?template=`) loaded, which felt broken.

## Changes

- **What**: Gate the first-launch templates modal on template URL
intent, alongside the existing shared-workflow (`?share=`) check. When a
template is being opened directly from the URL, the template modal no
longer opens. Behavior is unchanged when no template is in the URL — the
template modal still shows for first-time users.
- Test util: Added browser_tests/fixtures/utils/flashDetector.ts —
installs a pre-navigation requestAnimationFrame sampler that flags if a
[data-testid] element ever renders, even for a single frame. This
catches a brief flash that toBeHidden() (final-state only) cannot.

## Review Focus

`hasTemplateUrlIntent()` mirrors the existing
`hasSharedWorkflowIntent()` (direct `route.query` check plus
preserved-query fallback for the `/user-select` redirect path). Two
regression tests cover both the URL-param and preserved-intent cases.

**Coverage:**

- Unit (useWorkflowPersistenceV2.test.ts): the modal is not opened when
a template param is in the URL, and when template intent is preserved
across the /user-select redirect.
- E2E (templates.spec.ts): templates dialog never flashes when
first-time user opens a template link — verified red-without-fix,
green-with-fix.

Screen Recording 



https://github.com/user-attachments/assets/636094d4-0ef0-4e42-af32-d4e6c7ec5731



closes #12836
2026-06-17 21:00:02 +00:00
Matt Miller
810ada61fb ci: add team-gated Cursor review (thin caller for github-workflows) (#12859)
## Summary

Adds a team-gated, label-triggered multi-model Cursor review as a **thin
caller** for the reusable workflow in `Comfy-Org/github-workflows` — the
single source of truth for the panel, judge, prompts, and scripts. This
repo carries only the ~50-line caller, so there's no review logic to
drift out of sync.

## Changes

- **What**: `.github/workflows/pr-cursor-review.yaml` triggers on the
`cursor-review` label and calls
`Comfy-Org/github-workflows/.github/workflows/cursor-review.yml`, pinned
to `047ca48` (github-workflows#9, current main). Inheriting the reusable
workflow brings severity badges, line-anchored inline comments,
diff-size caps, prompt-injection hardening, and optional Slack DMs.
- **Config**: `diff_excludes` restated (overriding replaces the default
wholesale) with this repo's heavy paths added (Playwright snapshots,
generated manager types). Judge and panel both default to Opus 4.8 via
the reusable workflow — no overrides needed.

## Review Focus

- **Access control (the point).** Two layers, no allowlist: (1) only
triage+ users can apply a label in a public repo; (2) the reusable
workflow's secret-bearing jobs don't run on fork PRs, so
`CURSOR_API_KEY` is reachable only on internal branches.
- **Replaces a standalone draft.** Earlier revisions of this branch
carried a self-contained workflow + review/judge scripts; that
duplicated the reusable workflow, so it's been swapped for the thin
caller.

## Prerequisites (already done)

- `CURSOR_API_KEY` secret set on this repo.
- `cursor-review` label created.
- `SLACK_BOT_TOKEN` already present (enables the DM feature).

---------

Co-authored-by: GitHub Action <action@github.com>
2026-06-17 20:38:14 +00:00
Alexander Brown
ec91ecd695 ci: carry forward unit and e2e coverage flags (#12926)
## Summary

Enable Codecov carryforward on the `unit` and `e2e` flags so a missing
or late E2E coverage upload no longer produces a false `codecov/patch`
failure.

## Changes

- **What**: Add a `flags` block to `codecov.yml` marking `unit` and
`e2e` as `carryforward: true`. When a flag's upload is absent for a
commit, Codecov reuses that flag's last known coverage for unchanged
files instead of treating those lines as patch misses.

## Review Focus

The `e2e` flag is uploaded by the separate `ci-tests-e2e-coverage`
`workflow_run` job, which runs after CI and currently fails or skips
intermittently (uploaded with `fail_ci_if_error: false`). When that
upload is missing, the head report holds only the `unit` session, so
E2E-only code paths (canvas, vue-nodes, minimap, glsl, etc.) report as
uncovered and the patch status fails against the full-coverage base.
Carryforward fixes that symptom; the flaky coverage workflow remains a
separate root-cause fix.

Validated with `curl --data-binary @codecov.yml
https://codecov.io/validate` → `Valid!`. Carryforward takes effect after
merge once a `main` build establishes a baseline for both flags.

Co-authored-by: Amp <amp@ampcode.com>
2026-06-17 20:34:46 +00:00
Alexander Brown
c408f39cee test: harden assets media-type filter spec against VirtualGrid flake (#12897)
## Summary

Harden the cloud assets media-type filter spec against a VirtualGrid
virtualization flake that intermittently failed CI at the
`waitForAssets(4)` precondition.

## Changes

- **What**: Replace the 7 `waitForAssets(MIXED_JOBS.length)`
preconditions in `assets-filter.spec.ts` with `waitForAssets()` (first
card visible = data loaded), and document why.

## Review Focus

CI artifact (`playwright-report-cloud`) from the failing run showed only
3 of 4 cards in the DOM — the 3D card was missing and the audio card
rendered taller (inline player). `VirtualGrid.vue` sizes its render
window from a single uniform `itemHeight` measured off the first card,
so the taller audio card pushes the 3D card out of the initial window
and it is virtualized out of the DOM until a re-measure (same cause as
#11635).

Requiring all 4 cards to be mounted simultaneously fights
virtualization. `tab.open()` already waits for the first card (data
loaded), and filtering reads the full asset store regardless of what is
mounted, so the per-filter count assertions still provide the real
coverage. No behavioral change to the tests — only the
readiness/precondition strategy.

Verified `pnpm exec eslint` and `pnpm typecheck:browser` pass. Cloud E2E
could not be run locally (needs a running frontend + backend); relies on
the cloud CI job for confirmation.

Related to #11635

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-06-17 19:38:22 +00:00
Deep Mehta
ccf2f12b25 fix(website): correct top-up credit rollover copy on cloud pricing (#12923)
## Summary

The `/cloud/pricing` page's "Add more credits anytime" feature claims
unused top-up credits **roll over** to the next month. Per product
(Pablo, #cloud), top-up credits **do not** roll over. Fix the copy to
match policy and the docs.

## Changes

- **What**: `pricing.included.feature5.description` (en + zh-CN) in
`apps/website/src/i18n/translations.ts`.
- Before: "Purchase additional credits at any time. Unused top-ups roll
over to the next month automatically for up to 1 year."
- After: "Purchase additional credits at any time. Top-up credits are
valid for 1 year from the date of purchase and do not roll over with
your monthly plan."
- Keeps the accurate 1-year validity window (matches
`docs/interface/credits.mdx`).
- Scope: this is the **only** rollover claim on the page. The tier cards
(lines 1224/1255/1286) only say "top-ups available" — no change needed.
- **Breaking**: none (copy only).

## Deliberately out of scope

The Slack thread surfaced an **unresolved** question: Pablo said top-ups
don't roll over *"as opposed to subscription credits which do,"* but
`docs/interface/credits.mdx` says monthly credits don't roll over
either. This PR makes **no claim about subscription/monthly credit
rollover** (the new copy is neutral: "do not roll over *with your
monthly plan*"). Resolving that — and the optional FAQ entry / docs
alignment Glary-Bot proposed — needs a product decision first. Flagging
for follow-up.
2026-06-17 18:38:56 +00:00
jaeone94
6850d22d99 Redesign error overlay count and toast behavior (#12871)
## Summary

Redesign the run error overlay so its count and copy use the same
grouped error semantics as the redesigned Error tab, while keeping
single-error toast copy precise.

This is a stacked follow-up to #12828. The parent PR redesigns the Error
tab cards and centralizes their grouped/row-based presentation. This PR
applies the same mental model to the compact overlay shown from the Run
button path, so users no longer see one count in the Error tab and a
different count in the overlay.

## Changes

- **What**: Align the error overlay count with the Error tab's grouped
error count.
- The overlay now reads the same grouped error surface that powers the
Error tab hero instead of independently summing raw store error counts.
- Validation/runtime/prompt execution groups use their grouped `count`.
- Missing model/media/node/swap groups keep the row/group count
semantics introduced by the Error tab redesign.
- This avoids cases where the overlay headline says one number while the
panel summarizes another.

- **What**: Preserve the existing single-error toast behavior while
adding explicit multi-row handling.
- A single true leaf still uses the catalog/resolver toast title and
toast message.
- A single grouped execution error with multiple node/input items uses
that group's title/message instead of the generic aggregate copy.
- A single missing model/media group with multiple model/file rows uses
the generic aggregate copy because it represents multiple actionable
rows.
- A single missing model/media row referenced by multiple nodes uses the
group title/message, since there is still only one model/file to
resolve.
- Multiple top-level groups continue to use the aggregate "N errors
found" style copy.

- **What**: Restyle the overlay toast to match the new error-surface
direction.
- Adds a compact dark card with a destructive left accent and a visible
outline for better contrast against the workspace.
  - Keeps the close button in the card header area.
- Keeps the primary "View details" action, but adjusts spacing, size,
and typography to better match the Figma direction.
- Removes the older footer-style dismiss action so the overlay behaves
like a focused status toast rather than a secondary dialog.

- **What**: Share error grouping/count helpers instead of duplicating
local logic.
- Extracts execution item-list detection for reuse between the Error tab
render path and count logic.
- Extracts missing-model grouping/count helpers so missing-model row
count semantics have one implementation.
- Removes `groupedErrorMessages`, which became unused after the overlay
copy decision moved to grouped error state.

- **Breaking**: None.

- **Dependencies**: None.

## Review Focus

- **Stack boundary**: Please review this against
`jaeone/fe-816-error-card-redesign`, not against `main` directly. The
parent PR is #12828 and contains the Error tab card redesign that this
overlay work builds on.

- **Count semantics**: The visible overlay count intentionally changes
from raw store counts to grouped Error tab counts. This is not just a
refactor; it is the intended product behavior so the compact overlay and
the panel hero agree.

- **Overlay message branches**: The overlay can now choose between three
message modes. The goal is to keep precise single-error copy where it is
useful, but avoid showing one node-specific toast message when the
overlay actually represents multiple actionable rows.

| Branch | When it applies | Overlay title source | Overlay message
source | Example output |
| --- | --- | --- | --- | --- |
| Aggregate summary | More than one top-level error group, or one
missing model/media group with multiple actionable model/file rows. |
Generic aggregate title using grouped count. | Generic aggregate
message. | Title: `2 errors found`<br>Message: `Resolve them before
running the workflow.`<br>Example case: one Missing Models group
containing `first.safetensors` and `second.safetensors`. |
| Group summary | Exactly one error group that is not a true single
leaf, but should still be described by the group. This includes one
execution catalog group with multiple items, or one missing model/media
row referenced by multiple nodes. | The group's `displayTitle`. | The
group's `displayMessage`. | Title: `Missing connection`<br>Message:
`Required input slots have no connection feeding them.`<br>Example case:
one validation group with `KSampler - model` and `KSampler - positive`
rows. |
| Single leaf toast | Exactly one group, one actionable row/card, and at
most one node reference. | Resolver/catalog `toastTitle`. |
Resolver/catalog `toastMessage`. | Title: `Model missing`<br>Message:
`CheckpointLoaderSimple is missing missing.safetensors.`<br>Example
case: one missing model file referenced by one node. |

- **Scope control**: This PR intentionally does not redesign the full
Error Overlay flow beyond the compact toast/card. It also does not
revisit the deeper Error tab card layouts already handled in #12828.

- **Accessibility**: The toast keeps `role="status"` for polite
announcement semantics. The duplicate `aria-live` attribute was removed
during cleanup because `role="status"` already implies polite
live-region behavior.

## Validation

- `pnpm format`
- `pnpm lint`
- `pnpm typecheck`
- `pnpm knip`
- `pnpm test:unit src/components/error/useErrorOverlayState.test.ts
src/platform/missingModel/missingModelGrouping.test.ts
src/components/error/ErrorOverlay.test.ts
src/components/rightSidePanel/errors/useErrorGroups.test.ts`
- Pre-commit staged checks passed.
- Pre-push `knip` passed.

## Screenshots (if applicable)
<img width="454" height="179" alt="스크린샷 2026-06-16 오후 6 00 10"
src="https://github.com/user-attachments/assets/a85376ba-2b22-4cf8-a6fa-79f83fb8b244"
/>
<img width="453" height="179" alt="스크린샷 2026-06-16 오후 6 00 31"
src="https://github.com/user-attachments/assets/d9a1d4bd-92ab-451a-bb79-e7cfbc3af7c6"
/>
<img width="486" height="148" alt="스크린샷 2026-06-16 오후 6 00 55"
src="https://github.com/user-attachments/assets/b66faf96-65c8-4a22-9ff9-e8ccd450986e"
/>
<img width="395" height="127" alt="스크린샷 2026-06-16 오후 6 01 22"
src="https://github.com/user-attachments/assets/c64443f9-0eba-4b2b-8049-c1887c788b1e"
/>
<img width="384" height="134" alt="스크린샷 2026-06-16 오후 6 01 30"
src="https://github.com/user-attachments/assets/42f4fcae-b003-4df9-8f3a-0fda85a90880"
/>
<img width="376" height="129" alt="스크린샷 2026-06-16 오후 6 01 53"
src="https://github.com/user-attachments/assets/ce9030d0-2a98-4b38-9e7d-7a9c3103960f"
/>
<img width="379" height="128" alt="스크린샷 2026-06-16 오후 6 02 01"
src="https://github.com/user-attachments/assets/3d4ce356-1c22-4e3b-a1d0-fece351d9fbb"
/>
<img width="463" height="133" alt="스크린샷 2026-06-16 오후 6 02 33"
src="https://github.com/user-attachments/assets/6ae13a44-02aa-4167-8878-4906db468ad6"
/>
2026-06-17 13:31:07 +00:00
Dante
543a39a6b0 fix(billing): DES review polish on workspace billing UI (#12917)
### before
<img width="539" height="475" alt="Screenshot 2026-06-17 at 9 52 50 PM"
src="https://github.com/user-attachments/assets/dd562cb4-870c-43de-aca4-81e0118735e9"
/>

### after 
<img width="529" height="592" alt="Screenshot 2026-06-17 at 9 53 40 PM"
src="https://github.com/user-attachments/assets/ba8ed01e-ac91-4654-bfaa-d8f73923f378"
/>


Design-review polish on the team-workspace billing UI (3 of the items
from the Figma 'Team Plan - Workspaces' review). All three components
are on main.

### 1. Remove dead 'Upgrade' badge in account popover
`CurrentUserPopoverWorkspace.vue` — the white badge beside 'Plans &
pricing' was gated on `canUpgrade`, which is hardcoded `false` (PRO is
the only tier), so it never rendered. Removed the badge markup and the
dead computed. (Figma node 2797-724189.)

### 2. Subscribe-to-Run button height
`SubscribeToRun.vue` — used `size="sm"`, which didn't match the sibling
run/queue button (an `h-8` button group it swaps with in the same slot
via `CloudRunButtonWrapper`). Switched to `size="unset"` + `h-8
rounded-lg gap-1.5 px-4` to match.

### 3. Duplicate border/radius on member 'subscription inactive' dialog
`useSubscriptionDialog.ts` — the layout dialog already zeroes the
*content* border (`border-none shadow-none`), but the *root* pt kept
only `bg-transparent`, so the dialog frame's default border+radius
doubled with the card's own `rounded-2xl border`. Added `border-none
rounded-none shadow-none` to root so only the card's single
border/radius shows. (Figma node 3253-19473.)

Surfaced during Billing V1 design review (team workspaces).

---------

Co-authored-by: GitHub Action <action@github.com>
2026-06-17 13:18:02 +00:00
Dante
c5eb05a2e9 fix(billing): truncate long workspace names in the switcher (#12918)
The workspace name was hard-clipped by the switcher panel's
`overflow-hidden` instead of truncating with an ellipsis. The name span
had `min-w-0` but its parent row `<button>` (`flex flex-1`) did not, so
the button kept its content width and the long name overflowed.

Add `min-w-0` to the row button (and the name span) so the flex chain
can shrink and the name truncates.

Follow-up to FE-769 (#12763, merged) — the component shipped without
this.

## Before / After
<img width="1370" height="538" alt="fe769-before-after"
src="https://github.com/user-attachments/assets/b05d6faf-9dfe-4c93-9941-3c0a9bbfcc2d"
/>

Verified live in the team-workspaces mock (long-named "Acme Studio
Workspace").

### after
<img width="703" height="302" alt="Screenshot 2026-06-17 at 9 54 34 PM"
src="https://github.com/user-attachments/assets/725c3175-b65f-4224-aca9-3de777c95e85"
/>
2026-06-17 12:56:04 +00:00
Dante
5a846db6cf feat(billing): role-aware run-lock for cancelled/inactive team plans (FE-978) (#12786)
## Summary

Cancelled / inactive team plans keep members but lock runs; the run
button and the subscription-required dialog are now role-aware — owners
are routed to the pricing/subscribe flow, members (who cannot subscribe)
see "contact your workspace owner to resubscribe".

## Changes

- **What**: `SubscribeToRun.vue` becomes a role-aware locked run button
(owner → "Subscribe to Run"; member → neutral locked "Run" +
contact-owner tooltip; both open the subscription dialog).
`SubscriptionRequiredDialogContentWorkspace.vue` branches on role
(member → read-only contact-owner panel, no pricing/subscribe
affordance; owner → existing pricing/preview; member view suppressed for
`out_of_credits` so the active-but-low-credits path is unchanged).
`subscription.inactive.*` i18n keys.
- **Breaking**: none.

## Review Focus

- Role source =
`useWorkspaceUI().permissions.value.canManageSubscription` (owner /
personal = true, member = false) — the same accessor
`SubscriptionPanelContentWorkspace.vue` uses.
- **No BE work**: the run-gate already exists server-side
(`InactiveSubscriptionError`; `is_active` checked before funds). The
lock is gated on `is_active`, the same field the orchestrator uses, so
FE/BE stay consistent; leftover-credits-while-inactive remains blocked
by design.
- Complements #12785 (FE-878 precondition→modal routing); disjoint file
sets. Design: DES-197, Figma 3253-18670 / 3253-18671 / 3246-13962.
- Tests: `SubscribeToRun` (4) / `CloudRunButtonWrapper` (3) /
`SubscriptionRequiredDialogContentWorkspace` role cases — member sees
contact-owner (no subscribe), owner sees pricing, run locks on
`!is_active` and unlocks when active (22 total); full `test:unit` green.

Fixes FE-978
2026-06-17 01:52:06 +00:00
Alexander Brown
e994e4df58 refactor: store-backed WidgetId subgraph host widgets; delete widgetValueIO layer (#12617)
## Summary

Make `WidgetId` (`graphId:nodeId:name`) the single canonical widget
identity and represent subgraph promoted host widgets as ordinary
store-backed widgets addressed by it. This deletes two whole indirection
layers — the `world/*` widget-entity-IO layer and the
`PromotedWidgetView` runtime — leaving one model: a widget's data lives
in `widgetValueStore` keyed by `WidgetId`, and a `SubgraphNode` input
references it via `input.widgetId`.

**Net +304 lines across 107 files** (5,044 added / 4,740 deleted):
production code is net **−798** (1,521 added / 2,319 deleted) while
tests are net **+1,102** (3,523 added / 2,421 deleted). 10 files deleted
outright, 14 added.

## What got deleted

The old design wrapped every promoted subgraph widget in a synthetic
`IBaseWidget` "view" object with live getters that followed the source
widget, plus a manager to keep view identities stable, plus an IO
indirection layer over the store. All of it is gone:

- `promotedWidgetView.ts` — the `PromotedWidgetView` class (draw /
pointer / DOM-sync / projection / deepest-source resolution getters)
- `PromotedWidgetViewManager.ts` — view reconciliation/caching
- `world/widgetValueIO.ts` — the IO wrapper over `widgetValueStore`
- `world/entityIds.ts` + `world/brand.ts` — the `WidgetEntityId`
branded-id layer and the `entityId` field
- `widgetNodeTypeGuard.ts` — only used by the deleted view
- the per-`SubgraphNode` view machinery (`_promotedViewManager`,
`_cacheVersion`, view-key generation, DOM position-override cleanup) and
every now-dead `isPromotedWidgetView` branch across the panel, menu,
store, and util consumers
- `domWidgetStore` position-override APIs (`setPositionOverride` /
`clearPositionOverride`), only used to render a promoted DOM widget on a
different host node

## Why it's simpler

- One source of truth. A promoted host widget is `WidgetState` in the
store, seeded from the source at promotion (`registerWidget` with a
deep-cloned snapshot) and independent thereafter. No synthetic widget
objects, no runtime source-following, no view cache to invalidate.
- Resolution is data-driven. `resolveConcretePromotedWidget` walks
`SubgraphNode` inputs (`input.widgetId` + `resolveSubgraphInputTarget`)
instead of chasing view objects through `node.widgets`. This also
**fixes two-layer nested promotion** — the previously-skipped parity
test now passes and resolves through to the deepest concrete widget.
- The right-panel Parameters tab renders a subgraph node's promoted
widgets through the **same** store-backed path as ordinary node widgets:
display reads `WidgetState` via `widget.widgetId`, and value writes go
through `widgetValueStore.setValue(widgetId)`.

## Changes

- **What**:
- `WidgetId` branded type + `widgetId()` / `parseWidgetId()` /
`isWidgetId()` and a `WidgetState` type; `widgetValueStore` is
`WidgetId`-native (`registerWidget` / `getWidget` / `setValue` /
`deleteWidget`).
- Promotion creates host `WidgetState` then an input projection
(`input.widgetId`); demotion clears it; serialization and legacy
`proxyWidgets` migration round-trip through `input.widgetId`.
- `promotedInputWidget.ts` projects a store-backed ordinary widget from
an input slot; `SubgraphNode.widgets` is now a projected getter over
inputs (kept Litegraph-shaped so the canvas renderer and extensions
still read `node.widgets`). `invalidatePromotedViews()` is retained as a
no-op for extension compatibility.
- `promotedWidgetControl.ts` applies `control_after_generate` (e.g. seed
increment) on the host node, since the interior control widget is
link-fed and its value is dead; `syncPromotedComboHostOptions` mirrors
interior combo options onto host state.
- `multilineTextarea.ts` extracts the reusable multiline DOM-widget
behavior out of `useStringWidget` and adds promoted multiline
materialization via a `createPromotedHostWidget` app-layer hook (keeping
Litegraph core free of Vue/Pinia/DOM).
- Late-bound `LiteGraph` singleton holder (`litegraphInstance.ts`) to
break a widget-init import cycle.
- **Breaking**:
  - `IBaseWidget.entityId` removed — use `widgetId`.
- `SubgraphNode.widgets` no longer exposes the old `PromotedWidgetView`
objects; promoted state lives in `widgetValueStore` keyed by
`input.widgetId` and `widgets` is a projection of inputs. The
`widget-promoted` event now carries the concrete interior widget.
Extension code reading `entityId` or relying on `PromotedWidgetView` is
affected.

## Review Focus

- Promotion / demotion / serialization round-tripping through
`input.widgetId` + `widgetValueStore`, incl. the legacy `proxyWidgets`
migration.
- Snapshot-at-promotion semantics (host widget does not follow the
source after creation), and the combo-options exception via
`syncPromotedComboHostOptions`.
- Two-layer nested resolution in `resolveConcretePromotedWidget` +
`SubgraphNode` nested-source resolution.
- The unified Parameters tab (`TabSubgraphInputs` → `SectionWidgets`):
value edit / rename / favorite / hide / reorder for promoted inputs are
wired through the store but warrant a visual/e2e pass.
- Litegraph-compat seams worth a careful read: projected
`SubgraphNode.widgets`, the canvas-edit `callback` bridge back to the
store, host-level `control_after_generate`, and the late-bound
`LiteGraph` holder / `domWidget.ts` import ordering.

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2026-06-16 23:19:12 +00:00
Comfy Org PR Bot
2a7340ec6c 1.47.1 (#12862)
Patch version increment to 1.47.1

**Base branch:** `main`

---------

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.47.1
2026-06-16 23:02:15 +00:00
AustinMroz
cb52a3821b fix groups dragging children with control held (#12867)
When control is held, an active drag operation should cease applying
movements to nodes contained by selected groups. This functionality was
broken in vue mode because of unnecessary reimplementation of the code
for calculating items contained by groups during drag operations
2026-06-16 22:32:31 +00:00