mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-06-08 23:39:23 +00:00
ab14ab5d905795c19dfef0a841e1f15ea5ff4d78
8170 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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> |
||
|
|
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> |
||
|
|
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    ┆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> |
||
|
|
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>
|
||
|
|
1e01c7128b |
chore: Add org prefix for team in CODEOWNERS (#12590)
https://github.com/orgs/community/discussions/22751 |
||
|
|
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 |
||
|
|
3d8bb91069 |
Revert "feat: enrich App Mode telemetry with view_mode, workflow_id, and is_app" (#12583)
Reverts Comfy-Org/ComfyUI_frontend#12543 |
||
|
|
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> |
||
|
|
c4c1dfa58a |
Remove drag node test from interaction.spec.ts (#12579)
It's flaky. |
||
|
|
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. |
||
|
|
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> |
||
|
|
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"). |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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) |
||
|
|
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)
|
||
|
|
593586bbeb |
chore: Update CODEOWNERS (Should we just delete it?) (#12568)
It's requiring a lot of "Skip check" merges. |
||
|
|
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" /> |
||
|
|
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 |
||
|
|
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:` |
||
|
|
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> |
||
|
|
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 |
||
|
|
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) |
||
|
|
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. |
||
|
|
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) |
||
|
|
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> |
||
|
|
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> |
||
|
|
e97c4b6ab9 | Remove flake screenshot (#12529) | ||
|
|
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 |
||
|
|
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 |
||
|
|
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. |
||
|
|
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. |
||
|
|
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
|
||
|
|
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> |
||
|
|
26dfa5c547 |
refactor: drop redundant rotation field from Load3D camera_info (#12515)
## Summary As discussed in slack, we would like to remove rotation field |
||
|
|
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. |
||
|
|
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. |
||
|
|
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>
|
||
|
|
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="{..."github-stars":[0,""]}"
```
`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

---------
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
|
||
|
|
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" /> |
||
|
|
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. |
||
|
|
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> |
||
|
|
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> |
||
|
|
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. |
||
|
|
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 |
||
|
|
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 |