Skip to content

fix: selection bounding box not encompassing node footer overlay#10640

Open
jaeone94 wants to merge 7 commits intomainfrom
fix/selection-bounding-box-footer-overlay
Open

fix: selection bounding box not encompassing node footer overlay#10640
jaeone94 wants to merge 7 commits intomainfrom
fix/selection-bounding-box-footer-overlay

Conversation

@jaeone94
Copy link
Copy Markdown
Collaborator

@jaeone94 jaeone94 commented Mar 28, 2026

Summary

Fix multi-select bounding box not encompassing node footer overlays (Enter Subgraph, Advanced, Error buttons).

Changes

  • What: Extend node boundingRect via onBounding to include footer overlay height (32px expanded, 34px collapsed) and override collapsed width to MIN_NODE_WIDTH since Vue nodes lack canvas ctx for measure()
  • 8 E2E tests covering subgraph/regular nodes in expanded/collapsed states at bottom-left/bottom-right positions

Review Focus

Screenshots

Before
image

After
image

┆Issue is synchronized with this Notion page by Unito

Extend node boundingRect via onBounding to include footer overlay
height (32px expanded, 34px collapsed) and override collapsed width
to MIN_NODE_WIDTH since Vue nodes lack canvas ctx for measure().

Regression from #9360 which changed footer from inside-node to
overlay approach (absolute top-full).
@jaeone94 jaeone94 requested a review from a team March 28, 2026 03:21
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 28, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

📝 Walkthrough

Walkthrough

Adds a JSON test asset and Playwright test for verifying canvas selection bounding boxes, introduces helper methods to position and collapse nodes in browser tests, and updates the Vue LGraphNode component to adjust LiteGraph node bounding via a reactive watchEffect when footers or collapsed state apply.

Changes

Cohort / File(s) Summary
Test Asset
browser_tests/assets/selection/subgraph-with-regular-node.json
Adds a new test graph JSON with a subgraph-entry node, an EmptyLatentImage node, a LATENT link, layout/position data, bookkeeping fields, and an embedded subgraph definition.
Playwright Test
browser_tests/tests/selectionBoundingBox.spec.ts
New spec that loads a deterministic graph, applies fixed node positions, optionally collapses a node, computes DOM→canvas visual bounds (using ds.scale/ds.offset and footer areas), selects nodes, and asserts the selection bounding box encloses node visual bounds.
Test Helpers
browser_tests/fixtures/helpers/NodeOperationsHelper.ts
Adds loadWithPositions(positions) to serialize the graph, apply provided positions to nodes, and reload the workflow in-page.
LiteGraph Test Utils
browser_tests/fixtures/utils/litegraphUtils.ts
Adds NodeReference.setCollapsed(collapsed: boolean) to collapse/uncollapse a node in the browser context only when state differs.
Vue Renderer
src/renderer/extensions/vueNodes/components/LGraphNode.vue
Adds a watchEffect to keep the LiteGraph node's onBounding handler in sync with reactive hasFooter/isCollapsed state; wraps/restores prior handler and adjusts vertical (and collapsed width) bounding values when needed.

Sequence Diagram(s)

sequenceDiagram
  participant Test as Playwright Test
  participant App as window.app
  participant Node as LGraphNode (Vue)
  participant Canvas as Renderer/DOM

  Test->>App: load graph JSON (with fixed positions)  
  App->>Node: instantiate/render node components  
  Note right of Node: watchEffect installs/updates node.onBounding  
  Node->>Canvas: provide adjusted bounding during layout pass  
  App->>Canvas: render nodes with updated bounds  
  Test->>Canvas: trigger selection (Ctrl+A) and read DOM rects, ds.scale, ds.offset  
  Test->>App: compute selection bounding box from canvas items  
  Test->>Test: assert selection bounds contain node visual bounds
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped in to tweak and test,
A subgraph friend and one more guest,
I nudged the bounds, both tall and small,
Selection wraps and catches all,
Tail wags — the canvas is dressed. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: addressing the selection bounding box issue with node footer overlays.
Description check ✅ Passed The description covers all required template sections with sufficient detail: provides a clear summary, explains what changed, includes review focus points, and includes before/after screenshots.
End-To-End Regression Coverage For Fixes ✅ Passed Bug-fix PR includes end-to-end regression tests: new test spec file and 8 tests added covering the selection bounding box issue.
Adr Compliance For Entity/Litegraph Changes ✅ Passed Check not applicable; no changes in ADR-regulated paths (src/lib/litegraph/, src/ecs/, core entity files). All modifications in test and renderer extension layers.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/selection-bounding-box-footer-overlay

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 28, 2026

🎭 Playwright: ✅ 744 passed, 0 failed · 4 flaky

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 28, 2026

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 03/28/2026, 09:22:48 AM UTC

Links

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 28, 2026

📦 Bundle: 5.09 MB gzip 🟢 -75 B

Details

Summary

  • Raw size: 23.4 MB baseline 23.4 MB — 🔴 +633 B
  • Gzip: 5.09 MB baseline 5.09 MB — 🟢 -75 B
  • Brotli: 3.94 MB baseline 3.94 MB — 🔴 +226 B
  • Bundles: 249 current • 249 baseline • 114 added / 114 removed

Category Glance
Graph Workspace 🔴 +633 B (1.14 MB) · Vendor & Third-Party ⚪ 0 B (9.8 MB) · Other ⚪ 0 B (8.43 MB) · Data & Services ⚪ 0 B (2.96 MB) · Panels & Settings ⚪ 0 B (484 kB) · Utilities & Hooks ⚪ 0 B (334 kB) · + 5 more

