mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-11 00:10:40 +00:00
7149aefaa9810fd53f061007e3471524a9884ed1
7846 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7149aefaa9 | refactor: replace public input cache with vue query | ||
|
|
4afe0baa72 | test: add public input missing media regression | ||
|
|
f508a060a0 | refactor: extract asset hash verification | ||
|
|
5f43461623 | refactor: align asset pagination schema | ||
|
|
04918360eb |
Use hash lookup for missing asset detection (#11873)
## Summary
Use exact BLAKE3 hash lookups first for missing model/media detection,
and add a separate public-inclusive input asset cache so public input
assets are considered missing-detection candidates without changing the
user-only input assets shown in the UI.
## Changes
- **What**:
- Added `assetService.checkAssetHash()` for `HEAD
/api/assets/hash/{hash}` status-only existence checks.
- Added strict BLAKE3 hash helpers so only `blake3:<64 hex>` media
values and raw 64-hex BLAKE3 model metadata are sent to the hash
endpoint.
- Updated missing media detection to group BLAKE3 candidates by hash,
resolve them through the hash endpoint, and fall back to the legacy
asset list path for invalid/unverifiable/non-hash values.
- Updated missing model detection to use hash lookup for BLAKE3-backed
asset-supported candidates before falling back to the existing node-type
asset matching path.
- Added `assetService.getInputAssetsIncludingPublic()` backed by a
dedicated cache that fetches input assets with `include_public=true` for
missing media fallback checks.
- Kept `assetsStore.inputAssets` user-only for widget/UI display, while
invalidating the public-inclusive missing-detection cache when input
assets may change.
- Added abort handling for paginated asset fetches and shared
public-input cache callers so one aborted caller does not cancel the
shared fetch for other callers.
- Added regression coverage for hash lookup, fallback behavior, abort
paths, public input fallback detection, and cache invalidation.
- **Dependencies**: None.
- **Change size**:
- Production code: 4 files, 400 insertions, 24 deletions, net +376.
- Test code: 4 files, 806 insertions, 59 deletions, net +747.
- Total: 8 files, 1206 insertions, 83 deletions, net +1123.
## Review Focus
- The public-inclusive input asset cache is intentionally separate from
`assetsStore.inputAssets`. The existing store data is user-only and
drives the asset widgets/sidebar, so using it for missing input
detection misses public assets. Making that store public-inclusive would
change UI data semantics; this PR instead keeps the UI dataset unchanged
and adds a missing-detection-specific cache in `assetService`.
- Hash lookup is only used when the workflow exposes a valid BLAKE3
hash. Filename-like values and invalid hash values still use the legacy
fallback path.
- Missing model detection keeps the existing fallback behavior for
non-hash candidates and for hash checks that are invalid or fail
transiently.
- Async model download cache refresh behavior is left unchanged; this PR
avoids coupling model download completion to input asset cache
invalidation.
- No browser/e2e test was added because this changes the missing asset
detection data path, not UI interaction or rendering. The behavioral
coverage is in unit tests for the asset service and the missing
media/model scanners.
## Follow-up Items
- Fix `assetsStore.updateAssetTags()` partial-failure recovery. If
`removeAssetTags()` succeeds and `addAssetTags()` fails, the local model
asset cache can roll back to tags that the backend has already removed;
this should be handled in a focused model asset cache PR.
- Consider extracting shared hash-verification flow used by missing
media and missing model scans after this behavior stabilizes.
- Consider adding a concurrency cap or short-lived request cache for
large workflows with many unique hash lookups.
- Consider splitting `assetService.ts` further, e.g. hash helpers, abort
utilities, and the public-inclusive input asset cache.
- Consider tightening the asset hash service API shape so callers do not
directly depend on HTTP-oriented statuses such as `invalid`.
- Consider adding broader mutation-path coverage for public-inclusive
input cache invalidation once the cache has more consumers.
Linear: FE-534
## Screenshots (if applicable)
Before <false positive / missing image / public asset>
https://github.com/user-attachments/assets/db7ce2a9-b169-4fae-bf9f-98bb93d3ee6d
After
https://github.com/user-attachments/assets/29af9f9e-b536-4fcd-a426-3add40bcb165
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11873-Use-hash-lookup-for-missing-asset-detection-3556d73d36508165babafb16614be0d8)
by [Unito](https://www.unito.io)
|
||
|
|
af70d88860 |
fix: keep finished badge fully opaque in ProgressToastItem (#11542)
## Summary - fix **[slack](https://comfy-organization.slack.com/archives/C0A4XMHANP3/p1776801170742579)** - Move `opacity-50` off the row container onto the asset-name column only, so the contrast badge (white pill, dark label) is not dimmed to gray-on-gray when a download completes. - Matches the Figma intent that the `FINISHED` badge stands out — designer spec uses `base/foreground` for pill, `base/background` for text, which is unreadable when the parent is 50% opacity. <img width="560" height="269" alt="Screenshot 2026-04-23 at 2 46 17 PM" src="https://github.com/user-attachments/assets/fb84aa57-c348-4a86-9a65-9342c12400e1" /> <img width="764" height="332" alt="Screenshot 2026-04-23 at 2 46 41 PM" src="https://github.com/user-attachments/assets/ecbe6a5f-c2e8-4427-9c1d-f8f123009d2e" /> ## Before / After  ## Repro Cloud → trigger a model download → wait for completion → the `FINISHED` badge is the same tone as the toast surface (see Slack thread screenshots). ## Test plan - [ ] Complete a model download in cloud and confirm the `FINISHED` badge is clearly legible in both themes. - [ ] File name + subtitle still appear dimmed to signal the row is completed. - [ ] Running / failed / pending states unchanged. - Fixes [FE-237](https://linear.app/comfyorg/issue/FE-237/fix-honeytoast-badge-text-color-for-finished-job-matches-background) |
||
|
|
c955309b26 |
[chore] Update Comfy Registry API types from comfy-api@911406c (#11518)
## Automated API Type Update This PR updates the Comfy Registry API types from the latest comfy-api OpenAPI specification. - API commit: 911406c - Generated on: 2026-04-17T16:10:40Z These types are automatically generated using openapi-typescript. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11518-chore-Update-Comfy-Registry-API-types-from-comfy-api-911406c-3496d73d36508146a1e2e1ee90640fa4) by [Unito](https://www.unito.io) Co-authored-by: coderfromthenorth93 <213232275+coderfromthenorth93@users.noreply.github.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
7abd9d12c8 |
chore(website): refresh Ashby roles snapshot (#11851)
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 `25260868155`. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11851-chore-website-refresh-Ashby-roles-snapshot-3546d73d365081579f98f13f7b58e611) by [Unito](https://www.unito.io) Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com> |
||
|
|
dd9cb42fa1 |
feat: add Run your first workflow button to home hero (#11848)
## Summary Add an outline-style `BrandButton` to the right side of the home page hero section linking to the workflows page. ## Changes - **What**: - Added a `Run your first workflow` outline button below the subtitle in `apps/website/src/components/home/HeroSection.vue`, linking to `externalLinks.workflows`. Mirrors the button pattern from `product/local/HeroSection.vue`. - Added `hero.runFirstWorkflow` i18n entry (en + zh-CN) in `apps/website/src/i18n/translations.ts`. ## Review Focus - Confirmed alignment with design spec. <img width="1505" height="776" alt="image" src="https://github.com/user-attachments/assets/215e667d-1827-447b-99b8-eba8cb5ec7e3" /> <img width="335" height="700" alt="image" src="https://github.com/user-attachments/assets/aeac0876-74c3-4e12-a4b3-203f9e541bc2" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11848-feat-add-Run-your-first-workflow-button-to-home-hero-3546d73d365081358d54eddfda71111e) by [Unito](https://www.unito.io) Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
ccd19d8695 |
test: add metadata parser coverage (#11307)
## Summary Adds tests for metadata parsers ## Changes - **What**: - add test file generation script - identified & fixed bug in webp exif parsing over-reading - identified & fix bug in mp3/ogg parser where it would read from a fixed position instead of relative, causing incorrect reads throwing RangeError - added catch in latent + json parsing to resolve errors ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11307-test-add-metadata-parser-coverage-3446d73d36508108ac36dddcec0a54d4) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com> |
||
|
|
809fba7b36 |
feat: add Groove Jones customer story (#11849)
## Summary Add a new customer story for Groove Jones — Crocs x NFL FOOH holiday campaign for Dick's Sporting Goods, delivered with Comfy. ## Changes - **What**: - New entry in [`apps/website/src/config/customerStories.ts`](https://github.com/Comfy-Org/ComfyUI_frontend/blob/feat/groove-jones-customer-story/apps/website/src/config/customerStories.ts) registering slug `groove-jones` with cover image hosted on `media.comfy.org/website/customers/groove-jones/`. - Added `customers.story.groove-jones.{category,title,body}` and `customers.detail.groove-jones.topic-1` … `topic-10` translations (en + zh-CN) in [`apps/website/src/i18n/translations.ts`](https://github.com/Comfy-Org/ComfyUI_frontend/blob/feat/groove-jones-customer-story/apps/website/src/i18n/translations.ts). 10 sections matching design sidebar: INTRO, THE OUTPUT, THE PROBLEM, HOW COMFY SOLVED THE PROBLEM, BRAND-TRAINED LORAS, MULTI-MODEL ORCHESTRATION, THE PIPELINE, VERSION CONTROL, FINISHING IN NUKE, THE TAKEAWAY. - Includes 2 pull quotes (Doug Hogan, Dale Carman), 1 final blockquote + author card, and 3 inline images. - Routes `/customers/groove-jones` and `/zh-CN/customers/groove-jones` are auto-generated by `[slug].astro`. ## Review Focus - Contributors author card uses `TBD` placeholder names/roles — to be filled in. - No `readMoreHref` set yet (no public blog post URL). - All 4 images uploaded to `gs://comfy-org-videos/website/customers/groove-jones/` and served via `media.comfy.org`. <img width="1000" height="535" alt="Kapture 2026-05-02 at 23 17 04" src="https://github.com/user-attachments/assets/28654d24-0d49-4303-82ac-b6923cd6bc93" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11849-feat-add-Groove-Jones-customer-story-3546d73d36508128a64bd6809ad77447) by [Unito](https://www.unito.io) Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
df2ae6f2d0 |
fix(load3d): dispose THREE.Points GPU resources in clearModel() (#11836)
Fixes #11345 ## Summary `clearModel()` in `SceneModelManager` only traversed and disposed `THREE.Mesh` instances, leaving `THREE.Points` objects (created by `handlePLYModeSwitch()` for point-cloud mode) leaking GPU geometry and material memory on repeated point-cloud loads/clears. ## Changes - `SceneModelManager.ts`: extend the dispose traversal in `clearModel()` to also handle `THREE.Points`, mirroring the pattern already used by `removeAllMainModelsFromScene()`. - `SceneModelManager.test.ts`: add regression test verifying `geometry.dispose()` and `material.dispose()` are called for `THREE.Points` children on `clearModel()`. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11836-fix-load3d-dispose-THREE-Points-GPU-resources-in-clearModel-3546d73d365081718338e824bc3e737d) by [Unito](https://www.unito.io) |
||
|
|
3c7781190a |
feat(website): add llms.txt for GEO discovery by AI search agents (#11830)
## Summary Adds `/llms.txt` at the apex following the [llms.txt standard](https://llmstxt.org) — a curated, link-based markdown file that tells LLM-based search agents (ChatGPT search, Perplexity, Claude search, Google AI Overviews, etc.) what's most important on the site. It complements `robots.txt` (crawler permissions) and `sitemap-index.xml` (URL inventory) by giving AI agents a short, prose-friendly index they can ingest into a context window. ## What's in the file 28 links across 6 sections: - **Product** (6) — homepage, Local download, Cloud, Cloud pricing, API, Enterprise - **Workflows and Gallery** (2) — gallery + community workflows site - **Customers and Case Studies** (5) — customers index + 4 named studios (Series Entertainment, Moment Factory, Ubisoft Chord, Open Story Movement) - **Developers and Documentation** (4) — docs.comfy.org, ComfyUI repo, Comfy-Org GitHub org, registry.comfy.org - **Company** (6) — about, careers, contact, blog, privacy, terms - **Optional** (5) — `zh-CN` locale variant, long-form enterprise case studies, blog posts (de-prioritized per spec — agents can skip if context-limited) The intro paragraph names the four product surfaces (Local, Cloud, API, Enterprise), the named customers, and the use-case industries (VFX & animation, advertising, gaming, eCommerce/fashion) — so an agent that ingests only the prose still gets the elevator pitch. ## Verification - All 28 URLs verified live (`HTTP 200`) before commit. - File is plain markdown — no build step. Astro/Vercel will serve it from `apps/website/public/llms.txt` exactly as it serves `robots.txt` (which lives in the same directory and ships at `https://comfy.org/robots.txt`). - Will verify on the Vercel preview deploy after this PR opens that `curl -sI https://<preview>/llms.txt` returns `200` with a sensible `content-type`. (`robots.txt` currently serves as `text/plain; charset=utf-8` — `.txt` will likely do the same; that's fine for AI agents.) ## Decisions - **No `llms-full.txt` yet.** That variant inlines full prose of key pages and requires curating substantive content. Deferred to a follow-up — the marketing-site pages are mostly Vue-rendered hero/feature blocks rather than long-form prose, so a meaningful `llms-full.txt` would need either dedicated copy or a build step that flattens i18n strings + section text. Tracking separately. - **No comment line in `robots.txt`.** I considered adding a `# AI agents: see /llms.txt` comment above the `Sitemap:` directive, but decided against it: (a) the convention is to probe the well-known path `/llms.txt` directly, not to discover it via robots.txt; (b) `robots.txt` was just polished in #11823 with a deliberate compact design and adding a non-standard comment would muddy that; (c) zero implementations I checked actually parse robots.txt for llms.txt hints. Easy to add later if needed. ## Context Third of three follow-ups from the SEO/GEO sweep on 2026-05-02: 1. ~~Comfy-Router: add `X-Content-Type-Options: nosniff` to apex security headers~~ (separate PR on `Comfy-Org/comfy-router`) 2. ~~Cloudflare: enable "Always Use HTTPS"~~ (dashboard toggle, no PR) 3. **This PR** — add `llms.txt` for GEO discovery ## Testing - [x] All linked URLs return 200 - [x] File parses as valid markdown - [ ] Preview deploy serves `/llms.txt` (will verify once preview is up) ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11830-feat-website-add-llms-txt-for-GEO-discovery-by-AI-search-agents-3546d73d365081a98c6bfc5301699f64) by [Unito](https://www.unito.io) |
||
|
|
167a1e6a0c |
fix(website): override X-Robots-Tag to index,follow on production deployment (#11831)
## P0 SEO fix — entire marketing site is currently `noindex` A deep-audit sweep on **2026-05-02** confirmed that every page on `comfy.org` (home, `/about`, `/pricing`, `/customers`, `/careers`, …) is being served with: ``` x-robots-tag: noindex ``` This hides the entire marketing site from Google and other search engines. ### Root cause `comfy.org` reaches the Astro marketing site via a Cloudflare Worker reverse proxy ([`comfy-router`](https://github.com/Comfy-Org/comfy-router)) that does: ```js fetch('https://website-frontend-comfyui.vercel.app/...') ``` Per Vercel's KB article [Avoiding duplicate-content with vercel.app URLs](https://vercel.com/kb/guide/avoiding-duplicate-content-with-vercel-app-urls): > By default, Vercel adds an `X-Robots-Tag: noindex` HTTP header to all deployments hosted on `vercel.app` URLs. […] This header tells search engines like Google not to index these deployment URLs. Because `website-frontend-comfyui.vercel.app` is **not** registered as a Production custom domain on the Vercel project, Vercel applies that header — and our Worker faithfully forwards it back to `comfy.org` clients (and Googlebot). ### Fix Vercel's documented workaround for proxied `*.vercel.app` setups is to override the header via `vercel.json` ([blog post by Dan Denney](https://www.dandenney.com/posts/front-end-dev/fixing-x-robots-nofollow-with-vercel/)): ```json "headers": [ { "source": "/(.*)", "has": [ { "type": "host", "value": "website-frontend-comfyui.vercel.app" } ], "headers": [ { "key": "X-Robots-Tag", "value": "index, follow" } ] } ] ``` ### Why the `has` host filter Critical scoping detail: without the `has` filter, the override would also apply to PR-preview deployments at `comfy-website-preview-pr-N.vercel.app`. We want previews to **stay** `noindex` to avoid duplicate-content competition with production. The filter pins the override to the production deployment hostname only. The hostname matches `WEBSITE_ORIGIN` in `comfy-router/src/index.js`: ```js const WEBSITE_ORIGIN = 'https://website-frontend-comfyui.vercel.app'; ``` ### Defense in depth A parallel PR on `comfy-router` (TBD) will also strip `X-Robots-Tag: noindex` at the Worker layer, so the public `comfy.org` response is correct even if a future Vercel project change re-introduces the upstream header. ### Verification (after merge + Vercel production deploy) ```bash # Production should no longer be noindex curl -sI https://comfy.org/ | grep -i x-robots-tag curl -sI https://comfy.org/about | grep -i x-robots-tag curl -sI https://comfy.org/pricing | grep -i x-robots-tag # Expect: empty output, OR "x-robots-tag: index, follow" # Direct Vercel production hostname curl -sI https://website-frontend-comfyui.vercel.app/ | grep -i x-robots-tag # Expect: "x-robots-tag: index, follow" # Preview deploys must stay noindex (proves the host filter works) curl -sI https://comfy-website-preview-pr-<N>.vercel.app/ | grep -i x-robots-tag # Expect: "x-robots-tag: noindex" ``` ### Pre-merge state (for the record) ``` $ curl -sI https://comfy.org/ | grep -iE 'x-robots-tag|x-served-by' x-served-by: vercel-website x-robots-tag: noindex ``` ### Scope Minimum delta — only `apps/website/vercel.json`. No other files touched. ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11831-fix-website-override-X-Robots-Tag-to-index-follow-on-production-deployment-3546d73d365081489b62e6633d25dfe5) by [Unito](https://www.unito.io) --------- Co-authored-by: Christian Byrne <christian@comfy.org> Co-authored-by: GitHub Action <action@github.com> |
||
|
|
e4e1546458 |
test: add queue notification banners lifecycle browser tests (#11366)
*PR Created by the Glary-Bot Agent* --- ## Summary - Adds `browser_tests/tests/queueNotificationBanners.spec.ts` covering `useQueueNotificationBanners` composable E2E behavior - Adds `data-testid="queue-notification-banner"` to `QueueNotificationBannerHost.vue` for stable test targeting - Registers the new test ID in `TestIds.queue.notificationBanner` ### Test coverage added (7 tests) | Group | Tests | Behavior | |---|---|---| | Queuing lifecycle | 4 | `promptQueueing` → banner appears, `promptQueued` upgrades to queued, batch plural text, requestId mismatch doesn't upgrade | | Auto-dismiss | 1 | Banner disappears after 4s timeout | | FIFO queue | 1 | Second notification shows after first auto-dismisses | | Direct queued | 1 | `promptQueued` without prior `promptQueueing` shows banner directly | ### Approach Tests dispatch `promptQueueing`/`promptQueued` custom events directly via `window.app.api.dispatchCustomEvent()` inside `page.evaluate()`, matching how `app.ts` triggers these events during real queue operations. This avoids needing a running execution pipeline while exercising the full composable → component → DOM rendering chain. ### Verification - TypeScript: zero errors - ESLint: clean - oxlint: clean - oxfmt: formatted - Playwright execution requires running ComfyUI backend (not available in sandbox) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11366-test-add-queue-notification-banners-lifecycle-browser-tests-3466d73d36508172a7ffd3fe3b4fd925) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
c1954028d1 |
feat(ci): add Release:Website workflow to refresh Ashby snapshot (#11829)
## Summary
Adds a unified manual-trigger release flow for the `apps/website`
package
(careers/marketing site at comfy.org), mirroring how main-app releases
work.
**User-facing flow:**
```
workflow_dispatch ──► fresh Ashby pull ──► auto-PR with snapshot bump
│
▼
existing CI / Vercel preview deploy
│
▼
human merges ──► auto prod deploy
```
The careers data on comfy.org comes from Ashby and is fetched at build
time by
`apps/website/src/utils/ashby.ts`. Without `WEBSITE_ASHBY_API_KEY`, the
build
falls back to a committed snapshot at
`apps/website/src/data/ashby-roles.snapshot.json`. That snapshot has
been
going stale because no CI workflow was passing the API key. This PR
fixes
both: a manual refresh workflow + day-to-day secrets wiring.
## Files
**Added**
- `.github/actions/ashby-pull/action.yaml` — composite action that runs
`pnpm --filter @comfyorg/website ashby:refresh-snapshot` with the Ashby
secrets piped in. Uses the existing `setup-frontend` composite for
pnpm/Node setup.
- `.github/workflows/release-website.yaml` — `workflow_dispatch`-only
release workflow. Checks out `main`, refreshes the snapshot via the
composite action, opens a PR labelled `Release:Website` via
`peter-evans/create-pull-request@c0f553fe…` (the same SHA pin used by
`release-version-bump.yaml`).
**Modified**
- `.github/workflows/ci-website-build.yaml` — adds
`WEBSITE_ASHBY_API_KEY`
and `WEBSITE_ASHBY_JOB_BOARD_NAME` env to the `Build website` step.
- `.github/workflows/ci-vercel-website-preview.yaml` — adds the same env
to both `vercel build` steps (preview + production).
## Snapshot fallback preserved
`apps/website/src/utils/ashby.ts` keeps using the committed snapshot
when
the API key is absent (e.g. fork PRs, secret rotation). Verified
locally:
```
$ pnpm --filter @comfyorg/website ashby:refresh-snapshot
Snapshot refresh aborted. Outcome: stale; reason: missing WEBSITE_ASHBY_API_KEY...
```
The release workflow surfaces this as a job failure, which is the
desired
behavior for a manual release trigger.
## Validation
- `yamllint --config-file .yamllint` on all changed YAML — clean
- `./scripts/cicd/check-yaml.sh` — clean
- `pinact run --check` on new files — clean (matches `.pinact.yaml`
policy)
- `pnpm install --frozen-lockfile` — works with `.nvmrc` Node 24
- Husky pre-commit hooks (eslint + typecheck + lint-staged) passed
## Caveats
- **Cannot fully end-to-end test until merged.** `workflow_dispatch`
workflows only run from branches that exist on `main`. The first
manual run can only happen after this PR lands. The pieces that
*can* be tested pre-merge — yamllint, pinact pin validation, and
the existing `CI: Website Build` / `CI: Vercel Website Preview`
workflows now picking up the secret — will run on this PR.
- **`Release:Website` label needs to be created** in the repo before
the auto-PR step will successfully apply it.
`peter-evans/create-pull-request`
will warn but not fail if the label doesn't exist. Suggested color:
`#4f6ef5` (matches `cloud/*` family in `release-branch-create.yaml`).
- The release workflow uses `secrets.PR_GH_TOKEN` (matching
`release-version-bump.yaml`) so the auto-PR can be authored by a
PAT and trigger downstream CI workflows. Without `PR_GH_TOKEN` it
will fall back behavior is up to GitHub Actions defaults — confirm
the secret exists before the first run.
## Context
Came out of work on `comfy-router#22` + `ComfyUI_frontend#11823`
(comfy.org/countdown subpage / website refresh). Discovered the
8+-day-stale snapshot while auditing the website build path.
┆Issue is synchronized with this [Notion
page](https://app.notion.com/p/PR-11829-feat-ci-add-Release-Website-workflow-to-refresh-Ashby-snapshot-3546d73d3650811eb300d8bcb593c652)
by [Unito](https://www.unito.io)
|
||
|
|
5cad2c952b |
refactor+test: extract useSubscriptionCheckout composable, rewrite tests (#11396)
## Summary Adds 20 component tests for `SubscriptionRequiredDialogContentWorkspace.vue` covering: - **Initial rendering**: pricing table display, close/back button visibility, out_of_credits reason message - **Close button**: calls onClose callback - **Subscribe click flow**: pricing→preview transitions (new subscription & upgrade), error toasts for disallowed/missing/failed previews, monthly billing cycle - **Back button**: returns from preview to pricing step - **Add credit card**: handles subscribed status (success toast + close), needs_payment_method (opens Stripe URL), error state - **Confirm transition**: success path with close emit, error toast on failure - **Resubscribe**: success path with toast + close, error toast on failure ## Testing ```bash pnpm test:unit -- src/platform/workspace/components/SubscriptionRequiredDialogContentWorkspace.test.ts ``` All 20 tests pass. Quality gates (typecheck, lint, format, knip) pass. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11396-test-add-component-tests-for-SubscriptionRequiredDialogContentWorkspace-3476d73d36508156a218dcb67a2a334e) by [Unito](https://www.unito.io) |
||
|
|
e356addeb6 |
feat: add model links for default workflow (#11308)
We now support detecting the missing models when loading the workflow. But the default workflow didn't include an embedded model link, so users don't know where to download the model or which one to use. Users will see an error when loading the default workflow every time, so I updated it to include the model link. Before <img width="1920" height="1050" alt="image" src="https://github.com/user-attachments/assets/08774480-78ae-41b4-85bd-64b431079ec1" /> After <img width="1920" height="1050" alt="image" src="https://github.com/user-attachments/assets/dcec5a02-94ad-416f-9881-d761f4137fbd" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11308-feat-add-model-links-for-default-workflow-3446d73d365081188978e1d313c38ffe) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com> |
||
|
|
e831daae59 |
feat(website): point robots.txt at /sitemap-index.xml + AI crawler rules (#11823)
## Summary Once [comfy-router#22](https://github.com/Comfy-Org/comfy-router/pull/22) ships, `comfy.org/sitemap-index.xml` will return a unified index aggregating both the website (38 URLs) and workflow-templates sitemaps. This PR: 1. Reverts `Sitemap:` back to `/sitemap-index.xml` (was `/sitemap-0.xml` in #11802 as a workaround for the 404). 2. Adds explicit allow records for 21 search and AI/LLM crawlers (GPTBot, ChatGPT-User, OAI-SearchBot, Google-Extended, ClaudeBot, Claude-Web, anthropic-ai, PerplexityBot, Perplexity-User, Applebot-Extended, Bytespider, Amazonbot, CCBot, Meta-ExternalAgent, Meta-ExternalFetcher, Diffbot, etc.). 3. Adds `Disallow:` for `/_astro/`, `/_website/`, `/_vercel/` — Vercel build artifacts that aren't useful to crawl. ## Why granular UAs Stacked `User-agent:` records (per [RFC 9309 §2.2](https://datatracker.ietf.org/doc/html/rfc9309#section-2.2)) share one rule block. Listing each bot explicitly: - Signals intent to AI bots that look for their UA in robots.txt before crawling more aggressively. - Surfaces our crawl policy clearly to anyone inspecting the file. - Lets us add per-bot Disallows in future without restructuring. ## Merge order ⚠️ **Do NOT merge until comfy-router#22 is deployed to production.** Until then, `/sitemap-index.xml` returns 404 and this PR would re-break the issue PR #11802 patched. Verification: ```bash curl -sI https://comfy.org/sitemap-index.xml # expect: HTTP/2 200, x-served-by: worker-sitemap-index ``` Once that returns 200, this is safe to merge. ## Verification (after merge + deploy) ```bash # robots.txt is served and points at the unified index curl -s https://comfy.org/robots.txt | grep '^Sitemap:' # → Sitemap: https://comfy.org/sitemap-index.xml # Each AI crawler can fetch it for ua in 'GPTBot/1.0' 'ClaudeBot/1.0' 'PerplexityBot/1.0' 'Google-Extended' 'Applebot-Extended'; do curl -s -o /dev/null -w "$ua → %{http_code}\n" -A "$ua" https://comfy.org/robots.txt done # Sitemap is reachable from robots.txt SITEMAP=$(curl -s https://comfy.org/robots.txt | awk -F': ' '/^Sitemap:/ {print $2}') curl -s "$SITEMAP" | xmllint --noout - && echo "valid XML" ``` ## Linear / closes - Closes FE-437 (AI crawler rules) - Updates FE-432 — the robots.txt change in #11802 was a workaround that's no longer needed once #22 ships ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11823-feat-website-point-robots-txt-at-sitemap-index-xml-AI-crawler-rules-3546d73d3650811dbceedd06c00db444) by [Unito](https://www.unito.io) |
||
|
|
96575fcec9 |
feat: redesign cloud onboarding survey for ICP and persona signal (#11628)
## Summary Replaces the 4-step Cloud onboarding survey with a 7-step flow that captures both ICP attributes and user persona dimensions. The survey questions are now populated dynamically from remoteConfig. ## Changes - **What**: New survey questions — Usage, Familiarity, Role, Team size, Industry, Making, Source. Role / Team size / Industry are gated to "Work" usage; Education users see a Student / Educator short list for Role. Most option lists are randomized per visit (familiarity and team size stay ordered as ordinals). \`SurveyResponses\` extended with optional \`usage\`, \`role\`, \`teamSize\`, \`source\` fields. - **Breaking**: None — \`useCase\` and \`workflowRelationship\` remain optional in the type and existing telemetry normalization keeps working unchanged. ## Review Focus - The \`role\` step has a function-form \`options\` so the list can swap based on \`usage\`. \`steps\` is a computed that filters by \`showWhen()\` and resolves the option function — verify reactivity when \`usage\` changes. - Changing \`usage\` clears the previously-picked \`role\` via a watcher to prevent a stale value from carrying over between Work / Education modes. - Per-visit shuffle is stable: option lists are passed through \`randomize()\` once at module load, not on every render. ## Screenshots https://github.com/user-attachments/assets/3602a388-50dc-401e-ada9-ea9016c5052d ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11628-feat-redesign-cloud-onboarding-survey-for-ICP-and-persona-signal-34d6d73d365081f4a792cfe76a987ffb) by [Unito](https://www.unito.io) --------- Co-authored-by: Dante <bunggl@naver.com> |
||
|
|
e7e1ae25a6 |
fix(load3d): suppress error toast on 404 when loading output model file (#11807)
## Summary
- Adds `silentOnNotFound` option to `LoadModelOptions` interface,
threaded through `Load3d.loadModel` → `LoaderManager.loadModel`
- 404 errors (detected via message text or `response.status`) are
silently swallowed when `silentOnNotFound: true`; all other errors still
surface a toast
- Sets `silentOnNotFound: true` for output-folder loads in `load3d.ts`
and `saveMesh.ts` — covers shared workflows opened on a machine that
never ran them
## Test plan
- [x] `LoaderManager.test.ts` — 40 unit tests covering 404 suppression,
non-404 still toasts, stale load handling
- [x] `Load3DConfiguration.test.ts` — 4 unit tests verifying
`silentOnNotFound` propagates correctly through `configureForSaveMesh`
and `configure`
- [x] `load3d.spec.ts` — 2 E2E tests: 404 → no toast, 500 → toast
appears
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes error-handling behavior in the 3D model loading pipeline and
extends method signatures/options; risk is mainly missed call sites or
incorrectly classifying non-404 errors as 404 and hiding real failures.
>
> **Overview**
> Prevents noisy user-facing toasts when an *output* 3D model referenced
by `Preview3D`/`SaveGLB` is missing locally by adding a
`silentOnNotFound` flag and suppressing the "Error loading model" toast
specifically for HTTP 404 failures.
>
> Threads the new `LoadModelOptions` through `Load3d.loadModel` →
`LoaderManager.loadModel` and updates `Load3DConfiguration`/callers to
opt in for output-folder loads, with new unit + Playwright coverage (404
stays silent, non-404 still toasts).
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
|
||
|
|
4ed00cec08 |
update: robots.txt to point to /sitemap-0.xml (#11802)
## Summary <!-- One sentence describing what changed and why. --> robots.txt at [comfy.org](https://comfy.org/) references /sitemap-index.xml which returns 404. The actual working sitemap is at /sitemap-0.xml (200, 38 URLs). This blocks search engines from discovering the sitemap. ## Changes - **What**: <!-- Core functionality added/modified --> - Update robots.txt to point to /sitemap-0.xml, OR ## 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 --> ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11802-update-robots-txt-to-point-to-sitemap-0-xml-3536d73d365081bb9545eb96dd1e8025) by [Unito](https://www.unito.io) |
||
|
|
f566abdd6e |
ci: extract changes-filter composite action; fix docs-only PR stall (#11785)
## Summary Extract a `.github/actions/changes-filter` composite action and adopt it across path-gated CI workflows, fixing the docs-only PR stall and removing duplicated `paths:` / `paths-ignore:` filtering across 8 workflows. ## Background Docs-only PRs stalled on required status checks because workflows using `paths-ignore: ['**/*.md']` never created a check run, while branch protection still required it. Observed on #11776 (the `test` check from `ci-tests-unit.yaml` never appeared). The fix pattern: keep the workflow triggered, gate downstream jobs on a `changes` job whose outputs are computed from a path filter. Skipped jobs count as passing under branch protection. ## What the action emits | Output | Meaning | |---|---| | `should-run` | Any file outside `apps/`, `docs/`, `.storybook/`, `**/*.md` changed. | | `app-website-changes` | Shared deps or `apps/website/**` changed. | | `app-desktop-changes` | Shared deps or `apps/desktop-ui/**` changed. | | `app-frontend-changes` | Shared deps or `src/**` changed. | | `packages-changes` | Shared deps or `packages/**` changed. | | `storybook-changes` | Shared deps or `.storybook/**` changed. | | `docs-changes` | `docs/**` or any `**/*.md` changed (deps NOT folded in). | | `dependency-changes` | Root `package.json`, `pnpm-lock.yaml`, or `pnpm-workspace.yaml` changed. | Shared deps are folded into every `app-*`, `packages-changes`, and `storybook-changes` output so a lockfile bump correctly invalidates each granular gate. Outputs default to `'true'` for non-`pull_request` events to avoid the silent-skip footgun on push / merge_group. ## Workflows migrated | Workflow | Gate | Notes | |---|---|---| | `ci-tests-unit.yaml` | `should-run` | Required check (`test`). Fixes the original stall. | | `ci-tests-e2e.yaml` | `should-run` | Required check (`e2e-status`). Replaces inline filter. | | `ci-perf-report.yaml` | `should-run` | Removes `paths-ignore`. | | `ci-website-build.yaml` | `app-website-changes \|\| packages-changes` | Refactor — not a required check, but moves to job-level gating. Filter scope broadens from `packages/{design-system}` to all `packages/**` (strictly safer). | | `ci-website-e2e.yaml` | `app-website-changes \|\| packages-changes` | Same restructure; `post-starting-comment` also gated to avoid spurious "tests are running" when E2E is skipped. | | `ci-dist-telemetry-scan.yaml` | `should-run` | New gate; was previously running on every PR including docs-only. | | `ci-oss-assets-validation.yaml` | `should-run` | Same. | | `ci-size-data.yaml` | `should-run` | Preserves existing repository guard on the new `changes` job. | | `ci-tests-storybook.yaml` | `storybook-changes \|\| app-frontend-changes \|\| packages-changes` | Gates 4 of 6 jobs. `deploy-production` (push to main) left ungated; `update-comment-with-chromatic` cascades naturally. | ## Branch protection (verified) Required status checks on `main` and `core/**`/`cloud/**`: `test`, `lint-and-format`, `e2e-status`. Only `test` and `e2e-status` use the composite — `lint-and-format` correctly stays unfiltered (must run on docs/apps too). The other 6 migrations are refactor wins (less wasted CI on docs/apps-only PRs), not stall fixes. ## Changes - **What**: New `.github/actions/changes-filter` composite + 8 workflow migrations to consume it. - **Breaking**: None. - **Dependencies**: New pin on `dorny/paths-filter@de90cc6` — already covered by `ci-validate-action-pins`. ## Review Focus - The `should-run` filter excludes `.storybook/**` (granular `storybook-changes` covers it instead). Storybook's gate combines all three: `storybook-changes || app-frontend-changes || packages-changes`. - Two `dorny/paths-filter` steps inside the composite — `predicate-quantifier=every` is required for the negated globs in `should-run` but breaks the multi-pattern OR filters. - The website filter scope intentionally broadens from `packages/{design-system,tailwind-utils}/**` to all `packages/**` for consistency and safety. Fixes #11776 ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11785-ci-extract-changes-filter-composite-action-fix-docs-only-PR-stall-3526d73d36508172a1d7fe8c30fa6453) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
3c5695fd42 |
test: add Vue node error/validation ring e2e coverage (#11727)
## Summary Add additional test coverage for vue node errors ## Changes - **What**: - add tests for showing error on missing node, execution error, validation failure & resolved on fix - move ErrorsTabHelper to fixtures dir & update refs - add SLOW_MO env var for headed local tests ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11727-test-add-Vue-node-error-validation-ring-e2e-coverage-3506d73d365081069ff8f70f7970dd55) by [Unito](https://www.unito.io) |
||
|
|
4fff0c4b49 |
fix: report total file count, not job count, in ZIP export toast (#11737)
## Summary ZIP export toast now reports the total number of files instead of the number of selected jobs when any selected job has multiple outputs. ## Changes - **What**: In `downloadMultipleAssetsAsZip` (`src/platform/assets/composables/useMediaAssetActions.ts`), compute `fileCount` by summing each asset's `outputCount` metadata (fallback 1) and pass it to `mediaAsset.selection.exportStarted` instead of `assets.length`. The existing i18n string already handles `file`/`files` plural. - **Tests**: 3 new unit tests in `useMediaAssetActions.test.ts` covering multi-output, single-output fallback, and mixed selections. The `useToast` and `useI18n` mocks were lifted to hoisted refs so toast call args are assertable. ## Review Focus - Reduce uses `count > 1 ? count : 1`, mirroring the `hasMultiOutputJobs` gate above so a known `outputCount === 1` is still counted as 1 file (no double-counting). - Only `downloadMultipleAssetsAsZip` is touched; OSS individual-download path and direct-download path are unchanged. Fixes #11736 ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11737-fix-report-total-file-count-not-job-count-in-ZIP-export-toast-3516d73d3650811ba78dfdb0a0ae8ea1) by [Unito](https://www.unito.io) --------- Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
69dca2d600 |
test: add test coverage for workflow save settings (#11763)
## Summary Add tests for autosave/delay/node sort on save ## Changes - **What**: - add tests ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11763-test-add-test-coverage-for-workflow-save-settings-3516d73d365081d1b57bc1cf4b2e2ece) by [Unito](https://www.unito.io) |
||
|
|
004530b23a |
fix: search bar layout and autocomplete clipping on Desktop at small sizes (#11713)
## Summary
Fixes two visual bugs in the Desktop app at small window sizes: the
search bar getting pushed/clipped in modal headers, and autocomplete
suggestion dropdowns being cut off by `overflow-hidden` ancestors.
## Changes
- **`SearchAutocomplete.vue`**: Wrap `ComboboxContent` in
`ComboboxPortal` so the suggestions dropdown teleports to `<body>`,
escaping `overflow-hidden` ancestors (fixes z-index clipping in Manager
dialog and other modals using `BaseModalLayout`)
- **`BaseModalLayout.vue`**: Replace `shrink-0` with `min-w-0` on the
header content container so the search bar can shrink at narrow window
sizes instead of overflowing and being clipped by the modal root's
`overflow-hidden`
- **`GraphCanvas.vue`**: Fix dead code where the native drag
(`app-drag`) div was nested inside a `v-if="workflowTabsPosition ===
'Topbar'"` block with its own mutually exclusive condition — move it
before the block and add `pointer-events-auto` so Desktop window
dragging works when tabs are in Sidebar position
## Why no E2E tests
- **`SearchAutocomplete` portal**: The fix is structural (teleport to
`<body>`). A meaningful regression test would require opening the
Manager dialog with a real or mocked extension list — that is a
substantial standalone effort tracked in #11714.
- **`BaseModalLayout` header shrink**: A viewport-resize assertion would
test CSS layout behaviour, not application logic; it would be fragile
and low-value.
- **`GraphCanvas` app-drag**: Desktop/Electron-only.
`-webkit-app-region: drag` cannot be exercised in headless Chromium.
Unit tests for `SearchAutocomplete` cover the new code paths (portal
rendering, suggestion display, item selection).
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Low risk UI-only changes: adjusts layout CSS and combobox rendering
via `ComboboxPortal`, plus adds unit tests; no business logic or data
flow changes.
>
> **Overview**
> Fixes small-window Desktop UI issues where modal-header search inputs
could be clipped and autocomplete dropdowns could be cut off by
`overflow-hidden` ancestors.
>
> `SearchAutocomplete` now renders its suggestions list inside a
`ComboboxPortal` (teleporting the popper content outside clipping
containers) and adds a focused unit test suite covering empty/non-empty
suggestions, selection behavior, and `optionLabel` handling.
>
> `BaseModalLayout` tweaks header flexbox constraints (`min-w-0` on the
header content container) to allow the search bar to shrink instead of
overflowing.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
|
||
|
|
73d4e24ffa |
revert: roll back #10849 + #11697 (per-instance promoted widget values) (#11790)
## Summary Reverts #10849 (per-instance promoted widget value storage) and its companion test-pinning PR #11697. The fix in #10849 caused regressions in promoted-widget serialization (notably the Z-Image-Turbo template, see #10146 follow-up). A replacement fix is being developed on `fix/subgraph-promoted-widget-inline-state` and will land separately. ## Changes - **Revert #11697** — drops the `it.fails`-marked tests that pin the #10849 corruption symptom. With #10849 reverted, those markers would falsely flip to passing. - **Revert #10849** — removes per-instance `_instanceWidgetValues` map, `_pendingWidgetsValues` configure-time hydration, the `widgets_values` write path in `SubgraphNode.serialize()`, the `sourceSerialize` field on `PromotedWidgetView`, the multi-instance Vitest suite, and the multi-instance E2E test + asset. - **Conflict resolution** in `browser_tests/tests/subgraph/subgraphSerialization.spec.ts`: kept the restored test coverage from #11579 (which is post-#10849 on main) and removed only the now-unreachable multi-instance test, its helper, and its workflow constant. Auto-merge with #11698 (`incrementVersion`) and #11699 (ID type aliases) was clean. ## Review Focus - Confirm no other on-main code path has come to depend on `PromotedWidgetView.sourceSerialize` or `SubgraphNode._instanceWidgetValues` since #10849 (grep is clean locally). - Confirm we want to land this revert before the replacement fix on `fix/subgraph-promoted-widget-inline-state` is ready — this leaves the original #10146 (multi-instance widget value collision) unfixed in the meantime. - The retained #11579 test coverage now exercises pre-#10849 behavior; some of those assertions were written expecting the #10849 code path. CI will surface any that need adjustment. ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11790-revert-roll-back-10849-11697-per-instance-promoted-widget-values-3536d73d3650814094abd58b6b712d8d) by [Unito](https://www.unito.io) |
||
|
|
09790bd7f3 |
test: add unit tests for useLayoutMutations (#11313)
## Summary
A follow-up PR of
https://github.com/Comfy-Org/ComfyUI_frontend/issues/11106.
This PR only focus on `layoutMutations.ts`.
`layoutMutations.ts` is the central API for all node layout mutations in
the Vue renderer. It previously had zero test coverage despite
containing non-trivial logic such as guard clauses, ID normalization,
and z-index scanning. This PR addresses issue #11106 to prevent silent
regressions in node positioning and lifecycle.
## What was tested and how
All tests use the real `layoutStore` singleton (no mocks).
`initializeFromLiteGraph` resets node state before each test, and
results are verified via `getNodeLayoutRef().value`.
| Method | Tests | Logic covered |
| :--- | :--- | :--- |
| `moveNode` | 3 | **Guard** (missing node -> no-op); position written
to store; numeric ID coerced to string |
| `resizeNode` | 2 | **Guard**; size written to store |
| `setNodeZIndex` | 2 | **Guard**; zIndex written to store |
| `createNode` | 1 | Node becomes readable with the provided position
and size |
| `deleteNode` | 2 | **Guard**; node removed from store |
| `batchMoveNodes` | 4 | Empty array -> no-op; multiple nodes updated
**atomically**; existing size preserved; missing nodes skipped while
valid ones still update |
| `bringNodeToFront` | 1 | Target node ends up with a **higher zIndex**
than all other nodes |
## What was not tested and why
| Method | Reason skipped |
| :--- | :--- |
| `createLink` / `deleteLink` | `layoutStore` exposes no public API to
query link existence by ID; methods contain no logic beyond a straight
`applyOperation` call. |
| `createReroute` / `deleteReroute` / `moveReroute` | Same reason as
above. |
| `setSource` / `setActor` | Single-line delegation to `layoutStore`; no
logic to test. |
| Default value behavior in `createNode` | Avoids "change-detector"
tests; asserting hardcoded defaults adds no regression value and would
block valid product changes. |
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Test-only changes; no production logic is modified. Main risk is
potential flakiness due to reliance on singleton store state across
tests.
>
> **Overview**
> Adds a new `layoutMutations.test.ts` Vitest suite that exercises
`useLayoutMutations` against the real `layoutStore` singleton
initialized from LiteGraph data.
>
> Tests cover no-op guard clauses for missing nodes, node ID
normalization, position/size/z-index updates, node create/delete
behavior, `batchMoveNodes` semantics (empty input, skipping missing
nodes, preserving size), and `bringNodeToFront` z-index promotion
relative to other nodes.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
|
||
|
|
dafb944c3b |
refactor: replace PrimeVue InputText/Textarea with project UI components (#11324)
## Summary
Fix part of the
#https://github.com/Comfy-Org/ComfyUI_frontend/issues/11092
A total of 4 `// @ts-expect-error` directives were removed across 3
files — all caused by PrimeVue's legacy `$el` access pattern (`const
inputElement = inputRef.value.$el`) — by replacing PrimeVue with
Reka-based UI components. 1 corresponding unit test file was added.
## Changes
### `src/components/ui/input/Input.vue`
- **Exposed APIs**: Extended `defineExpose` to include `blur()` and
`setSelectionRange()`. This allows parent components to programmatically
control input behavior without direct DOM manipulation.
### `src/components/ui/textarea/Textarea.vue`
- **Exposed APIs**: Added `focus()` via `defineExpose`.
- **Cleanup**: Removed redundant attribute spreading (`...restAttrs`) to
lean on Vue’s default `$attrs` inheritance, making the component more
predictable.
---
## Refactored Feature Components
### `WidgetMarkdown.vue` (Note/Markdown Widgets)
- **Dependency Swap**: Replaced `primevue/textarea` with local
`Textarea.vue`.
- **Logic Simplification**: Simplified focus logic from
`textareaRef.value?.$el?.focus()` to a typed
`textareaRef.value?.focus()`.
- **Code Style**: Converted arrow functions to function declarations and
removed redundant section comments.
### `PromptDialogContent.vue` (Generic Prompt Dialogs)
- **Component Update**: Replaced PrimeVue `FloatLabel` and `InputText`
with a native `<label>` and local `Input.vue`.
- **Vue 3.5 Adoption**: Implemented **Reactive Destructuring** for
props.
- **Conflict Resolution**: Renamed internal `onConfirm` handler to
`handleConfirm` to prevent collision with destructured props.
### `EditableText.vue` (Node Titles & Sidebar Items)
- **Style Modernization**: Removed `<style scoped>` block in favor of
**Tailwind CSS** utility classes (e.g., `inline`, `w-full`).
- **Clean Implementation**: Replaced PrimeVue PassThrough (`:pt`) logic
with standard `@blur` and `v-bind` attributes.
---
## Testing & Quality Assurance
### Updated Tests
- **Redundancy Removal**: Cleaned up `EditableText.test.ts` and
`WidgetMarkdown.test.ts` by removing unused PrimeVue global
registrations. All 34 existing behavioral tests remain passing.
### New Coverage
- **`PromptDialogContent.test.ts`**: Added 3 new tests to verify:
1. Correct initialization with `defaultValue`.
2. Value persistence when clicking the Confirm button.
3. Form submission via the `Enter` key.
---
## Manual Test Screenshot
All functions have passed testing.
<img width="594" height="530" alt="test5"
src="https://github.com/user-attachments/assets/46a6b3b2-1855-414e-ac78-65668052ce50"
/>
<img width="1190" height="1074" alt="test4"
src="https://github.com/user-attachments/assets/89aa61ab-9401-44c2-9eae-9ca8761df675"
/>
<img width="1154" height="1028" alt="test3"
src="https://github.com/user-attachments/assets/3f63cfdf-8fbd-4dd3-9e42-dbebe4d8d421"
/>
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Moderate risk because it swaps underlying input/textarea components
and ref handling (focus/blur/selection) in interactive UI paths
(editable labels, prompt dialogs, markdown editor), which could subtly
change keyboard/blur behavior.
>
> **Overview**
> Refactors several Vue components to stop using PrimeVue
`InputText`/`Textarea` (and `$el` access) in favor of the project’s
`Input`/`Textarea` components, updating bindings/events and Tailwind
classes accordingly.
>
> Extends the shared `Input` to expose `blur`, `setSelectionRange`, and
`selectAll`, and updates `Textarea` to expose `focus`, enabling callers
to manage focus/selection without DOM internals.
>
> Adds a new unit test suite for `PromptDialogContent` and simplifies
existing tests by removing PrimeVue plugin/component setup; the groups
e2e test replaces a screenshot assertion with a functional visibility
check for the new title input.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
|
||
|
|
d429d481e8 |
test: use real vue-i18n plugin in useReconnectingNotification tests (#11386)
## Summary
Replace `vi.mock('vue-i18n')` stub with a real `createI18n` plugin
instance in `useReconnectingNotification` tests.
## Changes
- **What**: Add `setupComposable()` helper that renders a wrapper
component via `@testing-library/vue` with a real `createI18n` plugin.
Assertions now check translated values
(`'Reconnecting'`/`'Reconnected'`) instead of raw i18n keys. Removes the
brittle `vi.mock('vue-i18n')` stub.
## Review Focus
Straightforward test-only change — the composable requires a component
setup context for `useI18n()`, so we render a thin wrapper via
`@testing-library/vue` with the i18n plugin installed.
Fixes #11153
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11386-test-use-real-vue-i18n-plugin-in-useReconnectingNotification-tests-3476d73d3650814ba70eea6df91c8bbe)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
|
||
|
|
a9aae6af4a |
1.44.15 (#11787)
Patch version increment to 1.44.15 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11787-1-44-15-3536d73d3650814da3a6dfe300ff559c) by [Unito](https://www.unito.io) --------- Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com> Co-authored-by: github-actions <github-actions@github.com>v1.44.15 |
||
|
|
46ba65e25c |
fix: hide advanced footer button on collapsed Vue nodes (#11778)
Before https://github.com/user-attachments/assets/82e323d7-0c9b-4303-81eb-87cb7b62f1c1 After https://github.com/user-attachments/assets/56f1db45-1b5d-47a9-9960-9a73deb68e78 Fixes the Vue node footer so the Advanced button only appears while a node is expanded. Root cause: `showAdvancedInputsButton` only checked for advanced widgets and the global setting, so collapsed nodes with advanced widgets could still render the Advanced tab, including the combined error + advanced footer state. Changes: - Return `false` for advanced footer visibility when the Vue node is collapsed. - Add regression coverage for collapsed advanced nodes and collapsed error + advanced nodes. Validation: - `pnpm test:unit -- src/renderer/extensions/vueNodes/components/LGraphNode.test.ts` - Commit hook: format, stylelint, oxlint, eslint, `pnpm typecheck` - Push hook: `pnpm knip` ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11778-codex-Hide-advanced-footer-on-collapsed-Vue-nodes-3526d73d365081d79399ddda40f2a7f7) by [Unito](https://www.unito.io) |
||
|
|
11432f7d0e |
refactor: extract missing model refresh pipeline (#11751)
## Summary Extracts the missing-model pipeline orchestration out of `ComfyApp` and into an app-independent platform module, while tightening the workflow-flattening type boundary that refresh needs when rescanning the live LiteGraph graph. This PR is intentionally refactor-heavy. It is the follow-up to the earlier missing-model refresh work: instead of keeping refresh-specific candidate recheck logic beside the UI, this change makes the refresh path reuse the existing missing-model pipeline and removes the direct dependency on private `ComfyApp` pipeline methods. Linear: FE-499 Issues covered by this PR: - Fixes #11678 - Fixes #11680 - Partially addresses #11679 by removing the missing-model refresh path's unsafe `graph.serialize() as unknown as ComfyWorkflowJSON` cast and replacing it with the narrower flattenable workflow contract. Broader workflow serialization/type-boundary cleanup outside this missing-model refresh path remains deferred. ## Changes - **What**: - Added `src/platform/missingModel/missingModelPipeline.ts` as the orchestration module for missing-model detection/verification. - `runMissingModelPipeline(...)` now owns the pipeline previously embedded in `ComfyApp`: - candidate scan and enrichment - active ancestor filtering for muted/bypassed subgraph containers - pending warning cache updates - OSS folder path and file-size follow-up work - cloud asset verification follow-up work - surfaced missing-model errors via the existing execution error store - `refreshMissingModelPipeline(...)` handles the refresh-specific flow: - calls the injected `reloadNodeDefs()` first - serializes the current live graph - preserves model metadata by preferring active workflow `models`, then falling back to current missing-model candidate metadata - delegates back into the same pipeline used during workflow load - Kept `ComfyApp` as the compatibility caller instead of the owner of the pipeline. - `loadGraphData(...)` now calls `runMissingModelPipeline(...)` with `graph`, `graphData`, `missingNodeTypes`, and `silent` options. - `refreshMissingModels(...)` is now a thin wrapper around `refreshMissingModelPipeline(...)` and keeps the existing default `silent: true` refresh behavior. - The new pipeline module does not import `@/scripts/app`; app-owned data/actions are passed in as inputs. - Moved the workflow node-flattening helpers out of `workflowSchema.ts` and into `src/platform/workflow/core/utils/workflowFlattening.ts`. - This includes `flattenWorkflowNodes`, `buildSubgraphExecutionPaths`, and `isSubgraphDefinition`. - The move is intentional: these helpers are not zod schema definitions or workflow validation logic. They are core workflow traversal utilities used to flatten root workflow nodes plus nested subgraph definition nodes into the execution-shaped node list needed by missing-model scanning. - The refresh path receives data from `LGraph.serialize()`, whose return type is serialized LiteGraph data rather than validated `ComfyWorkflowJSON`. Previously this forced unsafe typing like `graph.serialize() as unknown as ComfyWorkflowJSON`. - The new `FlattenableWorkflowGraph` / `FlattenableWorkflowNode` structural contract describes only what flattening actually needs: `nodes`, `definitions.subgraphs`, node `id`, `type`, `mode`, `widgets_values`, and `properties`. - This lets both normal workflow-load data (`ComfyWorkflowJSON`) and refresh-time live graph serialization (`LGraph.serialize()`) flow into the same scan/enrichment path without pretending serialized LiteGraph output is a fully validated workflow schema document. - Updated `missingModelScan.ts` to consume that minimal flattenable workflow shape via `MissingModelWorkflowData`. - `MissingModelWorkflowData` extends the flattenable workflow contract with optional workflow-level `models` metadata. - Removed now-unnecessary casts around execution IDs, flattened nodes, and `widgets_values` object access. - Updated `getSelectedModelsMetadata(...)` to accept readonly widget value arrays so flattened workflow data can stay read-only. - Reduced the exported surface of the new pipeline module after `knip` flagged unused exported internal option/store interfaces. - Kept `workflowSchema.ts` focused on validation schemas. The flattening helpers are not re-exported from the schema module because they are internal workflow core utilities, not public schema API. - **Breaking**: None intended. - Internal imports were updated to the new core utility path. - This repo is not exposing these flattening helpers as a public package API, so the old schema-local helper location is treated as an internal implementation detail. - **Dependencies**: None. ## Review Focus - **Pipeline extraction / dependency direction**: - Please verify that `missingModelPipeline.ts` stays independent from `@/scripts/app`. - `ComfyApp` should remain the caller/adapter, not the owner of missing-model pipeline orchestration. - **Workflow flattening type boundary**: - The main type-cleanup goal is removing the refresh-time `graph.serialize() as unknown as ComfyWorkflowJSON` lie. - `LGraph.serialize()` and validated workflow JSON are not the same contract. The new flattenable workflow contract is deliberately smaller and structural because the missing-model enrichment path only needs enough data to flatten nodes and read embedded model metadata. - This is why the flattening helpers moved from `workflowSchema.ts` to `workflow/core/utils`: the logic is reusable workflow traversal, not validation schema. - **Behavior preservation**: - The PR is intended to preserve existing user-facing missing-model behavior while moving ownership out of `app.ts`. - Existing async follow-up behavior remains intentionally fire-and-forget: - cloud asset verification still surfaces after verification completes - OSS folder paths still update asynchronously before surfacing confirmed missing models - file-size metadata fetching remains asynchronous - More invasive behavior changes, such as adding non-cloud post-fetch `isMissingCandidateActive(...)` re-verification or redesigning the fire-and-forget result contract, are intentionally left for follow-up work because they are not pure extraction. - **Downloadable model metadata**: - `missingModels` returned for download metadata now requires both `url` and `directory`. - Candidates without a directory still remain in `confirmedCandidates`, but they are not exposed as downloadable model metadata. This keeps the returned downloadable list aligned with what the download flow can actually use. - **Test ownership**: - Complex missing-model pipeline behavior tests moved out of `src/scripts/app.test.ts` and into `src/platform/missingModel/missingModelPipeline.test.ts`. - `app.test.ts` now only covers thin delegation for `app.refreshMissingModels(...)`. - Workflow flattening tests moved with the helper from schema tests into `src/platform/workflow/core/utils/workflowFlattening.test.ts`. - **Deferred follow-ups**: - Broader function decomposition for cognitive complexity. - Wider dependency-injection/port cleanup for stores and services beyond the app boundary. - Cloud-specific pipeline unit tests, which need a separate `isCloud` mocking strategy. - Additional E2E coverage expansion beyond the existing OSS refresh path. - More general workflow serialization/type-boundary cleanup outside the missing-model refresh path. ## Validation - `pnpm format` - `pnpm lint` - Passed. Existing lint output included a pre-existing `no-misused-spread` warning and icon-name logs, but the command exited successfully. - `pnpm typecheck` - `pnpm test:unit` - `714 passed`, `9514 passed | 8 skipped` - Pre-push `pnpm knip` - Passed after reducing the exported surface of the new pipeline module. ## Screenshots (if applicable) Not applicable. This PR is a pipeline/type-boundary refactor with no UI changes. ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11751-refactor-extract-missing-model-refresh-pipeline-3516d73d3650816d9245d4b1324b71c9) by [Unito](https://www.unito.io) --------- Co-authored-by: DrJKL <DrJKL0424@gmail.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
9384beaec6 |
test: Add tests for bounding box widget (#11343)
## Summary Adds coverage for the bounding box widget ## Changes - **What**: - Validates user interactions and functionality on the widget ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11343-test-Add-tests-for-bounding-box-widget-3456d73d365081eb8d03f2220a837816) by [Unito](https://www.unito.io) --------- Co-authored-by: bymyself <cbyrne@comfy.org> Co-authored-by: GitHub Action <action@github.com> |
||
|
|
4a05d89fdb |
fix: detach DOM widget event listeners on widget removal (#11724)
## Summary Fixes leaked event listeners ## Changes - **What**: - update all listeners to use AbortController to signal removal on widget remove ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11724-fix-detach-DOM-widget-event-listeners-on-widget-removal-3506d73d3650811dae81c034c1098759) by [Unito](https://www.unito.io) --------- Co-authored-by: Alexander Brown <drjkl@comfy.org> |
||
|
|
ef98ba0e8f |
feat: add plum/ink color primitives and standardize design tokens (#11139)
*PR Created by the Glary-Bot Agent* --- ## Summary Adds new `plum` and `ink` color scales for Comfy Hub branding and standardizes existing tokens to align with current Figma design system. ### Changes **Phase 1 — New primitives** (`_palette.css`) - Added `plum-300/400/500/600` and `ink-100` through `ink-900` **Phase 2 — Token cleanup** (`style.css`) - Removed deprecated `slate-100/200/300` primitives (cool blue-grey, removed from Figma) - Removed duplicate `graphite-400` (identical hex to slate-100) - Dark mode: migrated 6 slate/graphite references to muted-foreground, smoke-700, smoke-800, charcoal-200 - Light mode: replaced 3 `ash-500` references with `smoke-800` per designer alignment ### Token migration detail | Dark mode token | Old value | New value | Rationale | |---|---|---|---| | `--node-component-header-icon` | slate-300 (#5b5e7d) | muted-foreground (smoke-800) | Figma `node/foreground-secondary` | | `--node-component-slot-text` | slate-200 (#9fa2bd) | smoke-700 (#a0a0a0) | Lighter neutral for text contrast | | `--node-component-surface-highlight` | slate-100 (#9c9eab) | smoke-800 (#8a8a8a) | Neutral grey highlight | | `--node-component-tooltip-border` | slate-300 (#5b5e7d) | charcoal-200 (#494a50) | Consistent with dark border tokens | | `--text-secondary` | slate-100 (#9c9eab) | smoke-700 (#a0a0a0) | Adequate contrast on dark surfaces | | `--widget-background-highlighted` | graphite-400 (#9c9eab) | smoke-800 (#8a8a8a) | Removed duplicate, neutral replacement | ### Visual note These changes shift some dark mode colors from cool blue-grey to neutral grey. This is intentional per the design team. The `--node-component-surface-highlight` and `--node-component-tooltip-border` tokens should be QA'd as the designer noted. ### Not included (Phase 3) Hub Dark overlay theme will ship separately once the Hub UI work is ready to validate against. ## Screenshots   ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11139-feat-add-plum-ink-color-primitives-and-standardize-design-tokens-33e6d73d365081418e13e0efe6161fb5) 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> Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
019c1787a5 |
[chore] Update Comfy Registry API types from comfy-api@e993818 (#11783)
## Automated API Type Update This PR updates the Comfy Registry API types from the latest comfy-api OpenAPI specification. - API commit: e993818 - Generated on: 2026-04-30T17:57:05Z These types are automatically generated using openapi-typescript. ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11783-chore-Update-Comfy-Registry-API-types-from-comfy-api-e993818-3526d73d3650810e9e14f55da44714b2) by [Unito](https://www.unito.io) Co-authored-by: coderfromthenorth93 <213232275+coderfromthenorth93@users.noreply.github.com> |
||
|
|
87fab87d84 |
docs: add reviewing-unit-tests skill (#11777)
## Summary Adds `.claude/skills/reviewing-unit-tests/SKILL.md`, a project-scoped agent skill that gives reviewing agents a concrete rubric for evaluating Vitest unit-test diffs. ## Changes - **What**: New skill at `.claude/skills/reviewing-unit-tests/SKILL.md`. Mirrors the structural template of the peer skill `.claude/skills/writing-playwright-tests/SKILL.md` (frontmatter shape, table-driven sections, terse prescriptive prose). Description triggers on PR review of `*.test.ts` files. Codifies: alias-by-renaming detection, `vi.mocked()` scope rule, unnecessary-cast detection, divergence checks against `docs/testing/unit-testing.md` / `store-testing.md` / `component-testing.md` / `vitest-patterns.md`, redundant `mockClear()` flag, `vue-i18n`-must-not-be-mocked rule (with `testI18n` reference), bugfix regression-validity check, leading-close framing flag, testing-library requirement for new component tests. ## Review Focus - **Retrospective validation against PR #11737** (head SHA `4573d62450fd12ac6f06e5e491f8af84ccbd27de`, file `src/platform/assets/composables/useMediaAssetActions.test.ts`). Mentally running the skill's checklist against that diff flags: 1. ✅ `getToastAddMock` / `getI18nTMock` module-level helpers — caught by the "Renaming ≠ Restructuring" section + Mocking Smells row "Module-level helper functions wrapping mocked composable returns". 2. ✅ `as ReturnType<typeof vi.fn>` casts in those helpers — caught by the "`vi.mocked()` Scope" section + Mocking Smells row on stray casts. 3. ✅ `vi.mock('vue-i18n', ...)` — caught by Mocking Smells row directing to mount real `createI18n` per `docs/testing/vitest-patterns.md` and the shared `testI18n` in `src/components/searchbox/v2/__test__/testUtils.ts`. 4. ✅ `vi.mock('primevue/usetoast', ...)` — explicitly **not** flagged. The "Distinguish" paragraph after the Mocking Smells table marks trivially-shaped third-party hooks as acceptable to mock; "Don't Mock What You Don't Own" applies to behavior-rich APIs, not single-method composables. - **Reference style**: links to `docs/testing/*.md` and `docs/guidance/typescript.md` instead of restating their content, per the FE-511 acceptance criterion. - **Open question**: scope. The repo-local skill is the FE-511 deliverable; equivalent prescriptive rules already live in the user's global `~/.config/amp/AGENTS.md` "Code Review Rigor" section as a separate artifact. No duplication intended — the skill is the project-scoped surface a reviewing agent loads when entering this repo. Closes FE-511 ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11777-docs-add-reviewing-unit-tests-skill-3526d73d365081848759e6a8fab942f0) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
f2a99adaa3 |
docs: prefer real createI18n over mocking vue-i18n in tests (#11776)
## Summary Document that `vue-i18n` should not be mocked in tests — mount with a real `createI18n` plugin instance instead. ## Changes - **What**: Expanded `docs/testing/vitest-patterns.md` "Don't Mock `vue-i18n`" section with a concrete code example (covering both component and composable tests), guidance for asserting on translation keys with empty messages, and a real-example link to `src/components/searchbox/v2/__test__/testUtils.ts`. Added a callout at the top of `docs/testing/unit-testing.md` "Mocking Composables with Reactive State" cross-linking the new section, since that section applies to *owned* composables. ## Review Focus - The previous `vitest-patterns.md` paragraph pointed at a non-existent `SearchBox.test.ts`; the new link points to the actual shared `testI18n` helper. - The "Mocking Composables with Reactive State" pattern should not be applied to third-party composables like `useI18n` — the callout makes that explicit. Surfaced during review of #11737, where the test file mocked `vue-i18n` and then accumulated structural workarounds (hoisted aliases, helper functions, type casts) to interact with the mocked `t`. A real `createI18n` instance avoids the entire ceremony. ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11776-docs-prefer-real-createI18n-over-mocking-vue-i18n-in-tests-3526d73d365081d4bc39fbf3c2502e49) by [Unito](https://www.unito.io) Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
a934056246 |
feat(website): add PostHog analytics (#11735)
## Summary Adds PostHog page analytics to the marketing website (`apps/website/`). ## Changes - **What**: New `posthog.ts` script with `initPostHog`/`capturePageview`. Wired into `BaseLayout.astro` behind `import.meta.env.PROD` (mirroring the GTM gate). Pageviews are captured on every `astro:page-load` so ClientRouter view-transition navigations are tracked, not just hard reloads. - **Dependencies**: `posthog-js` (already in the workspace catalog at `^1.358.1`; previously used by the workbench telemetry provider). ## Review Focus - API host is set to `https://t.comfy.org` to match `src/platform/telemetry/providers/cloud/PostHogTelemetryProvider.ts` — confirm that proxy is OK to share with the website surface. - Project token is hard-coded inline. It is a public `phc_…` frontend token (designed to ship to clients); this matches the pattern used for `gtmId` in the same file. Happy to switch to a `PUBLIC_POSTHOG_KEY` env var if preferred. - `person_profiles: 'identified_only'` to avoid creating profiles for every anonymous visitor — flag if you want full anonymous tracking instead. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11735-feat-website-add-PostHog-analytics-3516d73d3650811189c6d64c3af4ded9) by [Unito](https://www.unito.io) |
||
|
|
b8dfbfc0bb |
fix: ensure escape key/graph navigation cancels ghost node placement (#11779)
## Summary When inside a subgraph, the parent key handler intercepts the escape key before `processKey` is called, causing a stale ghost node to be added to the inner subgraph when the graph is changed to the parent. ## Changes - **What**: - move escape key handler to document, prevent propagating the event - add saftey net cancel in setGraph - tests ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11779-fix-ensure-escape-key-graph-navigation-cancels-ghost-node-placement-3526d73d3650812292e4ca10d384f783) by [Unito](https://www.unito.io) |
||
|
|
036c79259b |
1.44.14 (#11769)
Patch version increment to 1.44.14 **Base branch:** `main` ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11769-1-44-14-3526d73d3650816ba3a9cd87afeef035) by [Unito](https://www.unito.io) --------- Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> Co-authored-by: github-actions <github-actions@github.com>v1.44.14 |
||
|
|
17c18b0707 |
fix: embed HubSpot contact form (#11723)
## Summary Embed the HubSpot-hosted contact sales form on `/contact` and `/zh-CN/contact` so HubSpot owns submission handling, validation, anti-spam/security updates, tracking context, and form configuration. ## Changes - **What**: Replaces the custom local contact form stub with a HubSpot embed component using the HubSpot-hosted developer/raw HTML script and `hs-form-html` container. - **Localization**: Uses the existing English form ID by default and switches to the zh-CN form ID for `/zh-CN/contact`. - **Styling**: Applies HubSpot form CSS custom properties to match the Comfy contact page colors and `PP Formula` font more closely. - **Docs**: Updates the website README with the developer embed snippet and the zh-CN form ID. - **Dependencies**: None. ## Why Embedded Form - HubSpot docs say forms should be loaded with the HubSpot-hosted JavaScript file, and that security, anti-spam, accessibility, and performance improvements will not propagate if the embed runtime is copied or self-hosted: https://developers.hubspot.com/docs/cms/start-building/building-blocks/modules/forms - The direct form submission endpoint is documented under `v3 legacy`: https://developers.hubspot.com/docs/api-reference/legacy/marketing/forms/v3-legacy/submit-data-unauthenticated - HubSpot’s legacy API overview says numeric-versioned APIs are legacy and will be deprecated in a future release: https://developers.hubspot.com/docs/api-reference/legacy/overview ## Review Focus - Confirm the portal ID and form IDs are correct: - `en`: `94e05eab-1373-47f7-ab5e-d84f9e6aa262` - `zh-CN`: `6885750c-02ef-4aa2-ba0d-213be9cccf93` - Check visual fit on `/contact` and `/zh-CN/contact`, especially font, background, inputs, radio controls, and submit button. - Confirm the developer/raw HTML embed remains the preferred integration over a custom Forms API POST. ## Local Checks - `pnpm exec oxfmt --check apps/website/src/components/contact/HubspotFormEmbed.vue apps/website/README.md` - `pnpm exec eslint apps/website/src/components/contact/HubspotFormEmbed.vue` - `pnpm --filter @comfyorg/website typecheck` - `pnpm --filter @comfyorg/website test:unit` - `pnpm --filter @comfyorg/website build` - Commit hooks: stylelint, oxfmt, oxlint, eslint, `pnpm typecheck`, `pnpm typecheck:website` - Push hook: `pnpm knip --cache` Build completed with existing non-fatal environment warnings: Node `v25.8.1` vs requested `24.x`, unresolved website font paths deferred to runtime, `transformWithEsbuild` deprecation, and missing Ashby env falling back to the committed snapshot. Incredibly, during the taking of these screenshots, I discovered a bug in macos, where despite the snapshot/record bar not existing, from me esc'ing out, some of the tooltips persist. Closing and reopening the lid did not fix this. I didn't see the process in activity monitor. <img width="1512" height="862" alt="Screenshot 2026-04-29 at 7 09 55 PM" src="https://github.com/user-attachments/assets/92518795-6845-4b34-8da3-54048b440eb1" /> <img width="1512" height="862" alt="Screenshot 2026-04-29 at 7 13 18 PM" src="https://github.com/user-attachments/assets/f7609e58-898d-413c-975c-b02b70b84e73" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11723-fix-embed-HubSpot-contact-form-3506d73d365081528bfbe4b024c2a21f) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com> |
||
|
|
ca8407218b |
chore(husky): skip pre-push knip hook in CI (#11772)
*PR Created by the Glary-Bot Agent* --- ## Summary Fixes a false-positive knip failure in the `i18n: Update Core` workflow that's been blocking version-bump PRs (e.g. #11769, run [25141091787](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/25141091787/job/73690747401)). ## Root cause 1. The `update-locales` job uses `setup-comfyui-server`, which copies `tools/devtools/*` → `ComfyUI/custom_nodes/ComfyUI_devtools/*` at the workspace root. 2. The job's final `git push` triggers the husky `.husky/pre-push` hook, which runs `pnpm knip`. 3. Knip's `project` glob (`**/*.{js,ts,vue}` in `knip.config.ts`) sweeps the runtime `ComfyUI/` directory; the existing `tools/devtools/web/**` ignore doesn't cover the copied path, so `ComfyUI/custom_nodes/ComfyUI_devtools/web/legacyWidget.js` (added in #11574) is reported as unused → exit 1 → push aborted. ## Why fix the hook (not knip config or workflows) - **`knip.config.ts` ignore** (`'ComfyUI/**'`): tried first; `treatConfigHintsAsErrors: true` makes the unused-pattern hint fatal in normal CI runs where `ComfyUI/` doesn't exist, breaking `ci-lint-format` everywhere. - **`HUSKY=0` per workflow step**: works, but bots can't push workflow file changes without `workflows` scope, and it would need to be repeated on every CI workflow that pushes. - **Skip the hook in CI**: the canonical `pnpm knip` check runs in `.github/actions/lint-format-verify/action.yml` on every PR, so the pre-push duplicate is pure dev-side guardrail. Gating it on `$CI` (set to `true` by all GitHub Actions runners) cleanly removes it from every CI push without affecting local developer workflow. ## Verification Reproduced the failure locally by mirroring the CI sequence (`cp -r tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/` then `pnpm knip:no-cache`): - Without fix: `[log] Unused files (1) ComfyUI/custom_nodes/ComfyUI_devtools/web/legacyWidget.js` → exit 1 (matches CI log) - With `CI=true`, the hook short-circuits at `exit 0` before invoking knip - `pnpm knip` on a clean checkout (no `ComfyUI/`) still passes ┆Issue is synchronized with this [Notion page](https://app.notion.com/p/PR-11772-chore-husky-skip-pre-push-knip-hook-in-CI-3526d73d3650819f9a01d549d122b69c) by [Unito](https://www.unito.io) Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com> |
||
|
|
26ac1eece1 |
Allow website screenshot workflow to remove its label (#11725)
## Summary Allows the website screenshot update workflow to remove its own trigger label when a label-triggered run completes. ## Changes - **What**: Grants the screenshot update job `issues: write`, which is required for `issues.removeLabel`, and keeps the cleanup scoped to `Update Website Screenshots`. - **Dependencies**: None. ## Review Focus Confirm the workflow permission scope is appropriate and that unexpected label cleanup failures should fail the workflow instead of being silently swallowed. ## Validation - `/Users/ben/go/bin/actionlint .github/workflows/pr-update-website-screenshots.yaml` - `/Users/ben/Library/Python/3.9/bin/yamllint --config-file .yamllint .github/workflows/pr-update-website-screenshots.yaml` - `git diff --check HEAD~1 HEAD` No browser e2e regression was added because this change only adjusts GitHub Actions token permissions and label cleanup behavior; it does not change shipped app/runtime behavior. Fixes FE-487 |
||
|
|
810381ab63 |
Stabilize website GitHub stars in visual snapshots (#11771)
## Summary Stabilize the website nav GitHub star count in visual-test builds so snapshot comparisons do not drift as the live GitHub count changes. ## Changes - **What**: Added `WEBSITE_GITHUB_STARS_OVERRIDE` for build-time star-count overrides and set it to `111000` in the website E2E and screenshot-update workflows. - **Dependencies**: None. ## Review Focus Confirm the deterministic build-time override is preferable to screenshot masking, since Playwright masks draw a colored rectangle whose geometry can also drift when masked content changes size. ## Screenshots (if applicable) Not included; this keeps visual-test input data stable rather than changing the UI. |
||
|
|
cc1a737291 |
test: add unit tests for useImagePreviewWidget (#11394)
## Summary Add 23 unit tests for `useImagePreviewWidget` composable, improving coverage from ~52% to significantly higher. ## Test Coverage - Widget construction (factory return, name, type, serialization, options) - `computeLayoutSize` returns correct dimensions - `createCopyForNode` creates properly bound copy - Upload spinner rendering (animation, stroke color) - Single image rendering (draws image, handles missing node.size) - Image size text overlay (enabled/disabled via setting) - Multi-image thumbnail grid (compact vs non-compact mode, cell borders) - Pointer interaction (pointerDown cleanup, overIndex reset) - `previewImages` override from DrawWidgetOptions - `onPointerDown` drag handler setup - `onClick` no-op behavior ## Testing ```bash pnpm test:unit -- src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.test.ts ``` All 23 tests pass. No source changes. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11394-test-add-unit-tests-for-useImagePreviewWidget-3476d73d365081a69c78e87329e75d9f) by [Unito](https://www.unito.io) --------- Co-authored-by: Alexander Brown <drjkl@comfy.org> Co-authored-by: DrJKL <DrJKL0424@gmail.com> Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
c74e08e244 |
refactor: extract useBrushAdjustment from useBrushDrawing (#11544)
## Summary
Part of the `useBrushDrawing` decomposition plan (PR C). Extracts brush
size/hardness adjustment logic (Alt+drag interaction) into a dedicated
`useBrushAdjustment` composable. No runtime behavior is changed — pure
structural refactor.
## Changes
- **New** `src/composables/maskeditor/useBrushAdjustment.ts` —
encapsulates `startBrushAdjustment` and `handleBrushAdjustment`,
including dead zone filtering, dominant axis suppression, and
size/hardness clamping
- **New** `src/composables/maskeditor/useBrushAdjustment.test.ts` — unit
tests covering: no-op before start, dead zone suppression, size increase
on drag, size/hardness clamping, dominant axis lock
- **Updated** `src/composables/maskeditor/useBrushDrawing.ts` — removes
inlined adjustment state and functions, delegates to
`useBrushAdjustment(initialSettings)`
## Test Functionality
Open ComfyUI and enter the MaskEditor of any image node. On the canvas,
Alt + Right-click Drag:
- Drag Right → Increase brush size - pass
- Drag Left → Decrease brush size - pass
- Drag Up → Increase hardness - pass
- Drag Down → Decrease hardness - pass
https://github.com/user-attachments/assets/273e8383-dab5-4c82-ac7b-0a1534dfd770
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches core pointer-interaction logic for brush tuning and changes
adjustment behavior (removes delta saturation and uses initial values),
which could subtly affect UX even though scope is localized to the mask
editor.
>
> **Overview**
> Extracts the Alt-drag brush size/hardness adjustment logic out of
`useBrushDrawing` into a new `useBrushAdjustment` composable, and wires
`useBrushDrawing` to delegate to it.
>
> The extracted logic now bases adjustments off the captured initial
brush size/hardness and removes prior delta capping (no ±100px
saturation), which changes how large/continuous drags affect the final
values. Adds a Vitest suite covering dead-zone behavior, dominant-axis
suppression, clamping, and the no-op-before-start contract.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
|
||
|
|
8f9f452c86 |
fix: enable Chrome password autofill on signup form (#11636)
## Summary Add `name` attributes to the signup form's email, password, and confirm-password inputs so Chrome's password manager recognizes the form and offers autofill/save. ## Changes - **What**: Pass `name` through to the underlying `<input>` on the email field (via `pt:root:name`) and on both password fields (via `pt:pc-input-text:root:name`). Without `name`, Chrome can't pair the email with the password and won't surface the save-password / suggest-strong-password prompts. ## Review Focus - The PrimeVue passthrough syntax (`pt:root:*` for `InputText`, `pt:pc-input-text:root:*` for `Password`) lands the attribute on the actual `<input>` element — verified in DevTools. - `confirm-password` is not a standard `autocomplete` token; we keep `autocomplete="new-password"` on both password fields and only differentiate via `name`. ## Screenshots (if applicable) https://github.com/user-attachments/assets/e1e25ab5-8496-4c84-b5f1-630f31956c80 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-11636-fix-enable-Chrome-password-autofill-on-signup-form-34e6d73d36508180882cc9ebafb58417) by [Unito](https://www.unito.io) |