Skip to content

feat: add Wave 3 homepage sections (11 Vue components) [3/3]#10142

Open
christian-byrne wants to merge 3 commits intowebsite/02-layout-shellfrom
website/03-homepage-sections
Open

feat: add Wave 3 homepage sections (11 Vue components) [3/3]#10142
christian-byrne wants to merge 3 commits intowebsite/02-layout-shellfrom
website/03-homepage-sections

Conversation

@christian-byrne
Copy link
Contributor

@christian-byrne christian-byrne commented Mar 17, 2026

Summary

Adds all 11 homepage section components for the comfy.org marketing site.

Changes (incremental from #10141)

  • HeroSection.vue: C monogram left, headline right, CTAs
  • SocialProofBar.vue: 12 enterprise logos + metrics
  • ProductShowcase.vue: PLACEHOLDER workflow demo
  • ValuePillars.vue: Build/Customize/Refine/Automate/Run
  • UseCaseSection.vue: PLACEHOLDER industries
  • CaseStudySpotlight.vue: PLACEHOLDER bento grid
  • TestimonialsSection.vue: Filterable by industry
  • GetStartedSection.vue: 3-step flow
  • CTASection.vue: Desktop/Cloud/API cards
  • ManifestoSection.vue: Method Not Magic
  • AcademySection.vue: Learning paths CTA
  • Updated index.astro + zh-CN/index.astro

Stack (via Graphite)

┆Issue is synchronized with this Notion page by Unito

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 009b7e3c-fa90-455e-a88b-325fc3af762a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch website/03-homepage-sections
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor Author

christian-byrne commented Mar 17, 2026

@github-actions
Copy link

github-actions bot commented Mar 17, 2026

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 03/17/2026, 02:26:13 PM UTC

Links

@github-actions
Copy link

github-actions bot commented Mar 17, 2026

🎭 Playwright: ❌ 590 passed, 1 failed · 6 flaky

❌ Failed Tests

📊 Browser Reports
  • chromium: View Report (✅ 577 / ❌ 1 / ⚠️ 6 / ⏭️ 10)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 10 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

@christian-byrne christian-byrne changed the title fix: hide template selector after shared workflow accept (#9913) feat: add Wave 3 homepage sections (11 Vue components) [3/3] Mar 17, 2026
@christian-byrne christian-byrne marked this pull request as ready for review March 17, 2026 06:37
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Mar 17, 2026
@christian-byrne christian-byrne force-pushed the website/02-layout-shell branch from d56da59 to 49cb6fc Compare March 17, 2026 06:40
@christian-byrne christian-byrne force-pushed the website/03-homepage-sections branch from 7340838 to 04cf78b Compare March 17, 2026 06:40
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:XXL This PR changes 1000+ lines, ignoring generated files. labels Mar 17, 2026
@christian-byrne christian-byrne force-pushed the website/02-layout-shell branch from 49cb6fc to 336c0af Compare March 17, 2026 06:47
@christian-byrne christian-byrne force-pushed the website/03-homepage-sections branch 2 times, most recently from a3c47ad to 939e02b Compare March 17, 2026 07:07
@christian-byrne christian-byrne force-pushed the website/02-layout-shell branch 2 times, most recently from 9279f6f to 2802e91 Compare March 17, 2026 07:16
@christian-byrne christian-byrne force-pushed the website/03-homepage-sections branch from 939e02b to 50d5377 Compare March 17, 2026 07:16
@christian-byrne christian-byrne requested review from jaeone94 and removed request for jaeone94 March 17, 2026 09:53
Copy link
Collaborator

@jaeone94 jaeone94 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR only adds new section components and page files — all placeholder content that will be replaced when the actual design is implemented. No meaningful code review issues to flag. (There are some minor findings around accessibility, i18n, and component patterns, but these will naturally be addressed during the design implementation pass.)

The substantive issues live in #10140 and #10141, which handle the infrastructure/config layer: assetsPrefix strategy mismatch with the TDD, astro:after-swap event listener leak in SiteNav, font files 404 (apps/website/public/fonts/ is empty), and missing security headers. Those PRs would benefit from a closer review.

Hide the template selector when a first-time cloud user accepts a shared
workflow from a share link, so the shared workflow opens without the
onboarding template dialog lingering.

- **What**: Added shared-workflow loader behavior to close the global
template selector on accept actions (`copy-and-open` and `open-only`)
while keeping cancel behavior unchanged.
- **What**: Added targeted unit tests covering hide-on-accept and
no-hide-on-cancel behavior in the shared workflow URL loader.

Confirm that share-link accept paths now dismiss the template selector
and that cancel still leaves it available.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9913-fix-hide-template-selector-after-shared-workflow-accept-3236d73d365081099c04e350d499fad2)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>

fix: restore native copy/paste events for image paste support (#9914)

- Remove Ctrl+C and Ctrl+V keybindings from the keybinding service
defaults so native browser copy/paste events fire
- This restores image paste into LoadImage nodes, which broke after

PR #9459 moved Ctrl+C/V into the keybinding service, which calls
`event.preventDefault()` on keydown. This prevents the browser `paste`
event from firing, so `usePaste` (which detects images in the clipboard)
never runs. The `PasteFromClipboard` command only reads from
localStorage, completely bypassing image detection.

**Repro:** Copy a node → copy an image externally → try to paste the
image into a LoadImage node → gets old node data from localStorage
instead.

Remove Ctrl+C and Ctrl+V from `CORE_KEYBINDINGS` in `defaults.ts`. The
native browser events now fire as before, and `useCopy`/`usePaste`
handle them correctly. Ctrl+Shift+V, Ctrl+A, Delete, and Backspace
keybindings remain in the keybinding service.

Fixes #9459 (regression)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9914-fix-restore-native-copy-paste-events-for-image-paste-support-3236d73d365081c7ac53f983f316e10f)
by [Unito](https://www.unito.io)

fix: clear stale widget slotMetadata on link disconnect (#9885)

Fixes text field becoming non-editable when a previously linked input is
removed from a custom node.

When a widget's input was promoted to a slot, connected via a link, and
then the input was removed (e.g., by updating the custom node
definition), the widget retained stale `slotMetadata` with `linked:
true`. This prevented the widget from being editable.

In `refreshNodeSlots`, removed the `if (slotInfo)` guard so
`widget.slotMetadata` is always assigned — either to valid metadata or
`undefined`. This ensures stale linked state is cleared when inputs no
longer match widgets.

1. Text field remains editable after promote→connect→disconnect cycle
2. Text field returns to editable state when noodle disconnected
3. No mode switching needed to restore editability

- Added regression test: "clears stale slotMetadata when input no longer
matches widget"
- All existing tests pass (18/18 in affected file)

---
**Note: This PR currently contains only the RED (failing test) commit
for TDD verification. The GREEN (fix) commit will be pushed after CI
confirms the test failure.**

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9885-fix-clear-stale-widget-slotMetadata-on-link-disconnect-3226d73d365081269319c027b42d9f6b)
by [Unito](https://www.unito.io)

fix: stabilize subgraph promoted widget identity and rendering (#9896)

Fix subgraph promoted widget identity/rendering so on-node widgets stay
correct through configure/hydration churn, duplicate names, and
linked+independent coexistence.

- **Subgraph promotion reconciliation**: stabilize linked-entry identity
by subgraph slot id, preserve deterministic linked representative
selection, and prune stale alias/fallback entries without dropping
legitimate independent promotions.
- **Promoted view resolution**: bind slot mapping by promoted view
object identity (`getSlotFromWidget` / `getWidgetFromSlot`) to avoid
same-name collisions.
- **On-node widget rendering**: harden `NodeWidgets` identity and dedup
to avoid visual aliasing, prefer visible duplicates over hidden stale
entries, include type/source execution identity, and avoid collapsing
transient unresolved entries.
- **Mapping correctness**: update `useGraphNodeManager` promoted source
mapping to resolve by input target only when the promoted view is
actually bound to that input.
- **Subgraph input uniqueness**: ensure empty-slot promotion creates
unique input names (`seed`, `seed_1`, etc.) for same-name multi-source
promotions.
- **Safety fix**: guard against undefined canvas in slot-link
interaction.
- **Tests/fixtures**: add focused regressions for fixture path
`subgraph_complex_promotion_1`, linked+independent same-name cases,
duplicate-name identity mapping, dedup behavior, and input-name
uniqueness.

Validate behavior around transient configure/hydration states (`-1` id
to concrete id), duplicate-name promotions, linked representative
recovery, and that dedup never hides legitimate widgets while still
removing true duplicates.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9896-fix-stabilize-subgraph-promoted-widget-identity-and-rendering-3226d73d365081c8a1e8d0a5a22e826d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>

1.42.5 (#9906)

Patch version increment to 1.42.5

**Base branch:** `main`

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>

fix: skip redundant appScalePercentage updates during zoom/pan (#9403)

Add equality check before updating `appScalePercentage` reactive ref.

Firefox profiler shows 586 `setElementText` markers from continuous text
interpolation updates during zoom/pan. The rounded percentage value
often doesn't change between events.

Extract `updateAppScalePercentage()` helper with equality guard —
compares new rounded value to current before assigning to the ref.

Expected: eliminates ~90% of `setElementText` markers during zoom/pan

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9403-fix-skip-redundant-appScalePercentage-updates-during-zoom-pan-31a6d73d3650812db8f2d68ac73c95b0)
by [Unito](https://www.unito.io)

test: add browser test for textarea right-click context menu in subgraph (#9891)

Add E2E test coverage for the textarea widget right-click context menu
inside subgraphs.

The fix was shipped in #9840 — this PR adds the missing browser test.

- Loads a subgraph workflow with a CLIPTextEncode (textarea) node
- Navigates into the subgraph
- Right-clicks the textarea DOM element
- Asserts that the ComfyUI "Promote Widget" context menu option appears

- Fixes the test gap from #9840
- Notion ticket: d7a53160-e1e1-42bb-a5ac-c0c2702c629c

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9891-test-add-browser-test-for-textarea-right-click-context-menu-in-subgraph-3226d73d365081a4be51f89b5d505361)
by [Unito](https://www.unito.io)

feat: expand CDP perf metrics — add DOM nodes, script duration, event listeners (#9887)

Expands the performance testing infrastructure to collect 4 additional
CDP metrics that are already returned by `Performance.getMetrics` but
were not being read. This is a zero-cost expansion — no additional CDP
calls, just reading more fields from the existing response.

| Metric | CDP Source | What It Detects |
|---|---|---|
| `domNodes` | `Nodes` | DOM node count delta — widget DOM leaks during
node create/destroy |
| `jsHeapTotalBytes` | `JSHeapTotalSize` | Total heap delta — combined
with `heapDeltaBytes` shows GC pressure |
| `scriptDurationMs` | `ScriptDuration` | JS execution time vs total
task time — script vs rendering balance |
| `eventListeners` | `JSEventListeners` | Listener count delta — detects
listener accumulation across lifecycle |

- Added 4 fields to `PerfSnapshot` interface
- Added 4 fields to `PerfMeasurement` interface
- Wired through `getSnapshot()` and `stopMeasuring()`

- Added 4 fields to `PerfMeasurement` interface
- Expanded `MetricKey` type and `REPORTED_METRICS` array with 3 new
reported metrics (`domNodes`, `scriptDurationMs`, `eventListeners`)
- `jsHeapTotalBytes` is collected but not in `REPORTED_METRICS` — it's
used alongside `heapDeltaBytes` for GC pressure ratio analysis

From a gap analysis of all ~30 CDP metrics, these were identified as
highest priority for ComfyUI:
- **`Nodes`** (P0): ComfyUI dynamically creates/destroys widget DOM. DOM
bloat from leaked widgets is a key performance risk, especially for Vue
Nodes 2.0.
- **`ScriptDuration`** (P1): Separates JS execution from layout/paint.
Reveals whether perf issues are script-heavy or rendering-heavy.
- **`JSEventListeners`** (P1): Widget lifecycle can leak listeners
across node add/remove cycles.
- **`JSHeapTotalSize`** (P1): With `JSHeapUsedSize`, the ratio shows GC
fragmentation pressure.

The `PerfMeasurement` interface is extended (not changed). Old baseline
`perf-metrics.json` files without these fields will have `undefined`
values, which the report script handles gracefully (shows `—` for
missing data).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9887-feat-expand-CDP-perf-metrics-add-DOM-nodes-script-duration-event-listeners-3226d73d3650818abea1d4a441667c38)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>

fix: prevent white flash when opening mask editor (#9860)

- Remove hardcoded `bg-white` from mask editor canvas background div to
prevent white flash on dialog open
- Add a loading spinner while the mask editor initializes (image
loading, canvas setup, GPU resources)
- Background color is set dynamically by `setCanvasBackground()` after
initialization

Fixes #9852

https://github.com/user-attachments/assets/7da61e32-671b-4056-b5ec-8cb246fc7689

https://github.com/user-attachments/assets/bfdedc69-f690-42c5-8591-619623c04f55

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9860-fix-prevent-white-flash-when-opening-mask-editor-3226d73d365081de9b7ad4622438e6ed)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

fix: prevent live preview dimension flicker between frames (#9937)

Fix "Calculating dimensions" text flickering during live sampling
preview in Vue renderer.

- **What**: Stop resetting `actualDimensions` to `null` on every
`imageUrl` change. Previous dimensions are retained while the new frame
loads, eliminating the flicker. Error state is still reset correctly.

The watcher on `props.imageUrl` previously reset both `actualDimensions`
and `imageError`. Now it only resets `imageError`, since
`handleImageLoad` updates dimensions when the new frame actually loads.
This means stale dimensions show briefly between frames, which is
intentionally better than showing "Calculating dimensions" text.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9937-fix-prevent-live-preview-dimension-flicker-between-frames-3246d73d36508154a676e5996112354f)
by [Unito](https://www.unito.io)

feat: make Vue nodes (Nodes 2.0) default for new desktop installs (#9947)

Makes Vue nodes (Nodes 2.0) the default renderer for new desktop app
installs (version ≥1.41.0), matching the behavior already live for cloud
new installs.

Step 2 of the Nodes 2.0 rollout sequence:
1. ✅ Cloud new installs (≥1.41.0) — DONE
2. 👉 **Desktop app (new installs)** — this PR
3. ⬜ Local installs
4. ⬜ Remove Beta tag
5. ⬜ GTM announcement

No forced migration — only changes the default for new installs.
Existing users keep their setting. Rollback is a settings flip.

In `coreSettings.ts`, the `defaultsByInstallVersion` for
`Comfy.VueNodes.Enabled` changes from:
```typescript
defaultsByInstallVersion: { '1.41.0': isCloud },
```
to:
```typescript
defaultsByInstallVersion: { '1.41.0': isCloud || isDesktop },
```

- M2 perf target (≥52 FPS on 245-node workflow) — layer merge landed,
likely met
- M-DevRel migration docs (blocks Beta tag removal, not this flip)

Draft PR — ceremonial, to be merged when M2 checkpoint passes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9947-feat-make-Vue-nodes-Nodes-2-0-default-for-new-desktop-installs-3246d73d365081b280dfff932c7aa016)
by [Unito](https://www.unito.io)

fix: fix perf CI pipeline — z-score baselines, force-push staleness, baseline storage (#9886)

Fixes three critical issues with the CI performance reporting pipeline
that made perf reports useless on PRs (demonstrated by PR #9248 — deep
watcher removal merged without useful perf signal).

**Root cause:** PR #9305 added z-score statistical analysis code to
`perf-report.ts`, but the historical data download step was placed in
the wrong workflow file. The report is generated in
`pr-perf-report.yaml` (a `workflow_run`-triggered job), but the
historical download was in `ci-perf-report.yaml` (the test runner) —
different runners, different filesystems.

**Fix:** Implement `perf-data` orphan branch storage:
- On push to main: save `perf-metrics.json` to `perf-data` branch with
timestamped filename
- On PR report: fetch last 5 baselines from `perf-data` branch into
`temp/perf-history/`
- Rolling window of 20 baselines, oldest pruned automatically
- Same pattern used by `github-action-benchmark` (33.7k repos)

**Root cause:** `cancel-in-progress: true` kills the perf test run
before it uploads artifacts. The downstream report workflow only
triggers on `conclusion == 'success'` — cancelled runs are ignored, so
the comment from the first successful run goes stale.

**Fix:**
- Change `cancel-in-progress: false` — with GitHub's queue depth of 1,
rapid pushes (A,B,C,D) run A and D, skipping B and C
- Add SHA validation in `pr-perf-report.yaml` — before posting, check if
the workflow_run's head SHA still matches the PR's current head. Skip
posting stale results.

- `contents: write` on CI job (needed for pushing to perf-data branch)
- `actions: read` on both workflows (needed for artifact/baseline
access)

After merging, create the `perf-data` orphan branch:
```bash
git checkout --orphan perf-data
git rm -rf .
echo '# Performance Baselines' > README.md
mkdir -p baselines
git add README.md baselines
git commit -m 'Initialize perf-data branch'
git push origin perf-data
```

The first 2 pushes to main after setup will build up variance data, and
z-scores will start appearing in PR reports (threshold is
`historical.length >= 2`).

- YAML validated with `yaml.safe_load()`
- `perf-report.ts` `loadHistoricalReports()` already reads from
`temp/perf-history/<index>/perf-metrics.json` — no code changes needed
- All new steps use `continue-on-error: true` for graceful degradation

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9886-fix-fix-perf-CI-pipeline-z-score-baselines-force-push-staleness-baseline-storage-3226d73d365081538424c7945e71f308)
by [Unito](https://www.unito.io)

draft: add red-green-fix skill for verified bug fix workflow (#9954)

- Add a Claude Code skill (`/red-green-fix`) that enforces the red-green
commit pattern for bug fixes
- Ensures a failing test is committed first (red CI), then the fix is
committed separately (green CI)
- Gives reviewers proof that the test actually catches the bug
- Includes `reference/testing-anti-patterns.md` with common mistakes
contextualized to this codebase

```
.claude/skills/red-green-fix/
├── SKILL.md                              # Main skill definition
└── reference/
    └── testing-anti-patterns.md          # Anti-patterns guide
```

- [ ] Invoke `/red-green-fix <bug description>` in Claude Code and
verify the two-step workflow
- [ ] Confirm PR template includes red-green verification table
- [ ] Review anti-patterns reference for completeness

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9954-draft-add-red-green-fix-skill-for-verified-bug-fix-workflow-3246d73d365081339a83dc09263b0f33)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>

test: add large-graph perf test with 245-node workflow (backlog N5) (#9940)

Adds a 245-node workflow asset and two `@perf` tests to establish a
baseline for large-graph performance regressions (Tier 6 in the
performance backlog).

Backlog item N5: we need CI regression detection for compositor layer
management, GPU texture count, and transform pane cost at 245+ nodes.
This is PR1 of 2 — establishes baseline metrics on main. Future
optimization PRs will show improvement deltas against this baseline.

- **`large graph idle rendering`** — 120 frames idle with 245 nodes,
measures style recalcs, layouts, task duration, heap delta
- **`large graph pan interaction`** — middle-click pan across 245 nodes,
stresses compositor layer management and transform recalculation

`browser_tests/assets/large-graph-workflow.json` — 245 nodes (49
pipelines of CheckpointLoader → 2× CLIPTextEncode → KSampler +
EmptyLatentImage), 294 links. Minimal structure focused on node count.

- [x] `pnpm typecheck:browser` passes
- [x] `pnpm lint` passes (eslint on changed file)
- [x] All link references in JSON validated programmatically

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9940-test-add-large-graph-perf-test-with-245-node-workflow-backlog-N5-3246d73d365081f6b5d8ddb9a85e6ad0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>

feat: add Ingest API codegen with Zod schema generation (#9932)

- Add `packages/ingest-types/` package that auto-generates TypeScript
types and Zod schemas from the Ingest service OpenAPI spec
- Uses `@hey-api/openapi-ts` with built-in Zod plugin (Zod v3
compatible)
- Filters out overlapping endpoints shared with the local ComfyUI Python
backend
- Generates **493 TypeScript types** and **256 Zod schemas** covering
cloud-only endpoints
- Configure knip to ignore generated files

The cloud repo pushes generated types to this repo (push model, no
private repo cloning).
See: Comfy-Org/cloud#2858

Codegen targets are controlled by the **exclude list** in
`packages/ingest-types/openapi-ts.config.ts`. Everything in the Ingest
`openapi.yaml` is included **except** overlapping endpoints that also
exist in the local ComfyUI Python backend:

**Excluded (overlapping with ComfyUI Python):**
`/prompt`, `/queue`, `/history`, `/object_info`, `/features`,
`/settings`, `/system_stats`, `/interrupt`, `/upload/*`, `/view`,
`/jobs`, `/userdata`, `/webhooks/*`, `/internal/*`

**Included (cloud-only, codegen targets):**
`/workspaces/*`, `/billing/*`, `/secrets/*`, `/assets/*`, `/tasks/*`,
`/auth/*`, `/workflows/*`, `/workspace/*`, `/user`, `/settings/{key}`,
`/tags`, `/feedback`, `/invite_code/*`, `/experiment/models/*`,
`/global_subgraphs/*`

This PR only sets up the codegen infrastructure. A follow-up PR should
replace manually maintained types with imports from
`@comfyorg/ingest-types`:

| File | Lines | Current | Replace with |
|------|-------|---------|-------------|
| `src/platform/workspace/api/workspaceApi.ts` | ~270 | TS interfaces |
`import type { ... } from '@comfyorg/ingest-types'` |
| `src/platform/secrets/types.ts` | ~32 | TS interfaces | `import type {
... } from '@comfyorg/ingest-types'` |
| `src/platform/assets/schemas/assetSchema.ts` | ~125 | Zod schemas |
`import { ... } from '@comfyorg/ingest-types/zod'` |
| `src/platform/assets/schemas/mediaAssetSchema.ts` | ~50 | Zod schemas
| `import { ... } from '@comfyorg/ingest-types/zod'` |
| `src/platform/tasks/services/taskService.ts` | ~70 | Zod schemas |
`import { ... } from '@comfyorg/ingest-types/zod'` |
| `src/platform/workspace/workspaceTypes.ts` | ~6 | TS interface |
`export type { ... } from '@comfyorg/ingest-types'` |

- [x] `pnpm generate` in `packages/ingest-types/` produces
`types.gen.ts` and `zod.gen.ts`
- [x] `pnpm typecheck` passes
- [x] Pre-commit hooks pass (lint, typecheck, format)
- [x] Generated Zod schemas validate correct data and reject invalid
data
- [x] No import conflicts with existing code (generated types are
isolated in separate package)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>

feat: surface missing models in Error Tab for OSS and remove legacy dialog (#9921)

- Surface missing models in the Error Tab for OSS environments,
replacing the legacy modal dialog
- Add Download button per model and Download All button in group header
with file size display
- Move download business logic from `components/dialog/content` to
`platform/missingModel`
- Remove legacy missing models dialog components and composable

- **Pipeline**: Remove `isCloud` guard from `scanAllModelCandidates` and
`surfaceMissingModels` so OSS detects missing models
- **Grouping**: Group non-asset-supported models by directory in OSS
instead of lumping into UNSUPPORTED
- **UI**: Add Download button (matching Install Node Pack design) and
Download All header button
- **Store**: Add `folderPaths`/`fileSizes` state with setter methods,
race condition guard
- **Cleanup**: Delete `MissingModelsContent`, `MissingModelsHeader`,
`MissingModelsFooter`, `useMissingModelsDialog`, `missingModelsUtils`
- **Tests**: Add OSS/Cloud grouping tests, migrate Playwright E2E to
Error Tab, improve test isolation
- **Snapshots**: Reset Playwright screenshot expectations since OSS
missing model error detection now causes red highlights on affected
nodes
- **Accessibility**: Add `aria-label` with model name, `aria-expanded`
on toggle, warning icon for unknown category

- [x] Unit tests pass (86 tests)
- [x] TypeScript typecheck passes
- [x] knip passes
- [x] Load workflow with missing models in OSS → Error Tab shows missing
models grouped by directory
- [x] Download button triggers browser download with correct URL
- [x] Download All button downloads all downloadable models
- [x] Cloud environment behavior unchanged
- [x] Playwright E2E: `pnpm test:browser:local -- --grep "Missing models
in Error Tab"`

https://github.com/user-attachments/assets/12f15e09-215a-4c58-87ed-39bbffd1359c

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9921-feat-surface-missing-models-in-Error-Tab-for-OSS-and-remove-legacy-dialog-3236d73d365081f0a9dfc291978f5ecf)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>

fix: cloud subscribe redirect hangs waiting for billing init (#9965)

Fix /cloud/subscribe route hanging indefinitely because billing context
never initializes during the onboarding flow.

- **What**: Replace passive `await until(isInitialized).toBe(true)` with
explicit `await initialize()` in CloudSubscriptionRedirectView. Remove
unused `until` import.

![Kapture 2026-03-15 at 23 16
22](https://github.com/user-attachments/assets/0a12487b-b39a-4f96-9a4c-96a01facfdd8)

In the onboarding flow, `useTeamWorkspaceStore().activeWorkspace` is not
set, so `useBillingContext`'s internal watch (which triggers
`initialize()` on workspace change) enters the `!newWorkspaceId` branch
— it resets `isInitialized` to `false` and returns without ever calling
`initialize()`. The old code then awaited `isInitialized` becoming
`true` forever.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9965-fix-cloud-subscribe-redirect-hangs-waiting-for-billing-init-3246d73d3650812d93ebd477c544fa0a)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>

feat: add TBT/frameDuration metrics and new perf test scenarios (#9910)

Adds Total Blocking Time (TBT) and frame duration metrics to the
performance testing infrastructure, plus three new test scenarios
covering zoom, pan, and many-nodes-idle.

- **`totalBlockingTimeMs`** — Computed from PerformanceObserver
`longtask` entries: `sum(duration - 50ms)` for tasks >50ms. Measures
main thread blocking.
- **`frameDurationMs`** — Average frame duration via rAF timing (16.67ms
= 60fps target). Measures rendering smoothness.

| Scenario | Description |
|---|---|
| `canvas-zoom-sweep` | 10 zoom-in + 10 zoom-out cycles on default
workflow |
| `canvas-pan-many-nodes` | 10 pan sweeps over 100-node workflow |
| `canvas-many-nodes-idle` | 2-second idle measurement with 100 nodes
rendered |

- `PerformanceHelper.ts`: Installs PerformanceObserver for longtask,
collects TBT, measures frame duration via rAF
- `perf-report.ts`: Reports TBT and frame duration in PR comment tables
- `browser_tests/assets/perf/many_nodes_100.json`: 100-node (10×10 grid)
test fixture

- TBT collection clears entries at `startMeasuring()` and reads at
`stopMeasuring()` — ensure no race with observer buffering
- Frame duration sampling uses 10 frames — enough for signal without
slowing tests

Depends on: #9886, #9887

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9910-feat-add-TBT-frameDuration-metrics-and-new-perf-test-scenarios-3236d73d365081488ae3c594a8bf7cff)
by [Unito](https://www.unito.io)

fix: LGraphGroup paste position (#9962)

Fix group paste position: groups now paste at the cursor location
instead of on top of the original.

- **What**: Added LGraphGroup offset handling in _deserializeItems()
position adjustment loop, matching existing LGraphNode and Reroute
behavior.

Before:

https://github.com/user-attachments/assets/e317af10-8009-4092-9d14-de79316cd853

After:

https://github.com/user-attachments/assets/f4ffefd5-519a-4592-812c-c88e3b5940fd

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9962-fix-LGraphGroup-paste-position-3246d73d365081eea5b2e2507da861de)
by [Unito](https://www.unito.io)

fix: tree explorer nodes not filling parent container width (#9964)

Fix tree explorer nodes not filling the full width of the sidebar
container, causing text to overflow instead of truncating.

- **What**: Add `min-w-0` to `TreeRoot` to allow flex shrinking within
sidebar. Add `w-full` and `min-w-0` to tree node rows so
absolutely-positioned virtualizer items fill the container width and
text truncates correctly.
<img width="365" height="749" alt="image"
src="https://github.com/user-attachments/assets/320910f3-52ad-4634-a935-6bd1a40aea7f"
/>

The virtualizer renders each item with `position: absolute; left: 0` but
no explicit width, so rows would size to content rather than filling the
container. Adding `w-full` ensures rows stretch to 100% of the
virtualizer container, and `min-w-0` allows proper flex shrinking for
deep indentation levels.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9964-fix-tree-explorer-nodes-not-filling-parent-container-width-3246d73d36508138be38fdcac15ae4ef)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>

feat: add Copy URL button to missing model rows for OSS (#9966)

1.42.6 (#9986)

Patch version increment to 1.42.6

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9986-1-42-6-3256d73d365081a28bfad82022ce3440)
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>

fix: block missing e2e regression coverage in CodeRabbit (#9987)

Make the CodeRabbit end-to-end regression coverage check actually block
fix-like PRs until it is resolved or explicitly overridden by a
requested reviewer, and harden the prompt so it evaluates only PR-local
metadata.

- **What**: Set the `End-to-end regression coverage for fixes` custom
check mode from `warning` to `error`
- **What**: Enable `reviews.request_changes_workflow` so CodeRabbit can
block on failed `error` pre-merge checks
- **What**: Set
`reviews.pre_merge_checks.override_requested_reviewers_only` to `true`
so only requested reviewers can bypass a failed check
- **What**: Tighten the custom check instructions to use only PR
metadata in review context, avoid shell commands, and avoid reverse-diff
or base-branch file evaluation

Confirm this is the intended CodeRabbit enforcement model for missing
Playwright regression coverage on fix-like PRs and that the prompt
wording is strict enough to avoid false positives from reversed diffs.

fix: add reve and elevenlabs to icon safelist (#9990)

Reve and ElevenLabs provider icons were not displaying in the node
library because they were missing from the Tailwind icon safelist.

- **What**: Add `reve` and `elevenlabs` to the `@source inline` safelist
in `style.css` so `icon-[comfy--reve]` and `icon-[comfy--elevenlabs]`
classes are generated. Add corresponding `PROVIDER_COLORS` entries in
`categoryUtil.ts`.

<img width="308" height="106" alt="image"
src="https://github.com/user-attachments/assets/d488898a-fbad-4af0-8921-0e8ee7d4705a"
/>
<img width="308" height="78" alt="image"
src="https://github.com/user-attachments/assets/2b3b7172-095b-415e-a49a-d303977e0abc"
/>

The SVG files already existed in `packages/design-system/src/icons/` but
Tailwind's tree-shaking dropped the classes since they're only used
dynamically via `getProviderIcon()`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9990-fix-add-reve-and-elevenlabs-to-icon-safelist-3256d73d36508105994fcdd5d0568027)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>

fix: mask editor save shows blank image in Load Image node (#9984)

Mask editor save was showing a blank image in the Load Image node
(legacy nodes mode, not Nodes 2.0) because
`updateNodeWithServerReferences` called `updateNodeImages`, which
silently no-ops when the node has no pre-existing execution outputs.
Replaced with `setNodeOutputs` which properly creates output entries
regardless of prior state.

**Affects:** Legacy nodes mode only. Nodes 2.0 (Vue Nodes) renders
images via Vue components and is not affected.

- Fixes #9983
- Fixes #9782
- Fixes #9952

| Commit | SHA | CI Status | Run | Purpose |
|--------|-----|-----------|-----|---------|
| `test: add failing test for mask editor save showing blank image` |
`0ab66e8` | 🔴
[Red](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/23125427860)
| CI: Tests Unit **failure** | Proves the test catches the bug |
| `fix: mask editor save shows blank image in Load Image node` |
`564cc9c` | 🟢
[Green](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/23127289891)
| CI: Tests Unit **success** | Proves the fix resolves the bug |

https://github.com/user-attachments/assets/8d5c36ce-2c5e-4609-b246-dcf896c4a8e7

https://github.com/user-attachments/assets/c8ae4f0e-3da0-40f2-a543-d1d5a6bce795

- [x] CI red on test-only commit
- [x] CI green on fix commit
- [ ] E2E regression test not added: mask editor save requires canvas
pixel manipulation + server upload round-trip which is covered by the
existing unit test mocking the full `save()` flow. The Playwright test
infrastructure does not currently support mask editor interactions (draw
+ save).
- [x] Manual verification (legacy nodes mode): Load Image → upload →
mask editor → draw → save → verify image refreshes

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

fix: allow URL input for free tier users, gate on import button (#10024)

- Remove free-tier restriction from the URL input field in
`MissingModelUrlInput.vue` so it is always editable
- Move the subscription check (`canImportModels`) to the Import button
click handler — free-tier users see the upgrade modal only when they
attempt to import
- Extract inline ternary to named `handleImportClick` method for clarity

- [x] Unit tests added (`MissingModelUrlInput.test.ts`) verifying:
  - URL input is always editable regardless of subscription tier
  - Import button calls `handleImport` for paid users
- Import button calls `showUploadDialog` (upgrade modal) for free-tier
users
- [x] Verify URL input is editable for free-tier users on cloud
- [x] Verify clicking Import as free-tier opens the subscription modal
- [x] Verify paid users can import normally without changes

Playwright E2E regression tests are impractical for this change because
`MissingModelUrlInput` only renders when `isAssetSupported` is true,
which requires `isCloud` — a compile-time constant (`__DISTRIBUTION__`).
The OSS test build always sets `isCloud = false`, so the component never
renders in the E2E environment. Unit tests with mocked feature flags
provide equivalent behavioral coverage.

fix: prevent subscription UI from rendering on non-cloud distributions (#9958)

Prevent Plans & Pricing dialog, subscription buttons, and cloud-only
menu items from appearing on desktop/localhost distributions.

- **What**: Add `isCloud` guards to
`useSubscriptionDialog.showPricingTable`, `TopbarSubscribeButton`, and
`CurrentUserPopoverLegacy` so subscription UI only renders on cloud
- **Tests**: 24 tests across 3 test files (1 modified, 2 new) covering
cloud/non-cloud behavior

- Guard placement in `CurrentUserPopoverLegacy.vue` — multiple `v-if`
conditions updated to include `isCloud`
- Early-return in `showPricingTable` as a defense-in-depth measure

Fixes COM-16820

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9958-fix-prevent-subscription-UI-from-rendering-on-non-cloud-distributions-3246d73d365081559a9ee8650409c5b4)
by [Unito](https://www.unito.io)

Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>

fix: prevent animated preview duplication on Vue↔Litegraph switch (#9938)

SaveAnimatedPNG/WEBP nodes show duplicate output previews when switching
between Vue and Litegraph renderer modes.

The `ANIM_PREVIEW_WIDGET` (`$$comfy_animation_preview`) DOM widget
lacked `canvasOnly: true`, so `shouldRenderAsVue()` in the widget
registry included it in Vue mode rendering. This caused both:
1. Vue's `ImagePreview.vue` (via `nodeMedia` computed from
`nodeOutputStore`)
2. The legacy `ANIM_PREVIEW_WIDGET` DOM widget (rendered as `WidgetDOM`)

to display simultaneously — duplicating the output preview.

Add `canvasOnly: true` to the `ANIM_PREVIEW_WIDGET` options, matching
the pattern used by `IMAGE_PREVIEW` widget in
`useImagePreviewWidget.ts`. This ensures the legacy widget is filtered
out in Vue mode by `shouldRenderAsVue()`, leaving `ImagePreview.vue` as
the single source of truth.

- All 539 vueNodes tests pass
- All 22 nodeOutputStore tests pass
- All 140 composables/node tests pass
- Typecheck passes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9938-fix-prevent-animated-preview-duplication-on-Vue-Litegraph-switch-3246d73d365081019bbfd7e33a9c14fb)
by [Unito](https://www.unito.io)

1.43.0 (#10032)

Minor version increment to 1.43.0

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10032-1-43-0-3256d73d3650818e8408d25fdf28de48)
by [Unito](https://www.unito.io)

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>

Feat/3d thumbnail inline rendering (#9471)

The previous approach generated thumbnails server-side and uploaded them
as `model.glb.png` alongside the model file. This breaks on cloud
deployments where output files are renamed to content hashes, severing
the filename-based association between a model and its thumbnail.

Replace the server-upload approach with client-side Three.js rendering

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9471-Feat-3d-thumbnail-inline-rendering-31b6d73d3650816fbd7dd05b507aa80d)
by [Unito](https://www.unito.io)

test: add FeatureFlagHelper and QueueHelper for E2E test infrastructure (#9554)

Add 2 reusable test helpers for Playwright E2E tests, integrated into
the ComfyPage fixture. These provide standardized patterns for mocking
feature flags and queue state across all E2E tests.

- **`FeatureFlagHelper.ts`** — manage localStorage `ff:` prefixed
feature flags (`seedFlags` for init-time, `setFlags` for runtime) and
mock `/api/features` route
- **`QueueHelper.ts`** — mock `/api/queue` and `/api/history` routes
with configurable running/pending counts and success/error job entries
- **`ComfyPage.ts`** — integrate both helpers as
`comfyPage.featureFlags` and `comfyPage.queue`

- Helper API design: are `seedFlags`/`setFlags`/`mockServerFeatures` the
right abstractions for feature flag testing?
- Queue mock fidelity: does the mock history shape match real ComfyUI
API responses closely enough?
- These are test-only infrastructure — no production code changes.

This is the base PR for the Playwright E2E coverage stack. Waves 1-4 all
branch from this and can merge independently once this lands:
- **→ This PR**: Test infrastructure helpers
- #9555: Toasts, error overlay, selection toolbox, linear mode,
selection rectangle
- #9556: Node search, bottom panel, focus mode, job history, side panel
- #9557: Errors tab, node headers, queue notifications, settings sidebar
- #9558: Minimap, widget copy, floating menus, node library essentials

---------

Co-authored-by: GitHub Action <action@github.com>

feat: scaffold Astro 5 website app + design-system base.css

- Create apps/website/ with Astro 5, Vue 3, Tailwind v4 integration
- Static output, assetsPrefix /_website/, i18n (en + zh-CN)
- Nx targets: dev, serve, build, preview, typecheck
- Add base.css to design-system: brand tokens + Inter font-face only
- Add catalog entries: astro, @astrojs/vue, @astrojs/check, nanostores, @nanostores/vue

scaffold-01, scaffold-02

fix: add .gitignore and env.d.ts for Astro website app

feat: add layout shell — SEO head, analytics, nav, footer

- BaseLayout: OG/Twitter meta, canonical URL, GA4 GTM-NP9JM6K7,
  Vercel Analytics, ClientRouter for SPA navigation
- SiteNav: Comfy logo, Enterprise/Gallery/About/Careers links,
  Comfy Cloud + Comfy Hub CTA buttons, mobile hamburger menu
- SiteFooter: Product/Resources/Company/Legal columns,
  social icons (GitHub, Discord, X, Reddit, LinkedIn, Instagram)
- Add @vercel/analytics to workspace catalog and website deps

fix: address CodeRabbit review — ARIA wiring, absolute OG URLs, Analytics component

- SiteNav: add aria-controls, aria-expanded, and id for mobile menu
- BaseLayout: use absolute URLs for og:image and twitter:image
- BaseLayout: replace inline inject() with official <Analytics /> component

style: apply oxfmt formatting

fix: remove unused deps from website package.json (knip)

fix: clean up unused catalog entries from pnpm-workspace.yaml

feat: add Wave 3 homepage sections (hero, social proof, pillars, testimonials, CTAs, manifesto, academy, placeholders)
christian-byrne added a commit that referenced this pull request Mar 17, 2026
- Add private: true to package.json to prevent accidental npm publish
- Fix assetsPrefix to use Vercel URL (matching TDD) instead of /_website/ path
- Add Inter font files to public/fonts/ to fix 404s

Addresses review feedback:
#10142 (review)
@christian-byrne christian-byrne force-pushed the website/02-layout-shell branch from 2802e91 to afb2a47 Compare March 17, 2026 14:21
@christian-byrne christian-byrne force-pushed the website/03-homepage-sections branch from 50d5377 to cb1f771 Compare March 17, 2026 14:21
christian-byrne added a commit that referenced this pull request Mar 17, 2026
- Extract astro:after-swap handler to named function so it can be
  removed in onUnmounted (fixes event listener leak)
- Add vercel.json with security headers (X-Frame-Options, nosniff,
  Referrer-Policy, Permissions-Policy)

Addresses review feedback:
#10142 (review)
@github-actions
Copy link

📦 Bundle Size

⏳ Size data collection in progress…

⚡ Performance Report

No baseline found — showing absolute values.

Metric Value
canvas-idle: style recalcs 10
canvas-idle: layouts 0
canvas-idle: task duration 369ms
canvas-idle: DOM nodes 21
canvas-idle: script duration 20ms
canvas-idle: event listeners 13
canvas-idle: TBT 0ms
canvas-idle: frame duration 17ms
canvas-idle: heap delta -907.9 KB
canvas-mouse-sweep: style recalcs 78
canvas-mouse-sweep: layouts 12
canvas-mouse-sweep: task duration 827ms
canvas-mouse-sweep: DOM nodes 62
canvas-mouse-sweep: script duration 135ms
canvas-mouse-sweep: event listeners 5
canvas-mouse-sweep: TBT 0ms
canvas-mouse-sweep: frame duration 17ms
canvas-mouse-sweep: heap delta 5.0 MB
canvas-zoom-sweep: style recalcs 32
canvas-zoom-sweep: layouts 6
canvas-zoom-sweep: task duration 310ms
canvas-zoom-sweep: DOM nodes 80
canvas-zoom-sweep: script duration 26ms
canvas-zoom-sweep: event listeners 28
canvas-zoom-sweep: TBT 0ms
canvas-zoom-sweep: frame duration 17ms
canvas-zoom-sweep: heap delta 5.7 MB
dom-widget-clipping: style recalcs 13
dom-widget-clipping: layouts 0
dom-widget-clipping: task duration 348ms
dom-widget-clipping: DOM nodes 22
dom-widget-clipping: script duration 66ms
dom-widget-clipping: event listeners 2
dom-widget-clipping: TBT 0ms
dom-widget-clipping: frame duration 17ms
dom-widget-clipping: heap delta 12.4 MB
large-graph-idle: style recalcs 11
large-graph-idle: layouts 0
large-graph-idle: task duration 501ms
large-graph-idle: DOM nodes 24
large-graph-idle: script duration 98ms
large-graph-idle: event listeners 21
large-graph-idle: TBT 0ms
large-graph-idle: frame duration 17ms
large-graph-idle: heap delta -2.8 MB
large-graph-pan: style recalcs 71
large-graph-pan: layouts 0
large-graph-pan: task duration 1044ms
large-graph-pan: DOM nodes 22
large-graph-pan: script duration 401ms
large-graph-pan: event listeners 5
large-graph-pan: TBT 0ms
large-graph-pan: frame duration 17ms
large-graph-pan: heap delta 5.0 MB
minimap-idle: style recalcs 10
minimap-idle: layouts 0
minimap-idle: task duration 506ms
minimap-idle: DOM nodes 20
minimap-idle: script duration 96ms
minimap-idle: event listeners 4
minimap-idle: TBT 0ms
minimap-idle: frame duration 17ms
minimap-idle: heap delta -10.7 MB
subgraph-dom-widget-clipping: style recalcs 48
subgraph-dom-widget-clipping: layouts 0
subgraph-dom-widget-clipping: task duration 378ms
subgraph-dom-widget-clipping: DOM nodes 22
subgraph-dom-widget-clipping: script duration 130ms
subgraph-dom-widget-clipping: event listeners 16
subgraph-dom-widget-clipping: TBT 0ms
subgraph-dom-widget-clipping: frame duration 17ms
subgraph-dom-widget-clipping: heap delta 12.4 MB
subgraph-idle: style recalcs 10
subgraph-idle: layouts 0
subgraph-idle: task duration 361ms
subgraph-idle: DOM nodes 21
subgraph-idle: script duration 19ms
subgraph-idle: event listeners 5
subgraph-idle: TBT 0ms
subgraph-idle: frame duration 17ms
subgraph-idle: heap delta 1.4 MB
subgraph-mouse-sweep: style recalcs 78
subgraph-mouse-sweep: layouts 16
subgraph-mouse-sweep: task duration 669ms
subgraph-mouse-sweep: DOM nodes 65
subgraph-mouse-sweep: script duration 97ms
subgraph-mouse-sweep: event listeners 12
subgraph-mouse-sweep: TBT 0ms
subgraph-mouse-sweep: frame duration 17ms
subgraph-mouse-sweep: heap delta -6.4 MB
workflow-execution: style recalcs 18
workflow-execution: layouts 5
workflow-execution: task duration 123ms
workflow-execution: DOM nodes 159
workflow-execution: script duration 28ms
workflow-execution: event listeners 55
workflow-execution: TBT 0ms
workflow-execution: frame duration 17ms
workflow-execution: heap delta 4.2 MB
Raw data
{
  "timestamp": "2026-03-17T14:34:15.463Z",
  "gitSha": "594c6a7d1fcc84759c92d738ac0407c621970e89",
  "branch": "website/03-homepage-sections",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2033.3530000000053,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.816,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 384.28200000000004,
      "heapDeltaBytes": 715448,
      "domNodes": 16,
      "jsHeapTotalBytes": 16777216,
      "scriptDurationMs": 16.54200000000001,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "canvas-idle",
      "durationMs": 2011.498999999958,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.933,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 362.755,
      "heapDeltaBytes": -5064296,
      "domNodes": 22,
      "jsHeapTotalBytes": 24379392,
      "scriptDurationMs": 22.003,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-idle",
      "durationMs": 2037.8209999999513,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 12.490000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 359.564,
      "heapDeltaBytes": 1559688,
      "domNodes": 25,
      "jsHeapTotalBytes": 17301504,
      "scriptDurationMs": 22.418,
      "eventListeners": 30,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1818.2560000000194,
      "styleRecalcs": 74,
      "styleRecalcDurationMs": 37.242999999999995,
      "layouts": 12,
      "layoutDurationMs": 3.441,
      "taskDurationMs": 765.998,
      "heapDeltaBytes": -1899088,
      "domNodes": 57,
      "jsHeapTotalBytes": 15204352,
      "scriptDurationMs": 137.197,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2045.6970000000183,
      "styleRecalcs": 82,
      "styleRecalcDurationMs": 45.43800000000001,
      "layouts": 12,
      "layoutDurationMs": 3.162,
      "taskDurationMs": 965.9389999999999,
      "heapDeltaBytes": 20668496,
      "domNodes": 68,
      "jsHeapTotalBytes": 15204352,
      "scriptDurationMs": 136.99900000000002,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1846.7410000000086,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 38.371,
      "layouts": 12,
      "layoutDurationMs": 3.8440000000000003,
      "taskDurationMs": 747.792,
      "heapDeltaBytes": -2941600,
      "domNodes": 60,
      "jsHeapTotalBytes": 17563648,
      "scriptDurationMs": 129.91,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1740.8189999999877,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 17.36,
      "layouts": 6,
      "layoutDurationMs": 0.5639999999999998,
      "taskDurationMs": 312.66700000000003,
      "heapDeltaBytes": 5824176,
      "domNodes": 79,
      "jsHeapTotalBytes": 18087936,
      "scriptDurationMs": 25.943,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1740.3869999999984,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 18.792999999999996,
      "layouts": 6,
      "layoutDurationMs": 0.6980000000000001,
      "taskDurationMs": 313.39,
      "heapDeltaBytes": 6157196,
      "domNodes": 79,
      "jsHeapTotalBytes": 17301504,
      "scriptDurationMs": 27.078000000000003,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1759.9890000000187,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 17.805,
      "layouts": 6,
      "layoutDurationMs": 0.704,
      "taskDurationMs": 305.135,
      "heapDeltaBytes": 6026884,
      "domNodes": 82,
      "jsHeapTotalBytes": 18612224,
      "scriptDurationMs": 24.205000000000005,
      "eventListeners": 45,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 567.3960000000022,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 8.576,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 340.48400000000004,
      "heapDeltaBytes": 13092912,
      "domNodes": 22,
      "jsHeapTotalBytes": 15204352,
      "scriptDurationMs": 65.339,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 568.0160000000001,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 9.425000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 359.1259999999999,
      "heapDeltaBytes": 13080928,
      "domNodes": 22,
      "jsHeapTotalBytes": 17563648,
      "scriptDurationMs": 68.62299999999999,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 579.2650000000208,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 8.91,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 343.68399999999997,
      "heapDeltaBytes": 12975236,
      "domNodes": 22,
      "jsHeapTotalBytes": 15728640,
      "scriptDurationMs": 64.747,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000027
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2025.3459999999563,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 11.562000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 486.12,
      "heapDeltaBytes": -9588196,
      "domNodes": 25,
      "jsHeapTotalBytes": 8507392,
      "scriptDurationMs": 91.94800000000001,
      "eventListeners": 30,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "large-graph-idle",
      "durationMs": 1988.5999999999626,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.905000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 523.464,
      "heapDeltaBytes": 11073472,
      "domNodes": 22,
      "jsHeapTotalBytes": 6873088,
      "scriptDurationMs": 102.29400000000001,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2024.5910000000436,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 11.104,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 492.613,
      "heapDeltaBytes": -10205088,
      "domNodes": 25,
      "jsHeapTotalBytes": 8794112,
      "scriptDurationMs": 99.04599999999999,
      "eventListeners": 30,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2108.980000000031,
      "styleRecalcs": 70,
      "styleRecalcDurationMs": 16.765,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1058.1940000000002,
      "heapDeltaBytes": 6427660,
      "domNodes": 20,
      "jsHeapTotalBytes": 10285056,
      "scriptDurationMs": 416.131,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2098.634000000004,
      "styleRecalcs": 72,
      "styleRecalcDurationMs": 19.864,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1060.8870000000002,
      "heapDeltaBytes": 6109872,
      "domNodes": 24,
      "jsHeapTotalBytes": 9498624,
      "scriptDurationMs": 394.626,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2097.343999999964,
      "styleRecalcs": 71,
      "styleRecalcDurationMs": 17.285999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1013.2429999999999,
      "heapDeltaBytes": 3218164,
      "domNodes": 22,
      "jsHeapTotalBytes": 9236480,
      "scriptDurationMs": 391.46200000000005,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "minimap-idle",
      "durationMs": 2004.5129999999745,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.432,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 555.487,
      "heapDeltaBytes": -10715892,
      "domNodes": 20,
      "jsHeapTotalBytes": 8245248,
      "scriptDurationMs": 105.23599999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "minimap-idle",
      "durationMs": 2001.0240000000294,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 11.133999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 491.20399999999995,
      "heapDeltaBytes": -10572444,
      "domNodes": 22,
      "jsHeapTotalBytes": 8507392,
      "scriptDurationMs": 95.26499999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "minimap-idle",
      "durationMs": 2036.1279999999624,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.543000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 470.048,
      "heapDeltaBytes": -12232784,
      "domNodes": 18,
      "jsHeapTotalBytes": 6410240,
      "scriptDurationMs": 88.45899999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 587.5900000000343,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 12.44,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 385.26300000000003,
      "heapDeltaBytes": 12980412,
      "domNodes": 20,
      "jsHeapTotalBytes": 14942208,
      "scriptDurationMs": 135.72299999999998,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 571.8090000000302,
      "styleRecalcs": 49,
      "styleRecalcDurationMs": 14.034999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 378.444,
      "heapDeltaBytes": 13151656,
      "domNodes": 24,
      "jsHeapTotalBytes": 16515072,
      "scriptDurationMs": 128.723,
      "eventListeners": 32,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 574.2759999999976,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 11.507000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 369.095,
      "heapDeltaBytes": 12977408,
      "domNodes": 22,
      "jsHeapTotalBytes": 14680064,
      "scriptDurationMs": 127.00999999999999,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1991.9940000000338,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.043000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 357.45700000000005,
      "heapDeltaBytes": 903760,
      "domNodes": 20,
      "jsHeapTotalBytes": 17563648,
      "scriptDurationMs": 18.862,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1998.4010000000012,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.363000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 361.09499999999997,
      "heapDeltaBytes": 1832472,
      "domNodes": 20,
      "jsHeapTotalBytes": 17301504,
      "scriptDurationMs": 19.40800000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2005.5550000000153,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 10.321999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 364.44700000000006,
      "heapDeltaBytes": 1650156,
      "domNodes": 22,
      "jsHeapTotalBytes": 18350080,
      "scriptDurationMs": 18.945999999999998,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.680000000000017
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1708.215999999993,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 38.858000000000004,
      "layouts": 16,
      "layoutDurationMs": 4.6579999999999995,
      "taskDurationMs": 666.5190000000001,
      "heapDeltaBytes": -6442396,
      "domNodes": 64,
      "jsHeapTotalBytes": 17301504,
      "scriptDurationMs": 98.86,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1701.014999999984,
      "styleRecalcs": 78,
      "styleRecalcDurationMs": 41.173,
      "layouts": 16,
      "layoutDurationMs": 4.652,
      "taskDurationMs": 698.3729999999999,
      "heapDeltaBytes": -6615260,
      "domNodes": 66,
      "jsHeapTotalBytes": 14680064,
      "scriptDurationMs": 98.117,
      "eventListeners": 28,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1705.4630000000088,
      "styleRecalcs": 78,
      "styleRecalcDurationMs": 38.751000000000005,
      "layouts": 16,
      "layoutDurationMs": 4.518999999999999,
      "taskDurationMs": 642.594,
      "heapDeltaBytes": -7120800,
      "domNodes": 65,
      "jsHeapTotalBytes": 17563648,
      "scriptDurationMs": 92.67299999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "workflow-execution",
      "durationMs": 439.4530000000145,
      "styleRecalcs": 19,
      "styleRecalcDurationMs": 27.96,
      "layouts": 5,
      "layoutDurationMs": 1.611,
      "taskDurationMs": 131.62600000000003,
      "heapDeltaBytes": 4541196,
      "domNodes": 168,
      "jsHeapTotalBytes": 4456448,
      "scriptDurationMs": 29.069999999999993,
      "eventListeners": 55,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "workflow-execution",
      "durationMs": 433.06899999998905,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 24.692999999999998,
      "layouts": 5,
      "layoutDurationMs": 1.4629999999999999,
      "taskDurationMs": 117.83299999999997,
      "heapDeltaBytes": 4312500,
      "domNodes": 155,
      "jsHeapTotalBytes": 4194304,
      "scriptDurationMs": 25.635000000000005,
      "eventListeners": 55,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "workflow-execution",
      "durationMs": 442.875000000015,
      "styleRecalcs": 17,
      "styleRecalcDurationMs": 22.952,
      "layouts": 5,
      "layoutDurationMs": 1.365,
      "taskDurationMs": 118.40299999999999,
      "heapDeltaBytes": 4430784,
      "domNodes": 153,
      "jsHeapTotalBytes": 4194304,
      "scriptDurationMs": 29.631999999999998,
      "eventListeners": 55,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    }
  ]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants