Commit Graph

8170 Commits

Author SHA1 Message Date
dante01yoon
ab14ab5d90 feat(dialog): flip showConfirmDialog helper to the Reka renderer
The helper was the last confirm path still rendering through the
PrimeVue Dialog branch (it only set `pt`). All six callers
(SecretsPanel, useShareDialog, AssetCard, KeybindingPanel x2,
useBuilderSave) now render Reka chrome. The Confirm* sections carry
their own padding, so the pt zero-padding hacks become
headerClass/bodyClass/footerClass: 'p-0'.

Width note: PrimeVue auto-sized to content; Reka uses the fixed
size:'md' (max-w-xl) like dialogService.confirm(), making both confirm
flavors consistent.
2026-06-02 22:46:20 +09:00
dante01yoon
2870a8730f feat(dialog): migrate inline-style dialog callers to Reka renderer
The Reka DialogContent only accepts size/maximized/class — PrimeVue
inline `style` strings and `pt` section classes are dropped. Translate
the seven remaining style-string callers (mask editor, 3D viewers x4,
subscription dialogs x2) to renderer:'reka' + size/contentClass.

Infra to reach parity:
- dialogStore: add headerClass/bodyClass/footerClass (Reka-path
  replacements for pt.header/pt.content/pt.footer section styling)
- GlobalDialog: forward the section classes to DialogHeader/DialogFooter
  and merge bodyClass into the body wrapper
- DialogContent: maximized re-asserts its dimension classes after the
  caller's contentClass so maximize wins over fixed w/h, mirroring
  PrimeVue's .p-dialog-maximized !important behavior
- tailwind-utils: teach tailwind-merge the max-h-none class — without it
  'max-h-[80vh] max-h-none' keeps both and maximize cannot release the
  caller's max-height