App Entry Points — 22.3 kB (baseline 22.3 kB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-DaG1K88F.js (new) 22.3 kB 🔴 +22.3 kB 🔴 +7.93 kB 🔴 +6.82 kB
assets/index-DBSm7z_K.js (removed) 22.3 kB 🟢 -22.3 kB 🟢 -7.94 kB 🟢 -6.79 kB

Status: 1 added / 1 removed

Graph Workspace — 1.14 MB (baseline 1.14 MB) • 🔴 +633 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-DClMpujZ.js (new) 1.14 MB 🔴 +1.14 MB 🔴 +244 kB 🔴 +184 kB
assets/GraphView-DjLapvWQ.js (removed) 1.14 MB 🟢 -1.14 MB 🟢 -244 kB 🟢 -184 kB

Status: 1 added / 1 removed

Views & Navigation — 76.6 kB (baseline 76.6 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-DF0J1T39.js (removed) 15.7 kB 🟢 -15.7 kB 🟢 -3.4 kB 🟢 -2.91 kB
assets/CloudSurveyView-UFtk8PKO.js (new) 15.7 kB 🔴 +15.7 kB 🔴 +3.39 kB 🔴 +2.9 kB
assets/CloudLoginView-Bjodd309.js (new) 12 kB 🔴 +12 kB 🔴 +3.36 kB 🔴 +2.96 kB
assets/CloudLoginView-sQ8eQLLI.js (removed) 12 kB 🟢 -12 kB 🟢 -3.36 kB 🟢 -2.97 kB
assets/CloudSignupView-DFg0Pa3l.js (new) 9.78 kB 🔴 +9.78 kB 🔴 +2.85 kB 🔴 +2.49 kB
assets/CloudSignupView-DZDiAoWg.js (removed) 9.78 kB 🟢 -9.78 kB 🟢 -2.85 kB 🟢 -2.5 kB
assets/UserCheckView-BOAOENfg.js (removed) 9.04 kB 🟢 -9.04 kB 🟢 -2.33 kB 🟢 -2.03 kB
assets/UserCheckView-BODWwuFo.js (new) 9.04 kB 🔴 +9.04 kB 🔴 +2.33 kB 🔴 +2.03 kB
assets/CloudLayoutView-BXv7sXHs.js (removed) 7.54 kB 🟢 -7.54 kB 🟢 -2.36 kB 🟢 -2.07 kB
assets/CloudLayoutView-Dza-JgFv.js (new) 7.54 kB 🔴 +7.54 kB 🔴 +2.36 kB 🔴 +2.04 kB
assets/CloudForgotPasswordView-CfPImiRt.js (removed) 5.94 kB 🟢 -5.94 kB 🟢 -2.1 kB 🟢 -1.85 kB
assets/CloudForgotPasswordView-CHBviqIl.js (new) 5.94 kB 🔴 +5.94 kB 🔴 +2.09 kB 🔴 +1.84 kB
assets/CloudAuthTimeoutView-BgUenu6l.js (removed) 5.31 kB 🟢 -5.31 kB 🟢 -1.93 kB 🟢 -1.69 kB
assets/CloudAuthTimeoutView-Ci2rom36.js (new) 5.31 kB 🔴 +5.31 kB 🔴 +1.93 kB 🔴 +1.69 kB
assets/CloudSubscriptionRedirectView-B_kg-iKI.js (new) 5.08 kB 🔴 +5.08 kB 🔴 +1.91 kB 🔴 +1.69 kB
assets/CloudSubscriptionRedirectView-CYogtj_d.js (removed) 5.08 kB 🟢 -5.08 kB 🟢 -1.91 kB 🟢 -1.69 kB
assets/UserSelectView-DFuj5vzp.js (removed) 4.71 kB 🟢 -4.71 kB 🟢 -1.74 kB 🟢 -1.54 kB
assets/UserSelectView-Wwelg24y.js (new) 4.71 kB 🔴 +4.71 kB 🔴 +1.74 kB 🔴 +1.54 kB

Status: 9 added / 9 removed / 2 unchanged

Panels & Settings — 484 kB (baseline 484 kB) • ⚪ 0 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-0560KZ-G.js (removed) 46.6 kB 🟢 -46.6 kB 🟢 -9.53 kB 🟢 -8.47 kB
assets/KeybindingPanel-Cb8s5nxz.js (new) 46.6 kB 🔴 +46.6 kB 🔴 +9.52 kB 🔴 +8.47 kB
assets/SecretsPanel-BjMCLOlX.js (removed) 22.4 kB 🟢 -22.4 kB 🟢 -5.43 kB 🟢 -4.76 kB
assets/SecretsPanel-Ck3EmGYx.js (new) 22.4 kB 🔴 +22.4 kB 🔴 +5.42 kB 🔴 +4.76 kB
assets/LegacyCreditsPanel-BAYJksjX.js (new) 21.5 kB 🔴 +21.5 kB 🔴 +5.81 kB 🔴 +5.13 kB
assets/LegacyCreditsPanel-C_WyiB6b.js (removed) 21.5 kB 🟢 -21.5 kB 🟢 -5.82 kB 🟢 -5.13 kB
assets/SubscriptionPanel-465sq2Ac.js (new) 19.6 kB 🔴 +19.6 kB 🔴 +4.98 kB 🔴 +4.38 kB
assets/SubscriptionPanel-D6-O2VM1.js (removed) 19.6 kB 🟢 -19.6 kB 🟢 -4.98 kB 🟢 -4.38 kB
assets/AboutPanel-CdJlLBir.js (removed) 12 kB 🟢 -12 kB 🟢 -3.33 kB 🟢 -2.98 kB
assets/AboutPanel-DzkCR_0J.js (new) 12 kB 🔴 +12 kB 🔴 +3.32 kB 🔴 +2.98 kB
assets/ExtensionPanel-BvfsFdLs.js (removed) 9.78 kB 🟢 -9.78 kB 🟢 -2.82 kB 🟢 -2.5 kB
assets/ExtensionPanel-MmyGD5sV.js (new) 9.78 kB 🔴 +9.78 kB 🔴 +2.82 kB 🔴 +2.5 kB
assets/ServerConfigPanel-0m2dSzPr.js (removed) 6.85 kB 🟢 -6.85 kB 🟢 -2.28 kB 🟢 -2.03 kB
assets/ServerConfigPanel-DA1c_sST.js (new) 6.85 kB 🔴 +6.85 kB 🔴 +2.27 kB 🔴 +2.02 kB
assets/UserPanel-D6tEg805.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +2.15 kB 🔴 +1.9 kB
assets/UserPanel-DJ8w6vEX.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -2.16 kB 🟢 -1.9 kB
assets/cloudRemoteConfig-BbUtrHyh.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +900 B 🔴 +793 B
assets/cloudRemoteConfig-DgyaBX5b.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -903 B 🟢 -795 B
assets/refreshRemoteConfig-slkZ7M2z.js (new) 1.45 kB 🔴 +1.45 kB 🔴 +648 B 🔴 +552 B
assets/refreshRemoteConfig-Vra7X-sV.js (removed) 1.45 kB 🟢 -1.45 kB 🟢 -650 B 🟢 -553 B

Status: 10 added / 10 removed / 12 unchanged

User & Accounts — 17.1 kB (baseline 17.1 kB) • ⚪ 0 B

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-B5zFmV1L.js (removed) 3.57 kB 🟢 -3.57 kB 🟢 -1.26 kB 🟢 -1.08 kB
assets/auth-Ddo_gIKg.js (new) 3.57 kB 🔴 +3.57 kB 🔴 +1.26 kB 🔴 +1.07 kB
assets/SignUpForm-BivxO0_B.js (new) 3.16 kB 🔴 +3.16 kB 🔴 +1.28 kB 🔴 +1.14 kB
assets/SignUpForm-BpMqh7TV.js (removed) 3.16 kB 🟢 -3.16 kB 🟢 -1.29 kB 🟢 -1.14 kB
assets/UpdatePasswordContent-BzCUM01I.js (new) 2.66 kB 🔴 +2.66 kB 🔴 +1.19 kB 🔴 +1.06 kB
assets/UpdatePasswordContent-CveqmcNJ.js (removed) 2.66 kB 🟢 -2.66 kB 🟢 -1.19 kB 🟢 -1.05 kB
assets/authStore-CCvntHno.js (removed) 989 B 🟢 -989 B 🟢 -483 B 🟢 -427 B
assets/authStore-DUJDTFdf.js (new) 989 B 🔴 +989 B 🔴 +481 B 🔴 +425 B
assets/auth-Ct6ztQ7G.js (removed) 348 B 🟢 -348 B 🟢 -218 B 🟢 -188 B
assets/auth-CVjJwcP2.js (new) 348 B 🔴 +348 B 🔴 +217 B 🔴 +189 B

Status: 5 added / 5 removed / 2 unchanged

Editors & Dialogs — 109 kB (baseline 109 kB) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useShareDialog-BQx5Wwst.js (removed) 108 kB 🟢 -108 kB 🟢 -22.4 kB 🟢 -18.9 kB
assets/useShareDialog-DLY3wh9Y.js (new) 108 kB 🔴 +108 kB 🔴 +22.4 kB 🔴 +18.9 kB
assets/useSubscriptionDialog-BiKkjypu.js (removed) 969 B 🟢 -969 B 🟢 -478 B 🟢 -415 B
assets/useSubscriptionDialog-CmthCp8K.js (new) 969 B 🔴 +969 B 🔴 +474 B 🔴 +415 B

Status: 2 added / 2 removed

UI Components — 60.3 kB (baseline 60.3 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-B9K7D3Eq.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.79 kB 🟢 -3.37 kB
assets/ComfyQueueButton-CvsFHhLX.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.79 kB 🔴 +3.38 kB
assets/useTerminalTabs-BE82oWv3.js (removed) 10.7 kB 🟢 -10.7 kB 🟢 -3.6 kB 🟢 -3.17 kB
assets/useTerminalTabs-DVzCUhs7.js (new) 10.7 kB 🔴 +10.7 kB 🔴 +3.6 kB 🔴 +3.17 kB
assets/SubscribeButton-CrTFZ3e5.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.04 kB 🔴 +914 B
assets/SubscribeButton-DSkW9626.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.05 kB 🟢 -914 B
assets/cloudFeedbackTopbarButton-Dv0p0xyA.js (removed) 1.66 kB 🟢 -1.66 kB 🟢 -844 B 🟢 -748 B
assets/cloudFeedbackTopbarButton-y8JJQPI1.js (new) 1.66 kB 🔴 +1.66 kB 🔴 +842 B 🔴 +754 B
assets/ComfyQueueButton-D2SW2Fq9.js (removed) 1.03 kB 🟢 -1.03 kB 🟢 -491 B 🟢 -438 B
assets/ComfyQueueButton-DrhM7kln.js (new) 1.03 kB 🔴 +1.03 kB 🔴 +488 B 🔴 +441 B

Status: 5 added / 5 removed / 8 unchanged

Data & Services — 2.96 MB (baseline 2.96 MB) • ⚪ 0 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-CK_gKjGs.js (removed) 1.92 MB 🟢 -1.92 MB 🟢 -443 kB 🟢 -336 kB
assets/dialogService-qQZ8frRD.js (new) 1.92 MB 🔴 +1.92 MB 🔴 +443 kB 🔴 +336 kB
assets/api-BYBzzI7f.js (removed) 884 kB 🟢 -884 kB 🟢 -211 kB 🟢 -166 kB
assets/api-D7pl6e4U.js (new) 884 kB 🔴 +884 kB 🔴 +211 kB 🔴 +166 kB
assets/load3dService-BFJmYU3A.js (new) 92.5 kB 🔴 +92.5 kB 🔴 +19.7 kB 🔴 +16.9 kB
assets/load3dService-C9UIeMP_.js (removed) 92.5 kB 🟢 -92.5 kB 🟢 -19.7 kB 🟢 -16.9 kB
assets/workflowShareService-D97er5p3.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.88 kB 🔴 +4.32 kB
assets/workflowShareService-sNUCiqfb.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.88 kB 🟢 -4.32 kB
assets/keybindingService-BMJbJ7F2.js (removed) 13.8 kB 🟢 -13.8 kB 🟢 -3.66 kB 🟢 -3.21 kB
assets/keybindingService-CqKwb7Mp.js (new) 13.8 kB 🔴 +13.8 kB 🔴 +3.66 kB 🔴 +3.21 kB
assets/releaseStore-Cq45n-DQ.js (new) 8.12 kB 🔴 +8.12 kB 🔴 +2.28 kB 🔴 +2 kB
assets/releaseStore-DQQHsuC1.js (removed) 8.12 kB 🟢 -8.12 kB 🟢 -2.28 kB 🟢 -2 kB
assets/userStore-BabNURl0.js (removed) 2.24 kB 🟢 -2.24 kB 🟢 -870 B 🟢 -761 B
assets/userStore-kvl5WJoM.js (new) 2.24 kB 🔴 +2.24 kB 🔴 +869 B 🔴 +769 B
assets/audioService-CvRcUq72.js (removed) 1.8 kB 🟢 -1.8 kB 🟢 -880 B 🟢 -763 B
assets/audioService-DlNKZb07.js (new) 1.8 kB 🔴 +1.8 kB 🔴 +878 B 🔴 +764 B
assets/releaseStore-DBqoP5yK.js (removed) 993 B 🟢 -993 B 🟢 -482 B 🟢 -425 B
assets/releaseStore-sh1ESrlK.js (new) 993 B 🔴 +993 B 🔴 +479 B 🔴 +426 B
assets/workflowDraftStore-1xpzac0y.js (new) 969 B 🔴 +969 B 🔴 +473 B 🔴 +423 B
assets/workflowDraftStore-DzahZnKI.js (removed) 969 B 🟢 -969 B 🟢 -476 B 🟢 -422 B
assets/dialogService-CFtDUdUr.js (removed) 958 B 🟢 -958 B 🟢 -468 B 🟢 -417 B
assets/dialogService-DIpAVcJe.js (new) 958 B 🔴 +958 B 🔴 +466 B 🔴 +418 B
assets/settingStore-B27A-RAb.js (new) 956 B 🔴 +956 B 🔴 +469 B 🔴 +413 B
assets/settingStore-CwHBoaDu.js (removed) 956 B 🟢 -956 B 🟢 -472 B 🟢 -413 B
assets/assetsStore-BnRkAcRB.js (removed) 955 B 🟢 -955 B 🟢 -472 B 🟢 -417 B
assets/assetsStore-DRj9Xrmj.js (new) 955 B 🔴 +955 B 🔴 +469 B 🔴 +416 B

Status: 13 added / 13 removed / 4 unchanged

Utilities & Hooks — 334 kB (baseline 334 kB) • ⚪ 0 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-C76w8Huu.js (new) 232 kB 🔴 +232 kB 🔴 +51.3 kB 🔴 +41.8 kB
assets/useConflictDetection-DPjmlLA9.js (removed) 232 kB 🟢 -232 kB 🟢 -51.3 kB 🟢 -41.8 kB
assets/useLoad3dViewer-CahpGSQG.js (removed) 18.7 kB 🟢 -18.7 kB 🟢 -4.44 kB 🟢 -3.91 kB
assets/useLoad3dViewer-DUgOp0JU.js (new) 18.7 kB 🔴 +18.7 kB 🔴 +4.44 kB 🔴 +3.9 kB
assets/useLoad3d-CRX-1wiI.js (removed) 15 kB 🟢 -15 kB 🟢 -3.79 kB 🟢 -3.35 kB
assets/useLoad3d-D_ELBC3q.js (new) 15 kB 🔴 +15 kB 🔴 +3.79 kB 🔴 +3.38 kB
assets/useFeatureFlags-CD5qxCQe.js (removed) 5.78 kB 🟢 -5.78 kB 🟢 -1.75 kB 🟢 -1.48 kB
assets/useFeatureFlags-Dm_hioWm.js (new) 5.78 kB 🔴 +5.78 kB 🔴 +1.75 kB 🔴 +1.48 kB
assets/useCopyToClipboard-ChFrVYnR.js (new) 5.29 kB 🔴 +5.29 kB 🔴 +1.86 kB 🔴 +1.57 kB
assets/useCopyToClipboard-iRGVUYvY.js (removed) 5.29 kB 🟢 -5.29 kB 🟢 -1.86 kB 🟢 -1.57 kB
assets/useWorkspaceUI-Bj_SAce8.js (new) 3.34 kB 🔴 +3.34 kB 🔴 +980 B 🔴 +808 B
assets/useWorkspaceUI-CBW0M2gX.js (removed) 3.34 kB 🟢 -3.34 kB 🟢 -981 B 🟢 -810 B
assets/subscriptionCheckoutUtil-C9WLggWy.js (new) 2.97 kB 🔴 +2.97 kB 🔴 +1.31 kB 🔴 +1.14 kB
assets/subscriptionCheckoutUtil-GuzNFchM.js (removed) 2.97 kB 🟢 -2.97 kB 🟢 -1.31 kB 🟢 -1.14 kB
assets/assetPreviewUtil-BTcYsIMd.js (removed) 2.27 kB 🟢 -2.27 kB 🟢 -958 B 🟢 -832 B
assets/assetPreviewUtil-C1yCO5Fe.js (new) 2.27 kB 🔴 +2.27 kB 🔴 +958 B 🔴 +834 B
assets/useUpstreamValue-BUvCRIwO.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +803 B 🔴 +716 B
assets/useUpstreamValue-DSW3HchJ.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -805 B 🟢 -713 B
assets/useLoad3d-ZLgmvxHp.js (removed) 1.13 kB 🟢 -1.13 kB 🟢 -539 B 🟢 -478 B
assets/useLoad3d-zZ0TjZ-O.js (new) 1.13 kB 🔴 +1.13 kB 🔴 +537 B 🔴 +481 B
assets/useLoad3dViewer-CDyJ9r_9.js (removed) 1.07 kB 🟢 -1.07 kB 🟢 -506 B 🟢 -456 B
assets/useLoad3dViewer-Df1FcYKO.js (new) 1.07 kB 🔴 +1.07 kB 🔴 +505 B 🔴 +457 B
assets/useCurrentUser-AoGmRu-w.js (new) 955 B 🔴 +955 B 🔴 +469 B 🔴 +416 B
assets/useCurrentUser-CXzoUSmg.js (removed) 955 B 🟢 -955 B 🟢 -472 B 🟢 -416 B
assets/useWorkspaceSwitch-CrwYj4bU.js (new) 747 B 🔴 +747 B 🔴 +382 B 🔴 +333 B
assets/useWorkspaceSwitch-DVmkfacK.js (removed) 747 B 🟢 -747 B 🟢 -385 B 🟢 -332 B

Status: 13 added / 13 removed / 12 unchanged

Vendor & Third-Party — 9.8 MB (baseline 9.8 MB) • ⚪ 0 B

External libraries and shared vendor chunks

Status: 16 unchanged

Other — 8.43 MB (baseline 8.43 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-CI9D9gdM.js (removed) 76.8 kB 🟢 -76.8 kB 🟢 -19.9 kB 🟢 -16.9 kB
assets/core-DNqfcG55.js (new) 76.8 kB 🔴 +76.8 kB 🔴 +19.9 kB 🔴 +16.9 kB
assets/groupNode-7uk1FgsD.js (new) 74 kB 🔴 +74 kB 🔴 +18.5 kB 🔴 +16.3 kB
assets/groupNode-BhUW0yj4.js (removed) 74 kB 🟢 -74 kB 🟢 -18.5 kB 🟢 -16.3 kB
assets/WidgetSelect-B82b20Yb.js (new) 64.6 kB 🔴 +64.6 kB 🔴 +14.1 kB 🔴 +12.1 kB
assets/WidgetSelect-Dv72zhl7.js (removed) 64.6 kB 🟢 -64.6 kB 🟢 -14.1 kB 🟢 -12.2 kB
assets/SubscriptionRequiredDialogContentWorkspace-Dh-MDFUJ.js (new) 48.9 kB 🔴 +48.9 kB 🔴 +9.29 kB 🔴 +7.95 kB
assets/SubscriptionRequiredDialogContentWorkspace-q2OewIof.js (removed) 48.9 kB 🟢 -48.9 kB 🟢 -9.3 kB 🟢 -7.95 kB
assets/WidgetPainter-BbOrbfUV.js (removed) 33.3 kB 🟢 -33.3 kB 🟢 -8.11 kB 🟢 -7.2 kB
assets/WidgetPainter-gWTpa5Oh.js (new) 33.3 kB 🔴 +33.3 kB 🔴 +8.11 kB 🔴 +7.19 kB
assets/Load3DControls-DBRUcFw-.js (removed) 32.1 kB 🟢 -32.1 kB 🟢 -5.47 kB 🟢 -4.78 kB
assets/Load3DControls-DFxEYz6g.js (new) 32.1 kB 🔴 +32.1 kB 🔴 +5.47 kB 🔴 +4.77 kB
assets/WorkspacePanelContent-C9ZPvOpI.js (new) 29.9 kB 🔴 +29.9 kB 🔴 +6.33 kB 🔴 +5.56 kB
assets/WorkspacePanelContent-CqsTULD7.js (removed) 29.9 kB 🟢 -29.9 kB 🟢 -6.33 kB 🟢 -5.56 kB
assets/SubscriptionRequiredDialogContent-5zcXkq2D.js (removed) 28.2 kB 🟢 -28.2 kB 🟢 -7.17 kB 🟢 -6.31 kB
assets/SubscriptionRequiredDialogContent-BRGfJTbK.js (new) 28.2 kB 🔴 +28.2 kB 🔴 +7.17 kB 🔴 +6.3 kB
assets/Load3dViewerContent-B9IGE_K-.js (removed) 24.3 kB 🟢 -24.3 kB 🟢 -5.32 kB 🟢 -4.63 kB
assets/Load3dViewerContent-GQFpl6Iu.js (new) 24.3 kB 🔴 +24.3 kB 🔴 +5.32 kB 🔴 +4.63 kB
assets/WidgetImageCrop-BGlYHqL8.js (removed) 23.3 kB 🟢 -23.3 kB 🟢 -5.83 kB 🟢 -5.14 kB
assets/WidgetImageCrop-nkyJ-Jf5.js (new) 23.3 kB 🔴 +23.3 kB 🔴 +5.82 kB 🔴 +5.13 kB
assets/SubscriptionPanelContentWorkspace-Bwk3z2Gd.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.18 kB 🟢 -4.56 kB
assets/SubscriptionPanelContentWorkspace-y9adhJ65.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.18 kB 🔴 +4.55 kB
assets/SignInContent-BgG5LpQ2.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.29 kB 🟢 -4.63 kB
assets/SignInContent-DO8le8k0.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.29 kB 🔴 +4.63 kB
assets/CurrentUserPopoverWorkspace-BSeejrpm.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -4.84 kB 🟢 -4.32 kB
assets/CurrentUserPopoverWorkspace-C5Ug2VzY.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +4.83 kB 🔴 +4.33 kB
assets/WidgetInputNumber-DdE3_VfI.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.84 kB 🟢 -4.3 kB
assets/WidgetInputNumber-DNItb0M3.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.84 kB 🔴 +4.29 kB
assets/WidgetRecordAudio-CZncZk7c.js (removed) 18.1 kB 🟢 -18.1 kB 🟢 -5.18 kB 🟢 -4.64 kB
assets/WidgetRecordAudio-DhNnIpu4.js (new) 18.1 kB 🔴 +18.1 kB 🔴 +5.18 kB 🔴 +4.64 kB
assets/Load3D-BW0Y88Kt.js (new) 16.9 kB 🔴 +16.9 kB 🔴 +4.12 kB 🔴 +3.6 kB
assets/Load3D-CbDtxe2-.js (removed) 16.9 kB 🟢 -16.9 kB 🟢 -4.12 kB 🟢 -3.59 kB
assets/WidgetCurve-CasrK8Y-.js (new) 16.1 kB 🔴 +16.1 kB 🔴 +4.96 kB 🔴 +4.46 kB
assets/WidgetCurve-D5_KQ32H.js (removed) 16.1 kB 🟢 -16.1 kB 🟢 -4.97 kB 🟢 -4.46 kB
assets/load3d-BvLQl5hl.js (new) 15 kB 🔴 +15 kB 🔴 +4.32 kB 🔴 +3.75 kB
assets/load3d-Dmw-CZhE.js (removed) 15 kB 🟢 -15 kB 🟢 -4.32 kB 🟢 -3.73 kB
assets/WaveAudioPlayer-BM0H6qR7.js (removed) 13.4 kB 🟢 -13.4 kB 🟢 -3.68 kB 🟢 -3.23 kB
assets/WaveAudioPlayer-CuHpVQnE.js (new) 13.4 kB 🔴 +13.4 kB 🔴 +3.68 kB 🔴 +3.22 kB
assets/TeamWorkspacesDialogContent-B4vzbbAg.js (removed) 11.1 kB 🟢 -11.1 kB 🟢 -3.33 kB 🟢 -2.97 kB
assets/TeamWorkspacesDialogContent-Dk2SaoFP.js (new) 11.1 kB 🔴 +11.1 kB 🔴 +3.33 kB 🔴 +2.97 kB
assets/nodeTemplates-CTWJSZGe.js (removed) 9.58 kB 🟢 -9.58 kB 🟢 -3.38 kB 🟢 -2.97 kB
assets/nodeTemplates-DdeYdvN7.js (new) 9.58 kB 🔴 +9.58 kB 🔴 +3.37 kB 🔴 +2.97 kB
assets/InviteMemberDialogContent-BEfRQ0MX.js (new) 7.77 kB 🔴 +7.77 kB 🔴 +2.45 kB 🔴 +2.14 kB
assets/InviteMemberDialogContent-DSCruVYT.js (removed) 7.77 kB 🟢 -7.77 kB 🟢 -2.45 kB 🟢 -2.14 kB
assets/Load3DConfiguration-BuI2T5lE.js (removed) 6.6 kB 🟢 -6.6 kB 🟢 -2.04 kB 🟢 -1.78 kB
assets/Load3DConfiguration-DA4CPbis.js (new) 6.6 kB 🔴 +6.6 kB 🔴 +2.04 kB 🔴 +1.78 kB
assets/onboardingCloudRoutes-DcygmS8u.js (removed) 6.53 kB 🟢 -6.53 kB 🟢 -2.04 kB 🟢 -1.76 kB
assets/onboardingCloudRoutes-DL0PtOsn.js (new) 6.53 kB 🔴 +6.53 kB 🔴 +2.04 kB 🔴 +1.75 kB
assets/WidgetWithControl-CpAGViwQ.js (new) 5.99 kB 🔴 +5.99 kB 🔴 +2.38 kB 🔴 +2.14 kB
assets/WidgetWithControl-D7hUs53n.js (removed) 5.99 kB 🟢 -5.99 kB 🟢 -2.38 kB 🟢 -2.12 kB
assets/CreateWorkspaceDialogContent-BVOuF_-2.js (new) 5.95 kB 🔴 +5.95 kB 🔴 +2.15 kB 🔴 +1.87 kB
assets/CreateWorkspaceDialogContent-CLQWN8Py.js (removed) 5.95 kB 🟢 -5.95 kB 🟢 -2.15 kB 🟢 -1.89 kB
assets/FreeTierDialogContent-Cey5j8R9.js (removed) 5.82 kB 🟢 -5.82 kB 🟢 -2.05 kB 🟢 -1.8 kB
assets/FreeTierDialogContent-CuQ76ck1.js (new) 5.82 kB 🔴 +5.82 kB 🔴 +2.04 kB 🔴 +1.81 kB
assets/EditWorkspaceDialogContent-BJTpD9uB.js (new) 5.75 kB 🔴 +5.75 kB 🔴 +2.11 kB 🔴 +1.85 kB
assets/EditWorkspaceDialogContent-Dvn99d3e.js (removed) 5.75 kB 🟢 -5.75 kB 🟢 -2.11 kB 🟢 -1.84 kB
assets/WidgetTextarea-BF2AHq4R.js (removed) 5.53 kB 🟢 -5.53 kB 🟢 -2.17 kB 🟢 -1.92 kB
assets/WidgetTextarea-C0ACeG1y.js (new) 5.53 kB 🔴 +5.53 kB 🔴 +2.17 kB 🔴 +1.92 kB
assets/Preview3d-DFTg5jgE.js (removed) 5.36 kB 🟢 -5.36 kB 🟢 -1.79 kB 🟢 -1.57 kB
assets/Preview3d-QLSEn3NQ.js (new) 5.36 kB 🔴 +5.36 kB 🔴 +1.79 kB 🔴 +1.56 kB
assets/ValueControlPopover-ByRUWQ0g.js (new) 5.33 kB 🔴 +5.33 kB 🔴 +1.93 kB 🔴 +1.72 kB
assets/ValueControlPopover-m1WFr4LY.js (removed) 5.33 kB 🟢 -5.33 kB 🟢 -1.93 kB 🟢 -1.72 kB
assets/CancelSubscriptionDialogContent-CNKR55sd.js (removed) 5.22 kB 🟢 -5.22 kB 🟢 -1.95 kB 🟢 -1.7 kB
assets/CancelSubscriptionDialogContent-qGvAy-kB.js (new) 5.22 kB 🔴 +5.22 kB 🔴 +1.95 kB 🔴 +1.7 kB
assets/DeleteWorkspaceDialogContent-20ADMOXf.js (new) 4.65 kB 🔴 +4.65 kB 🔴 +1.79 kB 🔴 +1.55 kB
assets/DeleteWorkspaceDialogContent-f-tyDXlN.js (removed) 4.65 kB 🟢 -4.65 kB 🟢 -1.79 kB 🟢 -1.55 kB
assets/LeaveWorkspaceDialogContent-CVYT8t5l.js (removed) 4.48 kB 🟢 -4.48 kB 🟢 -1.74 kB 🟢 -1.51 kB
assets/LeaveWorkspaceDialogContent-DpAOoihT.js (new) 4.48 kB 🔴 +4.48 kB 🔴 +1.73 kB 🔴 +1.52 kB
assets/RemoveMemberDialogContent-B05Tr4n0.js (new) 4.46 kB 🔴 +4.46 kB 🔴 +1.69 kB 🔴 +1.47 kB
assets/RemoveMemberDialogContent-CUNuchE2.js (removed) 4.46 kB 🟢 -4.46 kB 🟢 -1.69 kB 🟢 -1.47 kB
assets/tierBenefits-C2DPs3nO.js (removed) 4.45 kB 🟢 -4.45 kB 🟢 -1.58 kB 🟢 -1.36 kB
assets/tierBenefits-X-iucza3.js (new) 4.45 kB 🔴 +4.45 kB 🔴 +1.57 kB 🔴 +1.36 kB
assets/RevokeInviteDialogContent-DlLHA68K.js (removed) 4.37 kB 🟢 -4.37 kB 🟢 -1.7 kB 🟢 -1.49 kB
assets/RevokeInviteDialogContent-Jlw_kmRL.js (new) 4.37 kB 🔴 +4.37 kB 🔴 +1.7 kB 🔴 +1.48 kB
assets/InviteMemberUpsellDialogContent-7IpQXsXL.js (removed) 4.27 kB 🟢 -4.27 kB 🟢 -1.57 kB 🟢 -1.37 kB
assets/InviteMemberUpsellDialogContent-DmDXgszS.js (new) 4.27 kB 🔴 +4.27 kB 🔴 +1.56 kB 🔴 +1.37 kB
assets/cloudSessionCookie-Cywza2xS.js (new) 4.12 kB 🔴 +4.12 kB 🔴 +1.48 kB 🔴 +1.29 kB
assets/cloudSessionCookie-PngY6sel.js (removed) 4.12 kB 🟢 -4.12 kB 🟢 -1.49 kB 🟢 -1.29 kB
assets/saveMesh-Cr4y4b3J.js (new) 3.92 kB 🔴 +3.92 kB 🔴 +1.68 kB 🔴 +1.48 kB
assets/saveMesh-DaDoXlib.js (removed) 3.92 kB 🟢 -3.92 kB 🟢 -1.68 kB 🟢 -1.48 kB
assets/Media3DTop-ChXrwGrm.js (removed) 3.85 kB 🟢 -3.85 kB 🟢 -1.62 kB 🟢 -1.43 kB
assets/Media3DTop-cspE71K2.js (new) 3.85 kB 🔴 +3.85 kB 🔴 +1.62 kB 🔴 +1.43 kB
assets/GlobalToast-Cu0Z8a1k.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.26 kB 🔴 +1.11 kB
assets/GlobalToast-DHeXWxSR.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.26 kB 🟢 -1.07 kB
assets/SubscribeToRun-25obNx7Y.js (new) 2.13 kB 🔴 +2.13 kB 🔴 +984 B 🔴 +870 B
assets/SubscribeToRun-o9hhgsZ5.js (removed) 2.13 kB 🟢 -2.13 kB 🟢 -984 B 🟢 -871 B
assets/MediaAudioTop-CgtIEFQR.js (new) 2.02 kB 🔴 +2.02 kB 🔴 +981 B 🔴 +830 B
assets/MediaAudioTop-q_O9Eo88.js (removed) 2.02 kB 🟢 -2.02 kB 🟢 -983 B 🟢 -878 B
assets/CloudRunButtonWrapper-DBqz9hvo.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +908 B 🔴 +808 B
assets/CloudRunButtonWrapper-Dj4yZtr_.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -912 B 🟢 -810 B
assets/graphHasMissingNodes-BselRpvM.js (removed) 1.83 kB 🟢 -1.83 kB 🟢 -861 B 🟢 -748 B
assets/graphHasMissingNodes-DSpiFuTN.js (new) 1.83 kB 🔴 +1.83 kB 🔴 +860 B 🔴 +756 B
assets/cloudBadges-BzXAG998.js (new) 1.77 kB 🔴 +1.77 kB 🔴 +890 B 🔴 +775 B
assets/cloudBadges-JdimmceW.js (removed) 1.77 kB 🟢 -1.77 kB 🟢 -893 B 🟢 -787 B
assets/cloudSubscription-CyXLRTQe.js (new) 1.68 kB 🔴 +1.68 kB 🔴 +811 B 🔴 +714 B
assets/cloudSubscription-Db-gOU7k.js (removed) 1.68 kB 🟢 -1.68 kB 🟢 -815 B 🟢 -712 B
assets/previousFullPath-C7uqb3YQ.js (removed) 1.53 kB 🟢 -1.53 kB 🟢 -695 B 🟢 -601 B
assets/previousFullPath-gRvLGpZe.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +692 B 🔴 +600 B
assets/Load3D-BaOkWnwH.js (removed) 1.34 kB 🟢 -1.34 kB 🟢 -611 B 🟢 -543 B
assets/Load3D-CknJXBiM.js (new) 1.34 kB 🔴 +1.34 kB 🔴 +613 B 🔴 +546 B
assets/nightlyBadges-C6RGACrT.js (new) 1.29 kB 🔴 +1.29 kB 🔴 +656 B 🔴 +578 B
assets/nightlyBadges-D65y8ADY.js (removed) 1.29 kB 🟢 -1.29 kB 🟢 -659 B 🟢 -580 B
assets/Load3dViewerContent-bF9Y3fXo.js (removed) 1.23 kB 🟢 -1.23 kB 🟢 -566 B 🟢 -500 B
assets/Load3dViewerContent-CBJ2liMS.js (new) 1.23 kB 🔴 +1.23 kB 🔴 +564 B 🔴 +502 B
assets/SubscriptionPanelContentWorkspace-Bd0Zf_8i.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +535 B 🔴 +467 B
assets/SubscriptionPanelContentWorkspace-CvfoMsUM.js (removed) 1.15 kB 🟢 -1.15 kB 🟢 -537 B 🟢 -467 B
assets/WidgetLegacy-BpK9-88d.js (new) 978 B 🔴 +978 B 🔴 +481 B 🔴 +423 B
assets/WidgetLegacy-Didx_B49.js (removed) 978 B 🟢 -978 B 🟢 -483 B 🟢 -422 B
assets/changeTracker-DGRZbS7X.js (removed) 952 B 🟢 -952 B 🟢 -472 B 🟢 -413 B
assets/changeTracker-DtyigRwu.js (new) 952 B 🔴 +952 B 🔴 +469 B 🔴 +415 B

Status: 55 added / 55 removed / 79 unchanged

⚡ Performance Report

⚠️ 5 regressions detected

Metric Baseline PR (median) Δ Sig
canvas-zoom-sweep: layout duration 1ms 1ms +34% ⚠️ z=2.0
canvas-zoom-sweep: frame duration 17ms 17ms +0% ⚠️ z=2.2
large-graph-pan: task duration 1136ms 1189ms +5% ⚠️ z=2.5
minimap-idle: task duration 531ms 641ms +21% ⚠️ z=2.4
minimap-idle: script duration 98ms 118ms +21% ⚠️ z=2.0
All metrics
Metric Baseline PR (median) Δ Sig
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 10ms 10ms +1% z=-0.8
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 10 10 +0% z=-2.1
canvas-idle: task duration 368ms 426ms +16% z=1.0
canvas-idle: script duration 19ms 25ms +32% z=-0.1
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: frame duration 17ms 17ms +0% z=0.8
canvas-idle: heap used 60.1 MB 61.3 MB +2%
canvas-idle: DOM nodes 20 20 +0% z=-2.0
canvas-idle: event listeners 6 6 +0% z=-1.2
canvas-mouse-sweep: layout duration 3ms 4ms +15% z=1.6
canvas-mouse-sweep: style recalc duration 43ms 38ms -12% z=-1.5
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 81 75 -7% z=-1.6
canvas-mouse-sweep: task duration 955ms 802ms -16% z=-1.1
canvas-mouse-sweep: script duration 135ms 143ms +6% z=1.1
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: frame duration 17ms 17ms -0% z=-2.6
canvas-mouse-sweep: heap used 55.6 MB 56.6 MB +2%
canvas-mouse-sweep: DOM nodes 65 58 -11% z=-1.7
canvas-mouse-sweep: event listeners 6 6 +0% z=-0.6
canvas-zoom-sweep: layout duration 1ms 1ms +34% ⚠️ z=2.0
canvas-zoom-sweep: style recalc duration 17ms 21ms +21% z=1.0
canvas-zoom-sweep: layout count 6 6 +0%
canvas-zoom-sweep: style recalc count 31 31 +0% z=-0.6
canvas-zoom-sweep: task duration 296ms 358ms +21% z=1.3
canvas-zoom-sweep: script duration 24ms 32ms +37% z=1.7
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: frame duration 17ms 17ms +0% ⚠️ z=2.2
canvas-zoom-sweep: heap used 64.2 MB 64.3 MB +0%
canvas-zoom-sweep: DOM nodes 79 80 +1% z=1.0
canvas-zoom-sweep: event listeners 19 19 +0% z=-0.9
dom-widget-clipping: layout duration 0ms 0ms +0%
dom-widget-clipping: style recalc duration 9ms 9ms +1% z=-0.7
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 13 13 +0% z=-0.2
dom-widget-clipping: task duration 344ms 362ms +5% z=-0.2
dom-widget-clipping: script duration 67ms 67ms +1% z=-0.1
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: frame duration 17ms 17ms -0% z=0.9
dom-widget-clipping: heap used 46.9 MB 46.7 MB -1%
dom-widget-clipping: DOM nodes 22 22 +0% z=-0.1
dom-widget-clipping: event listeners 2 2 +0% variance too high
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 9ms 10ms +16% z=-1.7
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 10 11 +10% z=-1.9
large-graph-idle: task duration 539ms 625ms +16% z=1.5
large-graph-idle: script duration 97ms 115ms +18% z=1.2
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: frame duration 17ms 17ms +0% z=0.6
large-graph-idle: heap used 54.1 MB 53.6 MB -1%
large-graph-idle: DOM nodes -258 -258 +0% z=-312.8
large-graph-idle: event listeners -127 -127 +0% z=-25.1
large-graph-pan: layout duration 0ms 0ms +0%
large-graph-pan: style recalc duration 16ms 16ms -2% z=-1.4
large-graph-pan: layout count 0 0 +0%
large-graph-pan: style recalc count 69 68 -1% z=-2.4
large-graph-pan: task duration 1136ms 1189ms +5% ⚠️ z=2.5
large-graph-pan: script duration 455ms 434ms -5% z=1.3
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: frame duration 17ms 17ms -0% z=-1.8
large-graph-pan: heap used 69.0 MB 65.6 MB -5%
large-graph-pan: DOM nodes -260 -261 +0% z=-169.7
large-graph-pan: event listeners -125 -127 +2% z=-159.3
large-graph-zoom: layout duration 7ms 8ms +3%
large-graph-zoom: style recalc duration 17ms 17ms -0%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 66 65 -2%
large-graph-zoom: task duration 1357ms 1430ms +5%
large-graph-zoom: script duration 511ms 530ms +4%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: frame duration 17ms 17ms +0%
large-graph-zoom: heap used 59.5 MB 61.9 MB +4%
large-graph-zoom: DOM nodes -262 -266 +2%
large-graph-zoom: event listeners -123 -123 +0%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 9ms 10ms +20% z=1.0
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 10 10 +0% z=0.6
minimap-idle: task duration 531ms 641ms +21% ⚠️ z=2.4
minimap-idle: script duration 98ms 118ms +21% ⚠️ z=2.0
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: frame duration 17ms 17ms +0% z=-2.0
minimap-idle: heap used 54.4 MB 54.8 MB +1%
minimap-idle: DOM nodes -259 -260 +0% z=-204.1
minimap-idle: event listeners -125 -127 +2% z=-199.3
subgraph-dom-widget-clipping: layout duration 0ms 0ms +0%
subgraph-dom-widget-clipping: style recalc duration 11ms 14ms +21% z=0.9
subgraph-dom-widget-clipping: layout count 0 0 +0%
subgraph-dom-widget-clipping: style recalc count 48 48 +0% z=0.1
subgraph-dom-widget-clipping: task duration 359ms 380ms +6% z=0.1
subgraph-dom-widget-clipping: script duration 126ms 128ms +2% z=0.0
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: frame duration 17ms 17ms +0% z=1.2
subgraph-dom-widget-clipping: heap used 47.1 MB 47.1 MB -0%
subgraph-dom-widget-clipping: DOM nodes 22 22 +0% z=-0.2
subgraph-dom-widget-clipping: event listeners 8 6 -25% z=-1.7
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 10ms 10ms -0% z=-0.4
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 11 11 +0% z=0.2
subgraph-idle: task duration 339ms 419ms +24% z=1.6
subgraph-idle: script duration 18ms 25ms +36% z=1.7
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: frame duration 17ms 17ms +0% z=1.5
subgraph-idle: heap used 60.0 MB 59.4 MB -1%
subgraph-idle: DOM nodes 22 20 -9% z=-1.2
subgraph-idle: event listeners 6 6 +0% variance too high
subgraph-mouse-sweep: layout duration 4ms 5ms +15% z=0.9
subgraph-mouse-sweep: style recalc duration 44ms 40ms -9% z=-0.8
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 84 76 -10% z=-2.1
subgraph-mouse-sweep: task duration 912ms 750ms -18% z=-0.2
subgraph-mouse-sweep: script duration 101ms 109ms +8% z=1.3
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: frame duration 17ms 17ms -0% z=-1.4
subgraph-mouse-sweep: heap used 52.4 MB 51.7 MB -1%
subgraph-mouse-sweep: DOM nodes 71 63 -11% z=-1.8
subgraph-mouse-sweep: event listeners 6 4 -33% variance too high
vue-large-graph-idle: layout duration 0ms 0ms +0%
vue-large-graph-idle: style recalc duration 0ms 0ms +0%
vue-large-graph-idle: layout count 0 0 +0%
vue-large-graph-idle: style recalc count 0 0 +0%
vue-large-graph-idle: task duration 12198ms 12506ms +3%
vue-large-graph-idle: script duration 609ms 613ms +1%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: frame duration 18ms 18ms +0%
vue-large-graph-idle: heap used 157.6 MB 157.6 MB -0%
vue-large-graph-idle: DOM nodes -8333 -8331 -0%
vue-large-graph-idle: event listeners -16464 -16466 +0%
vue-large-graph-pan: layout duration 0ms 0ms +0%
vue-large-graph-pan: style recalc duration 14ms 16ms +17%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 64 69 +8%
vue-large-graph-pan: task duration 14190ms 14766ms +4%
vue-large-graph-pan: script duration 860ms 873ms +1%
vue-large-graph-pan: TBT 0ms 0ms +0%
vue-large-graph-pan: frame duration 20ms 18ms -8%
vue-large-graph-pan: heap used 164.8 MB 153.2 MB -7%
vue-large-graph-pan: DOM nodes -8333 -8331 -0%
vue-large-graph-pan: event listeners -16460 -16458 -0%
workflow-execution: layout duration 1ms 1ms -3% z=-1.6
workflow-execution: style recalc duration 26ms 27ms +4% z=1.4
workflow-execution: layout count 5 5 +0% z=0.1
workflow-execution: style recalc count 19 18 -5% z=0.1
workflow-execution: task duration 126ms 133ms +6% z=0.9
workflow-execution: script duration 31ms 32ms +4% z=1.0
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: frame duration 17ms 17ms +0% z=1.6
workflow-execution: heap used 46.7 MB 46.7 MB +0%
workflow-execution: DOM nodes 157 158 +1% z=-0.4
workflow-execution: event listeners 71 69 -3% z=3.9
Historical variance (last 15 runs)
Metric μ σ CV
canvas-idle: layout duration 0ms 0ms 0.0%
canvas-idle: style recalc duration 11ms 1ms 8.2%
canvas-idle: layout count 0 0 0.0%
canvas-idle: style recalc count 11 1 5.0%
canvas-idle: task duration 395ms 31ms 7.9%
canvas-idle: script duration 25ms 2ms 8.8%
canvas-idle: TBT 0ms 0ms 0.0%
canvas-idle: frame duration 17ms 0ms 0.0%
canvas-idle: DOM nodes 23 1 5.6%
canvas-idle: event listeners 12 5 40.9%
canvas-mouse-sweep: layout duration 4ms 0ms 5.4%
canvas-mouse-sweep: style recalc duration 43ms 3ms 7.4%
canvas-mouse-sweep: layout count 12 0 0.0%
canvas-mouse-sweep: style recalc count 79 2 3.0%
canvas-mouse-sweep: task duration 865ms 58ms 6.7%
canvas-mouse-sweep: script duration 136ms 6ms 4.8%
canvas-mouse-sweep: TBT 0ms 0ms 0.0%
canvas-mouse-sweep: frame duration 17ms 0ms 0.0%
canvas-mouse-sweep: DOM nodes 62 3 4.2%
canvas-mouse-sweep: event listeners 8 4 49.4%
canvas-zoom-sweep: layout duration 1ms 0ms 7.0%
canvas-zoom-sweep: style recalc duration 19ms 2ms 8.0%
canvas-zoom-sweep: layout count 6 0 0.0%
canvas-zoom-sweep: style recalc count 31 0 1.5%
canvas-zoom-sweep: task duration 327ms 23ms 7.1%
canvas-zoom-sweep: script duration 27ms 3ms 11.1%
canvas-zoom-sweep: TBT 0ms 0ms 0.0%
canvas-zoom-sweep: frame duration 17ms 0ms 0.0%
canvas-zoom-sweep: DOM nodes 79 1 1.0%
canvas-zoom-sweep: event listeners 24 5 21.8%
dom-widget-clipping: layout duration 0ms 0ms 0.0%
dom-widget-clipping: style recalc duration 10ms 1ms 8.0%
dom-widget-clipping: layout count 0 0 0.0%
dom-widget-clipping: style recalc count 13 0 3.8%
dom-widget-clipping: task duration 365ms 16ms 4.5%
dom-widget-clipping: script duration 68ms 3ms 4.8%
dom-widget-clipping: TBT 0ms 0ms 0.0%
dom-widget-clipping: frame duration 17ms 0ms 0.0%
dom-widget-clipping: DOM nodes 22 1 6.4%
dom-widget-clipping: event listeners 8 6 81.2%
large-graph-idle: layout duration 0ms 0ms 0.0%
large-graph-idle: style recalc duration 12ms 1ms 8.6%
large-graph-idle: layout count 0 0 0.0%
large-graph-idle: style recalc count 12 0 2.7%
large-graph-idle: task duration 542ms 54ms 10.0%
large-graph-idle: script duration 102ms 11ms 10.3%
large-graph-idle: TBT 0ms 0ms 0.0%
large-graph-idle: frame duration 17ms 0ms 0.0%
large-graph-idle: DOM nodes 25 1 3.7%
large-graph-idle: event listeners 26 6 23.2%
large-graph-pan: layout duration 0ms 0ms 0.0%
large-graph-pan: style recalc duration 17ms 1ms 4.6%
large-graph-pan: layout count 0 0 0.0%
large-graph-pan: style recalc count 70 1 0.9%
large-graph-pan: task duration 1082ms 43ms 4.0%
large-graph-pan: script duration 408ms 20ms 4.8%
large-graph-pan: TBT 0ms 0ms 0.0%
large-graph-pan: frame duration 17ms 0ms 0.0%
large-graph-pan: DOM nodes 19 2 8.7%
large-graph-pan: event listeners 5 1 16.8%
minimap-idle: layout duration 0ms 0ms 0.0%
minimap-idle: style recalc duration 10ms 1ms 8.6%
minimap-idle: layout count 0 0 0.0%
minimap-idle: style recalc count 10 1 7.1%
minimap-idle: task duration 527ms 47ms 9.0%
minimap-idle: script duration 98ms 10ms 10.1%
minimap-idle: TBT 0ms 0ms 0.0%
minimap-idle: frame duration 17ms 0ms 0.0%
minimap-idle: DOM nodes 19 1 7.1%
minimap-idle: event listeners 5 1 14.4%
subgraph-dom-widget-clipping: layout duration 0ms 0ms 0.0%
subgraph-dom-widget-clipping: style recalc duration 13ms 1ms 7.4%
subgraph-dom-widget-clipping: layout count 0 0 0.0%
subgraph-dom-widget-clipping: style recalc count 48 1 1.2%
subgraph-dom-widget-clipping: task duration 378ms 18ms 4.9%
subgraph-dom-widget-clipping: script duration 128ms 6ms 4.9%
subgraph-dom-widget-clipping: TBT 0ms 0ms 0.0%
subgraph-dom-widget-clipping: frame duration 17ms 0ms 0.0%
subgraph-dom-widget-clipping: DOM nodes 22 1 5.0%
subgraph-dom-widget-clipping: event listeners 16 6 36.0%
subgraph-idle: layout duration 0ms 0ms 0.0%
subgraph-idle: style recalc duration 10ms 1ms 7.5%
subgraph-idle: layout count 0 0 0.0%
subgraph-idle: style recalc count 11 1 6.0%
subgraph-idle: task duration 370ms 31ms 8.5%
subgraph-idle: script duration 20ms 3ms 13.2%
subgraph-idle: TBT 0ms 0ms 0.0%
subgraph-idle: frame duration 17ms 0ms 0.0%
subgraph-idle: DOM nodes 22 1 6.9%
subgraph-idle: event listeners 10 7 64.5%
subgraph-mouse-sweep: layout duration 5ms 0ms 6.8%
subgraph-mouse-sweep: style recalc duration 42ms 3ms 7.8%
subgraph-mouse-sweep: layout count 16 0 0.0%
subgraph-mouse-sweep: style recalc count 80 2 2.4%
subgraph-mouse-sweep: task duration 766ms 69ms 9.0%
subgraph-mouse-sweep: script duration 101ms 7ms 6.5%
subgraph-mouse-sweep: TBT 0ms 0ms 0.0%
subgraph-mouse-sweep: frame duration 17ms 0ms 0.0%
subgraph-mouse-sweep: DOM nodes 67 2 3.3%
subgraph-mouse-sweep: event listeners 8 4 52.6%
workflow-execution: layout duration 2ms 0ms 9.4%
workflow-execution: style recalc duration 24ms 2ms 9.1%
workflow-execution: layout count 5 1 11.0%
workflow-execution: style recalc count 18 2 11.5%
workflow-execution: task duration 123ms 11ms 8.8%
workflow-execution: script duration 29ms 3ms 10.2%
workflow-execution: TBT 0ms 0ms 0.0%
workflow-execution: frame duration 17ms 0ms 0.0%
workflow-execution: DOM nodes 161 7 4.4%
workflow-execution: event listeners 52 4 8.4%
Trend (last 15 commits on main)
Metric Trend Dir Latest
canvas-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: style recalc duration ▇▇▆▆▃█▄▃▄▃▇▄▁▆▇ ➡️ 11ms
canvas-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
canvas-idle: style recalc count █▃▅▂▅▆▃▁▂▁▂▅▆▅▆ ➡️ 12
canvas-idle: task duration ▃▃▃▆▂▃▃▅▆▂█▃▁▃▃ ➡️ 391ms
canvas-idle: script duration ▄▃▅▇▂▅▃▆▇▅█▄▁▅▆ ➡️ 27ms
canvas-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: frame duration ▆▃▆▁▆▃▆█▆▆▄▃▃▄▃ ➡️ 17ms
canvas-idle: heap used ➡️ NaN MB
canvas-idle: DOM nodes █▇▆▅▃▇▃▁▂▂▅▆▆▆▇ ➡️ 24
canvas-idle: event listeners ▅█▅▄▁▅▁▁▁▄▅▅▁▅▄ 📉 11
canvas-mouse-sweep: layout duration ▁▃▂▄▁▂▁▃▆▂█▇▆▄▃ ➡️ 4ms
canvas-mouse-sweep: style recalc duration ▄▄▂▄▁▂▃▃▅▄█▆▂▄▄ ➡️ 43ms
canvas-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 12
canvas-mouse-sweep: style recalc count █▅▄▃▂▂▁▄▄▅▆▅▂▇▄ ➡️ 79
canvas-mouse-sweep: task duration █▆▄▂▂▃▂▄▄▅█▆▁▆▄ ➡️ 868ms
canvas-mouse-sweep: script duration ▄▅▄▆▄▆▆▆▅▅█▆▁▅▆ ➡️ 139ms
canvas-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-mouse-sweep: frame duration ▆█▆▃▁▃▁▆▆▁▃▆▆▃▃ ➡️ 17ms
canvas-mouse-sweep: heap used ➡️ NaN MB
canvas-mouse-sweep: DOM nodes █▅▃▃▁▂▂▃▂▄▆▅▃▅▅ ➡️ 64
canvas-mouse-sweep: event listeners █▁▁▁▁▁▇▁▁▁██▇▁█ 📈 13
canvas-zoom-sweep: layout duration ▆▅▅▄▁▁█▅▃▅▇▆▁▂▆ ➡️ 1ms
canvas-zoom-sweep: style recalc duration ▆▅▄▆▅▃█▆▇▅▇▄▁▃▅ ➡️ 20ms
canvas-zoom-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 6
canvas-zoom-sweep: style recalc count ▁▁▃▄▆▃▆█▄▄▆▁▆▁▆ ➡️ 32
canvas-zoom-sweep: task duration ▄▂▁▇▂▂▄▅▆▃█▄▁▁▅ ➡️ 338ms
canvas-zoom-sweep: script duration ▃▃▂▇▂▂▅▇▆▅█▄▁▂▆ ➡️ 30ms
canvas-zoom-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-zoom-sweep: frame duration ▅▅█▄▅▁▁▁▅▁▁▅▄▅▁ ➡️ 17ms
canvas-zoom-sweep: heap used ➡️ NaN MB
canvas-zoom-sweep: DOM nodes ▄▃▁▅█▁▃▆▄▅▅▃▃▄▃ ➡️ 79
canvas-zoom-sweep: event listeners ▁▁▂▅█▂▁▅▁▅▅▄▁▅▁ ➡️ 19
dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: style recalc duration ▆▆▂▆▄▃██▄▁▆▇▆▃▅ ➡️ 10ms
dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
dom-widget-clipping: style recalc count ▇█▅█▅▄█▇▇▁▇▄▇▂▅ ➡️ 13
dom-widget-clipping: task duration ▃▃▁▅▄▃▅▆▅▂▇█▁▅▅ ➡️ 371ms
dom-widget-clipping: script duration ▅▄▄▆▆▅▇▇▆▃█▇▁▇▇ ➡️ 71ms
dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: frame duration ▂▄▅▅▂▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
dom-widget-clipping: heap used ➡️ NaN MB
dom-widget-clipping: DOM nodes ▇▇▄▇▅▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: event listeners ▅▅▅▅▁▅██▁▁▁▁█▁▁ 📉 2
large-graph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: style recalc duration ▅▅▅▆▄▅▃▄▅▅▆█▁▄▆ ➡️ 13ms
large-graph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-idle: style recalc count █▆█▃▃▁▃▆▃▆▆▃▆██ ➡️ 12
large-graph-idle: task duration ▂▃▂▆▂▃▃▇▅▃██▁▂▅ ➡️ 569ms
large-graph-idle: script duration ▄▅▄▆▄▅▅▇▆▅█▆▁▃▆ ➡️ 110ms
large-graph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: frame duration ▅▅▅▅▅▂▁▂▄▅▄▂▂▅█ ➡️ 17ms
large-graph-idle: heap used ➡️ NaN MB
large-graph-idle: DOM nodes ▆█▅▂▅▃▁▂▃▅▅▆▂▆▅ ➡️ 25
large-graph-idle: event listeners ███▇██▄▁▄▇▇█▂█▇ ➡️ 29
large-graph-pan: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: style recalc duration ▃▂▄▄▁▅▂▂▁▄▄█▃▁▂ ➡️ 17ms
large-graph-pan: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-pan: style recalc count ▆▃█▂▃▂▂▂▁▇▅▃█▆▃ ➡️ 69
large-graph-pan: task duration ▄▃▄▆▄▄▄▆▄▄█▆▁▂▅ ➡️ 1100ms
large-graph-pan: script duration ▅▄▅▆▆▅▄▆▄▅█▄▁▄▅ ➡️ 413ms
large-graph-pan: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: frame duration ▆▃▃▆█▃▁█▆▆▆▆█▁▆ ➡️ 17ms
large-graph-pan: heap used ➡️ NaN MB
large-graph-pan: DOM nodes ▅▃▆▂▄▁▃▁▁▅▁▂█▅▂ ➡️ 18
large-graph-pan: event listeners █▆█▁▁▆▁▁▃▆▁▃██▃ ➡️ 5
minimap-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: style recalc duration ▄█▁█▅▅█▅▅▃▅▁▁▄▆ ➡️ 10ms
minimap-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
minimap-idle: style recalc count ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 9
minimap-idle: task duration ▃▄▁▅▁▃▄▅▇▃█▅▁▁▅ ➡️ 547ms
minimap-idle: script duration ▄▆▃▇▃▅▆▆▇▅█▅▁▃▆ ➡️ 106ms
minimap-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: frame duration ▃▆▆▃█▁█▆▆▃▃▆█▆█ ➡️ 17ms
minimap-idle: heap used ➡️ NaN MB
minimap-idle: DOM nodes ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 19
minimap-idle: event listeners ▃▃▆▁▁▁▃▁▁▆▁▃█▆▁ ➡️ 4
subgraph-dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: style recalc duration ▂▄▃▅▅▃▂▅▇▃▄█▁▄▆ ➡️ 14ms
subgraph-dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-dom-widget-clipping: style recalc count ▇█▆▃▆▃▁▆█▇▃▆▇█▅ ➡️ 48
subgraph-dom-widget-clipping: task duration ▂▃▃▆▅▅▂▅█▂▆█▁▂▇ ➡️ 398ms
subgraph-dom-widget-clipping: script duration ▃▃▃▄▅▅▂▄█▂▅▇▁▂▅ ➡️ 131ms
subgraph-dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: frame duration ▅▄▄▄▄▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-dom-widget-clipping: heap used ➡️ NaN MB
subgraph-dom-widget-clipping: DOM nodes ▅▇▅▂▅▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: event listeners ▅▅▅▂▅▁▅██▁▁█▅█▅ 📈 16
subgraph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: style recalc duration ▁▇▃▆▂▄▂▃▃▆▆▄▃▇█ ➡️ 12ms
subgraph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-idle: style recalc count ▃▆▃▃▂▅▁▂▁▆▃▃██▇ ➡️ 12
subgraph-idle: task duration ▁▃▁▇▁▁▃▆▅▂█▅▁▁▄ ➡️ 378ms
subgraph-idle: script duration ▁▃▂▇▁▂▃▇▆▂█▅▂▁▅ ➡️ 22ms
subgraph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: frame duration ▆▆█▁▆▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-idle: heap used ➡️ NaN MB
subgraph-idle: DOM nodes ▃▅▃▂▁▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: event listeners ▁▅▁▁▁▁▁▁▁▅▄▁███ 📈 21
subgraph-mouse-sweep: layout duration ▁▄▄▄▃▃▅▅▅▂█▇▂▃▆ ➡️ 5ms
subgraph-mouse-sweep: style recalc duration ▃▂▄▅▂▃▄▅█▃█▆▁▂▅ ➡️ 43ms
subgraph-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 16
subgraph-mouse-sweep: style recalc count ▅▂▅▅▁▄▃▅█▅▆▄▂▄▅ ➡️ 81
subgraph-mouse-sweep: task duration ▃▂▄▅▂▄▄▅▇▄█▆▁▃▅ ➡️ 785ms
subgraph-mouse-sweep: script duration ▄▅▄▇▅▅▆▇▆▅██▁▄▆ ➡️ 105ms
subgraph-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-mouse-sweep: frame duration ▅▄▁▃▃▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
subgraph-mouse-sweep: heap used ➡️ NaN MB
subgraph-mouse-sweep: DOM nodes ▅▁▄▅▁▄▃▃█▅▅▄▂▅▃ ➡️ 66
subgraph-mouse-sweep: event listeners ▇▁▂▇▁▂▂▂█▇▂▂▇▇▂ 📈 5
workflow-execution: layout duration ▁▆▁▃▂▄▃▂▃▃▅█▄▂▅ ➡️ 2ms
workflow-execution: style recalc duration ▃▇▅▇▁▅▆▇█▁██▂▄▆ ➡️ 25ms
workflow-execution: layout count ▁█▂▃▂▃▃▁▃▃▄▃▂▃▂ ➡️ 5
workflow-execution: style recalc count ▃█▅▇▁▄▅▆▅▅▅▅▄▄▂ ➡️ 15
workflow-execution: task duration ▂▅▄▅▁▄▆▆▆▁▇█▁▃▃ ➡️ 120ms
workflow-execution: script duration ▄▃▄▄▃▅▄▅▆▂▇█▁▃▄ ➡️ 29ms
workflow-execution: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
workflow-execution: frame duration ▆▆▆▄▆▆▃▄▁▄█▆▅▄▆ ➡️ 17ms
workflow-execution: heap used ➡️ NaN MB
workflow-execution: DOM nodes ▂█▃▆▁▄▃▅▃█▃▃▄▃▁ ➡️ 152
workflow-execution: event listeners ▅███▁▅███▁██▅█▅ ➡️ 49
Raw data
{
  "timestamp": "2026-03-28T09:44:30.714Z",
  "gitSha": "2ed65a1bb6f4e3c7d3a00f4a72565b670cf954da",
  "branch": "fix/selection-bounding-box-footer-overlay",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 1999.4509999999934,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.081999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 405.97099999999995,
      "heapDeltaBytes": 20838132,
      "heapUsedBytes": 64988488,
      "domNodes": 20,
      "jsHeapTotalBytes": 22806528,
      "scriptDurationMs": 24.274000000000004,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.680000000000017
    },
    {
      "name": "canvas-idle",
      "durationMs": 2012.943000000007,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.857,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 425.798,
      "heapDeltaBytes": 20019652,
      "heapUsedBytes": 63032084,
      "domNodes": 19,
      "jsHeapTotalBytes": 22806528,
      "scriptDurationMs": 24.970999999999997,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "canvas-idle",
      "durationMs": 2033.0350000000408,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 11.869,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 445.90399999999994,
      "heapDeltaBytes": 20125408,
      "heapUsedBytes": 64317724,
      "domNodes": 23,
      "jsHeapTotalBytes": 23068672,
      "scriptDurationMs": 29.357000000000003,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1817.766000000006,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 37.271,
      "layouts": 12,
      "layoutDurationMs": 3.979999999999999,
      "taskDurationMs": 800.6560000000001,
      "heapDeltaBytes": 15796068,
      "heapUsedBytes": 58347308,
      "domNodes": 58,
      "jsHeapTotalBytes": 23068672,
      "scriptDurationMs": 142.87999999999997,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1804.0659999999775,
      "styleRecalcs": 72,
      "styleRecalcDurationMs": 37.795,
      "layouts": 12,
      "layoutDurationMs": 3.92,
      "taskDurationMs": 802.2119999999999,
      "heapDeltaBytes": 16338596,
      "heapUsedBytes": 59317652,
      "domNodes": 53,
      "jsHeapTotalBytes": 22544384,
      "scriptDurationMs": 133.89800000000002,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 2017.5930000000335,
      "styleRecalcs": 82,
      "styleRecalcDurationMs": 44.869,
      "layouts": 12,
      "layoutDurationMs": 3.4799999999999995,
      "taskDurationMs": 1006.411,
      "heapDeltaBytes": 15862240,
      "heapUsedBytes": 59568200,
      "domNodes": 68,
      "jsHeapTotalBytes": 23330816,
      "scriptDurationMs": 142.536,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1750.267000000008,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 26.104999999999997,
      "layouts": 6,
      "layoutDurationMs": 0.7309999999999999,
      "taskDurationMs": 345.49600000000004,
      "heapDeltaBytes": 24650156,
      "heapUsedBytes": 66987364,
      "domNodes": 80,
      "jsHeapTotalBytes": 19660800,
      "scriptDurationMs": 31.088000000000005,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1724.775999999963,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 20.326999999999998,
      "layouts": 6,
      "layoutDurationMs": 0.743,
      "taskDurationMs": 357.809,
      "heapDeltaBytes": 24820680,
      "heapUsedBytes": 67584844,
      "domNodes": 80,
      "jsHeapTotalBytes": 20971520,
      "scriptDurationMs": 32.338,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1738.1199999999808,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 20.650000000000002,
      "layouts": 6,
      "layoutDurationMs": 0.735,
      "taskDurationMs": 376.126,
      "heapDeltaBytes": 24615376,
      "heapUsedBytes": 67388132,
      "domNodes": 79,
      "jsHeapTotalBytes": 20447232,
      "scriptDurationMs": 36.032000000000004,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 595.1739999999859,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 9.344000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 361.813,
      "heapDeltaBytes": 6194896,
      "heapUsedBytes": 48918916,
      "domNodes": 22,
      "jsHeapTotalBytes": 13631488,
      "scriptDurationMs": 67.427,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 559.1039999999907,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 9.213,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 346.35900000000004,
      "heapDeltaBytes": 6864736,
      "heapUsedBytes": 49261480,
      "domNodes": 22,
      "jsHeapTotalBytes": 12845056,
      "scriptDurationMs": 66.509,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 589.1079999998965,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 10.890999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 376.865,
      "heapDeltaBytes": -2158000,
      "heapUsedBytes": 48768776,
      "domNodes": 21,
      "jsHeapTotalBytes": 15204352,
      "scriptDurationMs": 70.494,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2037.4889999999937,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 10.454999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 613.399,
      "heapDeltaBytes": 19949464,
      "heapUsedBytes": 71555948,
      "domNodes": -254,
      "jsHeapTotalBytes": 15462400,
      "scriptDurationMs": 110.43700000000001,
      "eventListeners": -125,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2031.926999999996,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 12.164000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 624.5959999999999,
      "heapDeltaBytes": 4327564,
      "heapUsedBytes": 54851328,
      "domNodes": -258,
      "jsHeapTotalBytes": 15405056,
      "scriptDurationMs": 114.758,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2047.2469999999703,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 9.815999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 694.7700000000001,
      "heapDeltaBytes": 3774524,
      "heapUsedBytes": 56151868,
      "domNodes": -261,
      "jsHeapTotalBytes": 16191488,
      "scriptDurationMs": 123.18199999999999,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2149.7130000000197,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 16.177,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1189.1979999999999,
      "heapDeltaBytes": 17303648,
      "heapUsedBytes": 69137824,
      "domNodes": -258,
      "jsHeapTotalBytes": 19017728,
      "scriptDurationMs": 434.168,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2144.9340000000348,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 15.806999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1177.6740000000002,
      "heapDeltaBytes": 6764916,
      "heapUsedBytes": 62890248,
      "domNodes": -261,
      "jsHeapTotalBytes": 19517440,
      "scriptDurationMs": 414.933,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2191.833000000088,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 16.854999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1250.638,
      "heapDeltaBytes": 15348564,
      "heapUsedBytes": 68771484,
      "domNodes": -263,
      "jsHeapTotalBytes": 17784832,
      "scriptDurationMs": 448.171,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3257.4670000000197,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 17.166,
      "layouts": 60,
      "layoutDurationMs": 7.646,
      "taskDurationMs": 1430.272,
      "heapDeltaBytes": 9841020,
      "heapUsedBytes": 64895732,
      "domNodes": -266,
      "jsHeapTotalBytes": 15929344,
      "scriptDurationMs": 530.339,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3170.5069999999864,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 17.547,
      "layouts": 60,
      "layoutDurationMs": 7.562,
      "taskDurationMs": 1414.8100000000002,
      "heapDeltaBytes": 12634540,
      "heapUsedBytes": 68539056,
      "domNodes": -264,
      "jsHeapTotalBytes": 15986688,
      "scriptDurationMs": 511.76200000000006,
      "eventListeners": -125,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3216.1009999999806,
      "styleRecalcs": 64,
      "styleRecalcDurationMs": 16.429,
      "layouts": 60,
      "layoutDurationMs": 7.841000000000001,
      "taskDurationMs": 1475.05,
      "heapDeltaBytes": 9491388,
      "heapUsedBytes": 64520468,
      "domNodes": -269,
      "jsHeapTotalBytes": 15929344,
      "scriptDurationMs": 548.217,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "minimap-idle",
      "durationMs": 2014.4640000000322,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 9.485000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 598.7040000000001,
      "heapDeltaBytes": 2025684,
      "heapUsedBytes": 55258212,
      "domNodes": -264,
      "jsHeapTotalBytes": 15929344,
      "scriptDurationMs": 107.30600000000001,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "minimap-idle",
      "durationMs": 2041.2089999999807,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.405999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 641.0519999999999,
      "heapDeltaBytes": 4670836,
      "heapUsedBytes": 57653616,
      "domNodes": -258,
      "jsHeapTotalBytes": 15437824,
      "scriptDurationMs": 118.438,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "minimap-idle",
      "durationMs": 2065.303999999969,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 11.754000000000005,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 715.815,
      "heapDeltaBytes": 4705724,
      "heapUsedBytes": 57428044,
      "domNodes": -260,
      "jsHeapTotalBytes": 15405056,
      "scriptDurationMs": 128.189,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 577.7820000000133,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 16.951,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 376.073,
      "heapDeltaBytes": 6558352,
      "heapUsedBytes": 49320956,
      "domNodes": 22,
      "jsHeapTotalBytes": 13631488,
      "scriptDurationMs": 125.31899999999999,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 575.1859999999738,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 12.040000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 380.422,
      "heapDeltaBytes": 6499420,
      "heapUsedBytes": 49505272,
      "domNodes": 22,
      "jsHeapTotalBytes": 13369344,
      "scriptDurationMs": 128.46500000000003,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000027
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 633.0769999999575,
      "styleRecalcs": 49,
      "styleRecalcDurationMs": 13.565,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 404.245,
      "heapDeltaBytes": 6754540,
      "heapUsedBytes": 49353732,
      "domNodes": 23,
      "jsHeapTotalBytes": 13107200,
      "scriptDurationMs": 132.27,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000027
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2002.1490000000028,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 12.078,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 401.372,
      "heapDeltaBytes": 19933420,
      "heapUsedBytes": 62608212,
      "domNodes": 23,
      "jsHeapTotalBytes": 22806528,
      "scriptDurationMs": 25.958000000000002,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1997.3790000000236,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.118,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 418.90100000000007,
      "heapDeltaBytes": 11085072,
      "heapUsedBytes": 62315996,
      "domNodes": 20,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 24.804,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2019.0099999999802,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.771000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 456.268,
      "heapDeltaBytes": 10642512,
      "heapUsedBytes": 62319344,
      "domNodes": 19,
      "jsHeapTotalBytes": 25690112,
      "scriptDurationMs": 24.808000000000003,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1973.875000000021,
      "styleRecalcs": 84,
      "styleRecalcDurationMs": 54.378,
      "layouts": 16,
      "layoutDurationMs": 5.5809999999999995,
      "taskDurationMs": 1029.903,
      "heapDeltaBytes": 12247744,
      "heapUsedBytes": 55189780,
      "domNodes": 72,
      "jsHeapTotalBytes": 23068672,
      "scriptDurationMs": 112.646,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1700.538999999992,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 39.506,
      "layouts": 16,
      "layoutDurationMs": 4.984999999999999,
      "taskDurationMs": 749.937,
      "heapDeltaBytes": 2194720,
      "heapUsedBytes": 53926272,
      "domNodes": 63,
      "jsHeapTotalBytes": 26214400,
      "scriptDurationMs": 109.271,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1692.6650000000336,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 39.907,
      "layouts": 16,
      "layoutDurationMs": 4.295999999999999,
      "taskDurationMs": 743.9739999999999,
      "heapDeltaBytes": 11204336,
      "heapUsedBytes": 54198320,
      "domNodes": 62,
      "jsHeapTotalBytes": 23592960,
      "scriptDurationMs": 109.41199999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12472.185000000025,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12456.855000000001,
      "heapDeltaBytes": -35834472,
      "heapUsedBytes": 158070360,
      "domNodes": -8331,
      "jsHeapTotalBytes": 16867328,
      "scriptDurationMs": 623.749,
      "eventListeners": -16462,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.339999999999783
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12520.179999999982,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12505.845000000001,
      "heapDeltaBytes": -30883376,
      "heapUsedBytes": 165208668,
      "domNodes": -8331,
      "jsHeapTotalBytes": 27353088,
      "scriptDurationMs": 612.842,
      "eventListeners": -16466,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.340000000000146
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12740.56900000005,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12725.999000000002,
      "heapDeltaBytes": -30241956,
      "heapUsedBytes": 166058484,
      "domNodes": -8331,
      "jsHeapTotalBytes": 27615232,
      "scriptDurationMs": 612.8919999999999,
      "eventListeners": -16466,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.340000000000146
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14790.38300000002,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 17.586999999999964,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14765.925000000003,
      "heapDeltaBytes": -46075116,
      "heapUsedBytes": 148084656,
      "domNodes": -8331,
      "jsHeapTotalBytes": -3317760,
      "scriptDurationMs": 863.8199999999999,
      "eventListeners": -16492,
      "totalBlockingTimeMs": 50,
      "frameDurationMs": 18.329999999999927
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14498.443000000008,
      "styleRecalcs": 64,
      "styleRecalcDurationMs": 14.803999999999984,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14472.999999999998,
      "heapDeltaBytes": -21833848,
      "heapUsedBytes": 173322804,
      "domNodes": -8331,
      "jsHeapTotalBytes": 23597056,
      "scriptDurationMs": 873.0400000000001,
      "eventListeners": -16458,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 18.329999999999927
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 14954.773999999928,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 16.139999999999986,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 14933.820000000002,
      "heapDeltaBytes": -32985188,
      "heapUsedBytes": 160595792,
      "domNodes": -8331,
      "jsHeapTotalBytes": -3403776,
      "scriptDurationMs": 969.144,
      "eventListeners": -16456,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 19.990000000000144
    },
    {
      "name": "workflow-execution",
      "durationMs": 462.35699999999724,
      "styleRecalcs": 18,
      "styleRecalcDurationMs": 28.2,
      "layouts": 5,
      "layoutDurationMs": 1.451,
      "taskDurationMs": 140.88500000000005,
      "heapDeltaBytes": 4714704,
      "heapUsedBytes": 49232840,
      "domNodes": 169,
      "jsHeapTotalBytes": 0,
      "scriptDurationMs": 32.37,
      "eventListeners": 71,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "workflow-execution",
      "durationMs": 451.9040000000132,
      "styleRecalcs": 15,
      "styleRecalcDurationMs": 21.019000000000002,
      "layouts": 4,
      "layoutDurationMs": 1.272,
      "taskDurationMs": 114.17700000000004,
      "heapDeltaBytes": 4412760,
      "heapUsedBytes": 49020812,
      "domNodes": 150,
      "jsHeapTotalBytes": 0,
      "scriptDurationMs": 25.673,
      "eventListeners": 69,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000027
    },
    {
      "name": "workflow-execution",
      "durationMs": 461.0600000000886,
      "styleRecalcs": 19,
      "styleRecalcDurationMs": 27.252000000000002,
      "layouts": 5,
      "layoutDurationMs": 1.312,
      "taskDurationMs": 132.823,
      "heapDeltaBytes": 4489856,
      "heapUsedBytes": 48470592,
      "domNodes": 158,
      "jsHeapTotalBytes": 0,
      "scriptDurationMs": 32.044000000000004,
      "eventListeners": 69,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    }
  ]
}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@browser_tests/assets/selection/subgraph-with-regular-node.json`:
- Line 36: The node size in the test asset JSON currently uses "size": [315,
106], which is below the required minimum for browser tests; update the "size"
property in subgraph-with-regular-node.json (the object with "size": [315, 106])
to at least [400, 200] to conform to the guideline for
browser_tests/assets/**/*.json and prevent navigateIntoSubgraph timeouts.

In `@src/renderer/extensions/vueNodes/components/LGraphNode.vue`:
- Around line 786-809: The current watchEffect replaces node.onBounding and
clears it on cleanup, which clobbers other callbacks; instead capture the
existing node.onBounding (e.g., prevOnBounding = node.onBounding) inside the
watchEffect, set node.onBounding to a composed function that calls the existing
prevOnBounding(out) first (if present) and then applies this component's
footer/collapsed adjustments (use hasFooter.value, isCollapsed.value,
FOOTER_BOUNDING_OFFSET[_COLLAPSED], MIN_NODE_WIDTH), and in onCleanup restore
node.onBounding back to prevOnBounding; ensure the else branch still sets
node.onBounding to prevOnBounding (not undefined) so you don't drop other
extensions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0dbc84e9-9bf9-4e7d-91b4-0b38b5833d7a

📥 Commits

Reviewing files that changed from the base of the PR and between 82242f1 and a4cf00b.

📒 Files selected for processing (3)
  • browser_tests/assets/selection/subgraph-with-regular-node.json
  • browser_tests/tests/selectionBoundingBox.spec.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue

Capture and call any pre-existing node.onBounding before applying
footer/collapsed adjustments, and restore it on cleanup.
@jaeone94 jaeone94 added the New Browser Test Expectations New browser test screenshot should be set by github action label Mar 28, 2026
@github-actions
Copy link
Copy Markdown

Updating Playwright Expectations

@github-actions github-actions bot removed the New Browser Test Expectations New browser test screenshot should be set by github action label Mar 28, 2026
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 28, 2026
@jaeone94 jaeone94 requested a review from DrJKL March 28, 2026 03:53
@jaeone94 jaeone94 removed the request for review from DrJKL March 28, 2026 03:53
@jaeone94 jaeone94 added needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch core/1.41 Backport PRs for core 1.41 cloud/1.41 Backport PRs for cloud 1.41 core/1.42 Backport PRs for core 1.42 cloud/1.42 Backport PRs for cloud 1.42 labels Mar 28, 2026
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we add some of these to shared/centralized fixtures/helpers/page objects?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done !
Replaced waitForSelectedCount and waitForNodeLayout with existing fixture helpers (nodeOps.getSelectedGraphNodesCount via expect.poll, vueNodes.getNodeLocator.waitFor).

Extracted loadWithPositions and setCollapsed to shared fixtures:

  • NodeOperationsHelper.loadWithPositions — serialize-modify-reload pattern for placing nodes at specific positions without separate asset files
  • NodeReference.setCollapsed(collapsed) — deterministic setter using node.collapse() API with current state check (vs toggleCollapse() which depends on current state)

The remaining measureBounds is kept as a local helper — it combines multi-selection bounds with DOM visual bounds including footer measurement, too specialized to justify sharing.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
browser_tests/tests/selectionBoundingBox.spec.ts (1)

66-68: Use centralized TestIds instead of inline data-testid literals.

Please source these IDs from browser_tests/fixtures/selectors.ts and pass them into page.evaluate params, rather than hardcoding string literals in the selector.

Suggested refactor
+import { TestIds } from '../fixtures/selectors'
...
-      const footerEls = nodeEl.querySelectorAll(
-          '[data-testid="subgraph-enter-button"], [data-testid="node-footer"]'
-        )
+      const footerEls = nodeEl.querySelectorAll(
+          `[data-testid="${subgraphEnterButtonTestId}"], [data-testid="${nodeFooterTestId}"]`
+        )
...
-    { ids: nodeIds, padding: SELECTION_PADDING }
+    {
+      ids: nodeIds,
+      padding: SELECTION_PADDING,
+      subgraphEnterButtonTestId: TestIds.subgraph.enterButton,
+      nodeFooterTestId: TestIds.nodes.footer
+    }
   ) as Promise<MeasureResult>

As per coding guidelines, "Use centralized TestIds from fixtures/selectors.ts for DOM element selection in E2E tests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_tests/tests/selectionBoundingBox.spec.ts` around lines 66 - 68,
Replace the inline data-testid literals in the selector used to build footerEls
with centralized TestId values from browser_tests/fixtures/selectors.ts: import
the relevant constants (e.g., SUBGRAPH_ENTER_BUTTON and NODE_FOOTER) and pass
them into page.evaluate as arguments, then inside the page.evaluate callback
build the selector using those params and call nodeEl.querySelectorAll with the
composed selector (instead of hardcoded strings); update any references to
footerEls accordingly so the test uses the selectors module values rather than
inline literals.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@browser_tests/tests/selectionBoundingBox.spec.ts`:
- Around line 110-124: The helper setNodeCollapsed currently mutates
node.flags.collapsed directly which skips the graph version bump; instead,
inside the page.evaluate callback call the node's public collapse API
(LGraphNode.collapse) to toggle collapse state so the graph._version++ and
canvas dirtying behavior run; update the evaluate closure in setNodeCollapsed to
find the node by id and invoke node.collapse() or node.collapse(true/false) as
appropriate rather than setting node.flags.collapsed, ensuring graph versioning
and canvas updates happen correctly.

---

Nitpick comments:
In `@browser_tests/tests/selectionBoundingBox.spec.ts`:
- Around line 66-68: Replace the inline data-testid literals in the selector
used to build footerEls with centralized TestId values from
browser_tests/fixtures/selectors.ts: import the relevant constants (e.g.,
SUBGRAPH_ENTER_BUTTON and NODE_FOOTER) and pass them into page.evaluate as
arguments, then inside the page.evaluate callback build the selector using those
params and call nodeEl.querySelectorAll with the composed selector (instead of
hardcoded strings); update any references to footerEls accordingly so the test
uses the selectors module values rather than inline literals.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c4c4b001-28e2-4246-ac18-8699ba40bcb4

📥 Commits

Reviewing files that changed from the base of the PR and between 202c756 and d679913.

⛔ Files ignored due to path filters (1)
  • browser_tests/tests/vueNodes/interactions/node/imagePreview.spec.ts-snapshots/vue-node-multiple-promoted-previews-chromium-linux.png is excluded by !**/*.png
📒 Files selected for processing (1)
  • browser_tests/tests/selectionBoundingBox.spec.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
browser_tests/tests/selectionBoundingBox.spec.ts (1)

110-124: ⚠️ Potential issue | 🟠 Major

Use the node collapse API instead of mutating flags.collapsed directly.

At Line 121–123, directly assigning node.flags.collapsed can skip graph/node lifecycle side effects and make state/version updates less reliable. Prefer toggling through the node API only when the current state differs from the target state.

Suggested fix
 async function setNodeCollapsed(
   comfyPage: ComfyPage,
   nodeId: string,
   collapsed: boolean
 ) {
   await comfyPage.page.evaluate(
     ({ id, collapsed }) => {
       const node = window.app!.graph._nodes.find(
         (n: { id: number | string }) => String(n.id) === id
       )
       if (node) {
-        node.flags = node.flags || {}
-        node.flags.collapsed = collapsed
-        window.app!.canvas.setDirty(true, true)
+        const isCollapsed = Boolean(node.flags?.collapsed)
+        if (isCollapsed !== collapsed) {
+          node.collapse()
+        }
       }
     },
     { id: nodeId, collapsed }
   )
   await comfyPage.vueNodes.getNodeLocator(nodeId).waitFor()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_tests/tests/selectionBoundingBox.spec.ts` around lines 110 - 124, The
helper setNodeCollapsed currently mutates node.flags.collapsed directly inside
comfyPage.page.evaluate (finding nodes via window.app!.graph._nodes and then
setting node.flags.collapsed and calling window.app!.canvas.setDirty), which
bypasses node lifecycle side effects; instead, use the node collapse API on the
node object (e.g., call the provided node.toggleCollapse / node.setCollapsed /
graph API) and only invoke it if the node's current collapsed state differs from
the desired collapsed parameter, then let the API handle state/version updates
rather than manually setting flags or calling canvas.setDirty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@browser_tests/tests/selectionBoundingBox.spec.ts`:
- Around line 110-124: The helper setNodeCollapsed currently mutates
node.flags.collapsed directly inside comfyPage.page.evaluate (finding nodes via
window.app!.graph._nodes and then setting node.flags.collapsed and calling
window.app!.canvas.setDirty), which bypasses node lifecycle side effects;
instead, use the node collapse API on the node object (e.g., call the provided
node.toggleCollapse / node.setCollapsed / graph API) and only invoke it if the
node's current collapsed state differs from the desired collapsed parameter,
then let the API handle state/version updates rather than manually setting flags
or calling canvas.setDirty.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 082c42fa-a351-4320-824b-bda647bcd47f

📥 Commits

Reviewing files that changed from the base of the PR and between d679913 and 7d6f59d.

📒 Files selected for processing (1)
  • browser_tests/tests/selectionBoundingBox.spec.ts

- Add NodeOperationsHelper.loadWithPositions for reloading workflow
  with modified node positions
- Add NodeReference.setCollapsed using node.collapse() API with
  current state check for deterministic set (not toggle)
- Remove local helper duplicates from test file
@jaeone94
Copy link
Copy Markdown
Collaborator Author

@coderabbitai can you approve it ?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

Tip

For best results, initiate chat on the files or code changes.

The PR description images show the corrected multi-select bounding box behavior: a dashed selection rectangle now fully encompasses both the "Test Subgraph" node (including its "Enter Subgraph" footer overlay at the bottom) and the "Empty Latent Image" node. Both images confirm the fix is working — the selection bounding box correctly extends to include the footer overlays that were previously excluded due to the regression introduced in PR #9360.

@jaeone94 Sure! Approving the PR now.

[approve]


🧠 Learnings used
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 10574
File: browser_tests/tests/maskEditor.spec.ts:70-75
Timestamp: 2026-03-26T23:45:24.624Z
Learning: In Comfy-Org/ComfyUI_frontend browser_tests, the `dispatchEvent('contextmenu', { bubbles: true, cancelable: true, button: 2 })` pattern is only needed when the canvas z-999 overlay intercepts pointer events on canvas-level DOM widgets. Vue-rendered elements like `.lg-node-header` are NOT occluded by the canvas overlay; for these elements, the standard `locator.click()` (left-click to select) followed by `locator.click({ button: 'right' })` is correct and matches the established pattern in imagePreview.spec.ts. `getNodeLocator()` returns a Playwright `Locator`, not a NodeReference; `Locator.click('title')` is invalid syntax — only `NodeReference.click('title')` accepts a string part argument.

Learnt from: viva-jinyi
Repo: Comfy-Org/ComfyUI_frontend PR: 10499
File: src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdown.vue:208-210
Timestamp: 2026-03-25T03:30:30.411Z
Learning: In `src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdown.vue` (Comfy-Org/ComfyUI_frontend), do NOT suggest viewport-coordinate edge-aware placement (e.g., using `getBoundingClientRect()` to flip between `top-full`/`bottom-full` or `left-0`/`right-0`). Nodes live inside a CSS transform chain (TransformPane scale + LGraphNode translate), so any logic based on viewport/screen coordinates will produce the same broken placement that PrimeVue's `absolutePosition()` caused. The intentional fix is local-coordinate CSS positioning (`top-full left-0`); users pan the canvas to reveal content near viewport edges, which is standard behavior for canvas-based editors.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 10140
File: apps/website/package.json:1-79
Timestamp: 2026-03-17T07:28:27.677Z
Learning: In Comfy-Org/ComfyUI_frontend, PRs managed via Graphite stacked sets (using `gt split`) may show browser_tests/ files in the diff as artifacts of the split process. These files contain no actual changes relative to origin/main and should not be flagged as unrelated scope changes. Verify against the PR title/description stacking context before raising scope concerns.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 10548
File: browser_tests/tests/propertiesPanel/propertiesPanel.spec.ts:147-153
Timestamp: 2026-03-27T05:12:33.040Z
Learning: In Comfy-Org/ComfyUI_frontend browser_tests, `PropertiesPanelHelper.ts` intentionally uses `i[class*="lucide--pencil"]` as the selector for `titleEditIcon` as a temporary measure. Adding a `data-testid` to the pencil icon requires a source change to the PropertiesPanel Vue component and is deferred to a separate PR tracked as FIX-03 (raw CSS selector → TestId migration) in the test coverage overhaul. Do not flag this CSS class selector as a violation in future reviews of this file.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 10548
File: browser_tests/tests/propertiesPanel/propertiesPanel.spec.ts:57-66
Timestamp: 2026-03-27T05:12:28.417Z
Learning: In Comfy-Org/ComfyUI_frontend browser tests for the properties panel (`browser_tests/tests/propertiesPanel/propertiesPanel.spec.ts`), do not assert that the rendered node-entry count in the Nodes tab matches `comfyPage.nodeOps.getNodeCount()`. The default workflow composition can change, making count-based assertions fragile. Prefer asserting that a known node (e.g., 'KSampler') is visible. For filter assertions, asserting the matching entry is visible is sufficient; a non-match disappearing assertion is deferred to a future iteration.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 9149
File: src/lib/litegraph/src/LGraphCanvas.groupSelection.test.ts:154-162
Timestamp: 2026-03-12T14:45:59.909Z
Learning: In LGraphCanvas unit tests (e.g., src/lib/litegraph/src/LGraphCanvas.groupSelection.test.ts), calling `recomputeInsideNodes()` does NOT automatically place a node inside a group's `_children` unless `calculateBounding` has been called first (which only happens during a canvas render cycle). Without a render cycle, a node's `boundingRect` is uncomputed and `containsCentre` returns `false`, so the node is excluded from `_children`. When writing assertions in test environments without rendering, do not assume `recomputeInsideNodes()` will pick up nodes purely based on their `pos` coordinates.

Learnt from: jaeone94
Repo: Comfy-Org/ComfyUI_frontend PR: 10395
File: src/composables/graph/useVueNodeLifecycle.ts:90-102
Timestamp: 2026-03-23T15:05:24.847Z
Learning: In `src/composables/graph/useVueNodeLifecycle.ts` (Comfy-Org/ComfyUI_frontend), the `whenever(() => !shouldRenderVueNodes.value, ...)` handler (Vue→Legacy mode switch) correctly calls `node.arrange()` on each non-collapsed graph node before `setDirty(true, true)`. This is intentional: `arrange()` is the only public API that covers `_measureSlots()` + `_arrangeWidgets()` + widget-input-slot positioning so that `input.pos` is computed before the first legacy `drawConnections` frame. `_arrangeWidgetInputSlots()` is private and cannot be called externally. The `setDirty(true, true)` call is pre-existing (not introduced in PR `#10395`) and required for legacy canvas repaint. The `try/catch` skipping uninitialized nodes is intentional. Do NOT apply the draw-time "side-effect-free prepass" rule (from PR `#10195`) to this mode-switch handler — that rule is scoped to `LGraphCanvas.drawConnections` draw-time prepasses, not lifecycle/mode-switch transitions.

Learnt from: jaeone94
Repo: Comfy-Org/ComfyUI_frontend PR: 9360
File: src/renderer/extensions/vueNodes/components/LGraphNode.vue:576-602
Timestamp: 2026-03-04T16:13:02.775Z
Learning: In Comfy-Org/ComfyUI_frontend, in LGraphNode.vue (src/renderer/extensions/vueNodes/components/LGraphNode.vue), the computed properties rootBorderShapeClass, selectionShapeClass, and beforeShapeClass intentionally only handle RenderShape.BOX and RenderShape.CARD explicitly, letting all other RenderShape variants (ROUND, CIRCLE, HollowCircle, ARROW, GRID) fall through to a default rounded case. This is correct because only Default, Box, and Card shapes are exposed to users as selectable node shapes via the right-click context menu; the remaining RenderShape variants are used internally for canvas slot rendering (e.g., optional input dots) and are never applied as node shapes. Do not flag the missing explicit cases as an issue.

Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 9910
File: browser_tests/tests/performance.spec.ts:198-200
Timestamp: 2026-03-15T10:34:31.719Z
Learning: In browser_tests for Comfy-Org/ComfyUI_frontend, the minimap container `.litegraph-minimap` is rendered by litegraph (canvas-based), not by a Vue component, so it does not have a `data-testid` attribute. Using the raw CSS selector `.litegraph-minimap` is the correct and consistent approach (matching the pattern used in minimap.spec.ts). Do not flag this as a violation of the centralized TestIds guideline from selectors.ts — that guideline applies to Vue-rendered elements. The only minimap-related TestId in selectors.ts is `toggleMinimapButton`, which refers to the toolbar toggle button, not the minimap container itself.

Learnt from: jaeone94
Repo: Comfy-Org/ComfyUI_frontend PR: 10302
File: browser_tests/tests/dialog.spec.ts:31-32
Timestamp: 2026-03-22T14:21:47.598Z
Learning: In Comfy-Org/ComfyUI_frontend, `TestIds.dialogs.missingNodePacksGroup` (`error-group-missing-node`) and `TestIds.dialogs.missingModelsGroup` (`error-group-missing-model`) are rendered on `PropertiesAccordionItem` elements inside `TabErrors.vue`, NOT inside `ErrorOverlay.vue`. The overlay (`ErrorOverlay.vue`) renders missing-node/model error titles as plain `<li>` strings from `groupedErrorMessages`, so `errorOverlay.getByTestId(TestIds.dialogs.missingNodePacksGroup)` will fail at runtime. The correct approach in E2E tests for overlay-level assertions is `errorOverlay.getByText(/Missing Node Packs/)` scoped to the overlay's `data-testid="error-overlay"` container element.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
browser_tests/tests/selectionBoundingBox.spec.ts (1)

63-65: Avoid hardcoded data-testid literals in spec selectors.

Line 63–Line 65 uses raw test-id strings. Prefer pulling these from the shared selector source so test ids stay refactor-safe.

As per coding guidelines: "Use centralized TestIds from fixtures/selectors.ts for DOM element selection in E2E tests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@browser_tests/tests/selectionBoundingBox.spec.ts` around lines 63 - 65,
Replace the hardcoded test-id strings in the nodeEl.querySelectorAll call with
the centralized TestIds from fixtures/selectors.ts: import the TestIds (or
TEST_IDS) export and change the selector to use
`[data-testid="${TestIds.SUBGRAPH_ENTER_BUTTON}"],
[data-testid="${TestIds.NODE_FOOTER}"]` (keeping the variable footerEls and
nodeEl.querySelectorAll as-is) so E2E selectors come from the shared source and
remain refactor-safe.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@browser_tests/tests/selectionBoundingBox.spec.ts`:
- Around line 63-65: Replace the hardcoded test-id strings in the
nodeEl.querySelectorAll call with the centralized TestIds from
fixtures/selectors.ts: import the TestIds (or TEST_IDS) export and change the
selector to use `[data-testid="${TestIds.SUBGRAPH_ENTER_BUTTON}"],
[data-testid="${TestIds.NODE_FOOTER}"]` (keeping the variable footerEls and
nodeEl.querySelectorAll as-is) so E2E selectors come from the shared source and
remain refactor-safe.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 127596d5-6d81-495d-accd-00497bd40603

📥 Commits

Reviewing files that changed from the base of the PR and between d679913 and 8d985e3.

📒 Files selected for processing (3)
  • browser_tests/fixtures/helpers/NodeOperationsHelper.ts
  • browser_tests/fixtures/utils/litegraphUtils.ts
  • browser_tests/tests/selectionBoundingBox.spec.ts

@Comfy-Org Comfy-Org deleted a comment from coderabbitai bot Mar 28, 2026
Copy link
Copy Markdown
Contributor

@christian-byrne christian-byrne left a comment

Choose a reason for hiding this comment

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

Looks good to me generally. For the actual bugfix, I asked about some other possible ways to think about the solution. The good thing is, since you already made some nice working tests, you can potentially try a few different new approaches and still have the tests to easily validate they also fix the problem

Comment on lines +121 to +133
async ({ positions }) => {
const data = window.app!.graph.serialize()
for (const node of data.nodes) {
const pos = positions[String(node.id)]
if (pos) node.pos = pos
}
await window.app!.loadGraphData(
data as ComfyWorkflowJSON,
true,
true,
null
)
},
Copy link
Copy Markdown
Contributor

@christian-byrne christian-byrne Mar 28, 2026

Choose a reason for hiding this comment

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

nit: I think a better method name could be explored -- this is more about changing the node positions in batch rather than loading a graph (although it does use loadGraphData). It can also possibly be decomposed into smaller pure functions.

Comment on lines +335 to +344
async setCollapsed(collapsed: boolean) {
await this.comfyPage.page.evaluate(
([id, collapsed]) => {
const node = window.app!.canvas.graph!.getNodeById(id)
if (!node) throw new Error('Node not found')
if (node.collapsed !== collapsed) node.collapse(true)
},
[this.id, collapsed] as const
)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: In an ideal world, we can do this with user actions (e.g., find the collapse button and click it). Although for litegraph/canvas, it might be too much effort here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think most things outside the test.describe can actually be moved to a helper/utils file still

Comment on lines +786 to +810
watchEffect((onCleanup) => {
const node = lgraphNode.value
if (!node) return

const needsFooterOffset = hasFooter.value
const collapsed = isCollapsed.value
const previousOnBounding = node.onBounding

if (needsFooterOffset || collapsed) {
node.onBounding = function (out) {
previousOnBounding?.call(this, out)

if (needsFooterOffset)
out[3] += collapsed
? FOOTER_BOUNDING_OFFSET_COLLAPSED
: FOOTER_BOUNDING_OFFSET

// Must match CSS --min-node-width in the template.
if (collapsed) out[2] = MIN_NODE_WIDTH
}
}

onCleanup(() => {
node.onBounding = previousOnBounding
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would like to fully explore two other options, as I am not sure how maintainable this pattern is

  1. Make the footer part of normal document flow (not absolute-positioned overflow) so getBoundingClientRect() naturally includes it → ResizeObserver picks it up → litegraph gets the right size automatically. No onBounding hack needed.
  2. Adjust the ResizeObserver measurement in useVueNodeResizeTracking.ts to account for footer overflow (similar to how it already normalizes for NODE_TITLE_HEIGHT).

The current approach reaches across the boundary from the Vue layer into litegraph's internal callback system, hard-coding magic pixel offsets (32px, 34px) that must stay in sync with CSS. That's potentially quite fragile and the also sort of the kind of coupling the Vue node architecture was designed to avoid.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I've addressed the test code feedback and explored both approaches you mentioned. Here are two PRs for comparison:

Both PRs share the same foundation: footer moved into normal document flow, border/outline unified on root element, resize handles split for footer awareness, and the same E2E test suite.

Would appreciate your thoughts on which direction is preferable, or if there's a different approach worth exploring.

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

Labels

cloud/1.41 Backport PRs for cloud 1.41 cloud/1.42 Backport PRs for cloud 1.42 core/1.41 Backport PRs for core 1.41 core/1.42 Backport PRs for core 1.42 needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants