mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-11 01:28:03 +00:00
97c59f3adbbabe7678df9ccebcbcaf92ed708564
154 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
da55529d23 |
GTM-93 point Windows download at comfy.org proxy (#12974)
## Summary - Point the website Windows desktop download URL at `https://comfy.org/download/windows/nsis/x64`. - Keep macOS on the existing ToDesktop URL. - Update the download page smoke test to expect the new Windows href. ## Context This is the frontend leg of the GTM-93 Windows MVP. ToDesktop still controls `download.comfy.org`; instead of changing DNS, the website sends Windows users to a controlled `comfy.org` proxy path that the router PR handles. The proxy forwards to ToDesktop and adds a tokenized `Content-Disposition` filename for Desktop to consume on Windows. Linear: https://linear.app/comfyorg/issue/GTM-93/fix-posthog-identify-call-unblock-funnel-attribution-desktop-funnel Router PR: https://github.com/Comfy-Org/comfy-router/pull/33 Desktop PR: https://github.com/Comfy-Org/Comfy-Desktop/pull/1149 ## Validation - `pnpm --filter @comfyorg/website run typecheck` - `pnpm --filter @comfyorg/website run build` - `pnpm --filter @comfyorg/website exec playwright test e2e/download.spec.ts` - pre-commit: `pnpm typecheck`, `pnpm typecheck:website` |
||
|
|
49a7b7b558 |
feat(billing): UnifiedPricingTable — one table, personal/team plan toggle (B4 / FE-934) (#12666)
## What **B4 (FE-934): `UnifiedPricingTable`** — one pricing table for the **Jun-5 model** (typeless workspace; personal/team is a **plan**, shown as a Gamma-style **plan toggle** on one workspace), per **DES-197**. A new, flag-gated component that will replace the two legacy tables at cutover (strangler). ### video https://github.com/user-attachments/assets/82b704a4-101e-4609-8ff5-06b7cf7f9cd7 > **Stacked on #12644 (FE-935 `CreditSlider`).** Base is the slider branch — retarget to `main` once #12644 merges. The slider commits show in the diff until then. ## Changes - **`UnifiedPricingTable.vue`** — plan toggle (personal/team, flag-gated on `teamWorkspacesEnabled`); personal tier cards (facade `plans` + `TIER_PRICING` fallback, billing-cycle toggle); team column hosting `<CreditSlider>`; Enterprise card. Reuses `useBillingContext`; emits `subscribe`/`resubscribe` (personal) + `subscribeTeam` (team). - **`SubscriptionRequiredDialogContentUnified.vue`** — host; wires personal checkout through `useSubscriptionCheckout` (full flow incl. preview/transition + **new `'success'` step**); team checkout stubbed. - **Confirm/success screens (DES 3084-15873)** — aligned the reused `SubscriptionAddPaymentPreviewWorkspace` / `SubscriptionTransitionPreviewWorkspace` to DES-197 (drop `/member`, `comfy--credits` icon, plan-specific CTAs **"Subscribe to {plan}" / "Switch to {plan}"**); added **`SubscriptionSuccessWorkspace.vue`** ("You're all set") as the `'success'` checkout step. - **`showPricingTable`** — renders the unified host when `teamWorkspacesEnabled`, legacy `PricingTable.vue` for flag-off. (The old workspace-variant fork is removed; `PricingTableWorkspace.vue` becomes unused on flag-on → deleted at cutover.) - **i18n** — `subscription.planScope` / `teamPlan` / `enterprise` + `subscription.preview.{subscribeToPlan,switchToPlan}` + `subscription.success.*` keys. ## Strategy (new component + cutover — per FE-934) Build new, retire old at cutover (a later single PR deletes `PricingTable.vue` + `PricingTableWorkspace.vue` + the legacy dialog host + the dispatch fork; `TIER_PRICING` kept only as the flag-off/OSS fallback). Avoids half-migrated conditional cruft in the live tables. ## Screenshots Pricing captured live on the authenticated cloud-prod session (`local.comfy.org`, flag on). Confirm/success captured in Storybook (prop-driven) — a **personal** workspace can't reach these live until **B1/FE-966** flips its billing path off the legacy `/customers/*` adapter (whose `plans` is always empty); the screens themselves are unchanged regardless of source. **Pricing table** | Personal | Team | |---|---| | <img width="420" alt="pricing-personal" src="https://github.com/user-attachments/assets/2be3b8bc-ac54-41db-8c21-5c950d3e7338" /> | <img width="420" alt="pricing-team" src="https://github.com/user-attachments/assets/c4078eb4-ee7d-42f6-bcc3-375686ab7f1e" /> | **Confirm your payment / plan change** | New subscription | Plan change (Pro → Creator) | |---|---| | <img width="380" alt="confirm-new-subscription" src="https://github.com/user-attachments/assets/e371c744-dc64-43e8-b977-73f9f99f85bc" /> | <img width="380" alt="confirm-plan-change" src="https://github.com/user-attachments/assets/b1ee5ab3-c572-4c40-9b70-f078d66b78f4" /> | **You're all set (success)** <img width="380" alt="success-all-set" src="https://github.com/user-attachments/assets/8ec9e90f-8ba4-4cb0-81a7-6b4316e0c19e" /> ## ⚠️ BE-blocked (deferred) - **Team checkout**: the slider stop → plan-slug / subscribe-request shape is undefined (doc **Open Q#2** / "Team-slider contract", **BE-1254**). `subscribeTeam` is stubbed (toast "coming soon") until BE provides it. - **Live discount data**: stops come from the hardcoded **DES-197 fallback** (`teamPlanCreditStops.ts`) until `GET /api/billing/plans` carries them. - **Confirm "credits you'll get right away" line** + **async-success routing** (Stripe-tab/`pending_payment` → in-dialog success) deferred — see ticket notes; credits-cents unit is the open BE-1254 question. ## Verification - `vue-tsc --noEmit`: clean (pre-commit). - `oxlint`/`eslint`/`stylelint`/`oxfmt`: pass (pre-commit). - `vitest` `useSubscriptionDialog.test.ts` (11) + `useSubscriptionCheckout.test.ts` (17) + new `SubscriptionSuccessWorkspace.test.ts` (2): pass. - Personal checkout reuses the existing `useSubscriptionCheckout` flow; subscribed → new `'success'` step. ## Not in scope - Pixel-finalizing vs DES-197 (visual reference = Alex's **reference-only** #12042); personal-card detail refinement. - Settings / Misc-UX (FE-768/770); team-checkout wiring (BE contract). Design: **DES-197** / **3084-15873**. Survey: *FE Billing API Divergence* (B4 / P1 / P2 / P4). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: GitHub Action <action@github.com> |
||
|
|
fc4d44c3db |
[feat] migrate website navbar to shadcn-vue + mobile sheet drill-down (#12861)
## Summary - Migrate the website's top nav from bespoke components (`SiteNav`, `MobileMenu`, `NavDesktopLink`, `PillButton`, `MaskRevealButton`) to shadcn-vue primitives (`NavigationMenu`, `Sheet`, `Button`), split into `HeaderMain` → `HeaderMainDesktop` + `HeaderMainMobile`. - Mobile nav becomes a `Sheet` with drill-down sub-navigation, scroll lock, sticky CTAs, sr-only i18n title/description, and a back-to-home logo. - Desktop nav uses `NavigationMenu` with shared viewport, featured cards, `NavColumn` extraction, and centralized nav data in `data/mainNavigation.ts`. - Adds i18n strings for nav labels, dropdown column headers, close/back affordances, and the mobile menu description. ## Test plan - [ ] Desktop: hover PRODUCTS / COMMUNITY / COMPANY — dropdowns open with featured card + columns, NEW badges render, external links show the arrow-up-right icon, viewport is shared between triggers. - [ ] Mobile (<lg): open hamburger sheet — verify logo + close, body scroll is locked while open, top-level nav scrolls if it overflows, CTAs stay pinned at bottom. - [ ] Tap COMMUNITY / PRODUCTS / COMPANY — sub-panel slides over root nav, in-sheet BACK returns to root, links navigate correctly. - [ ] Reload `?locale=zh-CN`: dropdown labels, sheet title/description, BACK / close affordances all localized. - [ ] No regressions on `/cloud`, `/cloud/pricing`, `/customers`, etc. — pages that swap CTAs (`BrandButton` → shadcn `Button`) still render at every breakpoint. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: GitHub Action <action@github.com> Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
003303d8ac |
feat(website): pricing API copy + tutorial video captions (#12929)
## Summary - **Pricing copy** — replace "concurrent API jobs" wording with "workflows via API" on Creator/Pro plans, add a third feature line to Standard, and add a new "Run Workflows via API" entry to the "What's Included" section (existing feature11 → feature12). - **Tutorial captions** — wire per-locale VTT caption tracks into `TutorialDetailDialog`, so the CC button in `VideoPlayer` is now functional for the learning tutorials. `LearningTutorial.caption` is typed as `readonly VideoTrack[]`; `VideoTrack` is now exported from `VideoPlayer.vue`. - Tailwind class ordering in `PriceSection.vue` shifted due to the formatter on touched lines. ## Test plan - [ ] `/pricing` page (EN + zh-CN): Standard plan shows the new "1 workflow via API" line; Creator/Pro show "3/5 workflows via API"; "What's Included" lists "Run Workflows via API" above "Parallel job execution". - [ ] Open a learning tutorial → CC button is visible in the player → toggling CC shows English captions in sync with playback. - [ ] Tutorials without a `caption` entry hide the CC button. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
ccf2f12b25 |
fix(website): correct top-up credit rollover copy on cloud pricing (#12923)
## Summary The `/cloud/pricing` page's "Add more credits anytime" feature claims unused top-up credits **roll over** to the next month. Per product (Pablo, #cloud), top-up credits **do not** roll over. Fix the copy to match policy and the docs. ## Changes - **What**: `pricing.included.feature5.description` (en + zh-CN) in `apps/website/src/i18n/translations.ts`. - Before: "Purchase additional credits at any time. Unused top-ups roll over to the next month automatically for up to 1 year." - After: "Purchase additional credits at any time. Top-up credits are valid for 1 year from the date of purchase and do not roll over with your monthly plan." - Keeps the accurate 1-year validity window (matches `docs/interface/credits.mdx`). - Scope: this is the **only** rollover claim on the page. The tier cards (lines 1224/1255/1286) only say "top-ups available" — no change needed. - **Breaking**: none (copy only). ## Deliberately out of scope The Slack thread surfaced an **unresolved** question: Pablo said top-ups don't roll over *"as opposed to subscription credits which do,"* but `docs/interface/credits.mdx` says monthly credits don't roll over either. This PR makes **no claim about subscription/monthly credit rollover** (the new copy is neutral: "do not roll over *with your monthly plan*"). Resolving that — and the optional FAQ entry / docs alignment Glary-Bot proposed — needs a product decision first. Flagging for follow-up. |
||
|
|
eaa6776559 | Fix broken e2e test (#12818) | ||
|
|
fefbe7843c |
refactor(website): dedupe download label and add shared Platform type (#12776)
## Summary
Cleanup of the website download button: deduplicate the label lookup in
`DownloadLocalButton.vue`, and export a `Platform` domain type from
`useDownloadUrl` so the button spec, icon map, and analytics all consume
it instead of loose `string`s.
## Changes
- **What**:
- Hoist `t('download.hero.downloadLocal', locale)` into a single `label`
computed, reused by the fallback aria-labels and the button text
(previously evaluated in two places)
- Export `Platform` (`'windows' | 'mac'`) from `useDownloadUrl`;
`ButtonSpec.key`, the `ICONS` map (`Record<Platform, string>`), and
`captureDownloadClick` now consume it — adding a platform without an
icon becomes a compile error, and typo'd platform names can't reach
PostHog
- Drop the redundant `aria-hidden="true"` on the icon (`alt=""` already
marks it decorative)
## Review Focus
`ppformula-text-center` on the icon is intentionally kept — it is
positional (`position: relative; top: 0.19em`), not typographic, and
keeps the icon aligned with the optically-shifted PP Formula text.
No behavior change.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
6445690ed3 |
fix(website): rebrand "Comfy Local" to "Comfy Desktop" (#12794)
## Summary - Rebrand "Comfy Local" → "Comfy Desktop" across all user-facing copy on `apps/website`. The nav already showed "Comfy Desktop"; this pulls the rest of the site in line. - Updates the homepage product card, the `/cloud` page FAQ + pricing copy, the `/download` page SEO title/keywords, and matching Storybook stories. Both `en` and `zh-CN`. - Internal identifiers left untouched (i18n key names, the `src/components/product/local/` folder, the `Product = 'local' | …` type discriminator, and the `media.comfy.org/website/local/*` CDN paths). ## Test plan - [ ] `pnpm --filter @comfyorg/website dev` and visually verify: - [ ] `/` — product card section reads "Comfy Desktop" / "SEE DESKTOP FEATURES" - [ ] `/cloud` (en + zh-CN) — every FAQ "Local" reference now reads "Comfy Desktop" - [ ] `/download` — page `<title>` and meta keywords show new values - [ ] Nav + footer still read "Comfy Desktop" / "DOWNLOAD DESKTOP" (unchanged) - [ ] `pnpm --filter @comfyorg/website test:e2e -- navigation.spec.ts` passes - [ ] Storybook: `Website/Common/ProductCard` and `Website/Common/FooterLinkColumn` render with new labels - [ ] Regenerate any screenshot baselines that snapshot the homepage product cards or the `/cloud` FAQ section 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
79acf7be5e |
fix: re-encode favicon.ico with PNG frames to fix white corner artifacts (#12753)
## Summary The rebranded `favicon.ico` renders with **opaque white corners** in browsers — the rounded mark shows white slivers around its corners on any background. This is a decode bug, not a design issue: the ICO contains **BMP-format frames** whose alpha channel Chrome (and other consumers) mishandle. Verified by loading the raw `.ico` in headless Chrome on a dark page: corners render white instead of transparent. Every surface that consumes the `.ico` directly shows the artifact — Google search results, connector icon scraping, raw image views — while browser tabs look fine because they prefer the SVG favicon. ## Changes - **What**: Re-encode `apps/website/public/favicon.ico` and `public/assets/favicon.ico` with **PNG-format ICO frames** (16/32/48). PNG frames carry unambiguous alpha and decode correctly in all modern browsers. - **No design change**: identical rounded artwork, same transparency, same sizes and filenames. SVG, PNGs, apple-touch-icon, and manifest icons are untouched. - **Breaking**: none. ## Review Focus - Headless-Chrome verified: the old ico renders white corners on a dark page; the re-encoded one renders transparent corners. (Comparison in PR comments.) - PNG-in-ICO is supported by all modern browsers and Google's favicon pipeline. - After merge, please add `needs-backport` + `cloud/1.45` so cloud.comfy.org's copy gets the same fix. ## Screenshots (if applicable) --------- Co-authored-by: GitHub Action <action@github.com> |
||
|
|
9617e498c9 |
feat: track desktop download button clicks on website (#12770)
Adds a `website:download_button_clicked` PostHog event (with `platform` property) fired when a user clicks the desktop installer download button on comfy.org. Previously we only had `/download` pageviews as a proxy — autocapture is not active on the website project, so these clicks were untracked. Includes unit tests for the new capture helper. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ed4f7db7f4 |
feat(website): add affiliate program page at /affiliates (#12670)
## Summary - Adds the Comfy affiliate program landing page at `/affiliates`, composed from 8 reusable presentational blocks (`HeroSplit01`, `ChecklistSplit01`, `StepsGrid01`, `BenefitsGrid01`, `DetailsTable01`, `BrandAssetsGrid01`, `FAQSplit01`, `CtaCenter01`) and thin section wrappers under `templates/affiliate/*`. The page funnels through: Hero → Audience → How It Works → Why ComfyUI → Program Details → Brand Assets → FAQ → closing "Ready to start earning?" CTA. - Removes the older parallel implementation under `components/affiliates/*` (7 Vue sections, 3 data files, the i18n contract test) and the `affiliate-landing.*` translation keys — the page now has a single canonical i18n surface under `affiliate.*`. - Rewrites `e2e/affiliates.spec.ts` to target the new sections via semantic queries (no testids were added to production), covering hero indexability, the closing CTA's apply + terms links, FAQPage JSON-LD entry count, and FAQ toggle behavior. - Apply CTA links to the application form (`forms.gle/RS8L2ttcuGap4Q1v6`, `target=_blank`); terms link goes to `/affiliates/terms` (English-only by design, per the legal-reviewed terms document). ## Test plan - [ ] `pnpm --filter @comfyorg/website typecheck` passes (0 errors) - [ ] `pnpm --filter @comfyorg/website test:unit` passes (96 tests across 13 files) - [ ] `pnpm --filter @comfyorg/website knip --cache` reports no new orphans introduced by this branch - [ ] Visit `/affiliates` — all 8 sections render in order on desktop and mobile - [ ] APPLY NOW (in closing CTA section) opens `forms.gle/RS8L2ttcuGap4Q1v6` in a new tab - [ ] "Read the affiliate program terms" link navigates to `/affiliates/terms` in the same tab - [ ] FAQ items expand/collapse on click; `<script type="application/ld+json">` FAQPage entry contains all 8 Q/A pairs from `src/data/affiliateFaq.ts` - [ ] Run e2e: `pnpm --filter @comfyorg/website playwright test e2e/affiliates.spec.ts` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: GitHub Action <action@github.com> |
||
|
|
39157f2375 |
Feat/models page (#12429)
## Summary Add a new `/models` landing page (EN + zh-CN) for the marketing site, plus supporting tweaks: a generator-side mechanism for old→new model-slug 301 redirects, spacing/CTA polish on shared sections, and a per-item layout option on GalleryCard. ## Changes - **What**: - New `/models` and `/zh-CN/models` pages composed of a hero (autoplay video, modelName + i18n CTA), a creations gallery, and the shared `AIModelsSection`. - New `ModelsHeroSection.vue` and `ModelCreationsSection.vue` components; localised strings under `models.list.*` / `models.hero.*` in `translations.ts`. - Reused `AIModelsSection` on the models page (replaces the duplicated showcase markup that previously lived under `models/`) so the same component now powers both the cloud product page and the models page. - `generate-models.ts`: renamed the `grok` provider to **Grok Imagine** (`grok-imagine` slug) and added a `LEGACY_SLUG_REDIRECTS` constant that emits stub entries so the existing `canonicalSlug` mechanism in `[slug].astro` issues a 301 from old slugs (`grok-image` → `grok-imagine`). - `model-metadata.ts`: renamed the metadata key from `grok-image` to `grok-imagine` (hub slug unchanged). - `GalleryCard.vue`: added per-item `objectFit` / `objectPosition` overrides so individual gallery entries can opt out of the default `cover` crop. - `ModelsHeroSection.vue`: dropped the empty-string default on `videoAriaLabel` and omit `aria-label` (with `aria-hidden="true"`) when no label is provided — addresses the CodeRabbit accessibility note. - Minor vertical-spacing tightening on `ModelCreationsSection` and the shared `AIModelsSection`. - **Breaking**: None. Old `/p/supported-models/grok-image` URLs 301 to the new slug. - **Dependencies**: None. ## Review Focus - The `LEGACY_SLUG_REDIRECTS` constant in `apps/website/scripts/generate-models.ts` is the new source of truth for renamed slugs — future slug renames should follow the same pattern so the redirect survives regeneration of `generated-models.json`. - The shared `AIModelsSection` is now used in two places; confirm the spacing tweak (`py-24 → py-16`, `mt-24 → mt-16 lg:mt-24`) still looks correct on the cloud product page. - `generated-models.json` is fully regenerated by `pnpm generate:models` — diff size is large but mechanical. ## Screenshots (if applicable) <!-- Add screenshots or video recording to help explain your changes --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
68238a5742 |
fix(website): show Windows + Mac fallback on /download when UA detection fails (#12727)
## What The /download hero now surfaces both Windows AND macOS download buttons when the post-mount UA sniff comes back empty (Linux desktop, mobile, privacy-stripped UAs). Today those users see only the GitHub-install button — no actual download link. ## Why Field complaint: \"I can't find a download link at all on this page.\" Reproduced on Chrome with a Linux UA — \`useDownloadUrl\` returns \`platform: null\`, and \`<DownloadLocalButton v-show=\"platform\">\` collapses to \`display: none\`. The user only sees \"INSTALL FROM GITHUB\" next to it, with no obvious way to grab the Windows or Mac dmg. Showing both is the better fallback — they're the two artifacts we actually ship today (\`downloadUrls.windows\`, \`downloadUrls.macArm\`), the UA path already encodes them, and the buttons exist as fragment siblings so they slot into the parent's flex container next to the existing GitHub button without extra layout wrapping. ## How - \`useDownloadUrl\` now exposes a \`detected\` ref that flips to \`true\` post-mount regardless of whether UA matched. Lets the component distinguish three states: - **\`!detected\`** → pre-hydration, render nothing (avoids SSR flicker) - **\`detected && platform\`** → single matched CTA, current behaviour - **\`detected && !platform\`** → NEW: render both Windows AND Mac - \`DownloadLocalButton\` adds the fallback branch as a \`<template v-else-if>\` fragment so the two buttons slot directly into the parent's existing \`flex-col lg:flex-row\` layout next to the GitHub button. Each carries its OS icon and an \`aria-label\` combining the existing \"DOWNLOAD DESKTOP\" copy with the OS name so screen readers get equal context to the detected-OS path. - \`downloadUrls\` is now re-exported from the composable so the fallback path uses the same source of truth instead of duplicating the URLs. ## Tests - [x] New \`@smoke\` e2e test (\`HeroSection falls back to both Windows + Mac when UA is unrecognized\`) — uses Linux UA, asserts both fallback buttons appear with the right hrefs / target=\"_blank\", and confirms the auto-detected single CTA is NOT also present - [x] Existing Windows-UA test (\`HeroSection has download and GitHub buttons\`) still passes — single CTA branch unchanged - [ ] Manual: load /download on Linux Chrome → both buttons visible - [ ] Manual: load /download on Windows → still single \"DOWNLOAD DESKTOP\" button (no regression) - [ ] Manual: load /download on macOS → still single \"DOWNLOAD DESKTOP\" button with Apple icon - [ ] Manual: load /download on mobile (which detection treats as null) → both fallback buttons visible |
||
|
|
fa2d187c83 |
feat(website): add Desktop privacy policy at /privacy/desktop (#12714)
## Summary Publishes the Comfy Desktop privacy policy (currently bundled in-app only) as a public page at \`/privacy/desktop\`, so users can read what they consented to without re-opening the app and so the document is verifiable / link-shareable. Also adds a single-sentence link from the existing \`/privacy-policy\` page so people who land on the org-wide policy can find the surface-specific one. ## Changes - **What**: New marketing-site page at \`/privacy/desktop\` (en) and \`/zh-CN/privacy/desktop\` (zh), backed by ~50 new \`desktop_privacy.*\` i18n keys mirroring the in-app document. One-sentence addition to \`privacy.intro.block.3\` linking to the new page. - **Breaking**: None. - **Dependencies**: None. ## Why now Recent Discord audit by a user (cited Civitai as precedent) identified six legitimate gaps between our website privacy policy and what GDPR / UK GDPR Art 13 requires (named processors, lawful basis mapping, retention specifics, international transfer mechanism, etc.). The **in-app Desktop policy already addresses all of them**, but it isn't publicly visible. This PR makes it visible at a URL. A separate, counsel-reviewed PR will be needed to bring the org-wide \`/privacy-policy\` page up to the same standard — that's deliberately not in scope here. ## Content the new page covers Mirrors the Desktop in-app document (effective 2026-05-19): - Named processors: PostHog, Datadog, ToDesktop, Comfy Org analytics warehouse - Article 6 mapping: 6(1)(a) consent for analytics + crash reports; 6(1)(f) legitimate interests for update server - Retention: 24 months analytics / 15 days crash / 36 months aggregate / 90 days update logs / removed on uninstall - International transfers: Standard Contractual Clauses where required - Data subject rights + \`privacy@comfy.org\` as contact - Right to lodge complaint with ICO / EU supervisory authority / CPPA ## Review Focus 1. **i18n zh-CN placeholders.** Following the existing convention in this file (see e.g. \`affiliate-landing.footerCta\`), zh-CN values use the English text as placeholders. A follow-up PR will land Chinese translations. Flag if there's a different preferred convention. 2. **Source-of-truth coupling.** This page is a publish of the bundled in-app policy at \`src/renderer/src/lib/legalDocs.ts\` (\`PRIVACY_POLICY\` constant) in the \`Comfy-Desktop\` repo. There's no automated sync — the two need to stay aligned manually when the in-app policy is revised. Worth a quarterly review reminder. 3. **\`privacy@comfy.org\` mailbox.** The policy directs all data subject rights requests there. Confirm this inbox exists and is monitored before merge. 4. **No \`noindex\` on the new page.** Unlike the existing \`/privacy-policy\` page, this one is intentionally indexable — the point is that it should be discoverable. ## Test plan - [x] \`pnpm typecheck\` clean (0 errors, 0 warnings) - [x] \`pnpm build\` succeeds; both \`/privacy/desktop/index.html\` and \`/zh-CN/privacy/desktop/index.html\` are emitted - [x] All 385 site pages still build - [ ] After preview deploy: verify the link from \`/privacy-policy\` to \`/privacy/desktop\` renders correctly, and the page table-of-contents nav works (\`ContentSection\` infers sections from i18n keys) ## Companion items (not in this PR) - Update \`comfy.org/privacy-policy\` (the org-wide policy) with named processors, Art 6 mapping, retention specifics, and SCC language — requires privacy counsel review - Confirm Comfy Org Inc ICO registration status - One-page determination from privacy counsel on whether Art 37 requires a formal DPO (pseudonymous product telemetry is likely below threshold, but the question deserves a documented answer) |
||
|
|
6f22ca11c3 |
feat(website): rename "Local" CTAs/branding to "Desktop" (#12689)
*PR Created by the Glary-Bot Agent*
---
## Summary
Renames the user-facing "Local" copy to "Desktop" across the comfy.org
marketing site (English + Chinese). Covers three surfaces requested in
sequence by the user:
1. "Download Local" CTA → "Download Desktop" (top nav, mobile menu,
homepage Get-started section, download page hero button).
2. "Comfy Local" nav-dropdown / footer label → "Comfy Desktop".
3. The yellow `LOCAL` badge in the `/download` hero → `DESKTOP`.
## Changes
- **i18n** (`apps/website/src/i18n/translations.ts`)
- `getStarted.step1.downloadLocal`, `download.hero.downloadLocal`,
`nav.downloadLocal`: en `Local` → `Desktop`, zh-CN `本地版` → `桌面版`.
- `nav.comfyLocal`: en `Comfy Local` → `Comfy Desktop`, zh-CN `Comfy
本地版` → `Comfy 桌面版`.
- **Nav animated CTA** (`SiteNav.vue`): hardcoded `core: 'LOCAL'` →
`core: 'DESKTOP'` (the visible text in the desktop nav, which is
separate from the i18n aria-label).
- **Download hero badge** (`product/local/HeroSection.vue`): passes
`text="DESKTOP"` to `<ProductHeroBadge />`, matching how the
Cloud/API/Enterprise heroes already specify their own text.
- **Shared badge default** (`common/ProductHeroBadge.vue`): default
`text` changed from `'LOCAL'` to `'DESKTOP'` so future callers do not
inherit retired branding.
- **E2E tests** updated to the new strings (navigation, homepage,
download specs).
## Out of scope (intentionally unchanged)
- The internal product-family slug `local` (Vue component file names
like `DownloadLocalButton.vue`, directory `components/product/local/`,
translation key `comfyLocal`, route `/download`). Renaming these is
internal restructuring that would balloon the diff with no user-visible
benefit and was not requested.
- The homepage products-card strings `products.local.title` ("Comfy
Local") and `products.local.cta` ("SEE LOCAL FEATURES"). These also live
on the homepage and link to `/download`; the user has been driving this
rename surface-by-surface and has not flagged this card yet. Happy to
extend in a follow-up if desired.
- Other "local" occurrences (e.g. "本地运行" in the zh-CN download FAQ, "Run
on your hardware", etc.) — those describe the deployment model, not the
product brand.
## Verification
- `pnpm --filter @comfyorg/website typecheck`: 0 errors
- `pnpm exec oxlint` on every touched file: 0 errors, 0 warnings
- `pnpm exec oxfmt --check` on every touched file: clean
- Husky `lint-staged` (typecheck + lint + format) ran green on each
commit
- Manual browser verification with Playwright at desktop (1440×900) and
mobile (390×844) viewports on `/`, `/download`, `/zh-CN/`,
`/zh-CN/download`:
- Top nav CTA reads `DOWNLOAD DESKTOP` (aria-label `下载桌面版` on zh-CN)
- Mobile menu CTA reads `DOWNLOAD DESKTOP`
- Homepage Get-started section reads `Download Desktop`
- Products dropdown shows `Comfy Desktop` as the first item
- `/download` hero badge reads `Comfy DESKTOP` (en + zh-CN)
- DOM `innerText` contains 0 occurrences of `LOCAL`/`下载本地版`/`Comfy 本地版`
## Screenshots
Attached: desktop nav, homepage Get-started section, download hero badge
(en + zh-CN), mobile menu.
## Screenshots





---------
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Michael B <michael@imick.io>
|
||
|
|
5ddf5faef3 |
feat: scaffold Learning page with Featured Workflow, Tutorials, and CTA sections (#12602)
## Summary Adds a new Learning page to the website with a hero, featured workflow showcase, tutorials section, and CTA, wired into site nav and footer Resources. ## Changes - **What**: - New `/learning` page (Astro) with `HeroSection`, `FeaturedWorkflowSection`, `TutorialsSection`, and `CallToActionSection` - Localized for `zh-CN` at `/zh-CN/learning` - Featured workflow CTA links out to `comfy.org/workflows/<slug>` - Added `nav.learning` translation; added Learning entry to `SiteNav` and `SiteFooter` Resources - New shared `PillButton`, `MaskRevealButton`, `Badge`, and `VideoPlayer` work used by the page; `TutorialDetailDialog` for tutorial deep-dives - Featured demo video updated; poster image added - `routes.ts`: added `learning` route entry - `EventsSection` temporarily hidden pending content ## Review Focus - Copy on `learning.featured.description` (newly written, both `en` and `zh-CN`) - Tutorial data shape in `data/learningTutorials*.ts` - Internal-vs-external link styling: Learning shows the active-page yellow when viewing `/learning` (expected — internal route, no external arrow) ## Screenshots (if applicable) _Add deployment preview screenshots here._ --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
ddf9308cd8 |
feat(website): add Affiliate Program link to footer Resources (#12600)
*PR Created by the Glary-Bot Agent* --- Adds an **Affiliate Program** entry under the Resources column of the site footer, positioned immediately after the YouTube link. Links to `/affiliates` via the existing locale-invariant `routes.affiliates` helper. ### Stacked on #12002 This PR is branched off `glary/affiliates-landing-page` (PR #12002), not `main`, because: - `routes.affiliates` and the `/affiliates` page itself are added by #12002 and aren't on `main` yet. - Linking to `/affiliates` from the footer before #12002 merges would 404 in production. GitHub will auto-rebase this PR onto `main` once #12002 merges. **Re-target this PR's base from `glary/affiliates-landing-page` to `main` before reviewing for merge** (or simply merge #12002 first and GitHub will switch the base for you). ### Diff ``` apps/website/src/components/common/SiteFooter.vue +4 apps/website/src/i18n/translations.ts +4 ``` - New `footer.affiliateProgram` i18n key. English-only per the go-live direction for affiliate-related copy (zh-CN mirrors EN to satisfy the dictionary's `Record<Locale, string>` shape; matches how `nav.youtube` and other proper-noun footer entries handle this). - New `FooterLink` entry in `topColumns[1].links` (the Resources column), after the YouTube entry. Internal route, so no `external: true`. ### Local verification - `pnpm typecheck`: 0 errors - `pnpm build`: 380 pages, footer renders `Affiliate Program` → `/affiliates` in both desktop and mobile layouts, on `/`, `/about`, `/zh-CN/`, etc. Link ordering in Resources: `Blog → Discord → GitHub → Docs → YouTube → Affiliate Program` (verified by parsing the built HTML) - `pnpm test:unit`: passing (no SiteFooter unit test exists; it's exercised through e2e/visual coverage) - `pnpm lint`: no new warnings (3 pre-existing on unrelated files) - `pnpm format:check`: clean - `pnpm knip`: clean - `pnpm exec tsx scripts/check-unused-i18n-keys.ts`: clean (new key is used) ### Out of scope - No new Linear ticket (this is a follow-up to the affiliate-program go-live work tracked under FE-704). - Header nav / mobile menu changes — only the footer was requested. Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
f4088bcc63 |
feat(website): draft Affiliate Program landing page at /affiliates (#12002)
*PR Created by the Glary-Bot Agent* --- Conversion-oriented English landing page that drives applications to the Comfy Affiliate Program Google Form. Shipped behind `noindex` while copy and assets are finalized — a follow-up PR will flip it indexable and add the zh-CN locale. Mirrors the type scale, brand tokens, and noindex/sitemap/robots conventions from the [/affiliates/terms PR (#11954)](https://github.com/Comfy-Org/ComfyUI_frontend/pull/11954) so the two pages feel like a set. The footer's "Read the affiliate program terms" link points at `/affiliates/terms`, which lands with #11954. ## What's in the box Sections (in render order): hero → trust band → how-it-works → who-we're-looking-for → program details → brand assets → FAQ → footer CTA. - **i18n** — Copy lives under `affiliate-landing.*` in `src/i18n/translations.ts` (en only; zh-CN values mirror en until a localized version lands, so non-engineers can edit copy without touching components). - **Component reuse audit** — Reuses `common/FAQSection`, `common/BrandButton`, `common/SectionHeader`, `common/SectionLabel` verbatim. Sections without a clean primitive fit (split hero, trust band, 3-step flow, audience list, details table, brand-asset grid, footer CTA) render inline with brand tokens rather than forking new shared components. - **Brand assets** — Wired via a config object in `components/affiliates/brandAssets.ts` so 728×90 / 300×250 / 160×600 / 1200×628 banners can be dropped in later without code changes. Live tiles for `/icons/logo.svg` and `/icons/logomark.svg`; placeholder "Coming soon" tiles for the four banner sizes. - **CTA** — `Apply Now` → `https://forms.gle/RS8L2ttcuGap4Q1v6`, opens in new tab with `target="_blank"` and `rel="noopener noreferrer"`. Hero and footer-CTA both use it. - **Hero media slot** — Wired as a named slot (`<slot name="media">`) with a static node-graph SVG fallback, so a video can be dropped in later via the page without component changes. - **FAQ structured data** — Page emits `schema.org/FAQPage` JSON-LD so it's ready for SEO when `noindex` flips off. - **noindex** — `<meta name="robots" content="noindex, nofollow">` via `BaseLayout.noindex`. `/affiliates` added to `NOINDEX_PATHNAMES` in `astro.config.ts` (excluded from sitemap for both en and zh-CN locale prefixes via the existing `LOCALE_PREFIXES.flatMap` pattern) and to `robots.txt Disallow` under every UA block including the explicit AI-bot overrides (GPTBot, ClaudeBot, OAI-SearchBot, ChatGPT-User, Claude-User, Claude-SearchBot, PerplexityBot, Google-Extended). - **BrandButton** — Added `rel?: string` prop so external CTAs across the site can pair `target="_blank"` with `rel="noopener noreferrer"`. - **Routes** — Added `affiliates`, `affiliateTerms`, `affiliateApplicationForm` to `src/config/routes.ts`. ## Component-mapping (per AGENTS.md "no speculative variants" rule) | Section | Where it lives | Reuse decision | |---|---|---| | Hero (split text+media) | `components/affiliates/HeroSection.vue` | Inline. Existing `HeroSection`s are tightly coupled to specific i18n namespaces (`cloud.hero.*` etc.) and `legal/HeroSection` is title-only. | | Trust band | `components/affiliates/TrustBandSection.vue` | Inline. `common/SocialProofBarSection` hardcodes a 12-logo client strip; not a fit for a single text claim. | | How-it-works | `components/affiliates/HowItWorksSection.vue` + `common/SectionHeader` | Inline cards. Existing `StepsSection`s are tightly bound to product-specific namespaces. | | Who we're looking for | `components/affiliates/AudienceSection.vue` + `common/SectionHeader` | Inline icon-bullet list. `pricing/PricingPlanFeatureList` is shaped for plan features with descriptions. | | Program details | `components/affiliates/ProgramDetailsSection.vue` + `common/SectionHeader` | Inline `<table>` (desktop) + `<dl>` (mobile). `product/shared/ReasonSection` is a long-form "left heading + right list" layout, not a key/value table. | | Brand assets | `components/affiliates/BrandAssetsSection.vue` + `common/SectionHeader` | Inline grid (no existing download-grid primitive). | | FAQ | `common/FAQSection.vue` (verbatim reuse) | Reuse — the component is already generic over `headingKey`/`faqPrefix`/`faqCount`. | | Footer CTA | `components/affiliates/FooterCtaSection.vue` + `common/BrandButton` | Inline. `customers/WhatsNextSection` is an arrow-CTA card, not a centered band. | ## Responsive behaviour - Hero stacks (text above media) under `lg` (1024px). - Program Details collapses from `<table>` (md+) to a `<dl>` definition list (sm). - 3-step flow collapses from 3-up grid to vertical stack at `md`. - Brand-asset grid steps from `lg:grid-cols-4` → `sm:grid-cols-2` → `grid-cols-1`. ## Tests - **Vitest unit** (`src/components/affiliates/affiliateLanding.test.ts`) — asserts i18n key structure: every section prefix exists, sections render in order, no internal/draft keys leak, every indexed item (hero highlights, steps, audience items, details rows, FAQs) has an English translation. - **Playwright e2e** (`e2e/affiliates.spec.ts`) — desktop+mobile render/visibility under `@smoke`, plus a non-smoke `desktop interactions` describe that exercises the Apply Now click (popup + form URL) and FAQ accordion toggle. Mobile asserts table → definition-list collapse and no horizontal overflow on every section. FAQPage JSON-LD presence verified end-to-end. Local verification: `pnpm typecheck`, `pnpm test:unit`, `pnpm exec playwright test e2e/affiliates.spec.ts`, `pnpm knip` — all green (40 unit, 11/11 e2e). ## Out of scope (intentional follow-ups) - zh-CN locale + `/zh-CN/affiliates` route (omitted from `robots.txt` until the route exists; sitemap exclusion already handles it generically via `LOCALE_PREFIXES.flatMap`). - Flipping `noindex` off once copy and assets are finalized. - Dropping in the four banner SVGs (728×90, 300×250, 160×600, 1200×628) — config object is wired, just edit `components/affiliates/brandAssets.ts`. - Hero video (slot is wired). - Hooking the Apply Now CTA up to a tracked URL once the affiliate platform is live. ## Cross-PR dependency The footer "Read the affiliate program terms" link targets `/affiliates/terms`, which is added by #11954. Until that PR lands, the link 404s on this branch's preview deployment; once both merge to main, the pair is complete. ## Screenshots Desktop (1280w) render attached. ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12002-feat-website-draft-Affiliate-Program-landing-page-at-affiliates-3586d73d365081cc9d78cf7ebe04747d) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Miles Ryan <thedatalife@users.noreply.github.com> |
||
|
|
60db6e85bf |
chore: Remove unused tags, add a config option to prevent future unused tags (#12569)
Also updates the minor knip version. |
||
|
|
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> |
||
|
|
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> |
||
|
|
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) |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
db6b7a315c |
chore: remediate 51 Dependabot vulnerabilities (#12345)
## Summary Remediate 51 of 63 open Dependabot security alerts by bumping direct dependencies, bumping parent dependencies, and adding targeted pnpm overrides for transitive dependencies. ## Changes - **What**: Two batches of dependency security fixes - **Batch 1**: Bump catalog minimums for axios, dompurify, happy-dom, vite, uuid. Fix axios header type narrowing in api.ts. - **Batch 2**: Bump parent deps (@iconify/tailwind4, vue, knip) to pull fixed transitive deps. Add tilde-pinned pnpm overrides for protobufjs, flatted, defu where no parent fix is available. Unexport 6 unused types flagged by knip upgrade. - **Dependencies**: vue 3.5.13->3.5.34 required two type fixes (LazyImage ClassValue, dialogStore deep instantiation) ## Review Focus - pnpm overrides in package.json: protobufjs ~7.6.0, flatted ~3.4.2, defu ~6.1.7 - Vue 3.5.34 type narrowing fixes in LazyImage.vue and dialogStore.ts ## Remaining (12 alerts, separate PRs) - minimatch (4H) - 4 major version lines, needs per-consumer analysis - picomatch (2M) - two major version lines - brace-expansion (2M) - multiple major version lines - astro (2: 1L+1M) - major version bump 5->6 - postcss 8.5.8 (1M) - dev-only, from @vue/compiler-sfc@3.5.28 via storybook/devtools - yaml 1.10.2 (1M) - from cosmiconfig->nx, no upstream fix in yaml v1 - lodash/lodash-es (4: 2H+2M) - dev-only, upstream still uses 4.17.x - @babel/plugin-transform-modules-systemjs (1H) - dev-only via nx - fast-uri (2H) - dev-only via ajv->nx/stylelint Fixes #FE-762 --------- Co-authored-by: Austin Mroz <austin@comfy.org> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
7599c6a1ca |
chore: remove EA logo from website social proof bar (#12477)
Removes the EA (Electronic Arts) logo from the client social proof bar on the website. Drops it from the logos list in `SocialProofBarSection.vue` and deletes the now-unused `EA.svg` asset. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
08ee925811 |
theme-aware favicon for light and dark mode (#12457)
## Summary Serve separate SVG favicons via prefers-color-scheme so the icon stays legible against both light and dark browser chrome. Drop the unreferenced favicon.svg / favicon.png; keep favicon.ico as the legacy fallback. ## Changes - What: apps/website/src/layouts/BaseLayout.astro now links favicon-light.svg and favicon-dark.svg gated on prefers-color-scheme, with favicon.ico retained as the legacy fallback. Unreferenced favicon.svg / favicon.png removed from apps/website/public/. ## Review Focus - Naming convention: favicon-light.svg is the asset served in light mode (dark-backgrounded icon for contrast against light chrome); favicon-dark.svg is served in dark mode. Confirm this matches expectation. - Safari fallback: older Safari versions ignore prefers-color-scheme on <link rel="icon"> and will fall through to favicon.ico — that file is unchanged and should look acceptable in both modes. ## Screenshots Dark mode: <img width="224" height="30" alt="image" src="https://github.com/user-attachments/assets/5fa3c620-0021-4c90-bc18-013cd6ef45cf" /> Light mode: <img width="227" height="28" alt="image" src="https://github.com/user-attachments/assets/54a130e1-f976-46e8-b047-e27efe22e479" /> |
||
|
|
eeeacc9b03 |
feat(website): constrain sections to max-w-9xl on wide screens (#12428)
Add max-w-9xl mx-auto to section/container wrappers across the website so layout stays centered and capped at 96rem on screens wider than 1536px. ## Summary <!-- One sentence describing what changed and why. --> ## Changes - **What**: <!-- Core functionality added/modified --> - **Breaking**: <!-- Any breaking changes (if none, remove this line) --> - **Dependencies**: <!-- New dependencies (if none, remove this line) --> ## Review Focus <!-- Critical design decisions or edge cases that need attention --> <!-- If this PR fixes an issue, uncomment and update the line below --> <!-- Fixes #ISSUE_NUMBER --> ## Screenshots (if applicable) <!-- Add screenshots or video recording to help explain your changes --> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
60ce0ee0c3 | Add png and ico favicon for comfy.org (#12426) | ||
|
|
c703db5f6c |
chore: remove Nx and migrate monorepo workflows to pnpm (#12355)
## Summary - remove Nx root/config artifacts and workspace x metadata - replace Nx-based root scripts with direct pnpm workspace + native Vite/Vitest/Playwright commands - remove Nx dependencies/catalog entries and regenerate lockfile - clean residual Nx references from CI workflows, tooling config, and docs ## Validation - pnpm install --frozen-lockfile - pnpm lint - pnpm typecheck - pnpm build - pnpm build:cloud - pnpm build:desktop - pnpm build:types - pnpm test:browser -- --list - pnpm test:unit *(4 pre-existing failing tests)* ΓöåIssue is synchronized with this [Notion page](https://www.notion.so/PR-12355-chore-remove-Nx-and-migrate-monorepo-workflows-to-pnpm-3666d73d3650817888b9e85e24a10b22) by [Unito](https://www.unito.io) --- **For keeping Nx** - Better monorepo orchestration (task graph, affected runs, caching). - Can reduce CI/runtime cost at larger scale. - Unified interface across tools and packages. **Against keeping Nx** - Extra abstraction layer and config complexity. - Higher maintenance/debugging cost (plugins, cache behavior, drift). - Native `pnpm` + tool CLIs are simpler and clearer today. **Bottom line** - If current pain is low: prefer simplicity and remove Nx. - If measured scale pain is high: orchestration may be worth it (Nx or another tool later). --------- Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
9a2bea7283 |
chore(website): refresh Ashby and cloud nodes snapshots (#12410)
Automated refresh of remote-data snapshots used by the website build: - `apps/website/src/data/ashby-roles.snapshot.json` — Ashby job board API - `apps/website/src/data/cloud-nodes.snapshot.json` — Comfy Cloud `/api/object_info` **Flow:** 1. `Release: Website` workflow ran (manual trigger). 2. This PR opens with the regenerated snapshots. 3. `CI: Vercel Website Preview` deploys a preview for review. 4. Merging to `main` triggers the production Vercel deploy. The snapshot fallback in `apps/website/src/utils/ashby.ts` and `apps/website/src/utils/cloudNodes.ts` remains intact: builds without the respective API keys continue to use the committed snapshot (with a warning annotation in CI). Triggered by workflow run `26260485885`. Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com> |
||
|
|
0a07781a76 |
fix(website): fetch cloud nodes from registry API instead of object_info (#12408)
## Summary
- Fixes cloud-nodes search not finding nodes like FaceDetailer
- The `/api/object_info` endpoint only returns a subset of nodes per
pack (~39 for Impact Pack), but the registry API has the full list (~197
nodes)
- Now fetches complete node list from registry API while still using
object_info to determine which packs are cloud-supported
## Changes
- Add `fetchRegistryPacksWithNodes()` to fetch full node list from
registry (`/nodes/{packId}/versions/{version}/comfy-nodes`)
- Keep using object_info to determine which packs are cloud-supported
- Prefer registry nodes when available, fall back to object_info nodes
- Add retry logic for comfy-nodes fetching
- Add comprehensive tests (13 new tests, 36 total)
## Test plan
- [x] All existing cloudNodes tests pass (36 tests)
- [x] New tests cover registry node fetching, pagination, retry logic
- [x] Type check passes
- [x] Lint passes
- [ ] Verify search for "FaceDetailer" returns Impact Pack on deployed
preview
## Related
- Fixes failing test in #12388 (the data refresh PR)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
|
||
|
|
4931b0c4b2 |
fix(website): add dark-background favicon for legibility in search results (#12285)
*PR Created by the Glary-Bot Agent* --- ## Summary The comfy.org favicon was reported as illegible in Google search results. The current `logomark.svg` is a transparent yellow "C" — when Google (or any client) composites it onto a white surface (search results, light-theme tab strips), the yellow disappears into the background. Fix: ship a dedicated `/favicon.svg` that wraps the existing yellow logomark in a solid black square, and point `<link rel="icon">` at it. The in-page nav logo, `Organization.logo` Schema.org URL, and any other consumer of `logomark.svg` are left untouched, so transparent-composite contexts (knowledge panels, dark nav) continue to render cleanly. ## Changes - `apps/website/public/favicon.svg` *(new)* — 48×48 SVG: black square + scaled-down original logomark path. Existing path geometry is reused verbatim inside a `<g transform>` so the C glyph is byte-identical to the source. - `apps/website/src/layouts/BaseLayout.astro` — `<link rel="icon" href="/icons/logomark.svg">` → `<link rel="icon" href="/favicon.svg">`. One-line change. ## Why a new file (vs editing `logomark.svg` in place) `logomark.svg` is also used by `SiteNav.vue` (in-page header on the dark `--color-primary-comfy-ink` background) and by the JSON-LD `Organization.logo` URL. Both consumers want the transparent version. Editing it in place would draw an ugly black square in the site's own header. ## User report > "just google searched comfyui and logo isnt legible. We should update.." ## Verification **Built site** - `pnpm typecheck` (astro check): 0 errors, 0 warnings - `pnpm build` (astro build): 280 pages built, exit 0 - Built `dist/index.html` contains exactly one `<link rel="icon" href="/favicon.svg">` and zero references to the old icon path in `<head>` - `oxlint` on changed `.astro` file: 0 warnings, 0 errors **Visual (Playwright on local astro dev server)** - New favicon renders correctly at 16/32/64 px — yellow C centered on black square, no clipping. - In-page nav logo unchanged (yellow C floats cleanly on the dark `--color-primary-comfy-ink` nav background, no black wrapper visible). - Mock of Google search-result row shows the new favicon is high-contrast inside Google's white circular wrapper; the old one is nearly invisible. ## Screenshots ### Google-style search result simulation (before / after)  ### Favicon at native sizes + Google circular wrapper  ### In-page nav header (unchanged after the fix)  ## Notes for reviewers - The change deliberately uses pure black `#000` (matching the user's literal request "make the white background, black") rather than `--color-primary-comfy-ink` (`#211927`). Either would work; happy to switch if brand preference is the ink color. - Search-engine cached favicons can take days/weeks to refresh on Google's side after the new file is deployed. ## Screenshots    ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12285-fix-website-add-dark-background-favicon-for-legibility-in-search-results-3616d73d365081babbcbedf0b86d3d67) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
4e07fe3a43 |
feat(website): update Terms of Service to legal-approved 2026-05-13 copy (#12286)
*PR Created by the Glary-Bot Agent* --- ## Summary Replaces the `tos.*` i18n keys in `apps/website/src/i18n/translations.ts` with the legal-approved Terms of Service copy from `Comfy - Terms of Service (GP 5.12.26).docx` and surfaces an effective date below the hero on `/terms-of-service`. - Restructures the ToS into 14 sections (intro + 13 numbered sections) to match the new legal-approved structure. - Adds two new keys, `tos.effectiveDateLabel` and `tos.effectiveDate`, rendered as a centered `Effective Date: May 13, 2026` line between the hero and the content (matches the pattern used on the Affiliate Program Terms page). - Subsection labels (*Right to Access and Use Comfy Products.*, *Customer Data.*, etc.) render as h3 headings via the existing `block.N.heading` shape — no changes to `ContentSection.vue` or `contentSections.ts`. - English page meta description tightened to reflect the new scope (Comfy Products: Cloud, API, Enterprise — explicitly excluding Comfy OSS). ## Verbatim legal copy Per request, the copy is **verbatim from the legal-approved `.docx`**, including: - `[URL]` placeholder in §2.7 (Data Retention) where the legal doc has a placeholder pending the real docs page. - `[Address]` placeholder in §12.8 (Notices) where the legal doc has a placeholder pending the finalized mailing address. - Mixed casing in §8 (Disclaimer) and §9 (Limitation of Liability) — e.g. `THE Comfy Products AND OUTPUT…`, `…TOTAL LIABILITY OF Comfy…` — preserved exactly as the legal doc presents it. - §11(c) cross-reference left as written. These are intentional and flagged for follow-up with legal/docs before publishing. I have **not** silently substituted real values for the placeholders or normalized casing — that would be editing legal-approved text. ## Chinese (zh-CN) handling The legal-approved copy was provided in English only. To avoid serving English text under a Chinese page shell: - `apps/website/src/pages/zh-CN/terms-of-service.astro` is **removed**. - `getRoutes()` in `apps/website/src/config/routes.ts` treats `termsOfService` as locale-invariant, so the Chinese footer link emits `/terms-of-service` directly — no redirect hop. - `astro.config.ts` adds a redirect from `/zh-CN/terms-of-service` → `/terms-of-service` as a safety net for any stale external/cached links. - All `zh-CN` values on the new `tos.*` keys are filler (mirrored from English) so the `Record<Locale, string>` type contract holds; they are never served. ## Files changed - `apps/website/src/i18n/translations.ts` — 73 old `tos.*` keys removed, 136 new keys added matching the .docx structure. - `apps/website/src/pages/terms-of-service.astro` — imports `t`, renders effective date, updates meta description. - `apps/website/src/pages/zh-CN/terms-of-service.astro` — **removed**. - `apps/website/astro.config.ts` — adds `/zh-CN/terms-of-service` → `/terms-of-service` redirect. - `apps/website/src/config/routes.ts` — `termsOfService` route stays un-prefixed in non-English locales. ## Verification - `pnpm --filter=@comfyorg/website typecheck` — 0 errors (2 pre-existing hints in unrelated files). - `pnpm --filter=@comfyorg/website build` — 279 pages built, `/terms-of-service/` (English page) and `/zh-CN/terms-of-service/` (redirect stub with `noindex` + `canonical`) both emitted. - Pre-commit lint-staged ran `oxfmt`, `oxlint --type-aware`, `eslint --fix`, and `pnpm typecheck` on every commit — all green. - Rendered HTML spot-checked: English `/terms-of-service` contains the new content with verbatim `[URL]` and `[Address]` placeholders; zh-CN homepage footer now links directly to `/terms-of-service` (no redirect hop); `/zh-CN/privacy-policy` and other locale routes still correctly emit `/zh-CN/…` prefixes. - Manual visual check via `astro preview` + Playwright — sidebar nav, h2 section titles, h3 subsection headings, paragraph wrapping, and inline mailto/href anchors all render correctly. Screenshots attached. ## Code-review follow-ups addressed - **zh-CN regression** — Page removed, route override added, redirect kept as safety net. - **Page description mismatch** — Updated meta description to reflect new scope. - **`docs.comfy.org/data-retention` 404** — Now matches the docx placeholder `[URL]`; flagged to legal/docs. - **Disclaimer / Liability casing** — Restored to match docx verbatim. - **Mailing address** — Now matches the docx placeholder `[Address]`; flagged to legal. - **Section 11(c) cross-reference** — Left verbatim per legal doc. ## Scope notes - English-only legal update per request — no Chinese rewrite, no schema changes, no acceptance-tracking infrastructure. - The signup-flow link on `platform.comfy.org` (`website` repo) already points at `https://www.comfy.org/terms-of-service` and renders the new copy at the same URL — no change needed there. ## Screenshots   ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12286-feat-website-update-Terms-of-Service-to-legal-approved-2026-05-13-copy-3616d73d3650815b9262f84d12655dfa) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
3b79917011 |
fix(website): restore registry metadata for cloud nodes catalog (#12307)
*PR Created by the Glary-Bot Agent* --- ## Summary The [`/cloud/supported-nodes`](https://comfy-website-preview-pr-12271.vercel.app/cloud/supported-nodes) page was rendering packs without descriptions, icons, or download counts (PR #12271 preview, [`Release: Website` run](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/25866708684/job/76010642686)). The registry enrichment in `apps/website/src/utils/cloudNodes.registry.ts` was silently failing for **two** reasons: 1. **Missing `limit` query parameter.** `api.comfy.org /nodes` applies a default page size of `10` when no `limit` is sent. Each batch of up to 50 `node_id` filters was therefore truncated to 10 results, dropping metadata for every pack past the first ten. 2. **Schema rejected `null` for optional arrays.** The registry serializes empty server-side slices as JSON `null`, so any pack with `supported_os: null` or `supported_accelerators: null` failed Zod validation — and because parse failure is not retryable, the **entire batch** got `null` enrichment. Both bugs produce the same user-visible symptom ("packs fetched, but no metadata"), so they were entangled. ## Changes (2 files) - `cloudNodes.registry.ts`: send `?limit=<batch length>` on every batch and accept `null` for all optional registry fields. The schema normalizes `null → undefined` at the parse boundary via `.transform()`, so the parsed shape continues to match the generated OpenAPI `Node` type contract; downstream code (`toDomainPack`, the rendered `Pack`) is unchanged. - `cloudNodes.registry.test.ts`: two new regression tests: - Server-side default page size: simulates the pre-fix behavior (default `limit=10` truncates) and asserts all 30 batched IDs are enriched. - `null` registry fields: asserts `null` values are normalized to `undefined` on the parsed pack. ## Verification End-to-end fetch against the live registry on this branch (14 packs from the current snapshot): ``` Requested: 14, enriched: 14 comfyui-kjnodes downloads=3,404,416 rgthree-comfy downloads=3,105,034 comfyui-easy-use downloads=2,829,702 comfyui-impact-pack downloads=2,680,589 comfyui_essentials downloads=2,418,367 (supported_os null → undefined) ComfyUI-Crystools downloads=1,729,087 comfyui_layerstyle downloads=1,696,809 comfyui_ultimatesdupscale downloads=1,478,763 comfyui_ipadapter_plus downloads=1,236,442 (supported_os null → undefined) was-node-suite-comfyui downloads=993,960 (supported_os null → undefined) comfyui-advanced-controlnet downloads=600,849 comfyui-animatediff-evolved downloads=503,831 comfyui-cogvideoxwrapper downloads=121,716 (supported_os null → undefined) comfyui_steudio downloads=58,470 (supported_os null → undefined) ``` 5 of 14 packs returned `null` arrays — all parse cleanly now. Sort-by-downloads (already implemented in `useFilteredPacks.ts`) becomes meaningful again once `downloads` is populated. Quality gates: - `pnpm --filter @comfyorg/website test:unit` → 77/77 pass (includes 2 new regression tests) - `pnpm --filter @comfyorg/website typecheck` → 0 errors, 0 warnings on changed files - `pnpm exec eslint` on changed files → clean - `pnpm exec oxfmt --check` on changed files → clean ## Follow-ups (separate tickets) - "New" badge + `dateAdded` field for newly added packs. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12307-fix-website-restore-registry-metadata-for-cloud-nodes-catalog-3626d73d365081288a2cfc30003160cf) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
7160a9ee3f |
fix: QPO progress bar now shows node name in subgraphs (#7688)
## Summary
Resolve the queue progress node label from queued prompt metadata so
subgraph execution IDs show the correct node name without depending on
the live canvas.
## Changes
- **What**: Store a prompt-scoped `executionId -> { title, type }`
lookup from `p.output` when queueing a job, and use that lookup for the
active job's executing node label.
- **What**: Reuse the same job-scoped node info for the browser tab
title so it stays aligned with the queue overlay.
- **What**: Add unit coverage for root and subgraph execution IDs, and
merge the branch forward to current `main`.
## Review Focus
This keeps the fix scoped to the existing singular `activeJobId` path.
It fixes subgraph labels and avoids the workflow-switching regression
from resolving against `app.rootGraph`, but it does not redesign
concurrent multi-job selection yet.
Longer term, the cleaner solution is still prompt-scoped execution
metadata from the backend rather than frontend reconstruction.
## Screenshots (if applicable)
N/A
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
|
||
|
|
daab936d15 |
feat: add Cloud Status link to website footer (#12237)
*PR Created by the Glary-Bot Agent* --- ## Summary Adds a "Cloud Status" link to the Contact column of the website footer, pointing to https://status.comfy.org so users can discover the service status page from any page on the marketing site. ## Changes - **What**: New external link in the Contact column of `SiteFooter.vue`, plus matching i18n keys (`footer.cloudStatus` in `en`/`zh-CN`) and a `cloudStatus` entry in the `externalLinks` config. ## Review Focus - Placement: Slotted between Support and Press in the Contact column (alongside the other support/operational links). Open to moving to the Resources column instead if preferred. - URL: `https://status.comfy.org` — assumed convention; swap if a different status page URL is preferred. - Also tightened the `contactColumn` type annotation to `{ title: string; links: FooterLink[] }` to match `companyColumn` / `topColumns` so the `external: true` field is properly typed. ## Screenshots Desktop (Contact column, right side):  Mobile (2×2 grid, Contact column bottom-right):  ## Screenshots   ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12237-feat-add-Cloud-Status-link-to-website-footer-3606d73d36508190a906fdde6d86706d) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
f176d18fe0 |
fix(website): refresh cloud nodes snapshot in release workflow + strict production builds (#12219)
*PR Created by the Glary-Bot Agent* --- ## Summary `Release: Website` only refreshed the Ashby snapshot, so the cloud-nodes snapshot (`apps/website/src/data/cloud-nodes.snapshot.json`) was stale on every release. `loadPacksForBuild()` then silently fell back to that snapshot because `WEBSITE_CLOUD_API_KEY` was never plumbed through CI or Vercel, leaving production at `/cloud/supported-nodes` with placeholder data (e.g. `rgthree-comfy` listed as supported when it isn't — visible at line 104 of the committed snapshot, last fetched 2026-05-04). ## Changes - **New composite action `.github/actions/cloud-nodes-pull`** mirroring `ashby-pull`: runs `pnpm --filter @comfyorg/website cloud-nodes:refresh-snapshot` with `WEBSITE_CLOUD_API_KEY`. The script already `process.exit(1)`s on any non-`fresh` outcome, so refresh failures are loud. - **`release-website.yaml`** now runs both refreshes and opens a single PR with both updated snapshots. Renamed the job to `refresh-snapshots`, updated branch/commit/title/body for the wider scope, and kept the existing `Release:Website` label so downstream automation is unaffected. - **`cloudNodes.build.ts`** throws when the outcome is `'stale'` **and** `VERCEL_ENV === 'production'`. Preview / local builds keep the snapshot fallback so contributors without key access are unaffected. The CI reporter still runs first so the GitHub annotation explaining *why* it's stale is visible in the failed job. - **`ci-vercel-website-preview.yaml`**: passes `WEBSITE_CLOUD_API_KEY` to `vercel build` in both preview and production jobs, and adds a preflight step on `deploy-production` that hard-fails before `vercel build --prod` if the secret is missing — surfacing config drift with a maintainer-friendly error annotation instead of mid-build. - **`apps/website/README.md`**: documents the production-strictness behavior, the new required secret (GitHub Actions + Vercel env), and the manual refresh path. - **New unit tests** in `cloudNodes.build.test.ts` (6 cases): fresh, stale-no-VERCEL_ENV, stale-on-preview, stale-on-production, failed-regardless, and "still reports on stale-in-production before throwing". ## Manual / one-time steps required before merging This PR cannot finish the job alone. A maintainer must also: 1. Add `WEBSITE_CLOUD_API_KEY` as a **GitHub Actions repo secret** in `Comfy-Org/ComfyUI_frontend`. 2. Add `WEBSITE_CLOUD_API_KEY` to the **Vercel project environment** (`production` env at minimum; `preview` recommended). 3. Investigate why `rgthree-comfy` is in the current snapshot — either the Cloud API was actually returning it on 2026-05-04, the snapshot was generated against a non-production environment, or it was hand-edited. The first manual run of `Release: Website` after this PR merges will confirm. Without step 1, the new `Release: Website` job will fail loudly (the refresh script exits 1 with `missing WEBSITE_CLOUD_API_KEY`). Without step 2, the new preflight will fail the production deploy with a clear error annotation pointing at `apps/website/README.md`. Both failure modes are intentional — they replace today's silent stale snapshot. ## Related (out of scope for this PR) The other half of the original report — production 404s on `/p/supported-models/*`, `/cloud/supported-nodes/*`, `/demos/community-workflows` from PRs #11892 / #11903 / #11942 — is a `comfy-router` allow-list gap (those paths exist in the Vercel build as pre-rendered static HTML). That fix needs to land in `Comfy-Org/comfy-router` and is being handled separately since glary doesn't have access to that repo. ## Verification - `pnpm --filter @comfyorg/website test:unit` — 75/75 pass (6 new in `cloudNodes.build.test.ts`) - `pnpm --filter @comfyorg/website typecheck` — 0 errors, 0 warnings (2 pre-existing hints unrelated to this PR) - `pnpm format` + `pnpm exec eslint` on changed files — clean - `js-yaml` validates `release-website.yaml`, `cloud-nodes-pull/action.yaml`, `ci-vercel-website-preview.yaml` - Oracle code review (round 1) raised 1 warning + 1 suggestion; both addressed in commit 2. **Manual verification not applicable**: the runtime changes are GitHub Actions workflows and a Vercel-env-gated branch in a build-time module — they cannot meaningfully run outside of GitHub Actions / Vercel, and the strict-on-stale path is exhaustively covered by the 6 unit tests (including the exact assertions a manual run would check: throws on `VERCEL_ENV=production` + stale, passes on preview, reports observability annotation before throwing). The end-to-end behavior will be verified by the first `Release: Website` dispatch and the next production deploy after the maintainer adds the secret. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12219-fix-website-refresh-cloud-nodes-snapshot-in-release-workflow-strict-production-build-35f6d73d3650816d8f32d403cb39d733) by [Unito](https://www.unito.io) --------- Co-authored-by: glary <bot@glary.dev> |
||
|
|
e60ae14bc0 |
feat(website): hide Free tier behind SHOW_FREE_TIER flag (#12165)
*PR Created by the Glary-Bot Agent* --- ## Summary Disables the Free tier on the public marketing website (`comfy.org/cloud/pricing` and `comfy.org/cloud`) behind a single boolean flag so re-enabling is a one-line change. The Free tier was already removed from the Comfy Cloud sign-up flow; this PR removes the matching promotional surfaces on the marketing site so users hit the paywall directly. ## Changes - **New** `apps/website/src/config/features.ts` — exports `SHOW_FREE_TIER` (currently `false`). Flip to `true` to restore the previous UX. - **`apps/website/src/components/pricing/PriceSection.vue`** — when `SHOW_FREE_TIER` is `false`: - drops the Free plan card from the pricing array - desktop grid collapses from `lg:grid-cols-4` to `lg:grid-cols-3` - Standard plan's "Everything in Free, plus:" intro is replaced with an aria-hidden spacer so the three remaining cards stay vertically aligned - **`apps/website/src/components/product/cloud/PricingSection.vue`** — hides the "Start free. Upgrade when you're ready." tagline on the `/cloud` pricing teaser. - **New** `apps/website/e2e/pricing.spec.ts` — three @smoke tests asserting the paid tiers + Enterprise are visible and that all Free-tier surfaces are absent. All translation strings (`pricing.plan.free.*`, `cloud.pricing.tagline`) are retained so re-enabling requires no copy work. ## Verification - `pnpm typecheck` — clean (0 errors, 0 warnings; pre-existing hint unrelated) - `pnpm lint` / `oxfmt` — clean - `pnpm test:unit` — 30/30 passing - Playwright e2e (desktop project) — `pricing.spec.ts` 3/3 passing, `cloud.spec.ts` 13/13 still passing - Visual: desktop and mobile pricing pages render with balanced 3-column layout; `/cloud` teaser card still proportional. Screenshot below. ## Screenshots  ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12165-feat-website-hide-Free-tier-behind-SHOW_FREE_TIER-flag-35e6d73d36508164b4dfcfe9fee6b5e7) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
93edf166d0 |
fix(website): link careers page to Ashby job description, not application form (#12200)
*PR Created by the Glary-Bot Agent*
---
## Summary
The careers page at comfy.org/careers was linking every role to its
Ashby application form (`.../{id}/application`) instead of the job
description page (`.../{id}/`). Users expect to first read the role
description, not land on the submit-resume page.
Ashby's job board API returns both `jobUrl` (description) and `applyUrl`
(application form). `toDomainRole` was preferring `applyUrl`; this PR
switches to `jobUrl` and renames the `Role` field accordingly so the
field name matches its meaning.
## Changes
- `apps/website/src/utils/ashby.ts`: use `job.jobUrl` directly instead
of `job.applyUrl ?? job.jobUrl`.
- `apps/website/src/data/roles.ts`: rename `Role.applyUrl` →
`Role.jobUrl`.
- `apps/website/src/components/careers/RolesSection.vue`: update the `<a
:href>` binding.
- `apps/website/src/data/ashby-roles.snapshot.json`: regenerated
fallback snapshot — URLs stripped of `/application`, `id`s recomputed
from the new URLs.
- Unit + E2E tests updated; new E2E assertion that links do not end in
`/application` prevents regressions.
The Ashby schema (`ashby.schema.ts`) still accepts `applyUrl` since the
API returns it — we just no longer consume it.
## Verification
- `pnpm test:unit` — 70/70 pass
- `pnpm typecheck` — 0 errors
- `pnpm build` — succeeds; inspected `dist/careers/index.html`, all 19
Ashby links now point to description URLs and zero contain
`/application`
- Oracle code review — 0 issues
Fixes user report in #hiring-ideas (Slack).
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12200-fix-website-link-careers-page-to-Ashby-job-description-not-application-form-35e6d73d3650815cbedadf974f7d3364)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
|
||
|
|
6845d57a80 |
chore(website): refresh Ashby roles snapshot (#12191)
Automated refresh of `apps/website/src/data/ashby-roles.snapshot.json` from the Ashby job board API. **Flow:** 1. `Release: Website` workflow ran (manual trigger). 2. This PR opens with the regenerated snapshot. 3. `CI: Vercel Website Preview` deploys a preview for review. 4. Merging to `main` triggers the production Vercel deploy. The snapshot fallback in `apps/website/src/utils/ashby.ts` remains intact: builds without `WEBSITE_ASHBY_API_KEY` continue to use the committed snapshot. Triggered by workflow run `25746888214`. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12191-chore-website-refresh-Ashby-roles-snapshot-35e6d73d365081f4b2e1d802dd412a72) by [Unito](https://www.unito.io) Co-authored-by: Yourz <8287689+Yourz@users.noreply.github.com> |
||
|
|
469a5edf99 |
feat: cloud-nodes catalog at /cloud/supported-nodes (#11903)
*PR Created by the Glary-Bot Agent*
---
## Summary
Adds a comfy.org page that lists every custom-node pack supported on
Comfy Cloud, with per-pack detail subpages. Data is fetched at build
time from `cloud.comfy.org/api/object_info` (gated by
`WEBSITE_CLOUD_API_KEY`), sanitized of user content, joined with public
registry metadata from `api.comfy.org/nodes`, and falls back to a
committed snapshot — mirroring the existing Ashby careers integration
pattern.
- Index: `/cloud/supported-nodes` (en) and
`/zh-CN/cloud/supported-nodes` (zh-CN)
- Detail: `/cloud/supported-nodes/[pack]` and
`/zh-CN/cloud/supported-nodes/[pack]`, generated via `getStaticPaths()`
from the same fetcher as the index so the two routes can never diverge.
## What's new
**Shared package (extracted)**
- `@comfyorg/object-info-parser` — Zod schemas (`zComfyNodeDef`,
`validateComfyNodeDef`), node-source classifier (`getNodeSource`,
`isCustomNode`, `CORE_NODE_MODULES`), and helpers (`groupNodesByPack`,
`sanitizeUserContent`). `src/schemas/nodeDefSchema.ts` and
`src/types/nodeSource.ts` become 1-line re-export shims; existing
imports keep compiling.
**Build-time pipeline**
- `apps/website/src/utils/cloudNodes.ts` — Ashby-style fetcher:
retry/backoff `[1s, 2s, 4s]`, 10 s timeout via AbortController, Zod
envelope + per-node validation, snapshot fallback, memoized via
module-level `inflight` promise.
- `apps/website/src/utils/cloudNodes.registry.ts` — Public registry
enrichment (no auth, batches of 50, single retry, soft-fail).
- `apps/website/src/utils/cloudNodes.ci.ts` — GitHub Actions annotations
+ step summary mirroring the Ashby reporter.
- `apps/website/src/utils/cloudNodes.build.ts` — Single
`loadPacksForBuild()` consumed by both index and detail pages so they
share one source of truth.
- `apps/website/scripts/refresh-cloud-nodes-snapshot.ts` — atomic-rename
refresh CLI that walks pack/node string fields with a user-content
extension regex *before* renaming the snapshot into place.
- Mandatory user-content sanitization strips uploaded filenames from
combo lists (`LoadImage`, `LoadImageMask`, `LoadImageOutput`,
`LoadVideo`, `LoadAudio` zeroed; any combo value matching
`/\.(png|jpe?g|webp|gif|mp4|mov|webm|wav|mp3|flac|ogg|safetensors|ckpt|pt)$/i`
filtered).
**Page + components**
- `apps/website/src/pages/cloud/supported-nodes.astro` (en) + zh-CN
twin.
- `apps/website/src/pages/cloud/supported-nodes/[pack].astro` detail
(en) + zh-CN twin, async `getStaticPaths` driven by
`loadPacksForBuild()`.
-
`apps/website/src/components/cloud-nodes/{HeroSection,PackGridSection,PackCard,PackBanner,NodeList,PackDetail}.vue`
— Vue 3.5 destructured props, `cn()` from `@comfyorg/tailwind-utils`,
design-system tokens only, no PrimeVue.
- Pack card name links to its detail page; banner uses the shared
`fallback-gradient-avatar.svg` asset (copied into
`apps/website/public/assets/images/`) when `banner_url` and `icon` are
missing.
- 25 new `cloudNodes.*` i18n keys in `en` + `zh-CN`.
**Tests**
- 33 unit tests in `@comfyorg/object-info-parser` (schemas, classifier,
sanitizer, grouping).
- 19 new website unit tests covering fetcher (10), CI reporter (6),
registry enrichment (3) — Ashby patterns mirrored.
- E2E: index smoke + search + banner + detail click-through + direct
visit + zh-CN parity.
## Required maintainer follow-up
GitHub Apps cannot push `.github/workflows/*` changes (push was rejected
with `refusing to allow a GitHub App to create or update workflow …
without workflows permission`), so the workflow edits prepared in this
branch were reverted in commit `9be2abce8`. The intended diffs are
documented as copy-paste-ready snippets in `apps/website/README.md`
under the new "Cloud nodes integration → CI wiring" section.
A maintainer must:
1. Provision `WEBSITE_CLOUD_API_KEY` in the repo secrets and the Vercel
project env.
2. Apply the `ci-website-build.yaml` and
`ci-vercel-website-preview.yaml` diffs documented in the README directly
to `main` (or as a follow-up commit on this branch with a maintainer
account).
The committed snapshot lets builds succeed without the secret while the
maintainer step is pending — pages render from
`apps/website/src/data/cloud-nodes.snapshot.json`.
## Self-review (Oracle)
Two warnings caught and fixed in commits `deba5ab02` and `99dfc3381`:
- Index/detail pages now share a single source of truth
(`loadPacksForBuild`), so a fresh fetch can't expose packs whose detail
routes weren't generated.
- Refresh script validates parsed snapshot fields *before* the atomic
rename, instead of regex-scanning the serialized JSON after the file is
already in place.
## Quality gates (local)
```
pnpm --filter @comfyorg/object-info-parser test → 33 passed
pnpm --filter @comfyorg/website test:unit → 42 passed
pnpm --filter @comfyorg/website typecheck → 0 errors
pnpm --filter @comfyorg/website build → 47 pages built (incl. 6 cloud-nodes routes)
pnpm lint → 0 errors (1 pre-existing warning in unrelated test file)
pnpm knip → 0 errors (1 pre-existing tag hint in unrelated file)
```
E2E (`pnpm --filter @comfyorg/website test:e2e`) is intended to be run
by the Vercel/CI pipelines.
## Manual verification
Built `dist/`, served locally on port 4321, drove with Playwright:
- `/cloud/supported-nodes` renders both pack cards, search input, sort
dropdown
- `/cloud/supported-nodes/comfyui-impact-pack` renders the metadata grid
(publisher, downloads, stars, version, license, last updated) and 3
categorized node sections with 5 nodes total
- `/zh-CN/cloud/supported-nodes` localizes hero (`Comfy Cloud 上的自定义节点`),
label (`云端节点目录`), search placeholder (`搜索节点包或节点名称`), sort
- `/zh-CN/cloud/supported-nodes/comfyui-controlnet-aux` localizes every
metadata label (`查看仓库`, `发布者`, `下载量`, `GitHub 星标`, `最新版本`, `许可证`,
`最后更新`) and renders dates with `Intl.DateTimeFormat('zh-CN')`
(`2026年4月27日`)
- Search input narrows pack count from 2 to 1 when typing `impact`
(verified via DOM count)
Banners render the shared `fallback-gradient-avatar.svg` when the
snapshot's image URL doesn't resolve — expected in the local sandbox.
## Preview URL (after CI completes)
`https://comfy-website-preview-pr-{N}.vercel.app/cloud/supported-nodes`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11903-feat-cloud-nodes-catalog-at-cloud-supported-nodes-3566d73d36508194afdec5f389897585)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
|
||
|
|
35443e94f5 |
feat(website): SEO model pages — 207 models, FAQ JSON-LD, partner node support (#11892)
## Summary - Adds programmatic SEO model pages at `/p/supported-models/[slug]` for **207 models** auto-generated from `workflow_templates` (180 local + 27 partner nodes) - 3-file architecture: `generated-models.json` (auto-generated, checked in) + `model-metadata.ts` (editorial overrides) + `models.ts` (65-line merger) - Full JSON-LD per page: `SoftwareApplication` + `BreadcrumbList` + `FAQPage` (targeting AI Overviews / People Also Ask) - Partner node support: `directory: 'partner_nodes'` hides Download button, shows VIEW TUTORIAL - `generate-models.ts`: walks `workflow_templates` for local models + `API_PROVIDER_MAP` for 30+ partner integrations (Kling, Meshy, Luma, Runway, Stability AI, ByteDance, Google, etc.) - Weekly GH Actions workflow opens issue when new models appear in `workflow_templates` but not in `generated-models.json` - `add-model-page` Claude skill for Slack-driven model page PRs ## Files changed | File | Purpose | |------|---------| | `apps/website/src/config/generated-models.json` | Auto-generated, 207 models (27 partner + 180 local) | | `apps/website/src/config/model-metadata.ts` | Editorial overrides: docsUrl, blogUrl, featured (9 entries) | | `apps/website/src/config/models.ts` | 65-line merger — imports JSON + overrides, exports `models` + `getModelBySlug` | | `apps/website/scripts/generate-models.ts` | Build-time parser; run with `pnpm generate:models` | | `apps/website/src/i18n/translations.ts` | ~30 UI keys added (no per-model keys — displayName is plain string) | | `apps/website/src/pages/p/supported-models/[slug].astro` | Dynamic route with 3x JSON-LD schemas | | `apps/website/src/pages/p/supported-models/index.astro` | Model grid index page | | `apps/website/src/components/models/ModelHeroSection.vue` | Hero component | | `.github/workflows/model-page-discovery.yaml` | Weekly auto-discovery workflow | | `.claude/skills/add-model-page/SKILL.md` | Claude skill for adding/updating model pages | ## Test plan - [ ] `pnpm build` passes in `apps/website` - [ ] `/p/supported-models` index renders 207 model cards - [ ] `/p/supported-models/kling-ai` shows Partner Node eyebrow, no Download button, VIEW TUTORIAL CTA - [ ] `/p/supported-models/flux-1-dev` shows Diffusion Model eyebrow, Download + Tutorial buttons - [ ] `/p/supported-models/umt5-xxl-fp8-e4m3fn-scaled` redirects 301 to `umt5-xxl-fp16` (canonicalSlug) - [ ] Structured data validator shows FAQPage + SoftwareApplication + BreadcrumbList valid Fixes FE-421 --------- Co-authored-by: GitHub Action <action@github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
ceb9936058 |
fix(i18n): clamp unsupported browser locales to a shipped tag (#11712)
## Summary
Sidebar buttons rendered literal i18n keys (e.g.
`sideToolbar.labels.assets`) on a fresh install when the user's
`navigator.language` base tag wasn't one of the 12 shipped locales —
German/Italian/Polish/Dutch/Brazilian-Portuguese users among others.
## Changes
- **What**: Add `resolveSupportedLocale()` that tries the full BCP-47
tag first (preserves `zh-TW`, `pt-BR`), then the base tag, then `'en'`.
Wire through both entry points (`createI18n`'s initial locale,
`Comfy.Locale`'s `defaultValue`) and clamp inside `loadLocale`,
propagating the resolved tag to `GraphView` so a stale stored
`Comfy.Locale='de'` from older builds also recovers.
- **Side benefit**: Brazilian Portuguese users were previously falling
through `pt-BR` → `pt` (unshipped) → broken. The full-tag-first lookup
now correctly lands them on the `pt-BR` bundle.
- **Breaking**: None.
- **Dependencies**: None.
## Root Cause
Three-link chain:
1. `Comfy.Locale`'s default was `() => navigator.language.split('-')[0]
|| 'en'`. German → `'de'` (unshipped).
2. `loadLocale('de')` silently `console.warn`'d and returned without
throwing.
3. `GraphView` then ran `i18n.global.locale.value = 'de'` anyway.
4. `st(key, fallback) = te(key) ? t(key) : fallback`. vue-i18n's `te()`
checks **only** the current locale and ignores `fallbackLocale` — every
key missed → `st()` returned the literal key string.
Two pathways reached the broken state (defaultValue path, and
unset-setting path through `createI18n`'s own `navigator.language`
snapshot); the new helper closes both.
## Review Focus
- `loadLocale` now returns `SupportedLocale` (was `void`). Old `void`
callers continue to compile; the only change is `GraphView` consuming
the return value.
- Unit-tested in `src/i18n.test.ts` (added `resolveSupportedLocale`
block + updated the `loadLocale` unsupported-locale case from "warn" to
"clamp to en").
- Self-reproduced via Playwright with `navigator.language='de-DE'` +
fresh-install state on both `main` (shows the bug) and this branch
(shows the fix). Spec saved at
`temp/scripts/issue-10563-locale-bug.spec.ts`.
Fixes #10563
FE-480 — https://linear.app/comfyorg/issue/FE-480
## Screenshots
**Before** (from #10563, on `main`):
<img width="258" height="399" alt="Sidebar with literal i18n keys"
src="https://github.com/user-attachments/assets/098d1d76-8e89-4237-813f-5f030b34e51e"
/>
**After** (this branch, same `navigator.language='de-DE'`):
<img width="367" height="793" alt="Screenshot 2026-04-28 at 2 07 38 PM"
src="https://github.com/user-attachments/assets/9d279de3-50a8-4774-999f-ab4c3018a9ef"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11712-fix-i18n-clamp-unsupported-browser-locales-to-a-shipped-tag-3506d73d3650812f89d2f0fe3199de3a)
by [Unito](https://www.unito.io)
|
||
|
|
7ddf71d91b |
fix(website): center GitHubStarBadge text in Safari (#12138)
*PR Created by the Glary-Bot Agent* --- ## Summary The 10px star count text inside the desktop nav GitHub star badge rendered vertically off-center in Safari/WebKit. The text was visibly shifted upward inside the yellow badge body, while Chromium rendered it centered correctly. ## Root cause `NodeBadge.vue` centers its inner text span by setting `flex items-center justify-center` on the segment, then nudging the text with `translate-y-1` (or `translate-y-0` for the small variant). The text span itself is an `inline-block` with no explicit `line-height`, so it inherits the default `line-height: 1.5` (15px for a 10px font). Safari and Chromium distribute that extra leading differently for the `PP Formula Narrow` custom font: Safari pushes the glyph higher inside its 15px line box, while Chromium positions it near the middle. With only a 5px gap above/below the glyph to play with, that browser-specific divergence is enough to make the badge look misaligned in Safari. ## Fix Add `leading-none` to the star count text class so the inline-block's line box equals the font size (10px) and the parent flex container's `items-center` produces deterministic vertical centering across browsers. Verified at lg breakpoint (1440×900) in both WebKit and Chromium via Playwright; the badge now renders identically and is properly centered. ## Verification - `pnpm typecheck` (website) — clean - `pnpm build` (website) — 51 pages built successfully - Pre-commit hooks (stylelint, oxfmt, oxlint, eslint, typecheck, typecheck:website) — all passed - Visual inspection in WebKit and Chromium via Playwright Fixes FE-648 ## Screenshots    ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12138-fix-website-center-GitHubStarBadge-text-in-Safari-35d6d73d3650818aa0e8e0f341b60378) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
759ed3d4e2 |
feat(website): add community-workflows demo page (#11942)
*PR Created by the Glary-Bot Agent* --- Adds a new interactive demo page at `comfy.org/demos/community-workflows` for the [Explore and Use a Community Workflow from the Hub](https://app.arcade.software/flows/mqZh17oWDuWIyhK0xwEV/view) Arcade walkthrough. Built on top of the demo infrastructure merged in #11436. ## Changes - `apps/website/src/config/demos.ts` — register the new demo - `apps/website/src/i18n/translations.ts` — add en + zh-CN strings (title, description, transcript) - `apps/website/public/images/demos/community-workflows-og.png` — 1200×630 OG image so email/social previews render correctly - `apps/website/public/images/demos/community-workflows-thumb.webp` — 1280×720 WebP thumbnail - `apps/website/e2e/demos.spec.ts` — refactored to iterate `demos` from config so every demo (current + future) is exercised in both en and zh-CN, and the iframe `src` is asserted to contain the correct Arcade ID Adding a new demo only requires editing `demos.ts` + `translations.ts` going forward; the e2e refactor is a one-time generalization that gives future demos coverage automatically. ## Verification - `pnpm typecheck:website`: 0 errors, 0 warnings, 0 hints - Pre-commit hook ran `pnpm typecheck`, `oxfmt`, `oxlint`, `eslint` — all clean on staged files - `npx astro build`: 53 pages built; `/demos/community-workflows/` and `/zh-CN/demos/community-workflows/` generated and present in `sitemap-0.xml` - Page rendered in Playwright preview: hero (title, GETTING STARTED, BEGINNER, ~2 min), Arcade embed loads, transcript section present, "What's Next" links to `image-to-video` - zh-CN page shows localized title (探索并使用社区工作流), description, badges, and "What's Next" heading - OG meta tag references the new 1200×630 PNG ## Screenshots   ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11942-feat-website-add-community-workflows-demo-page-3576d73d36508139b647c774b1d39323) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
d901c63a0b |
feat: convert careers CategoryNav to scroll-spy locator (#12110)
*PR Created by the Glary-Bot Agent*
---
Converts the `CategoryNav` in the careers `RolesSection` from a
click-to-filter button into a scroll-spy section locator, matching the
pattern already used by `ContentSection.vue` (customer story details,
TOS, privacy policy).
## Changes
- **`apps/website/src/components/careers/RolesSection.vue`**
- Replaced category-based filtering with anchor navigation: clicking a
department in the sidebar smooth-scrolls (via existing Lenis/GSAP
`scrollTo` helper) to that department's section with a `-144px` header
offset.
- Removed the `ALL` button — every department is always rendered as its
own scroll target with `id="careers-dept-{key}"`.
- Added `useIntersectionObserver` (rootMargin `-20% 0px -60% 0px`) that
updates the active nav item as the user scrolls. An `isScrolling` guard
prevents the observer from fighting click-jumps mid-animation.
- Added a viewport-entry fade/slide-up animation on each department
section, gated by `motion-safe:` so users with `prefers-reduced-motion`
see content immediately. The reveal state is sticky (one-way) so
sections don't disappear once revealed.
- Active state is driven by raw department keys; both the nav model and
the observer's id-to-key mapping use a single consistent identifier.
- **`apps/website/e2e/careers.spec.ts`**
- Replaced the obsolete "ENGINEERING filter narrows the list" test with
one that validates locator behavior: clicking the department button
scrolls the section into the viewport, sets `aria-pressed="true"`, and
keeps the full role list rendered.
## Verification
- `pnpm --filter @comfyorg/website typecheck` — clean.
- `pnpm exec oxfmt` / `pnpm exec eslint` / `pnpm exec oxlint` — clean.
- Pre-commit lint-staged hooks (stylelint, oxfmt, oxlint, eslint,
typecheck) — passing.
- Manual smoke test via Playwright on `astro dev`: careers page renders
all departments stacked vertically, active department in the sidebar
highlights based on viewport position (DESIGN active on initial scroll),
nav items reflect each department instead of including an `ALL` button.
## Screenshots

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12110-feat-convert-careers-CategoryNav-to-scroll-spy-locator-35b6d73d3650818a9226e5dcb1244756)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Glary <glary@bot.local>
|