feat: add Wave 3 homepage sections (11 Vue components) [3/3]#10142
feat: add Wave 3 homepage sections (11 Vue components) [3/3]#10142christian-byrne wants to merge 3 commits intowebsite/02-layout-shellfrom
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ❌ 590 passed, 1 failed · 6 flaky❌ Failed Tests📊 Browser Reports
|
d56da59 to
49cb6fc
Compare
7340838 to
04cf78b
Compare
49cb6fc to
336c0af
Compare
a3c47ad to
939e02b
Compare
9279f6f to
2802e91
Compare
939e02b to
50d5377
Compare
There was a problem hiding this comment.
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.  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)
- 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)
2802e91 to
afb2a47
Compare
50d5377 to
cb1f771
Compare
- 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)
📦 Bundle Size
⚡ Performance ReportNo baseline found — showing absolute values.
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
}
]
} |

Summary
Adds all 11 homepage section components for the comfy.org marketing site.
Changes (incremental from #10141)
Stack (via Graphite)
┆Issue is synchronized with this Notion page by Unito