The mask-editor-dialog hook class moves from pt.root to contentClass so
browser_tests selectors keep working unchanged.
2026-06-02 22:46:20 +09:00
dante01yoon
00d89f3aea refactor(dialog): drop dead ConfirmationService registration
No useConfirm()/<ConfirmDialog>/<ConfirmPopup> consumer remains in src/
after Phase 5 (#12502) — the confirm flow routes through the Reka
showConfirmDialog helper / dialogService.confirm. apps/desktop-ui keeps
its own registration (TaskListPanel still uses PrimeVue confirm) and is
unaffected.
2026-06-02 22:46:20 +09:00
Dante
87aca15dc4 feat(dialog): migrate ConfirmDialog callers to Reka-UI (Phase 5) (#12502)
## Summary

Phase 5 of the dialog migration. Replaces the remaining PrimeVue
`useConfirm` / `<ConfirmDialog>` usage in the main frontend (`src/`)
with the `showConfirmDialog` helper added in Phase 1. Public API of
`useDialogService` / `dialogStore` is unchanged.

Parent:
[FE-571](https://linear.app/comfyorg/issue/FE-571/dialog-system-migration-primevue-reka-ui-parent)
This phase:
[FE-577](https://linear.app/comfyorg/issue/FE-577/phase-5-migrate-confirmdialog-callers-secretspanel)
Predecessors: #11719 (Phase 0, merged), #12041 (Phase 1, merged), #12109
(Phase 2, merged), #12182 (Phase 3, merged), #12403 (Phase 4, **stacked
PR base**)

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

## Changes

### before
<img width="1586" height="840" alt="phase4-before-primevue-confirm"
src="https://github.com/user-attachments/assets/2cb78cc2-7324-4349-b44b-98b5e696a83f"
/>

### after
<img width="1586" height="840" alt="phase5-after-reka-confirm"
src="https://github.com/user-attachments/assets/c092b026-828b-46aa-a4ff-f8611906f692"
/>

### `src/platform/secrets/components/SecretsPanel.vue`

Delete-secret confirmation routes through the Reka helper:

| Before | After |
| --- | --- |
| `useConfirm()` + `<ConfirmDialog group="secrets" />` +
`confirm.require({ acceptClass: 'p-button-danger', accept: () =>
deleteSecret(secret) })` | `showConfirmDialog({ headerProps,
props.promptText, footerProps: { confirmText: t('g.delete'),
confirmVariant: 'destructive', onCancel, onConfirm } })` with explicit
`dialogStore.closeDialog(dialog)` on both branches |

Header / message i18n keys (`secrets.deleteConfirmTitle`,
`secrets.deleteConfirmMessage`) unchanged. Confirm button now uses
`t('g.delete')` instead of the implicit PrimeVue "Yes" default, matching
the Reka helper convention used in `KeybindingPanel` / `AssetCard`.

### `src/components/sidebar/tabs/BaseWorkflowsSidebarTab.vue`

Drops the vestigial `<ConfirmDialog />` mount and its import. The script
section never called `useConfirm()` — close/delete workflow flows were
already migrated to `dialogService.confirm()` (the Reka path) in Phase
1, so the dangling overlay served no consumer.

### Tests

- `src/platform/secrets/components/SecretsPanel.test.ts` **(new)** — 3
tests: helper invoked with destructive variant + correct i18n props,
`onConfirm` closes the dialog **with the exact helper handle** and calls
`deleteSecret`, `onCancel` closes with the handle without deleting. The
mock returns a stable `DIALOG_HANDLE` so a regression that passes the
wrong arg to `closeDialog` fails the suite.
- `src/components/sidebar/tabs/BaseWorkflowsSidebarTab.test.ts` —
removed the now-orphan `vi.mock('primevue/confirmdialog', ...)`.

## Verification

After this PR, the only remaining references to PrimeVue's confirm
system in `src/` are the global `ConfirmationService` registration in
`main.ts`, which is owned by Phase 6 (FE-578) alongside the broader
Dialog / CSS cleanup:

```
$ grep -rn 'primevue/useconfirm\|primevue/confirmdialog\|useConfirm\b' src --include='*.ts' --include='*.vue'
(no matches)
```

## Out of scope

**Phase 6 (FE-578) — main frontend cleanup**
- Remove `ConfirmationService` registration from `src/main.ts`.
- Remove PrimeVue `Dialog` imports from `GlobalDialog.vue` and its
remaining `<style>` overrides (including `.manager-dialog`).

**`apps/desktop-ui/` — not part of FE-577**
- `apps/desktop-ui/src/components/maintenance/TaskListPanel.vue` still
uses `useConfirm()` + `<ConfirmPopup />`, and
`apps/desktop-ui/src/main.ts` + `.storybook/preview.ts` still register
`ConfirmationService`. The desktop app is a separate workspace package
consuming the main frontend; its PrimeVue confirm usage is intentionally
**not** migrated here. Tracking separately (follow-up ticket).

**Renderer scope caveat**
- `showConfirmDialog()` does not currently pass `renderer: 'reka'` to
`dialogStore.showDialog`. The helper continues to render through the
PrimeVue branch in `GlobalDialog.vue` until the helper itself is flipped
(Phase 1/6 territory). This PR only routes callers **to** the helper —
flipping the helper's render path is intentionally out of scope.

## Quality gates

- [x] `pnpm typecheck` — clean
- [x] `pnpm lint` — 0 errors (3 pre-existing warnings in unrelated
files: `useLoad3d.test.ts`, `useWorkspaceBilling.test.ts`)
- [x] `pnpm format` — applied
- [x] `pnpm test:unit` (touched + adjacent):
- `SecretsPanel.test.ts` — 3/3 (hardened to assert handle pass-through)
  - `BaseWorkflowsSidebarTab.test.ts` — 3/3
- `src/platform/secrets` + `src/components/sidebar/tabs` +
`src/components/dialog` — 178/178
- [x] Manual verification on `cloud.comfy.org` via local HTTPS dev
server (Settings → Secrets → delete flow, before & after side-by-side)
- [ ] CI Playwright matrix

## Public API impact

None. `useDialogService` / `dialogStore` surface is unchanged.

## Review focus

1. **Destructive variant choice** — `acceptClass: 'p-button-danger'`
(PrimeVue) → `confirmVariant: 'destructive'` (Reka helper). Same
red-affirmative semantic; visual match confirmed in screenshots above.
2. **Confirm label** — switched from PrimeVue's implicit "Yes" to
`t('g.delete')` for the delete flow, matching how `KeybindingPanel` /
`AssetCard` configure the helper.
3. **`BaseWorkflowsSidebarTab.vue` deletion** — verifying the
`<ConfirmDialog />` was genuinely vestigial: `grep useConfirm
src/components/sidebar/tabs/BaseWorkflowsSidebarTab.vue` returns
nothing, and `workflowService.deleteWorkflow` / `closeWorkflow` route
through `dialogService.confirm()` (Reka path).
4. **desktop-ui scope** — see "Out of scope" above. If the team wants
desktop-ui folded into this phase, happy to extend the PR.

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: GitHub Action <action@github.com>
2026-06-02 12:59:40 +00:00
Alexander Brown
162908a421 docs: ECS pattern survey appendix + PromotionStore cleanup (#12580)
## Summary

Add the ECS pattern survey appendix to ADR 0008's companion-documents
table, and drop stale `PromotionStore` references across architecture
docs to reflect the ADR 0009 removal.

## Changes

- **What**:
- New `docs/architecture/appendix-ecs-pattern-survey.md` — surveys
bitECS, miniplex, koota, ECSY, Bevy, and Thyseus: patterns adopted,
departed from, and when to revisit.
  - ADR 0008 companion table gains a row pointing at the new appendix.
- `docs/adr/0009-…/before-after-flows.md`,
`docs/architecture/ecs-target-architecture.md`,
`docs/architecture/entity-problems.md` — drop references to
`PromotionStore` / `usePromotionStore` (the legacy three-layer mechanism
is gone; promoted value widgets are now linked `SubgraphInput`s).
- `docs/architecture/subgraph-boundaries-and-promotion.md` — reframes
its "current mechanism" section as historical context with an explicit
"removed by ADR 0009" callout.
- **Breaking**: None — docs-only.

## Review Focus

Wording in the historical-context callout on
`subgraph-boundaries-and-promotion.md`. Everything else is a small
cleanup or a new standalone document.

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
2026-06-02 00:25:34 -07:00
Christian Byrne
c5cc09dbc1 feat: draft Affiliate Program Terms page at /affiliates/terms (#11954)
*PR Created by the Glary-Bot Agent*

---

Draft, `noindex`-gated legal page rendering the **Comfy.org Affiliate
Program Terms and Conditions** verbatim from the source document. Treats
the copy as legal-final-pending-signoff: lives in i18n translations so
legal/non-engineers can edit text without a code change, the page is
excluded from search engines (`<meta robots noindex>` + `robots.txt` +
sitemap filter), and a follow-up PR will flip it to indexable once legal
signs off.

## What this adds

- **Route:** `/affiliates/terms` (en) + `/zh-CN/affiliates/terms`
(zh-CN), matching the existing privacy-policy / terms-of-service
localization pattern.
- **Anchor IDs match the spec exactly:** `#1-program-overview`,
`#2-eligible-products`, …, `#11-miscellaneous` — stable deep-linking.
- **Sticky desktop TOC** with active-section highlighting
(IntersectionObserver, smooth scroll, header offset,
`prefers-reduced-motion` aware).
- **Collapsed `<details>` accordion TOC on mobile** that auto-closes
after a click.
- **Effective Date** is a single i18n key
(`affiliate-terms.effective-date`) — currently `[TBD]`. One config var,
consumed by the page footer.
- **Plain accessible legal-doc styling:** site design tokens, no
marketing flourish or gradients, narrow column for readable line length,
high-contrast text.
- **noindex everywhere:** robots `noindex,nofollow` meta via
`BaseLayout.noindex`, `Disallow: /affiliates/terms` added to every UA
block in `robots.txt` (including the AI-bot overrides), and the route
excluded from `@astrojs/sitemap` for both locales.

## Implementation notes

The website does not currently use MDX — it uses i18n translation keys +
Vue `ContentSection` components for legal pages. I followed that
established pattern (per AGENTS.md "Align with rest of subrepo on impl
and design") rather than introducing MDX integration just for this page.
Editing affiliates terms copy is a one-file change in
`src/i18n/translations.ts`, no code touch required, which satisfies the
"Nav or legal can edit without a code change" intent.

A new component `LegalContentSection.vue` is added (rather than reusing
`ContentSection.vue` directly) because legal docs need: (a) a
`<details>` mobile accordion TOC instead of the existing
horizontal-scroll `CategoryNav`, (b) a footer Effective Date, and (c) a
tighter block-type set (paragraph + bullet list only). Privacy /
Terms-of-Service pages are untouched.

## Skipped per spec

Per the original brief, "Competitive analysis" bullets and the "Open
questions for legal review" callout are internal-only and were not
copied into the i18n keys. There is a unit test asserting no key
matching `competitive-analysis|open-questions|legal-review` exists under
the `affiliate-terms.*` namespace.

## ⚠️ Flag for legal review

The source legal copy contains an internal contradiction that I copied
verbatim rather than silently editing:
- **Section 3 (Commission Structure)** says: *"Commission duration: 3
months from the referred customer's first paid subscription"*
- **Section 7 (Termination)** says: *"Commissions on referred customers
will cease at the time of termination, even if within the **12-month**
commission window"*

These two clauses imply different commission windows (3 months vs 12
months). I left the copy as-is for legal to resolve before the indexable
follow-up PR — flagging here so it doesn't get missed.

## Verification

- `pnpm typecheck` (root) — clean
- `pnpm typecheck:website` — clean (91 Astro files, 0 errors)
- `pnpm test:unit` (website) — 35/35 passing including 5 new unit tests
for the affiliate-terms section structure
- `pnpm exec playwright test affiliates-terms` — 8/8 passing (5 desktop
`@smoke`, 3 mobile `@mobile`)
- `pnpm format:check` — clean
- `pnpm build` — clean, both routes generated, sitemap correctly
excludes both locale variants
- All pre-commit hooks pass (stylelint + oxfmt + oxlint + eslint +
typecheck + typecheck:website + check-unused-i18n-keys)

## Manual verification

Screenshots embedded below from the local Astro preview build at desktop
(1280×900) and mobile (Pixel 5 / 393×852) viewports. The mobile
accordion's collapsed and expanded states are both captured.

## Sub-agent review

Ran the Oracle review against `main`. Initial pass flagged the
Last-updated/Effective-Date footer duplication and a missing zh-CN route
— both addressed in commit `8a1ce890d`. The 3-month vs 12-month
contradiction is left for legal (see above).

## Preview URL

The Vercel preview URL with the route appended will be posted as a PR
comment after CI completes:

`<vercel-preview-host>/affiliates/terms`

## Screenshots

![Desktop view at 1280px showing the Affiliate Terms hero, sticky 'On
this page' sidebar TOC with all 11 sections, and the first two sections
rendered with proper inline links and bolded defined
terms](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/27749b93a2505a1e163124b9a7a5d72763303bd73dbd488a0fed576999523401/pr-images/1777973250517-aa672747-ffa2-481e-a55d-3f1773905903.png)

![Mobile view at 393px showing the hero, the collapsed On this page
details accordion with chevron, and section 1 rendered cleanly within
the narrow
viewport](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/27749b93a2505a1e163124b9a7a5d72763303bd73dbd488a0fed576999523401/pr-images/1777973250852-a5373a7a-2428-4f07-9ad7-d18a13f3be08.png)

![Mobile view with the accordion expanded, showing all 11 section links
as tap targets and the active section highlighted in
yellow](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/27749b93a2505a1e163124b9a7a5d72763303bd73dbd488a0fed576999523401/pr-images/1777973251199-2ed02d90-9009-41ee-bf7a-2d681611dde5.png)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11954-feat-draft-Affiliate-Program-Terms-page-at-affiliates-terms-3576d73d36508121851ef666a8c4c537)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: glary-bot <glary-bot@comfy.org>
2026-06-02 03:18:37 +00:00
Alexander Brown
e566ec4ca3 refactor: relocate UUID and NodeId out of litegraph (#12581)
## Summary

Move the canonical `UUID` utilities and the `NodeId` alias up out of
`src/lib/litegraph/` so non-litegraph code can reference them without
crossing the litegraph layer boundary.

## Changes

- **What**:
- `src/lib/litegraph/src/utils/uuid.ts` → `src/utils/uuid.ts` (full
file: `UUID`, `zeroUuid`, `createUuidv4`).
- `NodeId` moves from `src/lib/litegraph/src/LGraphNode.ts` to
`src/world/entityIds.ts`. `LGraphNode.ts` re-exports it; the litegraph
barrel still re-exports `createUuidv4` / `UUID` so the package's public
surface is unchanged.
- All 22 importers updated to `@/utils/uuid` (both
`@/lib/litegraph/src/utils/uuid` and the litegraph-internal
`./utils/uuid` relative paths).
- Drops the two `import-x/no-restricted-paths` ESLint disables in
`src/world/entityIds.ts` that were waiting on these moves.
- **Breaking**: None — litegraph re-exports preserve backward
compatibility for downstream consumers.

## Review Focus

- Each importer's change is identical (`@/lib/litegraph/src/utils/uuid`
→ `@/utils/uuid`), generated by `sed`.
- `src/lib/litegraph/src/LGraphNode.ts` now does `import type { NodeId }
from '@/world/entityIds'` + `export type { NodeId }` — confirm this
satisfies the litegraph layer boundary rules.
- `src/world/entityIds.ts` defines `NodeId` locally as `number |
string`; no semantic change.

Co-authored-by: Amp <amp@ampcode.com>
2026-06-02 03:11:15 +00:00
Alexander Brown
1e01c7128b chore: Add org prefix for team in CODEOWNERS (#12590)
https://github.com/orgs/community/discussions/22751
2026-06-02 02:26:03 +00:00
Comfy Org PR Bot
ee65074edc 1.46.7 (#12546)
Patch version increment to 1.46.7

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.46.7
2026-06-02 02:13:53 +00:00
Alexander Brown
3d8bb91069 Revert "feat: enrich App Mode telemetry with view_mode, workflow_id, and is_app" (#12583)
Reverts Comfy-Org/ComfyUI_frontend#12543
2026-06-01 18:30:21 -07:00
Dante
60a4dc3001 fix: dedupe Bypass context-menu items via state-aware legacy label (FE-720) (#12500)
## Summary

Right-clicking a bypassed node showed two bypass-related items in the
Vue "More Options" context menu (FE-720):

- Plain `Bypass` from the legacy LiteGraph `getExtraMenuOptions` hook in
`litegraphService.ts`
- `Remove Bypass` (with `Ctrl+B` and an icon) from the Vue
`getBypassOption` composable

The Vue menu's exact-label deduplicator in `contextMenuConverter.ts`
collapsed the unbypassed case (both emit `Bypass` → Vue source wins) but
not the bypassed case (`Bypass` vs `Remove Bypass`), so the duplicate
leaked through whenever the node was bypassed.

### before
<img width="1920" height="958" alt="fe-720-before"
src="https://github.com/user-attachments/assets/ef001aca-d70e-4798-ac61-01cc34c31e44"
/>

### after
<img width="1920" height="958" alt="fe-720-after"
src="https://github.com/user-attachments/assets/d6d2bf4b-cb98-4b30-9dac-9bd4b68a7e36"
/>

#### single active node (KSampler)
<img width="1920" height="958" alt="fe-720-1-unbypassed-node-menu"
src="https://github.com/user-attachments/assets/bec9cd47-2f2d-4adb-b95b-266e7969a36c"
/>

#### single bypassed node (Load Checkpoint)
<img width="1920" height="958" alt="fe-720-2-bypassed-node-menu"
src="https://github.com/user-attachments/assets/91f80157-836d-4fce-adad-474f31baff04"
/>

#### KSampler + bypassed Load Checkpoint
<img width="1920" height="958" alt="fe-720-3-mixed-selection-menu"
src="https://github.com/user-attachments/assets/e4780b16-08e5-4f87-80e9-3ff65a5acdae"
/>

## Root cause

`src/services/litegraphService.ts` pushes a `Bypass` entry from its
legacy `getExtraMenuOptions` hook in addition to the Vue
`getBypassOption`. In Vue-menu mode both reach the menu; the exact-label
dedup in `contextMenuConverter.ts` only collapses them when the labels
match, which fails once the node is bypassed and the Vue side switches
to `Remove Bypass`.

## Fix

Add `Bypass` and `Remove Bypass` to the `HARD_BLACKLIST` in
`contextMenuConverter.ts`. The blacklist filters the legacy emission out
of the Vue conversion pipeline (`convertContextMenuToOptions`) before it
is ever merged, so Vue's `getBypassOption` is the single source of the
bypass item in every node state — no duplicate is created in the first
place. This is the established convention for legacy items that the Vue
menu replaces (`Properties`, `Colors`, `Shapes`, `Title`, `Mode`,
`Properties Panel`, `Copy (Clipspace)`); Bypass is the same category.

`litegraphService.ts` reverts to a plain `content: 'Bypass'` and no
longer imports `areAllSelectedNodesInMode` or i18n keys for this entry.

The Vue `getBypassOption` label is still derived from the same
selection-aware predicate (`areAllSelectedNodesInMode`) that
`toggleSelectedNodesMode` uses, so on mixed selections the label stays
in sync with the action — it shows `Bypass` when clicking would bypass
the rest, rather than `Remove Bypass`.

**Trade-off:** the classic LiteGraph canvas menu
(`Comfy.VueNodes.Enabled: false`) renders `litegraphService`'s options
directly without going through `convertContextMenuToOptions`, so it
shows a plain `Bypass` regardless of node state. This matches the pre-PR
behavior (the legacy push was already a hardcoded `Bypass`), so it is
not a regression.

## Considered and rejected

- **`equivalents` map** (`bypass: ['bypass', 'remove bypass']`) — would
collapse `Bypass` and `Remove Bypass` as synonyms, which is semantically
wrong: they are distinct actions that must stay distinguishable, and the
rule would also misfire on the unbypassed case. A converter test locks
in that they are not treated as equivalents.
- **State-aware label on the legacy push** (matching the Vue label so
the exact-label dedup collapses them) — works, and additionally gives
the classic canvas menu a state-aware label, but it couples
`litegraphService` to the selection predicate and i18n keys solely to
keep a downstream dedup load-bearing. `HARD_BLACKLIST` removes the
duplicate at the source instead of creating, converting, then collapsing
it. The only thing lost is the classic-menu state-aware label, which was
never present pre-PR.
- **Gating the legacy push on `Comfy.UseNewMenu === 'Disabled'`** — the
setting that selects the legacy vs Vue context menu is
`Comfy.VueNodes.Enabled`, not `Comfy.UseNewMenu` (an unrelated
top-menu-bar toggle). Gating on `UseNewMenu` would drop the Bypass entry
from the legacy canvas menu for the OSS default (`VueNodes.Enabled:
false` + `UseNewMenu: 'Top'`).
- **Suppressing the legacy callback via
`SUPPRESSED_LITEGRAPH_CALLBACKS`** — matches by callback identity and
adds cross-file coupling for what is a simple label-based filter that
`HARD_BLACKLIST` already expresses.

## Cleanups (review feedback)

- Removed the now-dead `NodeSelectionState.bypassed` field and its
producer (no consumers after the label switch).
- Replaced the `vue-i18n` mock in `useNodeMenuOptions.test.ts` with a
real `createI18n` instance per `docs/testing/vitest-patterns.md`;
removed a `ts-expect-error` via a typed hoisted `app` mock.
- Simplified `getSelectedNodeArray` to
`Object.values(app.canvas.selected_nodes ?? {})`.

## Tests

- `useSelectedLiteGraphItems.test.ts` — `areAllSelectedNodesInMode`:
all-bypassed → true, mixed → false, empty → false.
- `useNodeMenuOptions.test.ts` — Vue label is `Bypass` (active / mixed)
and `Remove Bypass` (all bypassed).
- `contextMenuConverter.test.ts` — the legacy `Bypass` push is filtered
by `HARD_BLACKLIST` so the Vue item is the only bypass entry (keeps
shortcut/source); `Bypass` and `Remove Bypass` are not treated as label
equivalents.
- `browser_tests/tests/vueNodes/interactions/node/contextMenu.spec.ts` —
e2e regression: exactly one bypass-family item per node state.

Verified live on a bypassed Load Checkpoint: single `Remove Bypass` →
toggle un-bypasses → single `Bypass`; no duplicate, rest of the menu
intact.

- Fixes FE-720

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-06-02 01:12:55 +00:00
Alexander Brown
c4c1dfa58a Remove drag node test from interaction.spec.ts (#12579)
It's flaky.
2026-06-02 01:05:48 +00:00
AustinMroz
1938ba809b Track undo state on subgraph conversion (#12575)
When converting to subgraph, `beforeChange` and `afterChange` were being
called, but these functions exclusively called vestigial change handlers
that don't actually affect change tracking.

Consequentially, if you made a change to the graph (updating a widget),
converted a node to a subgraph using the selectionToolbox, and then
pushed Ctrl+Z before performing any other canvas interaction, it would
incorrectly undo the prior widget edit as well.

This is resolved by calling the important handlers directly. Adding them
to `beforeChange`/`afterChange` was considered, but caused breakage in
other functions (`connect`) which failed to even attempt symmetric calls
of the function.
2026-06-02 00:36:43 +00:00
Alexander Brown
9a86b33c77 fix: silence icon utility probe warnings (#12512)
## Summary

Silence false Iconify warning spam during Tailwind lint analysis while
preserving icon utility generation and real failure signals.

## Changes

- **What**: Local wrapper around `@iconify/tailwind4` that swallows the
noise from `better-tailwindcss/enforce-canonical-classes` probes.
Wrapper delegates icon resolution to upstream's `getDynamicCSSRules`;
only custom-comfy folder loading is reimplemented. `lucideStrokePlugin`
uses a `InvalidIconProbeError` sentinel so missing icons / missing deps
still surface.
- **Dependencies**: None.

## Background

Both upstreams have refused to address this:

- `better-tailwindcss`
([schoero/eslint-plugin-better-tailwindcss#377](https://github.com/schoero/eslint-plugin-better-tailwindcss/issues/377),
open) — owner declines to filter third-party warnings; says it should be
fixed in iconify.
- `@iconify/tailwind4`
([iconify/iconify#357](https://github.com/iconify/iconify/issues/357),
closed) — maintainer defends the `console.warn` as intentional; *"not
something plugin has control over"*.

Trigger path: `enforce-canonical-classes` → Tailwind
`canonicalizeCandidates()` → iconify `matchComponents` handler throws on
non-icon strings → upstream catches + `console.warn`.

## Review Focus

- Inline `loadComfyIconSet` in `iconifyDynamicPlugin.js` skips
`@iconify/tools`' `parseColors → currentColor` normalization. Verified
the comfy SVGs already use `currentColor` directly; multi-color icons
(bria, bytedance) keep their explicit fills as expected.
- `lucideStrokePlugin.js` catch is now type-discriminated — only
`InvalidIconProbeError` returns `{}`; `Cannot load icon set` and `Cannot
find icon` re-throw.

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-06-01 21:57:13 +00:00
Matt Miller
faac0347aa fix(cloud/oauth): mint session cookie when resuming consent while already signed in (#12571)
## Problem

When a user is **already signed in** and a client (e.g. an MCP client)
starts the OAuth flow, the consent screen fails with:

> OAuth request failed. Please restart from the client app.

**Repro:** be signed in to Cloud, then trigger an OAuth authorization
from a client. Instead of the consent screen, the consent view renders
the generic error.

## Root cause

The consent challenge request (`GET /oauth/authorize`) is authenticated
by the Cloud **session cookie** — a distinct credential from the
Firebase client login that `isLoggedIn` reflects.

- The **post-login** resume path (`useOAuthPostLoginRedirect`) mints
that cookie via `createSessionOrThrow()` before navigating to consent.
- The **already-signed-in** path — the `cloud-login` / `cloud-signup`
`beforeEnter` guards in `onboardingCloudRoutes.ts`, which short-circuit
straight to consent when `isLoggedIn` is `true` — skipped that step.

With no session cookie the consent challenge request is unauthenticated,
so the server redirects it to login; `fetch` follows the redirect to an
HTML page, `response.json()` throws, and `OAuthConsentView` falls
through to `genericError`.

The `Comfy.Cloud.SessionCookie` extension's `createSession()` on
auth-resolve is fire-and-forget and races the redirect, so it doesn't
reliably cover this path.

## Fix

`oauthConsentRedirect()` now mints the session cookie
(`createSessionOrThrow()`) before redirecting an already-signed-in user
to consent — symmetric with the post-login path. Best-effort: on failure
it logs and still lands on the consent view so the user gets an
actionable message rather than a silently dropped flow.

## Test plan

- [x] New `onboardingCloudRoutes.test.ts`: resume → mints cookie then
routes to consent; no pending OAuth → `cloud-user-check` (no mint); mint
failure → still routes to consent so the view surfaces the error.
- [x] `vue-tsc` typecheck, eslint, oxlint clean.
- [ ] Manual: already signed in to Cloud, start OAuth from a client →
consent screen renders (no "OAuth request failed").
2026-06-01 21:53:50 +00:00
Shibo Zhou
71f4b28207 feat: enrich App Mode telemetry with view_mode, workflow_id, and is_app (#12543)
## Summary

Stamp App Mode telemetry with the properties needed to measure the App
Builder product metrics validly in PostHog. Three small, independent
enrichments on top of the App-Mode execution attribution.

## Changes

- **What**:
- `view_mode` on `execution_start` / `execution_success` /
`execution_error` (captured at queue time alongside `is_app_mode`). Lets
the North Star be `execution_success` where `view_mode='app'` — genuine
app runs, excluding `builder:arrange` builder-preview runs that bare
`is_app_mode` also counts.
- `workflow_id` on `app:workflow_saved` and `app:app_mode_opened`
(sources `workflow` / `template_url`) via a shared
`workflowTelemetryId()` helper; `storeJob` refactored onto it so save /
open / run events share one join key. Enables distinct-app counts,
activated apps (created → ≥1 successful run), and per-app quality.
- intrinsic `is_app` on `app:share_flow` `link_created` (from the
workflow's `initialMode`, not the share-time view) plus `workflow_id`;
`is_app` on `app:workflow_imported` / `opened` (from the loaded graph's
`extra.linearMode`). Enables virality by true app-ness and app-traffic
attribution.
- **Breaking**: none.
- **Dependencies**: none.

## Review Focus

- **The commits to review are the three after the foundation**:
`view_mode`, `workflow_id`, and `is_app`. The first commit in the diff
(`feat: attribute workflow executions to App Mode in telemetry`) is the
pre-existing foundation this builds on — its branch is not currently on
the remote, so this PR is based on `main` and carries it forward.
Reviewing per-commit is easiest.
- **Join-key consistency**: `workflowTelemetryId()` is the single
definition of the workflow id (`activeState.id ?? initialState.id`),
shared by the new save/open events and the existing execution events. A
divergence would silently break the created→run and opened→run joins.
Unit-tested.
- **Scope (YAGNI)**: `workflow_id` / `is_app` added only where a locked
metric consumes it — `share_flow` only on `link_created`;
`app_mode_opened` only on the `workflow` / `template_url` sources (not
`app_builder` / `keybind`).
- **No double serialize**: `app.ts` reuses a single
`rootGraph.serialize()` for both the `is_app` derivation and
`afterLoadNewGraph`.

Cloud-only (telemetry is tree-shaken from OSS builds). No UI changes.

---------

Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2026-06-01 14:47:09 -07:00
imick-io
fef35e7dda fix(website): responsive fixes for pricing, product heroes, and cloud banner (#12570)
## Summary

Responsive polish: stack mobile pricing plans, rework the cloud product
hero so the illustration and text fit at all viewports, shrink the local
product hero illustration below xl, and reduce the cloud banner font
size on mobile.

## Changes

- **What**:
- `pricing/PriceSection.vue`: replace mobile tab-toggle pattern with
stacked plan cards; drop the `activePlanIndex` ref and tab buttons.
- `product/cloud/HeroSection.vue`: rework layout so illustration and
text fit across breakpoints (md/lg sizing, spacing, max-widths).
- `product/local/HeroSection.vue`: shrink illustration container below
xl (`max-w-xs`/`md:max-w-sm`/`lg:max-w-md`).
- `product/shared/CloudBannerSection.vue`: smaller font on mobile
(`text-sm` instead of `text-lg`).

## Review Focus

- Mobile pricing UX: confirm stacked cards are preferable to the prior
tab-toggle for our target viewports.
- Cloud hero layout at md/lg/xl breakpoints — check for overflow or
awkward spacing.

## Screenshots (if applicable)

<!-- Add before/after screenshots of the pricing page (mobile) and
product hero pages. -->

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-06-01 21:30:30 +00:00
nav-tej
e4d481f893 fix(telemetry): harden PostHog init — person_profiles, cookie_domain, before_send (#12479)
## Summary

Hardens PostHog initialization in the cloud app and website with three
missing config options identified during PostHog QA audit.

## Changes

- **What**: Adds `person_profiles: 'identified_only'`, `cookie_domain:
'.comfy.org'`, and `before_send` PII redaction hook to both PostHog init
calls
- **person_profiles**: Prevents anonymous Person records being created
in PostHog for pre-auth visitors — only creates Person after
`identify()` is called (matches website config which already had this)
- **cookie_domain**: Enables cross-subdomain session stitching across
`app.comfy.org`, `docs.comfy.org`, `www.comfy.org` — without this, each
subdomain creates a separate PostHog session
- **before_send**: Last-line PII guard — strips `email`, `prompt`,
`user_email`, `$email` from event properties before transmission. Placed
after `...serverConfig` spread so it cannot be overridden by remote
config.

## Review Focus

- `before_send` runs before GeoIP enrichment — deliberately does NOT
drop `$ip` since country-code targeting needs it for the pricing
experiment
- `person_profiles: 'identified_only'` + anonymous flag evaluation:
PostHog confirmed this still allows flag eval via `$anon_distinct_id`;
stitched retroactively on `identify()`
- `serverConfig` spread is before the three new fields — these take
precedence over any remote config values (intentional for
security/correctness)

Closes MAR-233, MAR-235, MAR-236

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Miles <miles@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: nav <nav@mac.lan>
Co-authored-by: Miles Ryan <thedatalife@users.noreply.github.com>
2026-06-01 20:53:00 +00:00
Alexander Brown
da836cb681 chore: upgrade Astro to v6 (#12532)
## Summary

Upgrades the website app from Astro 5.10 to Astro 6.4.2.

## Changes

- **What**: Bumps `astro` (5.10 → 6.4.2), `@astrojs/vue` (5 → 6.0.1),
`@astrojs/sitemap` (3.7.1 → 3.7.3), and `@astrojs/check` (0.9.8 → 0.9.9)
in the workspace catalog. Refreshes `vite` in the lockfile from `8.0.0`
to `8.0.14` to clear a Vite/Rolldown "Not implemented" regression in dep
optimization that Astro v6 triggered on `8.0.0`.
- **Breaking**: None for our code. Audit against the [Astro v6 upgrade
guide](https://docs.astro.build/en/guides/upgrade-to/v6/) found no
breaking change applies:
  - Already on `<ClientRouter />` (no removed `<ViewTransitions />`)
- No content collections (`defineCollection`/`getCollection`) — legacy
removal n/a
- `getStaticPaths` functions don't access the deprecated `Astro` global
- No `<Image>`/`<Picture>`/`getImage()` usage — SVG rasterization &
client-throw n/a
  - No `import.meta.env.ASSETS_PREFIX`
- No file-extension endpoints (`*.xml.ts`/`*.json.ts`) so the
trailing-slash change is moot
  - No markdown content (heading-ID compat n/a)
- i18n config (`prefixDefaultLocale: false`, no
`redirectToDefaultLocale`) is compatible with the new
`redirectToDefaultLocale: false` default
  - No experimental flags enabled

## Review Focus

- The Vite lockfile bump from 8.0.0 → 8.0.14 inside the same `^8.0.0`
range. Without it, `astro check` on v6 hits `Not implemented` inside
Vite's `esbuildPlugin.generateBundle` via Rolldown.
- Sanity-check the audit above against any in-flight work that might add
content collections / `<Image>` etc.

## Verification

- `pnpm typecheck` → 0 errors
- `pnpm build` → 379 pages built
- `pnpm test:unit` → 85/85 passing
- `pnpm lint` → 0 errors (3 pre-existing warnings)
2026-06-01 20:38:13 +00:00
Deep Mehta
a549bd0123 fix: add static fallback favicon to index.html (#12537)
## Summary

cloud.comfy.org (and any route before the graph view mounts, e.g. the
login screen) renders no favicon. Add a static fallback so a favicon
always shows.

## Changes

- **What**: The favicon is only ever set at runtime —
`useFavicon('/assets/favicon.ico')` in `LayoutDefault.vue` and the
progress frames in `useProgressFavicon.ts`. Before that JS runs (first
paint, login screen, routes that don't mount
`GraphView`/`LayoutDefault`), no `<link rel="icon">` exists, and the
browser's default `/favicon.ico` request hits the SPA catch-all and
returns HTML, so no favicon renders. Added a single static `<link
rel="icon" href="/assets/favicon.ico">` (asset already exists) to
`index.html`.
- **Breaking**: none.

## Review Focus

- Single icon link is intentional: vueuse `useFavicon` overwrites the
`href` of every `link[rel*="icon"]` at runtime (including
`apple-touch-icon`), so the progress-favicon animation assumes one
controllable icon link. Adding multiple static links (svg/png) would get
their `href` clobbered to the progress PNG mid-generation and render
inconsistently — so this PR deliberately adds just the `.ico`.
- The progress-favicon still works: vueuse updates this same link's
`href`.
- Path matches what the app already uses at runtime
(`/assets/favicon.ico`).

## Screenshots (if applicable)
2026-06-01 20:31:16 +00:00
Alexander Brown
593586bbeb chore: Update CODEOWNERS (Should we just delete it?) (#12568)
It's requiring a lot of "Skip check" merges.
2026-06-01 19:56:19 +00:00
Terry Jia
7df62ca75e feat: add PreviewGaussianSplat + PreviewPointCloud extensions (#12545)
## Summary
Two dedicated 3D viewer extensions for the splat / point-cloud.

- Comfy.PreviewGaussianSplat targets backend node 'PreviewGaussianSplat'
(.ply / .spz / .splat / .ksplat).
- Comfy.PreviewPointCloud targets backend node 'PreviewPointCloud' (.ply
point clouds).

PLY auto-dispatch, no more user-facing engine choice for 3DGS:

**Please be aware that I have not yet implemented any UI optimizations
on the frontend for world models such as World Labs' Marble, no WSAD
controls, no scale optimization yet**

BE: https://github.com/Comfy-Org/ComfyUI/pull/14194

## Screenshots (if applicable)
ksplat file:
<img width="2714" height="1391" alt="image"
src="https://github.com/user-attachments/assets/9024db9d-20e9-44ea-ab14-500810d2946a"
/>
splat file:
<img width="2938" height="1410" alt="image"
src="https://github.com/user-attachments/assets/de768fa5-9d55-4560-9fb3-b218b96ea0c7"
/>
spz file:
<img width="1729" height="845" alt="image"
src="https://github.com/user-attachments/assets/cc09e568-77c9-45b3-a6cc-8f5d1062f3ec"
/>
ply (splat) file:
<img width="1702" height="843" alt="image"
src="https://github.com/user-attachments/assets/2a51c2ce-046b-4843-9e58-634bc45cbcce"
/>
ply (point cloud) file:
<img width="1701" height="842" alt="image"
src="https://github.com/user-attachments/assets/db75808e-3481-4ecc-8582-e4fec21163fd"
/>
2026-06-01 14:25:06 -04:00
Dante
e16a0bfe82 fix(knip): narrow Playwright entrypoints so browser-test dead exports are reported (FE-717) (#12496)
## Summary

Narrow Knip's Playwright `entry` to actual spec files so dead exports in
browser-test fixtures are reported instead of being hidden by treating
every helper as an entrypoint.

## Changes

- **What**:
- `knip.config.ts`: Playwright `entry` changed from the broad
`['**/*.@(spec|test)…', 'browser_tests/**/*.ts']` to
`['browser_tests/**/*.@(spec|test).?(c|m)[jt]s?(x)']`.
`globalSetup`/`globalTeardown` stay covered via Knip's playwright config
resolution; fixtures remain in the project graph so their unused exports
surface.
- Resolved the 54 dead findings this exposed: over-exported symbols used
only within their own module are now module-private (dropped `export`,
no behavioral change); genuinely unreferenced fixtures were deleted
(asset/template `ALL_*` aggregators + orphaned `STABLE_*` data,
`TemplateHelper` distribution helpers + `generateTemplates`, dead
types/utils, and the unused `nodeDefinitions.ts` module).
- **Breaking**: none — test-only changes.

## Review Focus

- Deletions are limited to fixtures with zero importers on `main`
(verified via `pnpm knip`); the bulk of the diff is `export`-keyword
removal.
- Verified: `pnpm knip` (browser_tests clean), `pnpm typecheck`, `pnpm
typecheck:browser`, oxfmt/oxlint/eslint all pass.

Linear: FE-717
2026-06-01 07:53:29 +00:00
Dante
3a8ddfb6f1 fix: wrap long workflow name in Open shared workflow dialog (FE-828) (#12540)
## Summary

The "Open shared workflow" dialog rendered the workflow name in an
`<h2>` with no
wrapping control. A long, space-free name (e.g. a content-hash filename)
is a
single unbreakable "word", so with the default `overflow-wrap: normal`
it could
not wrap. It overflowed its box and, because PrimeVue's
`.p-dialog-content` is
`overflow-x: auto`, the dialog scrolled horizontally instead of
wrapping.

CDP measurement on the unfixed build (96-char name): dialog content
`scrollWidth 1336` vs `clientWidth 702` -> horizontal scroll. After
adding
`wrap-anywhere` to the heading: `scrollWidth 702 == clientWidth 702`,
name wraps
to multiple lines, full name still in the DOM.

### before
<img width="704" height="295" alt="before-dialog"
src="https://github.com/user-attachments/assets/ea05ab32-a80d-4210-951c-f43d595bd6eb"
/>

### after
<img width="704" height="359" alt="after-dialog"
src="https://github.com/user-attachments/assets/cbf3019e-5e71-4dba-a1fd-ea3586dd995a"
/>

## Changes

- `OpenSharedWorkflowDialogContent.vue`: add `wrap-anywhere` to the
workflow-name
`<h2>` so a long unbreakable name wraps within the dialog bounds instead
of
  forcing horizontal scroll. The parent already has `min-w-0`.
- Breaking: none

## Red-Green Verification

| Commit | CI | Purpose |
|--------|-----|---------|
| [`test:`
d9158d1](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/26678596015)
| 🔴 Red (failure) | Proves the test catches the bug — cloud
e2e failed at `expect(scrollWidth).toBeLessThanOrEqual(clientWidth +
1)`: received 1332, expected <= 703 |
| [`fix:`
08e75a1](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/26678974321)
| 🟢 Green (success) | Proves the fix resolves it |

Fixes FE-828

## Test Plan

- [x] CI red on test-only commit
- [x] CI green on fix commit
- [x] E2E regression
`browser_tests/tests/dialogs/openSharedWorkflowDialog.spec.ts` (@cloud):
mocks a long-named shared workflow, asserts the dialog does not overflow
horizontally
- [x] Manual CDP verification (before/after above)
2026-06-01 07:53:16 +00:00
Dante
5a53df8d79 feat(dialog): migrate Manager dialog to Reka-UI (Phase 4) (#12403)
## Summary

Phase 4 of the dialog migration. Flips `useManagerDialog` onto the Reka
renderer added in Phase 0, with content sizing that matches the legacy
`.manager-dialog` CSS (1724px × 80vh, expanding to 2200×1320 above
3000px). Public API of `useManagerDialog` / `useDialogService` is
unchanged.

Parent:
[FE-571](https://linear.app/comfyorg/issue/FE-571/dialog-system-migration-primevue-reka-ui-parent)
This phase:
[FE-576](https://linear.app/comfyorg/issue/FE-576/phase-4-migrate-manager-dialog-designer-review)
Predecessors: #11719 (Phase 0, merged), #12041 (Phase 1, merged), #12109
(Phase 2, merged), #12182 (Phase 3, **stacked PR base**)

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

## Changes

### `src/workbench/extensions/manager/composables/useManagerDialog.ts`
(+12)

| Field | Value | Reason |
| --- | --- | --- |
| `renderer` | `'reka'` | Flip onto the new path |
| `size` | `'full'` | Free `DialogContent` to take the contentClass
dimensions |
| `contentClass` | `w-[90vw] max-w-[1724px] sm:max-w-[1724px] h-[80vh]
max-h-[1026px] min-[3000px]:max-w-[2200px] min-[3000px]:max-h-[1320px]
rounded-2xl overflow-hidden` | Mirrors legacy `.manager-dialog` global
CSS exactly |
| `modal` | `false` | Manager hosts PrimeVue overlays (`SingleSelect`,
`SearchAutocomplete`-host scope, sort dropdown) teleported to body. Reka
modal trap disables their pointer-events. Same fix Phase 3 applied to
Settings |

Intentionally left for Phase 6: the global `.manager-dialog` CSS in
`GlobalDialog.vue` `<style>` and the matching `class="manager-dialog"`
on `BaseModalLayout`. Removing them here is a cascade-order risk (they
currently override `BaseModalLayout` size="lg" 1280px cap with 1724px);
Phase 6 owns the CSS-overrides cleanup pass.

### Tests

-
`src/workbench/extensions/manager/composables/useManagerDialog.test.ts`
**(new)** — 5 tests: renderer flip + Manager sizing, non-modal,
`initialTab` forwarding, `initialPackId` forwarding, `hide()` closes.

## Verification

### DOM probes (local dev, `useManagerDialog().show()` against ComfyUI
on :8189)

| Probe | Result |
| --- | --- |
| Manager dialog node | `[role="dialog"]` at z-1804, rect 1724×794 —
`contentClass` applied exactly |
| SingleSelect listbox | Teleported to `BODY > DIV > listbox`, z-3000 —
escapes Reka `overflow-hidden` |
| SearchAutocomplete | Uses Reka `ComboboxPortal` (`z-3000`,
`position="popper"`) — same teleport guarantee by construction |
| Stacked Reka confirm over Manager | Manager z-1804, confirm z-1806 —
vRekaZIndex orders correctly |
| ESC on stacked confirm | Top confirm closes, Manager remains open |

Screenshots from local verification will be attached as PR comments.

## Quality gates

- [x] `pnpm typecheck` — clean
- [x] `pnpm lint` — clean for touched files
- [x] `pnpm format` — applied
- [x] `pnpm test:unit` (touched + adjacent):
  - `useManagerDialog.test.ts` — 5/5
  - `src/workbench/extensions/manager/composables/` — 156/156
- `useSettingsDialog.test.ts` + `src/components/dialog/` (Phase 3
regression net) — 86/86
- [ ] CI Playwright matrix
- [x] Manual verification on a backend (ComfyUI :8189)

## Public API impact

None. `useManagerDialog().show(initialTab?, initialPackId?)` keeps the
same signature.

## Out of scope (later phases)

- `ConfirmDialog` callers — Phase 5 (FE-577)
- Removing PrimeVue `Dialog`/`<style>` overrides in `GlobalDialog.vue`
(incl. `.manager-dialog`) — Phase 6 (FE-578)
- Designer pass on Manager dimensions (FE-576 acceptance #1) — owner
Jaewon, async with this PR

## Review focus

1. **Sizing translated literally** — `contentClass` mirrors the existing
`.manager-dialog` CSS rule (height + max-width + max-height + 3000px
breakpoint). Net visible should be byte-identical to today. Worth a
designer pass per FE-576 acceptance criteria.
2. **`modal: false` rationale** — same as Phase 3 Settings: Manager's
PrimeVue-overlay children break under Reka modal focus trap. Acceptance
#2 ("Install/uninstall flows behave identically") is preserved because
no overlay component changed; only the outer dialog renderer.
3. **Phase 6 deferred cleanup** — `.manager-dialog` CSS rule +
`class="manager-dialog"` on `BaseModalLayout` are kept on purpose. They
override `BaseModalLayout size="lg"`'s 1280px cap with 1724px on small
viewports; removing them naively regresses width. Phase 6 will replace
via `size` prop or new variant.

## screenshot 

<img width="1440" height="828" alt="keybinding-panel"
src="https://github.com/user-attachments/assets/c8e4ae15-860c-4f61-b48f-795f5e24912d"
/>
<img width="1440" height="828" alt="nested-modify-keybinding"
src="https://github.com/user-attachments/assets/a6890183-be0f-4831-919e-4edb2ee2bb04"
/>
<img width="1920" height="992" alt="phase4-manager-node-pack-dropdown"
src="https://github.com/user-attachments/assets/e4f97486-8b89-408a-9f83-92fbbfd5c29b"
/>
<img width="1440" height="828" alt="settings-dialog-reka"
src="https://github.com/user-attachments/assets/7764ac51-703e-40a9-9ff8-af664b9ad11a"
/>


## Test plan

- [x] Unit: 5/5 new + 156/156 manager composables + 86/86 adjacent
- [ ] CI: full Vitest + Playwright matrix
- [x] Manual on a backend:
- Open Manager (nav, search filter, install button visible, scroll grid)
- SingleSelect (Node Pack) dropdown not clipped by dialog
`overflow-hidden`
  - Stacked Reka confirm over Manager renders above with correct z-index
  - ESC closes only the top dialog

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: GitHub Action <action@github.com>
2026-06-01 13:41:53 +09:00
Comfy Org PR Bot
9e32b7db51 1.46.6 (#12535)
Patch version increment to 1.46.6

**Base branch:** `main`

---------

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

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

## Changes

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

## Review Focus

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

## Before / After

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

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

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

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

## Changes

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

## Review Focus

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

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

## Tests

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

### current prod

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

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

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

## Follow-up

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

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

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

## Changes

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

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

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

## Review Focus

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

## Verification

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

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

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

---------

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

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

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

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

## Scope note

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

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

---------

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

**Base branch:** `main`

---------

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

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

## Changes

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

## Review Focus

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

Linear: FE-851

## Red-Green Verification

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

## Test Plan

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

## Screenshots (Before/After)
Before


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

After


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

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

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

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

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

## What This PR Targets

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

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

Unknown node execution errors resolve to the general runtime fallback:

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


## Implementation Notes

### Resolver Split

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

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

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

### Conservative Matching

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

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

### Raw Detail Preservation

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

## Out Of Scope / Follow-Ups

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

Planned follow-up work remains:

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

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

## Validation

Ran successfully:

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

Notes:

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

## Screenshots (Before / After) 

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

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

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

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

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

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

## Scope of suppression

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

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

## Tests

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

Supersedes #12175.

---------

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

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

## Changes

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

## Review Focus

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

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

Key behavior changes:

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

Coverage added or updated:

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

Validation run:

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

## Screenshots (if applicable)

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

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

## Changes

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

## Review Focus

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

## Verification

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

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

---

## Problem

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

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

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

## Root cause

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

## Fix

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

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

## Verification

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

## Scope

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

## Screenshots

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

---------

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

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

## Changes

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

## Screenshots

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

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

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

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

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

---------

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

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

## How it works

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

## Test plan

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

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

---------

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

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

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

## Changes

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

## Review Focus

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

## Validation

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

## Screenshots (if applicable)


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

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.46.4
2026-05-28 17:19:09 +00:00