Skip to content

test(load3d): add unit tests for AnimationManager, CameraManager, RecordingManager, and load3dService#11733

Open
jtydhr88 wants to merge 1 commit intomainfrom
load3d-tier1-manager-tests
Open

test(load3d): add unit tests for AnimationManager, CameraManager, RecordingManager, and load3dService#11733
jtydhr88 wants to merge 1 commit intomainfrom
load3d-tier1-manager-tests

Conversation

@jtydhr88
Copy link
Copy Markdown
Collaborator

@jtydhr88 jtydhr88 commented Apr 29, 2026

Summary

Add unit tests for the four largest untested logic-heavy modules in the load3d domain (AnimationManager, CameraManager, RecordingManager, and the load3dService façade).

Changes

  • What: 78 new unit tests across 4 files covering animation lifecycle (mixer setup, clip switching, play/pause/seek, dispose), camera state (perspective↔orthographic toggling, FOV gating, state round-trip, controls rebinding, resize math, model fitting), recording lifecycle (MediaRecorder wiring, indicator visibility, chunk handling, export/clear paths, dispose ordering), and the service singleton (sync/async map access, viewer cache, handleViewerClose apply-changes flow, handleViewportRefresh camera-toggle dance).

Review Focus

  • Coverage: AnimationManager 100%, CameraManager 88.8%, RecordingManager 89.1%, load3dService 54.5% lines / 88.9% functions. The service gap is concentrated in one method — copyLoad3dState (lines 217-333) — which is entangled with 3-4 subsystems and is intentionally deferred to a follow-up PR.
  • happy-dom shims in RecordingManager.test.ts: MediaRecorder, HTMLCanvasElement.prototype.captureStream, and getContext('2d') are all stubbed because happy-dom doesn't provide them. THREE.TextureLoader is also mocked because the constructor eagerly loads an SVG indicator.
  • Singleton state reset in load3dService.test.ts: the service holds a module-level viewerInstances Map that can't be reached from outside. Tests track every node they create in a Set and drain via removeViewer in beforeEach. Cleaner than vi.resetModules() (which would re-import the service and break the singleton identity).
  • One subtle THREE behavior: AnimationManager.setAnimationTime clamps to duration, but AnimationMixer.setTime(duration) with LoopRepeat wraps action.time back to 0. The clamping is therefore only observable through the emitted progress event, not via action.time directly — the test asserts via the event.

┆Issue is synchronized with this Notion page by Unito

@jtydhr88 jtydhr88 requested a review from a team April 29, 2026 00:40
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

This pull request introduces four comprehensive Vitest test suites for core 3D loading and animation modules: AnimationManager, CameraManager, RecordingManager, and useLoad3dService. The tests validate lifecycle management, state transitions, event emissions, and edge cases across all public APIs. No production code is modified.

Changes

Cohort / File(s) Summary
Test Suite Additions
src/extensions/core/load3d/AnimationManager.test.ts, src/extensions/core/load3d/CameraManager.test.ts, src/extensions/core/load3d/RecordingManager.test.ts, src/services/load3dService.test.ts
Comprehensive Vitest test coverage for animation setup/playback, camera toggling/state management, recording lifecycle, and 3D service singleton behavior with mocked dependencies.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops through test suites bright,
With mocks and spies, assertions tight!
Four modules tested, edge cases caught,
Quality woven into code well-wrought.

🚥 Pre-merge checks | ✅ 6 | ❌ 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 (6 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: adding unit tests for four specific load3d modules.
Description check ✅ Passed The description comprehensively follows the template with a clear summary, detailed changes, and thorough review focus including coverage metrics, test strategies, and implementation notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
End-To-End Regression Coverage For Fixes ✅ Passed PR title uses test language without bug-fix terminology; files are new unit tests, not browser_tests; no regression test explanation needed.
Adr Compliance For Entity/Litegraph Changes ✅ Passed This check applies only to pull requests modifying litegraph, ECS, or graph entity files. The changed test files are unrelated to these areas.

✏️ 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 load3d-tier1-manager-tests

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Apr 29, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 29, 2026

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 04/29/2026, 12:41:41 AM UTC

Links

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 29, 2026

🎭 Playwright: ✅ 1425 passed, 0 failed

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 29, 2026

📦 Bundle: 5.23 MB gzip 🔴 +217 B

Details

Summary

  • Raw size: 24.1 MB baseline 24.1 MB — ⚪ 0 B
  • Gzip: 5.23 MB baseline 5.23 MB — 🔴 +217 B
  • Brotli: 4.05 MB baseline 4.05 MB — 🔴 +306 B
  • Bundles: 257 current • 257 baseline • 118 added / 118 removed

Category Glance
Vendor & Third-Party ⚪ 0 B (9.88 MB) · Other ⚪ 0 B (8.83 MB) · Data & Services ⚪ 0 B (3.04 MB) · Graph Workspace ⚪ 0 B (1.24 MB) · Panels & Settings ⚪ 0 B (484 kB) · Utilities & Hooks ⚪ 0 B (364 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-5sDhvCav.js (new) 22.5 kB 🔴 +22.5 kB 🔴 +7.98 kB 🔴 +6.87 kB
assets/index-wXNYevcc.js (removed) 22.5 kB 🟢 -22.5 kB 🟢 -7.98 kB 🟢 -6.87 kB

Status: 1 added / 1 removed

Graph Workspace — 1.24 MB (baseline 1.24 MB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-BH9O1pzX.js (new) 1.24 MB 🔴 +1.24 MB 🔴 +264 kB 🔴 +199 kB
assets/GraphView-D8sgf9IJ.js (removed) 1.24 MB 🟢 -1.24 MB 🟢 -264 kB 🟢 -199 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-Cfqkm4jz.js (removed) 15.7 kB 🟢 -15.7 kB 🟢 -3.41 kB 🟢 -2.92 kB
assets/CloudSurveyView-ChHq-R3z.js (new) 15.7 kB 🔴 +15.7 kB 🔴 +3.41 kB 🔴 +2.91 kB
assets/CloudLoginView-Aeyo3Ojg.js (removed) 12.2 kB 🟢 -12.2 kB 🟢 -3.43 kB 🟢 -3.04 kB
assets/CloudLoginView-D7VXvFr1.js (new) 12.2 kB 🔴 +12.2 kB 🔴 +3.43 kB 🔴 +3.04 kB
assets/CloudSignupView-B126mPwq.js (removed) 9.94 kB 🟢 -9.94 kB 🟢 -2.93 kB 🟢 -2.58 kB
assets/CloudSignupView-C4HET-6Y.js (new) 9.94 kB 🔴 +9.94 kB 🔴 +2.93 kB 🔴 +2.58 kB
assets/UserCheckView-C5RjhaZc.js (new) 9.07 kB 🔴 +9.07 kB 🔴 +2.33 kB 🔴 +2.04 kB
assets/UserCheckView-CcvmSCuT.js (removed) 9.07 kB 🟢 -9.07 kB 🟢 -2.33 kB 🟢 -2.04 kB
assets/CloudLayoutView-BHp15bE5.js (new) 7.7 kB 🔴 +7.7 kB 🔴 +2.44 kB 🔴 +2.13 kB
assets/CloudLayoutView-g5xSkWO-.js (removed) 7.7 kB 🟢 -7.7 kB 🟢 -2.44 kB 🟢 -2.13 kB
assets/CloudForgotPasswordView-DHRa7IfV.js (new) 6.11 kB 🔴 +6.11 kB 🔴 +2.17 kB 🔴 +1.93 kB
assets/CloudForgotPasswordView-DQJlI3Jj.js (removed) 6.11 kB 🟢 -6.11 kB 🟢 -2.17 kB 🟢 -1.9 kB
assets/CloudAuthTimeoutView-BPHPOo26.js (removed) 5.47 kB 🟢 -5.47 kB 🟢 -2 kB 🟢 -1.76 kB
assets/CloudAuthTimeoutView-BZh7pmDb.js (new) 5.47 kB 🔴 +5.47 kB 🔴 +2.01 kB 🔴 +1.76 kB
assets/CloudSubscriptionRedirectView-C8LrGvOm.js (new) 5.25 kB 🔴 +5.25 kB 🔴 +1.98 kB 🔴 +1.75 kB
assets/CloudSubscriptionRedirectView-vfo8RB4a.js (removed) 5.25 kB 🟢 -5.25 kB 🟢 -1.98 kB 🟢 -1.75 kB
assets/UserSelectView-BWseMvfZ.js (removed) 4.73 kB 🟢 -4.73 kB 🟢 -1.75 kB 🟢 -1.55 kB
assets/UserSelectView-DzkKemUw.js (new) 4.73 kB 🔴 +4.73 kB 🔴 +1.75 kB 🔴 +1.55 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-CIS29MES.js (removed) 46.7 kB 🟢 -46.7 kB 🟢 -9.61 kB 🟢 -8.53 kB
assets/KeybindingPanel-D3o0qgxG.js (new) 46.7 kB 🔴 +46.7 kB 🔴 +9.61 kB 🔴 +8.53 kB
assets/SecretsPanel-BSWXF8yE.js (removed) 22.9 kB 🟢 -22.9 kB 🟢 -5.54 kB 🟢 -4.87 kB
assets/SecretsPanel-pE9yryXr.js (new) 22.9 kB 🔴 +22.9 kB 🔴 +5.54 kB 🔴 +4.87 kB
assets/LegacyCreditsPanel-_fBaTBGI.js (new) 21.7 kB 🔴 +21.7 kB 🔴 +5.89 kB 🔴 +5.21 kB
assets/LegacyCreditsPanel-Cp0KV004.js (removed) 21.7 kB 🟢 -21.7 kB 🟢 -5.89 kB 🟢 -5.19 kB
assets/SubscriptionPanel-BUS3ybbj.js (removed) 19.9 kB 🟢 -19.9 kB 🟢 -5.06 kB 🟢 -4.46 kB
assets/SubscriptionPanel-tnV2dhYa.js (new) 19.9 kB 🔴 +19.9 kB 🔴 +5.06 kB 🔴 +4.46 kB
assets/AboutPanel-BLWjmHAW.js (removed) 12 kB 🟢 -12 kB 🟢 -3.33 kB 🟢 -2.99 kB
assets/AboutPanel-D44XsloT.js (new) 12 kB 🔴 +12 kB 🔴 +3.33 kB 🔴 +2.99 kB
assets/ExtensionPanel-bpuLEMX5.js (removed) 9.94 kB 🟢 -9.94 kB 🟢 -2.9 kB 🟢 -2.57 kB
assets/ExtensionPanel-CGJGu3Og.js (new) 9.94 kB 🔴 +9.94 kB 🔴 +2.9 kB 🔴 +2.57 kB
assets/ServerConfigPanel-DGO74A_i.js (removed) 7.02 kB 🟢 -7.02 kB 🟢 -2.35 kB 🟢 -2.09 kB
assets/ServerConfigPanel-n8swUFGb.js (new) 7.02 kB 🔴 +7.02 kB 🔴 +2.35 kB 🔴 +2.1 kB
assets/UserPanel-BZ8QNjmi.js (removed) 6.73 kB 🟢 -6.73 kB 🟢 -2.22 kB 🟢 -1.95 kB
assets/UserPanel-CjjJzhCd.js (new) 6.73 kB 🔴 +6.73 kB 🔴 +2.23 kB 🔴 +1.95 kB
assets/cloudRemoteConfig-BXuJDGMo.js (removed) 2.02 kB 🟢 -2.02 kB 🟢 -973 B 🟢 -841 B
assets/cloudRemoteConfig-DCUgjzgs.js (new) 2.02 kB 🔴 +2.02 kB 🔴 +976 B 🔴 +839 B
assets/refreshRemoteConfig-cIshcIRw.js (removed) 1.45 kB 🟢 -1.45 kB 🟢 -646 B 🟢 -549 B
assets/refreshRemoteConfig-DbpZwJuv.js (new) 1.45 kB 🔴 +1.45 kB 🔴 +647 B 🔴 +554 B

Status: 10 added / 10 removed / 11 unchanged

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-B8IFmV3e.js (new) 3.57 kB 🔴 +3.57 kB 🔴 +1.26 kB 🔴 +1.07 kB
assets/auth-C2WwsQkQ.js (removed) 3.57 kB 🟢 -3.57 kB 🟢 -1.26 kB 🟢 -1.07 kB
assets/SignUpForm-DSsuTFmK.js (new) 3.16 kB 🔴 +3.16 kB 🔴 +1.29 kB 🔴 +1.15 kB
assets/SignUpForm-j0PqTHAG.js (removed) 3.16 kB 🟢 -3.16 kB 🟢 -1.29 kB 🟢 -1.15 kB
assets/UpdatePasswordContent-BVeM53u1.js (new) 2.87 kB 🔴 +2.87 kB 🔴 +1.29 kB 🔴 +1.15 kB
assets/UpdatePasswordContent-DXI6i7Eg.js (removed) 2.87 kB 🟢 -2.87 kB 🟢 -1.29 kB 🟢 -1.15 kB
assets/authStore-8Sifo7vC.js (new) 1.16 kB 🔴 +1.16 kB 🔴 +556 B 🔴 +498 B
assets/authStore-fzpvXCTx.js (removed) 1.16 kB 🟢 -1.16 kB 🟢 -552 B 🟢 -500 B
assets/auth-BLPdGHXu.js (new) 348 B 🔴 +348 B 🔴 +218 B 🔴 +194 B
assets/auth-Bx1s2MYW.js (removed) 348 B 🟢 -348 B 🟢 -217 B 🟢 -206 B

Status: 5 added / 5 removed / 2 unchanged

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyHubPublishDialog-BotIah8Y.js (removed) 86.9 kB 🟢 -86.9 kB 🟢 -18.8 kB 🟢 -16 kB
assets/ComfyHubPublishDialog-CQbgn_xB.js (new) 86.9 kB 🔴 +86.9 kB 🔴 +18.8 kB 🔴 +16.1 kB
assets/useShareDialog-BCf1-vyJ.js (new) 23.7 kB 🔴 +23.7 kB 🔴 +5.77 kB 🔴 +5.11 kB
assets/useShareDialog-e1y9g_Bg.js (removed) 23.7 kB 🟢 -23.7 kB 🟢 -5.77 kB 🟢 -5.11 kB
assets/ComfyHubPublishDialog-DBVCpeeM.js (new) 1.32 kB 🔴 +1.32 kB 🔴 +616 B 🔴 +547 B
assets/ComfyHubPublishDialog-DPxF_jxs.js (removed) 1.32 kB 🟢 -1.32 kB 🟢 -613 B 🟢 -556 B
assets/useSubscriptionDialog-D62PsUHt.js (removed) 1.14 kB 🟢 -1.14 kB 🟢 -543 B 🟢 -482 B
assets/useSubscriptionDialog-l3kKSnSY.js (new) 1.14 kB 🔴 +1.14 kB 🔴 +546 B 🔴 +482 B

Status: 4 added / 4 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-B_lAHOtR.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.79 kB 🟢 -3.38 kB
assets/ComfyQueueButton-Cr1Q1q0x.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.79 kB 🔴 +3.38 kB
assets/useTerminalTabs-DgvzZFRd.js (new) 11 kB 🔴 +11 kB 🔴 +3.72 kB 🔴 +3.28 kB
assets/useTerminalTabs-wAbBheK1.js (removed) 11 kB 🟢 -11 kB 🟢 -3.71 kB 🟢 -3.27 kB
assets/SubscribeButton-B7AXOA8u.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.05 kB 🔴 +939 B
assets/SubscribeButton-Cjx3-_lQ.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.05 kB 🟢 -930 B
assets/cloudFeedbackTopbarButton-Bt3ocrPD.js (removed) 1.8 kB 🟢 -1.8 kB 🟢 -930 B 🟢 -815 B
assets/cloudFeedbackTopbarButton-CcU1al9x.js (new) 1.8 kB 🔴 +1.8 kB 🔴 +931 B 🔴 +819 B
assets/ComfyQueueButton-B69HyJ1c.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -581 B 🟢 -519 B
assets/ComfyQueueButton-BtAeIWhN.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +581 B 🔴 +516 B

Status: 5 added / 5 removed / 8 unchanged

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

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-Cl5waVzF.js (removed) 1.97 MB 🟢 -1.97 MB 🟢 -454 kB 🟢 -344 kB
assets/dialogService-xHT7ySxC.js (new) 1.97 MB 🔴 +1.97 MB 🔴 +454 kB 🔴 +344 kB
assets/api-CkkclUPt.js (removed) 894 kB 🟢 -894 kB 🟢 -214 kB 🟢 -168 kB
assets/api-Dc_Y4rXd.js (new) 894 kB 🔴 +894 kB 🔴 +214 kB 🔴 +168 kB
assets/load3dService-BPzjfOOo.js (removed) 114 kB 🟢 -114 kB 🟢 -24.8 kB 🟢 -21 kB
assets/load3dService-DXxvvmP4.js (new) 114 kB 🔴 +114 kB 🔴 +24.8 kB 🔴 +21.1 kB
assets/workflowShareService-B6Qw4OIv.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.89 kB 🟢 -4.33 kB
assets/workflowShareService-CisQ1kR5.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.89 kB 🔴 +4.33 kB
assets/keybindingService-CxJgNBnu.js (new) 13.8 kB 🔴 +13.8 kB 🔴 +3.67 kB 🔴 +3.22 kB
assets/keybindingService-DL8OOVja.js (removed) 13.8 kB 🟢 -13.8 kB 🟢 -3.67 kB 🟢 -3.22 kB
assets/releaseStore-BnnSg-SP.js (new) 8.12 kB 🔴 +8.12 kB 🔴 +2.28 kB 🔴 +2 kB
assets/releaseStore-DrRfyg9s.js (removed) 8.12 kB 🟢 -8.12 kB 🟢 -2.28 kB 🟢 -2 kB
assets/userStore-Bh9HnWIP.js (new) 2.24 kB 🔴 +2.24 kB 🔴 +872 B 🔴 +762 B
assets/userStore-BuT_qVEa.js (removed) 2.24 kB 🟢 -2.24 kB 🟢 -870 B 🟢 -760 B
assets/audioService-BykbUFNp.js (new) 1.8 kB 🔴 +1.8 kB 🔴 +876 B 🔴 +765 B
assets/audioService-DLuuMOpB.js (removed) 1.8 kB 🟢 -1.8 kB 🟢 -876 B 🟢 -761 B
assets/releaseStore-CIZ-prn_.js (new) 1.16 kB 🔴 +1.16 kB 🔴 +549 B 🔴 +487 B
assets/releaseStore-DlH2PtXb.js (removed) 1.16 kB 🟢 -1.16 kB 🟢 -548 B 🟢 -491 B
assets/workflowDraftStore-BOg7d56j.js (new) 1.14 kB 🔴 +1.14 kB 🔴 +545 B 🔴 +485 B
assets/workflowDraftStore-DUDf2uNc.js (removed) 1.14 kB 🟢 -1.14 kB 🟢 -542 B 🟢 -482 B
assets/dialogService-CbuYMOTR.js (removed) 1.13 kB 🟢 -1.13 kB 🟢 -535 B 🟢 -481 B
assets/dialogService-DYtxFc6e.js (new) 1.13 kB 🔴 +1.13 kB 🔴 +538 B 🔴 +480 B
assets/settingStore-C9jUs1ot.js (new) 1.12 kB 🔴 +1.12 kB 🔴 +540 B 🔴 +479 B
assets/settingStore-CKCOB6os.js (removed) 1.12 kB 🟢 -1.12 kB 🟢 -537 B 🟢 -481 B
assets/assetsStore-CKjzK_ta.js (removed) 1.12 kB 🟢 -1.12 kB 🟢 -537 B 🟢 -482 B
assets/assetsStore-DqeWXY2T.js (new) 1.12 kB 🔴 +1.12 kB 🔴 +541 B 🔴 +481 B

Status: 13 added / 13 removed / 4 unchanged

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-CYjBfRoU.js (new) 233 kB 🔴 +233 kB 🔴 +51.8 kB 🔴 +42.2 kB
assets/useConflictDetection-X8WTKdIP.js (removed) 233 kB 🟢 -233 kB 🟢 -51.8 kB 🟢 -42.2 kB
assets/useLoad3d-0KeKW8jI.js (new) 22.3 kB 🔴 +22.3 kB 🔴 +5.09 kB 🔴 +4.52 kB
assets/useLoad3d-Bqleb5BE.js (removed) 22.3 kB 🟢 -22.3 kB 🟢 -5.09 kB 🟢 -4.51 kB
assets/useLoad3dViewer-CTKDLl26.js (removed) 20.8 kB 🟢 -20.8 kB 🟢 -4.91 kB 🟢 -4.3 kB
assets/useLoad3dViewer-SVJ0zETu.js (new) 20.8 kB 🔴 +20.8 kB 🔴 +4.91 kB 🔴 +4.29 kB
assets/useFeatureFlags-bh9sQe0Y.js (removed) 5.95 kB 🟢 -5.95 kB 🟢 -1.79 kB 🟢 -1.52 kB
assets/useFeatureFlags-C-t4uS9z.js (new) 5.95 kB 🔴 +5.95 kB 🔴 +1.8 kB 🔴 +1.52 kB
assets/useCopyToClipboard-BqK3t_p1.js (new) 5.29 kB 🔴 +5.29 kB 🔴 +1.86 kB 🔴 +1.57 kB
assets/useCopyToClipboard-GY2eRfO0.js (removed) 5.29 kB 🟢 -5.29 kB 🟢 -1.86 kB 🟢 -1.58 kB
assets/useWorkspaceUI-C1ugrsbB.js (removed) 3.34 kB 🟢 -3.34 kB 🟢 -981 B 🟢 -809 B
assets/useWorkspaceUI-CX3wYD97.js (new) 3.34 kB 🔴 +3.34 kB 🔴 +984 B 🔴 +810 B
assets/subscriptionCheckoutUtil-CGdV_xwc.js (new) 3.31 kB 🔴 +3.31 kB 🔴 +1.36 kB 🔴 +1.19 kB
assets/subscriptionCheckoutUtil-ZG1MET1L.js (removed) 3.31 kB 🟢 -3.31 kB 🟢 -1.36 kB 🟢 -1.19 kB
assets/assetPreviewUtil-BOviIBqu.js (new) 2.27 kB 🔴 +2.27 kB 🔴 +958 B 🔴 +835 B
assets/assetPreviewUtil-XnQ-7OZ1.js (removed) 2.27 kB 🟢 -2.27 kB 🟢 -959 B 🟢 -839 B
assets/useUpstreamValue-CNu5a3W8.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -805 B 🟢 -713 B
assets/useUpstreamValue-mx4JyHJW.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +805 B 🔴 +720 B
assets/useLoad3d-CBdpKWFj.js (removed) 1.3 kB 🟢 -1.3 kB 🟢 -606 B 🟢 -566 B
assets/useLoad3d-DjM5BT5D.js (new) 1.3 kB 🔴 +1.3 kB 🔴 +607 B 🔴 +562 B
assets/useLoad3dViewer-BVxXjqtd.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +574 B 🔴 +518 B
assets/useLoad3dViewer-DZ9taU2R.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -572 B 🟢 -518 B
assets/useCurrentUser-D8h9PwWe.js (removed) 1.12 kB 🟢 -1.12 kB 🟢 -538 B 🟢 -482 B
assets/useCurrentUser-Dc01yJai.js (new) 1.12 kB 🔴 +1.12 kB 🔴 +541 B 🔴 +483 B
assets/useWorkspaceSwitch-64XForik.js (removed) 747 B 🟢 -747 B 🟢 -384 B 🟢 -329 B
assets/useWorkspaceSwitch-Bs4oIn6I.js (new) 747 B 🔴 +747 B 🔴 +385 B 🔴 +330 B

Status: 13 added / 13 removed / 18 unchanged

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

External libraries and shared vendor chunks

Status: 16 unchanged

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

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-BugQIaCy.js (removed) 76.6 kB 🟢 -76.6 kB 🟢 -19.8 kB 🟢 -16.9 kB
assets/core-BX1-imql.js (new) 76.6 kB 🔴 +76.6 kB 🔴 +19.8 kB 🔴 +16.9 kB
assets/groupNode-DIbWQ3iY.js (new) 74.9 kB 🔴 +74.9 kB 🔴 +18.7 kB 🔴 +16.5 kB
assets/groupNode-KJpWtlg6.js (removed) 74.9 kB 🟢 -74.9 kB 🟢 -18.7 kB 🟢 -16.5 kB
assets/WidgetSelect-BmGa2tks.js (new) 67.2 kB 🔴 +67.2 kB 🔴 +14.6 kB 🔴 +12.6 kB
assets/WidgetSelect-DJ-wfw-t.js (removed) 67.2 kB 🟢 -67.2 kB 🟢 -14.6 kB 🟢 -12.6 kB
assets/SubscriptionRequiredDialogContentWorkspace-CmK8MqIz.js (new) 49.3 kB 🔴 +49.3 kB 🔴 +9.42 kB 🔴 +8.04 kB
assets/SubscriptionRequiredDialogContentWorkspace-ToLT91i4.js (removed) 49.3 kB 🟢 -49.3 kB 🟢 -9.42 kB 🟢 -8.04 kB
assets/Load3DControls-BJQEvZG8.js (removed) 46.1 kB 🟢 -46.1 kB 🟢 -7.51 kB 🟢 -6.54 kB
assets/Load3DControls-C0vsh7nn.js (new) 46.1 kB 🔴 +46.1 kB 🔴 +7.51 kB 🔴 +6.55 kB
assets/WidgetPainter-C2De9FZl.js (removed) 34 kB 🟢 -34 kB 🟢 -8.29 kB 🟢 -7.35 kB
assets/WidgetPainter-CD-L4TJU.js (new) 34 kB 🔴 +34 kB 🔴 +8.29 kB 🔴 +7.33 kB
assets/WorkspacePanelContent-B2-I1UOD.js (removed) 32.8 kB 🟢 -32.8 kB 🟢 -6.99 kB 🟢 -6.18 kB
assets/WorkspacePanelContent-DZ_zO_rF.js (new) 32.8 kB 🔴 +32.8 kB 🔴 +7 kB 🔴 +6.18 kB
assets/Load3dViewerContent-BulXARu6.js (new) 28 kB 🔴 +28 kB 🔴 +5.85 kB 🔴 +5.07 kB
assets/Load3dViewerContent-E5BoBos6.js (removed) 28 kB 🟢 -28 kB 🟢 -5.85 kB 🟢 -5.07 kB
assets/SubscriptionRequiredDialogContent-CYmjsCIr.js (removed) 27.5 kB 🟢 -27.5 kB 🟢 -6.96 kB 🟢 -6.17 kB
assets/SubscriptionRequiredDialogContent-D0PTKZRt.js (new) 27.5 kB 🔴 +27.5 kB 🔴 +6.96 kB 🔴 +6.15 kB
assets/WidgetImageCrop-BwxhbMfU.js (new) 24.3 kB 🔴 +24.3 kB 🔴 +6.19 kB 🔴 +5.44 kB
assets/WidgetImageCrop-DXZaRi61.js (removed) 24.3 kB 🟢 -24.3 kB 🟢 -6.18 kB 🟢 -5.44 kB
assets/SubscriptionPanelContentWorkspace-_rDi18n5.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.18 kB 🔴 +4.56 kB
assets/SubscriptionPanelContentWorkspace-CrKLK_cv.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.17 kB 🟢 -4.56 kB
assets/SignInContent-C_P5qvQV.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +5.36 kB 🔴 +4.7 kB
assets/SignInContent-XTQtxWK2.js (removed) 20.6 kB 🟢 -20.6 kB 🟢 -5.35 kB 🟢 -4.68 kB
assets/CurrentUserPopoverWorkspace-BL2or4nF.js (removed) 20.5 kB 🟢 -20.5 kB 🟢 -4.89 kB 🟢 -4.39 kB
assets/CurrentUserPopoverWorkspace-flBJI9PC.js (new) 20.5 kB 🔴 +20.5 kB 🔴 +4.9 kB 🔴 +4.38 kB
assets/WidgetInputNumber-a0Fn12SC.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.84 kB 🔴 +4.29 kB
assets/WidgetInputNumber-e0bMfKQH.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.84 kB 🟢 -4.29 kB
assets/Load3D-D31sDpXP.js (new) 18.5 kB 🔴 +18.5 kB 🔴 +4.39 kB 🔴 +3.82 kB
assets/Load3D-DjuEufQ-.js (removed) 18.5 kB 🟢 -18.5 kB 🟢 -4.39 kB 🟢 -3.83 kB
assets/WidgetRecordAudio-BfHZb9mA.js (removed) 17.4 kB 🟢 -17.4 kB 🟢 -5 kB 🟢 -4.48 kB
assets/WidgetRecordAudio-CJ3Lv8mJ.js (new) 17.4 kB 🔴 +17.4 kB 🔴 +5 kB 🔴 +4.47 kB
assets/WidgetRange-BhH90xLZ.js (new) 17 kB 🔴 +17 kB 🔴 +4.6 kB 🔴 +4.1 kB
assets/WidgetRange-DzqYwnGr.js (removed) 17 kB 🟢 -17 kB 🟢 -4.59 kB 🟢 -4.11 kB
assets/load3d-CiKkRXES.js (new) 15.7 kB 🔴 +15.7 kB 🔴 +4.57 kB 🔴 +3.95 kB
assets/load3d-qoWvG0Ui.js (removed) 15.7 kB 🟢 -15.7 kB 🟢 -4.57 kB 🟢 -3.94 kB
assets/WaveAudioPlayer-CGVnZ3bM.js (removed) 13.4 kB 🟢 -13.4 kB 🟢 -3.69 kB 🟢 -3.23 kB
assets/WaveAudioPlayer-fWYrf3QB.js (new) 13.4 kB 🔴 +13.4 kB 🔴 +3.69 kB 🔴 +3.23 kB
assets/WidgetCurve-D1Y3QL_1.js (new) 12.1 kB 🔴 +12.1 kB 🔴 +3.92 kB 🔴 +3.54 kB
assets/WidgetCurve-jKWkpZZN.js (removed) 12.1 kB 🟢 -12.1 kB 🟢 -3.91 kB 🟢 -3.55 kB
assets/TeamWorkspacesDialogContent-BBbKxCXO.js (removed) 11.3 kB 🟢 -11.3 kB 🟢 -3.4 kB 🟢 -3.03 kB
assets/TeamWorkspacesDialogContent-BRlNNwNI.js (new) 11.3 kB 🔴 +11.3 kB 🔴 +3.41 kB 🔴 +3.02 kB
assets/nodeTemplates-C_TQvL9s.js (new) 9.81 kB 🔴 +9.81 kB 🔴 +3.47 kB 🔴 +3.06 kB
assets/nodeTemplates-DtKqkgK1.js (removed) 9.81 kB 🟢 -9.81 kB 🟢 -3.46 kB 🟢 -3.06 kB
assets/NightlySurveyController-BJh65DGv.js (new) 8.99 kB 🔴 +8.99 kB 🔴 +3.15 kB 🔴 +2.78 kB
assets/NightlySurveyController-Ci3xJs0J.js (removed) 8.99 kB 🟢 -8.99 kB 🟢 -3.14 kB 🟢 -2.78 kB
assets/InviteMemberDialogContent-CXWQjmLW.js (removed) 7.91 kB 🟢 -7.91 kB 🟢 -2.51 kB 🟢 -2.21 kB
assets/InviteMemberDialogContent-DRiQnRfo.js (new) 7.91 kB 🔴 +7.91 kB 🔴 +2.51 kB 🔴 +2.21 kB
assets/Load3DConfiguration-Dg753SNR.js (removed) 7.82 kB 🟢 -7.82 kB 🟢 -2.31 kB 🟢 -2.02 kB
assets/Load3DConfiguration-ZWVqtgXr.js (new) 7.82 kB 🔴 +7.82 kB 🔴 +2.31 kB 🔴 +2.02 kB
assets/onboardingCloudRoutes-CM0x5e9l.js (removed) 6.58 kB 🟢 -6.58 kB 🟢 -2.06 kB 🟢 -1.77 kB
assets/onboardingCloudRoutes-el4VdaSI.js (new) 6.58 kB 🔴 +6.58 kB 🔴 +2.05 kB 🔴 +1.77 kB
assets/CreateWorkspaceDialogContent-ByOAVGDo.js (removed) 6.12 kB 🟢 -6.12 kB 🟢 -2.22 kB 🟢 -1.93 kB
assets/CreateWorkspaceDialogContent-C82arrn9.js (new) 6.12 kB 🔴 +6.12 kB 🔴 +2.23 kB 🔴 +1.95 kB
assets/WidgetWithControl-DgsGRoW0.js (new) 6.02 kB 🔴 +6.02 kB 🔴 +2.4 kB 🔴 +2.13 kB
assets/WidgetWithControl-tAqDU4uj.js (removed) 6.02 kB 🟢 -6.02 kB 🟢 -2.4 kB 🟢 -2.13 kB
assets/FreeTierDialogContent-D6oGnLq1.js (new) 5.99 kB 🔴 +5.99 kB 🔴 +2.12 kB 🔴 +1.88 kB
assets/FreeTierDialogContent-DzlOKVRL.js (removed) 5.99 kB 🟢 -5.99 kB 🟢 -2.12 kB 🟢 -1.88 kB
assets/EditWorkspaceDialogContent-CAi13edO.js (new) 5.92 kB 🔴 +5.92 kB 🔴 +2.19 kB 🔴 +1.93 kB
assets/EditWorkspaceDialogContent-uslFHf7A.js (removed) 5.92 kB 🟢 -5.92 kB 🟢 -2.18 kB 🟢 -1.93 kB
assets/WidgetTextarea-BCd0BZ8E.js (removed) 5.73 kB 🟢 -5.73 kB 🟢 -2.26 kB 🟢 -1.99 kB
assets/WidgetTextarea-C1pj-KBS.js (new) 5.73 kB 🔴 +5.73 kB 🔴 +2.26 kB 🔴 +1.99 kB
assets/Preview3d-AvNYunM6.js (new) 5.7 kB 🔴 +5.7 kB 🔴 +1.91 kB 🔴 +1.67 kB
assets/Preview3d-m4nAnLrO.js (removed) 5.7 kB 🟢 -5.7 kB 🟢 -1.91 kB 🟢 -1.68 kB
assets/ValueControlPopover-4iSvRChw.js (removed) 5.5 kB 🟢 -5.5 kB 🟢 -2 kB 🟢 -1.8 kB
assets/ValueControlPopover-hzIqvONv.js (new) 5.5 kB 🔴 +5.5 kB 🔴 +2 kB 🔴 +1.8 kB
assets/CancelSubscriptionDialogContent-DqhJlgex.js (removed) 5.46 kB 🟢 -5.46 kB 🟢 -2.04 kB 🟢 -1.79 kB
assets/CancelSubscriptionDialogContent-DX_I_JZE.js (new) 5.46 kB 🔴 +5.46 kB 🔴 +2.05 kB 🔴 +1.79 kB
assets/DeleteWorkspaceDialogContent-BFAYlM1J.js (removed) 4.82 kB 🟢 -4.82 kB 🟢 -1.86 kB 🟢 -1.62 kB
assets/DeleteWorkspaceDialogContent-BjhM3erG.js (new) 4.82 kB 🔴 +4.82 kB 🔴 +1.86 kB 🔴 +1.62 kB
assets/LeaveWorkspaceDialogContent-D_OrJ3dp.js (removed) 4.65 kB 🟢 -4.65 kB 🟢 -1.8 kB 🟢 -1.57 kB
assets/LeaveWorkspaceDialogContent-Dkyjgkd3.js (new) 4.65 kB 🔴 +4.65 kB 🔴 +1.81 kB 🔴 +1.57 kB
assets/RemoveMemberDialogContent-CAEeTk6c.js (removed) 4.63 kB 🟢 -4.63 kB 🟢 -1.76 kB 🟢 -1.54 kB
assets/RemoveMemberDialogContent-DNV7N5ZU.js (new) 4.63 kB 🔴 +4.63 kB 🔴 +1.76 kB 🔴 +1.54 kB
assets/RevokeInviteDialogContent-Bs9f0azi.js (removed) 4.54 kB 🟢 -4.54 kB 🟢 -1.77 kB 🟢 -1.56 kB
assets/RevokeInviteDialogContent-D46ZbD3V.js (new) 4.54 kB 🔴 +4.54 kB 🔴 +1.77 kB 🔴 +1.56 kB
assets/tierBenefits-B-QBs_0y.js (new) 4.45 kB 🔴 +4.45 kB 🔴 +1.57 kB 🔴 +1.36 kB
assets/tierBenefits-CTfFUr_1.js (removed) 4.45 kB 🟢 -4.45 kB 🟢 -1.57 kB 🟢 -1.36 kB
assets/InviteMemberUpsellDialogContent-BK2BWxJF.js (new) 4.44 kB 🔴 +4.44 kB 🔴 +1.64 kB 🔴 +1.43 kB
assets/InviteMemberUpsellDialogContent-C8n2LvLU.js (removed) 4.44 kB 🟢 -4.44 kB 🟢 -1.63 kB 🟢 -1.44 kB
assets/cloudSessionCookie-Cr-VCp5B.js (new) 4.28 kB 🔴 +4.28 kB 🔴 +1.56 kB 🔴 +1.36 kB
assets/cloudSessionCookie-ko984j3c.js (removed) 4.28 kB 🟢 -4.28 kB 🟢 -1.56 kB 🟢 -1.36 kB
assets/Media3DTop-C1ac7qlH.js (new) 4.01 kB 🔴 +4.01 kB 🔴 +1.7 kB 🔴 +1.49 kB
assets/Media3DTop-C38v2K0S.js (removed) 4.01 kB 🟢 -4.01 kB 🟢 -1.7 kB 🟢 -1.5 kB
assets/saveMesh-BIVGcxzS.js (new) 3.98 kB 🔴 +3.98 kB 🔴 +1.74 kB 🔴 +1.53 kB
assets/saveMesh-WDWDrj55.js (removed) 3.98 kB 🟢 -3.98 kB 🟢 -1.73 kB 🟢 -1.53 kB
assets/GlobalToast-DkLWEWn2.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.26 kB 🟢 -1.11 kB
assets/GlobalToast-DTch2bci.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.26 kB 🔴 +1.11 kB
assets/CloudRunButtonWrapper-BbTviYxG.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -1 kB 🟢 -891 B
assets/CloudRunButtonWrapper-DhHtLSZN.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +1.01 kB 🔴 +893 B
assets/SubscribeToRun-D-tu_urw.js (removed) 2.13 kB 🟢 -2.13 kB 🟢 -981 B 🟢 -859 B
assets/SubscribeToRun-Jok8Sq_N.js (new) 2.13 kB 🔴 +2.13 kB 🔴 +984 B 🔴 +879 B
assets/MediaAudioTop-AbY8i0z_.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -1.01 kB 🟢 -857 B
assets/MediaAudioTop-DDZgmlMq.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +1.01 kB 🔴 +862 B
assets/cloudBadges-CdRYkANu.js (new) 1.93 kB 🔴 +1.93 kB 🔴 +962 B 🔴 +833 B
assets/cloudBadges-CoCUy1ea.js (removed) 1.93 kB 🟢 -1.93 kB 🟢 -958 B 🟢 -831 B
assets/cloudSubscription-s15MvpLL.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -883 B 🟢 -770 B
assets/cloudSubscription-xnoq6VFT.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +885 B 🔴 +766 B
assets/graphHasMissingNodes-Brt3XdBn.js (new) 1.84 kB 🔴 +1.84 kB 🔴 +859 B 🔴 +759 B
assets/graphHasMissingNodes-COdq1LQd.js (removed) 1.84 kB 🟢 -1.84 kB 🟢 -861 B 🟢 -775 B
assets/Load3D-Bre6ANsd.js (removed) 1.55 kB 🟢 -1.55 kB 🟢 -696 B 🟢 -623 B
assets/Load3D-D5HKb3I7.js (new) 1.55 kB 🔴 +1.55 kB 🔴 +698 B 🔴 +620 B
assets/previousFullPath-gXus4Hey.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +694 B 🔴 +597 B
assets/previousFullPath-L-T9T-xi.js (removed) 1.53 kB 🟢 -1.53 kB 🟢 -694 B 🟢 -596 B
assets/nightlyBadges-CUeor8P7.js (new) 1.46 kB 🔴 +1.46 kB 🔴 +732 B 🔴 +668 B
assets/nightlyBadges-CwWFIw59.js (removed) 1.46 kB 🟢 -1.46 kB 🟢 -732 B 🟢 -667 B
assets/Load3dViewerContent-CH0WRDEH.js (removed) 1.43 kB 🟢 -1.43 kB 🟢 -649 B 🟢 -578 B
assets/Load3dViewerContent-CpabOlaQ.js (new) 1.43 kB 🔴 +1.43 kB 🔴 +652 B 🔴 +579 B
assets/SubscriptionPanelContentWorkspace-BS3S2Zj_.js (removed) 1.32 kB 🟢 -1.32 kB 🟢 -601 B 🟢 -529 B
assets/SubscriptionPanelContentWorkspace-CBUSRd1a.js (new) 1.32 kB 🔴 +1.32 kB 🔴 +605 B 🔴 +526 B
assets/WidgetLegacy-D3zR4cRG.js (removed) 1.15 kB 🟢 -1.15 kB 🟢 -549 B 🟢 -487 B
assets/WidgetLegacy-DsblTICF.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +552 B 🔴 +488 B
assets/changeTracker-CE2RRCKH.js (new) 1.12 kB 🔴 +1.12 kB 🔴 +541 B 🔴 +472 B
assets/changeTracker-D7AsGeMC.js (removed) 1.12 kB 🟢 -1.12 kB 🟢 -538 B 🟢 -473 B

Status: 57 added / 57 removed / 78 unchanged

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 64.5 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 45.0 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 68.5 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.6 MB heap
large-graph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 66.6 MB heap
large-graph-pan: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 55.9 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 65.9 MB heap
minimap-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.4 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 53.5 MB heap
subgraph-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 64.7 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 55.4 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 66.0 MB heap
vue-large-graph-idle: · 58.1 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 165.7 MB heap
vue-large-graph-pan: · 58.1 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 23ms TBT · 164.6 MB heap
workflow-execution: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 50.3 MB heap

No regressions detected.

All metrics
Metric Baseline PR (median) Δ Sig
canvas-idle: avg frame time 17ms 17ms +0% z=-0.1
canvas-idle: p95 frame time 17ms 17ms -1%
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 10ms 8ms -23% z=-3.2
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 11 10 -9% z=-2.1
canvas-idle: task duration 434ms 333ms -23% z=-2.0
canvas-idle: script duration 27ms 22ms -19% z=-1.6
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: heap used 63.6 MB 64.5 MB +1%
canvas-idle: DOM nodes 22 20 -9% z=-2.0
canvas-idle: event listeners 6 6 +0% z=-1.2
canvas-mouse-sweep: avg frame time 17ms 17ms +0% z=-0.4
canvas-mouse-sweep: p95 frame time 17ms 17ms +0%
canvas-mouse-sweep: layout duration 4ms 3ms -24% z=-3.4
canvas-mouse-sweep: style recalc duration 39ms 33ms -16% z=-3.1
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 76 73 -4% z=-2.4
canvas-mouse-sweep: task duration 833ms 714ms -14% z=-2.6
canvas-mouse-sweep: script duration 124ms 115ms -7% z=-3.1
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: heap used 45.0 MB 45.0 MB -0%
canvas-mouse-sweep: DOM nodes -261 -259 -1% z=-124.2
canvas-mouse-sweep: event listeners -127 -127 +0% z=-32.5
canvas-zoom-sweep: avg frame time 17ms 17ms +0% z=0.5
canvas-zoom-sweep: p95 frame time 17ms 17ms +0%
canvas-zoom-sweep: layout duration 1ms 0ms -26% z=-3.4
canvas-zoom-sweep: style recalc duration 18ms 14ms -23% z=-3.6
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 317ms 273ms -14% z=-2.4
canvas-zoom-sweep: script duration 29ms 17ms -42% z=-3.5
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: heap used 47.3 MB 68.5 MB +45%
canvas-zoom-sweep: DOM nodes 79 77 -3% z=-2.8
canvas-zoom-sweep: event listeners 21 21 +0% z=-0.5
dom-widget-clipping: avg frame time 17ms 17ms +0% z=0.1
dom-widget-clipping: p95 frame time 17ms 17ms +0%
dom-widget-clipping: layout duration 0ms 0ms +0%
dom-widget-clipping: style recalc duration 8ms 8ms -2% z=-2.4
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 12 11 -8% z=-4.2
dom-widget-clipping: task duration 338ms 302ms -10% z=-3.8
dom-widget-clipping: script duration 59ms 52ms -11% z=-4.9
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: heap used 53.2 MB 51.6 MB -3%
dom-widget-clipping: DOM nodes 20 18 -10% z=-2.9
dom-widget-clipping: event listeners 2 2 +0% variance too high
large-graph-idle: avg frame time 17ms 17ms +0% z=-0.2
large-graph-idle: p95 frame time 17ms 17ms +1%
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 9ms 7ms -19% z=-4.5
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 10 9 -10% z=-8.3
large-graph-idle: task duration 576ms 488ms -15% z=-1.0
large-graph-idle: script duration 100ms 88ms -12% z=-1.4
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: heap used 61.9 MB 66.6 MB +8%
large-graph-idle: DOM nodes -259 -261 +1% z=-316.1
large-graph-idle: event listeners -127 -127 +0% z=-25.1
large-graph-pan: avg frame time 17ms 17ms +0% z=-0.8
large-graph-pan: p95 frame time 17ms 17ms -1%
large-graph-pan: layout duration 0ms 0ms +0%
large-graph-pan: style recalc duration 18ms 16ms -10% z=-1.4
large-graph-pan: layout count 0 0 +0%
large-graph-pan: style recalc count 68 69 +1% z=-0.9
large-graph-pan: task duration 1109ms 974ms -12% z=-2.5
large-graph-pan: script duration 390ms 363ms -7% z=-2.3
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: heap used 62.7 MB 55.9 MB -11%
large-graph-pan: DOM nodes -264 -261 -1% z=-169.7
large-graph-pan: event listeners -125 -125 +0% z=-156.9
large-graph-zoom: avg frame time 17ms 17ms -0%
large-graph-zoom: p95 frame time 17ms 17ms -1%
large-graph-zoom: layout duration 7ms 7ms -13%
large-graph-zoom: style recalc duration 19ms 16ms -18%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 66 65 -2%
large-graph-zoom: task duration 1291ms 1176ms -9%
large-graph-zoom: script duration 462ms 457ms -1%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: heap used 64.3 MB 65.9 MB +3%
large-graph-zoom: DOM nodes -267 -267 +0%
large-graph-zoom: event listeners -123 -123 +0%
minimap-idle: avg frame time 17ms 17ms -0% z=-0.9
minimap-idle: p95 frame time 17ms 17ms -1%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 8ms 8ms -1% z=-2.1
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 8 9 +13% z=-0.8
minimap-idle: task duration 547ms 477ms -13% z=-1.1
minimap-idle: script duration 94ms 85ms -10% z=-1.3
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: heap used 64.5 MB 62.4 MB -3%
minimap-idle: DOM nodes -262 -263 +0% z=-206.3
minimap-idle: event listeners -127 -127 +0% z=-199.3
subgraph-dom-widget-clipping: avg frame time 17ms 17ms +0% z=1.2
subgraph-dom-widget-clipping: p95 frame time 17ms 17ms +0%
subgraph-dom-widget-clipping: layout duration 0ms 0ms +0%
subgraph-dom-widget-clipping: style recalc duration 11ms 10ms -5% z=-2.8
subgraph-dom-widget-clipping: layout count 0 0 +0%
subgraph-dom-widget-clipping: style recalc count 46 47 +2% z=-1.6
subgraph-dom-widget-clipping: task duration 339ms 317ms -7% z=-3.3
subgraph-dom-widget-clipping: script duration 112ms 108ms -4% z=-3.2
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: heap used 51.8 MB 53.5 MB +3%
subgraph-dom-widget-clipping: DOM nodes 18 20 +11% z=-1.9
subgraph-dom-widget-clipping: event listeners 8 8 +0% z=-1.4
subgraph-idle: avg frame time 17ms 17ms +0% z=0.4
subgraph-idle: p95 frame time 17ms 17ms -1%
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 9ms 9ms +3% z=-1.9
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 10 11 +10% z=0.2
subgraph-idle: task duration 373ms 333ms -11% z=-1.2
subgraph-idle: script duration 20ms 18ms -7% z=-0.7
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: heap used 64.6 MB 64.7 MB +0%
subgraph-idle: DOM nodes 20 22 +10% z=0.1
subgraph-idle: event listeners 6 6 +0% variance too high
subgraph-mouse-sweep: avg frame time 17ms 17ms +0% z=0.4
subgraph-mouse-sweep: p95 frame time 17ms 17ms -0%
subgraph-mouse-sweep: layout duration 5ms 4ms -24% z=-3.1
subgraph-mouse-sweep: style recalc duration 40ms 32ms -19% z=-3.1
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 76 76 +0% z=-2.1
subgraph-mouse-sweep: task duration 710ms 594ms -16% z=-2.5
subgraph-mouse-sweep: script duration 92ms 85ms -7% z=-2.4
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: heap used 56.6 MB 55.4 MB -2%
subgraph-mouse-sweep: DOM nodes 61 62 +2% z=-2.2
subgraph-mouse-sweep: event listeners 4 4 +0% variance too high
viewport-pan-sweep: avg frame time 17ms 17ms -0%
viewport-pan-sweep: p95 frame time 17ms 17ms +1%
viewport-pan-sweep: layout duration 0ms 0ms +0%
viewport-pan-sweep: style recalc duration 52ms 45ms -13%
viewport-pan-sweep: layout count 0 0 +0%
viewport-pan-sweep: style recalc count 250 249 -0%
viewport-pan-sweep: task duration 3709ms 3243ms -13%
viewport-pan-sweep: script duration 1258ms 1143ms -9%
viewport-pan-sweep: TBT 0ms 0ms +0%
viewport-pan-sweep: heap used 64.8 MB 66.0 MB +2%
viewport-pan-sweep: DOM nodes -258 -261 +1%
viewport-pan-sweep: event listeners -111 -111 +0%
vue-large-graph-idle: avg frame time 17ms 17ms +0%
vue-large-graph-idle: p95 frame time 17ms 17ms -0%
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 10867ms 10222ms -6%
vue-large-graph-idle: script duration 601ms 512ms -15%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: heap used 160.2 MB 165.7 MB +3%
vue-large-graph-idle: DOM nodes -9850 -9850 +0%
vue-large-graph-idle: event listeners -23969 -23961 -0%
vue-large-graph-pan: avg frame time 18ms 17ms -3%
vue-large-graph-pan: p95 frame time 17ms 17ms +0%
vue-large-graph-pan: layout duration 0ms 0ms +0%
vue-large-graph-pan: style recalc duration 16ms 14ms -14%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 64 66 +3%
vue-large-graph-pan: task duration 12480ms 13002ms +4%
vue-large-graph-pan: script duration 865ms 811ms -6%
vue-large-graph-pan: TBT 0ms 23ms
vue-large-graph-pan: heap used 163.7 MB 164.6 MB +1%
vue-large-graph-pan: DOM nodes -9850 -9850 +0%
vue-large-graph-pan: event listeners -23959 -23983 +0%
workflow-execution: avg frame time 17ms 17ms +0% z=0.6
workflow-execution: p95 frame time 17ms 17ms -0%
workflow-execution: layout duration 2ms 1ms -15% z=-1.8
workflow-execution: style recalc duration 21ms 22ms +4% z=-1.1
workflow-execution: layout count 5 5 +0% z=0.1
workflow-execution: style recalc count 16 17 +6% z=-0.4
workflow-execution: task duration 131ms 116ms -11% z=-0.7
workflow-execution: script duration 23ms 21ms -7% z=-2.7
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: heap used 51.6 MB 50.3 MB -2%
workflow-execution: DOM nodes 156 167 +7% z=0.9
workflow-execution: event listeners 71 71 +0% z=4.4
Historical variance (last 15 runs)
Metric μ σ CV
canvas-idle: avg frame time 17ms 0ms 0.0%
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: DOM nodes 23 1 5.6%
canvas-idle: event listeners 12 5 40.9%
canvas-mouse-sweep: avg frame time 17ms 0ms 0.0%
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: DOM nodes 62 3 4.2%
canvas-mouse-sweep: event listeners 8 4 49.4%
canvas-zoom-sweep: avg frame time 17ms 0ms 0.0%
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: DOM nodes 79 1 1.0%
canvas-zoom-sweep: event listeners 24 5 21.8%
dom-widget-clipping: avg frame time 17ms 0ms 0.0%
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: DOM nodes 22 1 6.4%
dom-widget-clipping: event listeners 8 6 81.2%
large-graph-idle: avg frame time 17ms 0ms 0.0%
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: DOM nodes 25 1 3.7%
large-graph-idle: event listeners 26 6 23.2%
large-graph-pan: avg frame time 17ms 0ms 0.0%
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: DOM nodes 19 2 8.7%
large-graph-pan: event listeners 5 1 16.8%
minimap-idle: avg frame time 17ms 0ms 0.0%
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: DOM nodes 19 1 7.1%
minimap-idle: event listeners 5 1 14.4%
subgraph-dom-widget-clipping: avg frame time 17ms 0ms 0.0%
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: DOM nodes 22 1 5.0%
subgraph-dom-widget-clipping: event listeners 16 6 36.0%
subgraph-idle: avg frame time 17ms 0ms 0.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: DOM nodes 22 1 6.9%
subgraph-idle: event listeners 10 7 64.5%
subgraph-mouse-sweep: avg frame time 17ms 0ms 0.0%
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: DOM nodes 67 2 3.3%
subgraph-mouse-sweep: event listeners 8 4 52.6%
workflow-execution: avg frame time 17ms 0ms 0.0%
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: 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: avg frame time ▆▃▆▁▆▃▆█▆▆▄▃▃▄▃ ➡️ 17ms
canvas-idle: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
canvas-idle: DOM nodes █▇▆▅▃▇▃▁▂▂▅▆▆▆▇ ➡️ 24
canvas-idle: event listeners ▅█▅▄▁▅▁▁▁▄▅▅▁▅▄ 📉 11
canvas-mouse-sweep: avg frame time ▆█▆▃▁▃▁▆▆▁▃▆▆▃▃ ➡️ 17ms
canvas-mouse-sweep: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
canvas-mouse-sweep: DOM nodes █▅▃▃▁▂▂▃▂▄▆▅▃▅▅ ➡️ 64
canvas-mouse-sweep: event listeners █▁▁▁▁▁▇▁▁▁██▇▁█ 📈 13
canvas-zoom-sweep: avg frame time ▅▅█▄▅▁▁▁▅▁▁▅▄▅▁ ➡️ 17ms
canvas-zoom-sweep: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
canvas-zoom-sweep: DOM nodes ▄▃▁▅█▁▃▆▄▅▅▃▃▄▃ ➡️ 79
canvas-zoom-sweep: event listeners ▁▁▂▅█▂▁▅▁▅▅▄▁▅▁ ➡️ 19
dom-widget-clipping: avg frame time ▂▄▅▅▂▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
dom-widget-clipping: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
dom-widget-clipping: DOM nodes ▇▇▄▇▅▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: event listeners ▅▅▅▅▁▅██▁▁▁▁█▁▁ 📉 2
large-graph-idle: avg frame time ▅▅▅▅▅▂▁▂▄▅▄▂▂▅█ ➡️ 17ms
large-graph-idle: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
large-graph-idle: DOM nodes ▆█▅▂▅▃▁▂▃▅▅▆▂▆▅ ➡️ 25
large-graph-idle: event listeners ███▇██▄▁▄▇▇█▂█▇ ➡️ 29
large-graph-pan: avg frame time ▆▃▃▆█▃▁█▆▆▆▆█▁▆ ➡️ 17ms
large-graph-pan: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
large-graph-pan: DOM nodes ▅▃▆▂▄▁▃▁▁▅▁▂█▅▂ ➡️ 18
large-graph-pan: event listeners █▆█▁▁▆▁▁▃▆▁▃██▃ ➡️ 5
minimap-idle: avg frame time ▃▆▆▃█▁█▆▆▃▃▆█▆█ ➡️ 17ms
minimap-idle: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
minimap-idle: DOM nodes ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 19
minimap-idle: event listeners ▃▃▆▁▁▁▃▁▁▆▁▃█▆▁ ➡️ 4
subgraph-dom-widget-clipping: avg frame time ▅▄▄▄▄▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-dom-widget-clipping: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
subgraph-dom-widget-clipping: DOM nodes ▅▇▅▂▅▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: event listeners ▅▅▅▂▅▁▅██▁▁█▅█▅ 📈 16
subgraph-idle: avg frame time ▆▆█▁▆▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-idle: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
subgraph-idle: DOM nodes ▃▅▃▂▁▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: event listeners ▁▅▁▁▁▁▁▁▁▅▄▁███ 📈 21
subgraph-mouse-sweep: avg frame time ▅▄▁▃▃▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
subgraph-mouse-sweep: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
subgraph-mouse-sweep: DOM nodes ▅▁▄▅▁▄▃▃█▅▅▄▂▅▃ ➡️ 66
subgraph-mouse-sweep: event listeners ▇▁▂▇▁▂▂▂█▇▂▂▇▇▂ 📈 5
workflow-execution: avg frame time ▆▆▆▄▆▆▃▄▁▄█▆▅▄▆ ➡️ 17ms
workflow-execution: p95 frame time ➡️ NaNms
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: heap used ➡️ NaN MB
workflow-execution: DOM nodes ▂█▃▆▁▄▃▅▃█▃▃▄▃▁ ➡️ 152
workflow-execution: event listeners ▅███▁▅███▁██▅█▅ ➡️ 49
Raw data
{
  "timestamp": "2026-04-29T00:53:18.960Z",
  "gitSha": "94388109ee5e1f09ce396afd3402ef7a32550e09",
  "branch": "load3d-tier1-manager-tests",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2043.7919999999963,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 6.566000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 354.041,
      "heapDeltaBytes": 828296,
      "heapUsedBytes": 67309044,
      "domNodes": 18,
      "jsHeapTotalBytes": 18972672,
      "scriptDurationMs": 13.468,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-idle",
      "durationMs": 2023.6710000000357,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.185000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 327.02,
      "heapDeltaBytes": 1513880,
      "heapUsedBytes": 67626028,
      "domNodes": 22,
      "jsHeapTotalBytes": 19496960,
      "scriptDurationMs": 22.015,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-idle",
      "durationMs": 2006.9270000000188,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 7.9399999999999995,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 333.473,
      "heapDeltaBytes": 2501760,
      "heapUsedBytes": 68531812,
      "domNodes": 20,
      "jsHeapTotalBytes": 21233664,
      "scriptDurationMs": 21.722,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1759.0109999999868,
      "styleRecalcs": 73,
      "styleRecalcDurationMs": 33.991,
      "layouts": 12,
      "layoutDurationMs": 2.937,
      "taskDurationMs": 719.8340000000001,
      "heapDeltaBytes": -18987112,
      "heapUsedBytes": 47138576,
      "domNodes": -255,
      "jsHeapTotalBytes": 19623936,
      "scriptDurationMs": 120.06400000000001,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1734.8610000000235,
      "styleRecalcs": 72,
      "styleRecalcDurationMs": 30.058,
      "layouts": 12,
      "layoutDurationMs": 2.972,
      "taskDurationMs": 714.366,
      "heapDeltaBytes": -20169308,
      "heapUsedBytes": 46517744,
      "domNodes": -263,
      "jsHeapTotalBytes": 19361792,
      "scriptDurationMs": 112.069,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1759.5939999999928,
      "styleRecalcs": 73,
      "styleRecalcDurationMs": 32.74399999999999,
      "layouts": 12,
      "layoutDurationMs": 2.941,
      "taskDurationMs": 706.369,
      "heapDeltaBytes": -19298756,
      "heapUsedBytes": 47175080,
      "domNodes": -259,
      "jsHeapTotalBytes": 19886080,
      "scriptDurationMs": 115.24699999999999,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1735.0959999999986,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 15.107000000000001,
      "layouts": 6,
      "layoutDurationMs": 0.636,
      "taskDurationMs": 280.35900000000004,
      "heapDeltaBytes": 5425912,
      "heapUsedBytes": 71869052,
      "domNodes": 77,
      "jsHeapTotalBytes": 19234816,
      "scriptDurationMs": 16.60799999999999,
      "eventListeners": 21,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1724.2889999999989,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 13.629999999999997,
      "layouts": 6,
      "layoutDurationMs": 0.48900000000000005,
      "taskDurationMs": 272.745,
      "heapDeltaBytes": 5690624,
      "heapUsedBytes": 71981488,
      "domNodes": 77,
      "jsHeapTotalBytes": 19234816,
      "scriptDurationMs": 21.911999999999995,
      "eventListeners": 21,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1705.1510000000007,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 13.575,
      "layouts": 6,
      "layoutDurationMs": 0.45899999999999996,
      "taskDurationMs": 254.16700000000003,
      "heapDeltaBytes": 132352,
      "heapUsedBytes": 48366712,
      "domNodes": 76,
      "jsHeapTotalBytes": 14417920,
      "scriptDurationMs": 15.999000000000002,
      "eventListeners": 21,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 506.0579999999959,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 8.01,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 302.464,
      "heapDeltaBytes": -10660972,
      "heapUsedBytes": 55834692,
      "domNodes": 18,
      "jsHeapTotalBytes": 20447232,
      "scriptDurationMs": 52.01599999999999,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 547.6400000000012,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 8.225,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 311.72299999999996,
      "heapDeltaBytes": -11804528,
      "heapUsedBytes": 54126260,
      "domNodes": 22,
      "jsHeapTotalBytes": 20021248,
      "scriptDurationMs": 58.148,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 463.78200000003744,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 5.46,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 279.76199999999994,
      "heapDeltaBytes": 4746252,
      "heapUsedBytes": 51772860,
      "domNodes": 16,
      "jsHeapTotalBytes": 10223616,
      "scriptDurationMs": 49.27000000000001,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2028.2210000000305,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.055999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 485.60499999999996,
      "heapDeltaBytes": 12222924,
      "heapUsedBytes": 70307464,
      "domNodes": -261,
      "jsHeapTotalBytes": -229376,
      "scriptDurationMs": 87.977,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2039.5519999999578,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 7.49,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 497.36899999999997,
      "heapDeltaBytes": 11730548,
      "heapUsedBytes": 69801672,
      "domNodes": -261,
      "jsHeapTotalBytes": 1081344,
      "scriptDurationMs": 89.307,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2020.7169999999905,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 7.477000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 487.774,
      "heapDeltaBytes": 4097824,
      "heapUsedBytes": 61258992,
      "domNodes": -261,
      "jsHeapTotalBytes": 557056,
      "scriptDurationMs": 86.25099999999999,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2102.359999999976,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 16.169000000000004,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 960.8639999999999,
      "heapDeltaBytes": -1791840,
      "heapUsedBytes": 57296592,
      "domNodes": -263,
      "jsHeapTotalBytes": 1286144,
      "scriptDurationMs": 360.735,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2116.3589999999886,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 17.000999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 973.9,
      "heapDeltaBytes": 531240,
      "heapUsedBytes": 58656028,
      "domNodes": -261,
      "jsHeapTotalBytes": 1286144,
      "scriptDurationMs": 362.556,
      "eventListeners": -125,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2105.536000000029,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 15.702000000000004,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 980.4599999999999,
      "heapDeltaBytes": 11798772,
      "heapUsedBytes": 71041640,
      "domNodes": -251,
      "jsHeapTotalBytes": 761856,
      "scriptDurationMs": 379.236,
      "eventListeners": -125,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3135.1270000000113,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 17.319000000000003,
      "layouts": 60,
      "layoutDurationMs": 6.577,
      "taskDurationMs": 1188.4869999999999,
      "heapDeltaBytes": 9296864,
      "heapUsedBytes": 69057692,
      "domNodes": -267,
      "jsHeapTotalBytes": 5799936,
      "scriptDurationMs": 458.21099999999996,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3116.9539999999643,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 15.526000000000002,
      "layouts": 60,
      "layoutDurationMs": 6.515999999999999,
      "taskDurationMs": 1174.4620000000002,
      "heapDeltaBytes": 8385984,
      "heapUsedBytes": 69153312,
      "domNodes": -267,
      "jsHeapTotalBytes": 3899392,
      "scriptDurationMs": 446.651,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3132.111000000009,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 15.847,
      "layouts": 60,
      "layoutDurationMs": 6.44,
      "taskDurationMs": 1175.7040000000002,
      "heapDeltaBytes": 8583804,
      "heapUsedBytes": 69342428,
      "domNodes": -266,
      "jsHeapTotalBytes": 5275648,
      "scriptDurationMs": 456.751,
      "eventListeners": -123,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2012.3779999999556,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 6.561000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 473.196,
      "heapDeltaBytes": 6431600,
      "heapUsedBytes": 65440324,
      "domNodes": -264,
      "jsHeapTotalBytes": 557056,
      "scriptDurationMs": 85.368,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "minimap-idle",
      "durationMs": 2028.173000000038,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 11.097,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 483.965,
      "heapDeltaBytes": 13238176,
      "heapUsedBytes": 72212212,
      "domNodes": -262,
      "jsHeapTotalBytes": 294912,
      "scriptDurationMs": 84.81700000000001,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2021.3990000000877,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 7.817999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 476.81300000000005,
      "heapDeltaBytes": 4918056,
      "heapUsedBytes": 63990292,
      "domNodes": -263,
      "jsHeapTotalBytes": 557056,
      "scriptDurationMs": 86.63600000000001,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 510.39800000000923,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 9.406,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 305.61499999999995,
      "heapDeltaBytes": 9139256,
      "heapUsedBytes": 58782804,
      "domNodes": 20,
      "jsHeapTotalBytes": 15466496,
      "scriptDurationMs": 107.83,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.669999999999998,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 525.7239999999683,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 10.038999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 316.651,
      "heapDeltaBytes": -10436780,
      "heapUsedBytes": 56051340,
      "domNodes": 20,
      "jsHeapTotalBytes": 20971520,
      "scriptDurationMs": 105.09100000000001,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.669999999999998,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 532.4459999999362,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 10.538,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 346.943,
      "heapDeltaBytes": -10520616,
      "heapUsedBytes": 55758792,
      "domNodes": 22,
      "jsHeapTotalBytes": 21233664,
      "scriptDurationMs": 118.289,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2005.326000000025,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 9.163,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 332.71,
      "heapDeltaBytes": 1592888,
      "heapUsedBytes": 67984024,
      "domNodes": 23,
      "jsHeapTotalBytes": 19496960,
      "scriptDurationMs": 18.536000000000005,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1992.1689999999899,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 8.525000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 324.33799999999997,
      "heapDeltaBytes": 925596,
      "heapUsedBytes": 67500872,
      "domNodes": 22,
      "jsHeapTotalBytes": 18972672,
      "scriptDurationMs": 17.131,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2006.5069999999423,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 8.959999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 333.32300000000004,
      "heapDeltaBytes": 1315628,
      "heapUsedBytes": 67854724,
      "domNodes": 22,
      "jsHeapTotalBytes": 19234816,
      "scriptDurationMs": 18.449,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1997.4619999999845,
      "styleRecalcs": 83,
      "styleRecalcDurationMs": 38.269,
      "layouts": 16,
      "layoutDurationMs": 3.701,
      "taskDurationMs": 861.421,
      "heapDeltaBytes": 3842220,
      "heapUsedBytes": 52487832,
      "domNodes": -261,
      "jsHeapTotalBytes": 14544896,
      "scriptDurationMs": 85.174,
      "eventListeners": -127,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1691.4730000000873,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 32.031,
      "layouts": 16,
      "layoutDurationMs": 3.86,
      "taskDurationMs": 594.349,
      "heapDeltaBytes": -7425596,
      "heapUsedBytes": 58065260,
      "domNodes": 63,
      "jsHeapTotalBytes": 21069824,
      "scriptDurationMs": 84.976,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1676.0649999999941,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 30.889,
      "layouts": 16,
      "layoutDurationMs": 3.525,
      "taskDurationMs": 579.895,
      "heapDeltaBytes": -6037060,
      "heapUsedBytes": 60302176,
      "domNodes": 62,
      "jsHeapTotalBytes": 20447232,
      "scriptDurationMs": 83.194,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8141.294000000016,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 45.26,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3547.635,
      "heapDeltaBytes": 18665784,
      "heapUsedBytes": 76840976,
      "domNodes": -264,
      "jsHeapTotalBytes": 7053312,
      "scriptDurationMs": 1299.184,
      "eventListeners": -111,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333338,
      "p95FrameDurationMs": 16.80000000000109
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8158.346000000051,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 46.355999999999995,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3243.414,
      "heapDeltaBytes": 11002524,
      "heapUsedBytes": 69251160,
      "domNodes": -261,
      "jsHeapTotalBytes": 3121152,
      "scriptDurationMs": 1140.647,
      "eventListeners": -111,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8118.290999999999,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 44.642,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3230.501,
      "heapDeltaBytes": 9928464,
      "heapUsedBytes": 67956676,
      "domNodes": -261,
      "jsHeapTotalBytes": 7577600,
      "scriptDurationMs": 1142.502,
      "eventListeners": -111,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.80000000000109
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 10234.077000000014,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 10222.312,
      "heapDeltaBytes": -43721712,
      "heapUsedBytes": 173795296,
      "domNodes": -9850,
      "jsHeapTotalBytes": -16715776,
      "scriptDurationMs": 505.78999999999996,
      "eventListeners": -23961,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 10267.415999999912,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 10251.101,
      "heapDeltaBytes": -31269480,
      "heapUsedBytes": 167997276,
      "domNodes": -9850,
      "jsHeapTotalBytes": 24702976,
      "scriptDurationMs": 549.809,
      "eventListeners": -23960,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 9907.836999999972,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 9895.302,
      "heapDeltaBytes": -28153024,
      "heapUsedBytes": 173889008,
      "domNodes": -9850,
      "jsHeapTotalBytes": 24965120,
      "scriptDurationMs": 512.4759999999999,
      "eventListeners": -23963,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 13068.19500000006,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 14.081000000000039,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 13049.938999999998,
      "heapDeltaBytes": -53853492,
      "heapUsedBytes": 164814600,
      "domNodes": -9850,
      "jsHeapTotalBytes": -12783616,
      "scriptDurationMs": 771.778,
      "eventListeners": -23983,
      "totalBlockingTimeMs": 23,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 13022.888999999963,
      "styleRecalcs": 67,
      "styleRecalcDurationMs": 14.63600000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 13002.209999999997,
      "heapDeltaBytes": -41557580,
      "heapUsedBytes": 176444560,
      "domNodes": -9848,
      "jsHeapTotalBytes": -10510336,
      "scriptDurationMs": 813.852,
      "eventListeners": -23983,
      "totalBlockingTimeMs": 48,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 12855.70800000005,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 14.11800000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12836.624999999998,
      "heapDeltaBytes": -34969164,
      "heapUsedBytes": 172610780,
      "domNodes": -9850,
      "jsHeapTotalBytes": -16539648,
      "scriptDurationMs": 811.0630000000001,
      "eventListeners": -23957,
      "totalBlockingTimeMs": 17,
      "frameDurationMs": 17.223333333333237,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "workflow-execution",
      "durationMs": 463.87000000004264,
      "styleRecalcs": 17,
      "styleRecalcDurationMs": 22.721000000000004,
      "layouts": 5,
      "layoutDurationMs": 1.374,
      "taskDurationMs": 131.749,
      "heapDeltaBytes": -14926720,
      "heapUsedBytes": 52508112,
      "domNodes": 167,
      "jsHeapTotalBytes": 4816896,
      "scriptDurationMs": 27.346999999999998,
      "eventListeners": 71,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999727
    },
    {
      "name": "workflow-execution",
      "durationMs": 474.7790000000123,
      "styleRecalcs": 20,
      "styleRecalcDurationMs": 21.819,
      "layouts": 5,
      "layoutDurationMs": 1.283,
      "taskDurationMs": 114.111,
      "heapDeltaBytes": 5145388,
      "heapUsedBytes": 56286560,
      "domNodes": 178,
      "jsHeapTotalBytes": 0,
      "scriptDurationMs": 20.579,
      "eventListeners": 71,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.663333333333338,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "workflow-execution",
      "durationMs": 462.1759999999995,
      "styleRecalcs": 16,
      "styleRecalcDurationMs": 19.419,
      "layouts": 4,
      "layoutDurationMs": 0.9430000000000003,
      "taskDurationMs": 115.67699999999999,
      "heapDeltaBytes": -14991828,
      "heapUsedBytes": 52783712,
      "domNodes": 154,
      "jsHeapTotalBytes": 4030464,
      "scriptDurationMs": 21.055,
      "eventListeners": 71,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    }
  ]
}

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 (7)
src/extensions/core/load3d/CameraManager.test.ts (2)

34-37: Remove the inline explanatory comment.

The helper name already explains the intent, and the comment adds noise without changing behavior. As per coding guidelines, avoid new usage of code comments; do not add or retain redundant comments.

Suggested cleanup
 function makeRenderer(): THREE.WebGLRenderer {
-  // CameraManager only stores `_renderer` but never reads it. An empty object
-  // suffices and avoids needing a WebGL context in happy-dom.
   return {} as THREE.WebGLRenderer
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/extensions/core/load3d/CameraManager.test.ts` around lines 34 - 37,
Remove the inline explanatory comment inside the makeRenderer() helper in
CameraManager.test.ts so the function only returns the cast empty object; keep
the function name and implementation (return {} as THREE.WebGLRenderer)
unchanged and do not add any new comments—just delete the redundant comment
text.

18-32: Type the controls stub instead of casting it at each use site.

makeControlsStub() currently returns an object that is later forced through as unknown as OrbitControls in several tests. That weakens the test contract and makes future CameraManager changes easier to miss.

Suggested refactor
 import * as THREE from 'three'
 import type { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
 import { beforeEach, describe, expect, it, vi } from 'vitest'

 import { CameraManager } from './CameraManager'
 import type { CameraState, EventManagerInterface } from './interfaces'

 function makeMockEventManager() {
   return {
     addEventListener: vi.fn(),
     removeEventListener: vi.fn(),
     emitEvent: vi.fn()
   } satisfies EventManagerInterface
 }

 type ControlsListener = () => void
+type ControlsStub = Pick<
+  OrbitControls,
+  'target' | 'object' | 'update' | 'addEventListener'
+> & {
+  fire(event: string): void
+}

-function makeControlsStub() {
+function makeControlsStub(): ControlsStub {
   const listeners: Record<string, ControlsListener[]> = {}
   return {
     target: new THREE.Vector3(),
     object: null as THREE.Camera | null,
     update: vi.fn(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/extensions/core/load3d/CameraManager.test.ts` around lines 18 - 32,
makeControlsStub() is untyped and tests keep using "as unknown as OrbitControls"
casts; change makeControlsStub to return a properly typed stub (e.g., declare
its signature as returning OrbitControls or a narrow stub interface matching the
used subset: target, object, update, addEventListener, fire) so callers no
longer need casts. Update the function signature in CameraManager.test.ts to use
that type, implement the ControlsListener and required method shapes inside the
returned object to satisfy the type, and remove all "as unknown as
OrbitControls" cast sites in the tests.
src/extensions/core/load3d/AnimationManager.test.ts (1)

33-37: Restore spies in afterEach to keep failures isolated.

vi.clearAllMocks() only clears call history; if a warning assertion fails before warn.mockRestore(), the mocked console.warn can leak into later tests. A shared teardown makes the suite self-cleaning even on early exits.

Proposed fix
-import { beforeEach, describe, expect, it, vi } from 'vitest'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

 describe('AnimationManager', () => {
   let events: ReturnType<typeof makeMockEventManager>
   let manager: AnimationManager

   beforeEach(() => {
     vi.clearAllMocks()
     events = makeMockEventManager()
     manager = new AnimationManager(events)
   })
+
+  afterEach(() => {
+    vi.restoreAllMocks()
+  })

As per coding guidelines, keep module mocks contained; do not use global mutable state within test files.

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

In `@src/extensions/core/load3d/AnimationManager.test.ts` around lines 33 - 37,
Add a shared teardown that restores spies after each test to prevent mocked
globals leaking: in the test file add an afterEach that calls vi.clearAllMocks()
and vi.restoreAllMocks() (or explicitly calls restore on any spy variables like
the console.warn spy used in the suite) so that mocks set up during
beforeEach/individual tests (e.g., via vi.spyOn(console, "warn") or similar) are
fully restored and isolated between tests involving AnimationManager and
makeMockEventManager.
src/services/load3dService.test.ts (2)

26-35: Keep this tracker out of module scope.

createdNodes is shared mutable state across the whole file, so cleanup now depends on every path going through makeNode(). Move the tracker into vi.hoisted() or otherwise scope it per test so the suite stays isolated.

As per coding guidelines, keep module mocks contained; do not use global mutable state within test files; use vi.hoisted() if necessary.

♻️ Possible cleanup
-const createdNodes = new Set<LGraphNode>()
+const createdNodes = vi.hoisted(() => new Set<LGraphNode>())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/load3dService.test.ts` around lines 26 - 35, The shared mutable
Set createdNodes and factory makeNode are declared at module scope; move
createdNodes into test-scoped hoisting using vi.hoisted() (or initialize it
inside a beforeEach) and update makeNode to reference that
hoisted/locally-scoped Set so each test gets an isolated tracker; ensure
beforeEach drains the hoisted createdNodes and keep references to the unique
symbols createdNodes and makeNode consistent so load3dService's viewerInstances
cleanup still runs per test.

37-49: Tighten these fixtures instead of double-casting.

makeLoad3d(), the factory as unknown as typeof useLoad3dViewerMock cast, and the Object.assign(... ) as LGraphNode fixture all bypass type checking. A minimal typed fixture or satisfies-based helper would make these tests fail when the service contract drifts.

Based on learnings, prefer satisfies InterfaceType instead of type assertions in test helpers.

Also applies to: 194-207, 255-257

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

In `@src/services/load3dService.test.ts` around lines 37 - 49, Replace the unsafe
double-casts with minimally typed fixtures using TypeScript’s "satisfies" or a
small generic test-helper so the compiler enforces the service contract: change
makeLoad3d() to return an object that satisfies the Load3d interface (instead of
"as unknown as Load3d"), update the factory cast (the "factory as unknown as
typeof useLoad3dViewerMock" usage) to produce a properly typed mock via a
helper/generic returning satisfies typeof useLoad3dViewerMock, and replace the
"Object.assign(... ) as LGraphNode" assertion with an Object.assign result typed
to satisfy LGraphNode; apply the same pattern for the other occurrences noted
(around the factory and lines 194-207, 255-257) so tests will fail if the
service contract drifts.
src/extensions/core/load3d/RecordingManager.test.ts (2)

29-50: Keep the MediaRecorder mock state local to the suite.

MockMediaRecorder.instances is shared mutable state across the file. It works only because beforeEach resets it, but that makes the suite fragile if setup fails early. Prefer capturing the constructed recorder in a suite-local holder instead.

As per coding guidelines, "Keep module mocks contained; do not use global mutable state within test files; use vi.hoisted() if necessary."

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

In `@src/extensions/core/load3d/RecordingManager.test.ts` around lines 29 - 50,
The MockMediaRecorder.instances static array is shared mutable state; remove the
static instances property from class MockMediaRecorder and instead capture each
constructed recorder in a suite-local variable (e.g., declare a hoisted let
mockRecorder via vi.hoisted or a top-level let assigned in beforeEach) inside
the test file; in MockMediaRecorder's constructor assign the newly created
instance to that suite-local holder so tests call methods like
mockRecorder.pushChunk(...) and assertions use that local variable rather than
MockMediaRecorder.instances, ensuring no global mutable state is relied on.

205-210: Avoid mutating recordingStartTime directly.

This reaches into a private field and relies on a type cast, so the test will break on internal refactors even if public behavior stays correct. Prefer controlling time through the public path (Date.now()/fake timers) so the assertion stays behavior-focused.

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

In `@src/extensions/core/load3d/RecordingManager.test.ts` around lines 205 - 210,
The test "reports a non-zero duration after recording" currently mutates the
private recordingStartTime field; instead, control time via the public API by
using fake timers or mocking Date.now so the test remains behavior-focused.
Replace the direct assignment to recordingStartTime with a setup that calls
manager.startRecording() while using
jest.useFakeTimers()/jest.setSystemTime(...) or a Date.now mock to simulate
Date.now() returning Date.now() - 2000, then proceed to stop/inspect the
recording duration via the public methods on manager to assert a ~2s duration;
keep references to manager.startRecording and the recording duration assertions
(no private-field access).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/extensions/core/load3d/RecordingManager.test.ts`:
- Around line 90-97: The test currently calls vi.spyOn on
HTMLCanvasElement.prototype.captureStream which doesn't exist in happy-dom
20.0.11; define the shim first (e.g., assign or Object.defineProperty
HTMLCanvasElement.prototype.captureStream = makeStream) before calling vi.spyOn
so the property exists, then spy or mock it as needed—refer to
HTMLCanvasElement.prototype, captureStream, makeStream, and vi.spyOn in the
RecordingManager.test setup to locate where to add the definition.

---

Nitpick comments:
In `@src/extensions/core/load3d/AnimationManager.test.ts`:
- Around line 33-37: Add a shared teardown that restores spies after each test
to prevent mocked globals leaking: in the test file add an afterEach that calls
vi.clearAllMocks() and vi.restoreAllMocks() (or explicitly calls restore on any
spy variables like the console.warn spy used in the suite) so that mocks set up
during beforeEach/individual tests (e.g., via vi.spyOn(console, "warn") or
similar) are fully restored and isolated between tests involving
AnimationManager and makeMockEventManager.

In `@src/extensions/core/load3d/CameraManager.test.ts`:
- Around line 34-37: Remove the inline explanatory comment inside the
makeRenderer() helper in CameraManager.test.ts so the function only returns the
cast empty object; keep the function name and implementation (return {} as
THREE.WebGLRenderer) unchanged and do not add any new comments—just delete the
redundant comment text.
- Around line 18-32: makeControlsStub() is untyped and tests keep using "as
unknown as OrbitControls" casts; change makeControlsStub to return a properly
typed stub (e.g., declare its signature as returning OrbitControls or a narrow
stub interface matching the used subset: target, object, update,
addEventListener, fire) so callers no longer need casts. Update the function
signature in CameraManager.test.ts to use that type, implement the
ControlsListener and required method shapes inside the returned object to
satisfy the type, and remove all "as unknown as OrbitControls" cast sites in the
tests.

In `@src/extensions/core/load3d/RecordingManager.test.ts`:
- Around line 29-50: The MockMediaRecorder.instances static array is shared
mutable state; remove the static instances property from class MockMediaRecorder
and instead capture each constructed recorder in a suite-local variable (e.g.,
declare a hoisted let mockRecorder via vi.hoisted or a top-level let assigned in
beforeEach) inside the test file; in MockMediaRecorder's constructor assign the
newly created instance to that suite-local holder so tests call methods like
mockRecorder.pushChunk(...) and assertions use that local variable rather than
MockMediaRecorder.instances, ensuring no global mutable state is relied on.
- Around line 205-210: The test "reports a non-zero duration after recording"
currently mutates the private recordingStartTime field; instead, control time
via the public API by using fake timers or mocking Date.now so the test remains
behavior-focused. Replace the direct assignment to recordingStartTime with a
setup that calls manager.startRecording() while using
jest.useFakeTimers()/jest.setSystemTime(...) or a Date.now mock to simulate
Date.now() returning Date.now() - 2000, then proceed to stop/inspect the
recording duration via the public methods on manager to assert a ~2s duration;
keep references to manager.startRecording and the recording duration assertions
(no private-field access).

In `@src/services/load3dService.test.ts`:
- Around line 26-35: The shared mutable Set createdNodes and factory makeNode
are declared at module scope; move createdNodes into test-scoped hoisting using
vi.hoisted() (or initialize it inside a beforeEach) and update makeNode to
reference that hoisted/locally-scoped Set so each test gets an isolated tracker;
ensure beforeEach drains the hoisted createdNodes and keep references to the
unique symbols createdNodes and makeNode consistent so load3dService's
viewerInstances cleanup still runs per test.
- Around line 37-49: Replace the unsafe double-casts with minimally typed
fixtures using TypeScript’s "satisfies" or a small generic test-helper so the
compiler enforces the service contract: change makeLoad3d() to return an object
that satisfies the Load3d interface (instead of "as unknown as Load3d"), update
the factory cast (the "factory as unknown as typeof useLoad3dViewerMock" usage)
to produce a properly typed mock via a helper/generic returning satisfies typeof
useLoad3dViewerMock, and replace the "Object.assign(... ) as LGraphNode"
assertion with an Object.assign result typed to satisfy LGraphNode; apply the
same pattern for the other occurrences noted (around the factory and lines
194-207, 255-257) so tests will fail if the service contract drifts.
🪄 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: 2545f52b-9dcc-4648-a231-69272958a6c7

📥 Commits

Reviewing files that changed from the base of the PR and between bb74ec9 and 38fb092.

📒 Files selected for processing (4)
  • src/extensions/core/load3d/AnimationManager.test.ts
  • src/extensions/core/load3d/CameraManager.test.ts
  • src/extensions/core/load3d/RecordingManager.test.ts
  • src/services/load3dService.test.ts

Comment on lines +90 to +97
// happy-dom canvases lack captureStream; stub it on the prototype so
// every canvas the production code creates gets a usable stream.
vi.spyOn(
HTMLCanvasElement.prototype as unknown as {
captureStream: (fps?: number) => MediaStream
},
'captureStream'
).mockImplementation(makeStream)
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "captureStream" src

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 503


🏁 Script executed:

cat package.json | grep -A 5 -B 5 "happy-dom"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 397


🏁 Script executed:

rg "happy-dom" --type json

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 112


🏁 Script executed:

# Check if there's any test setup or vitest config that handles this
fd "vitest.config|setupFiles|test.setup" src

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

# Check the test file more closely to understand the full context
cat -n src/extensions/core/load3d/RecordingManager.test.ts | head -100

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3746


🏁 Script executed:

# Find the actual happy-dom version from lock files
fd "pnpm-lock|package-lock|yarn.lock" | head -1 | xargs cat | rg "happy-dom@.*version" -A 2 | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

# Check pnpm workspace catalog if present
find . -name "pnpm-workspace.yaml" -o -name ".pnpmfile.cjs" | xargs cat 2>/dev/null | rg "happy-dom" -A 2

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 122


🏁 Script executed:

# Look for .npmrc or pnpm config that defines catalog
cat .npmrc .pnpmfile.cjs pnpm-workspace.yaml 2>/dev/null | rg -A 10 "catalog\|happy-dom"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

# Check if there's a package.json root with catalog definitions
fd "package.json" . -type f | xargs rg "\"happy-dom\"" -B 5 -A 5 | grep -A 10 "catalog"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 729


🌐 Web query:

happy-dom 20.0.11 HTMLCanvasElement captureStream

💡 Result:

In happy-dom version 20.0.11, HTMLCanvasElement.captureStream is not supported. Happy DOM provides only basic structural support for HTMLCanvasElement (added via issue #1332, closed August 2024), but lacks full Canvas API implementation, particularly getContext('2d') and advanced methods like captureStream. The related CanvasCaptureMediaStreamTrack.ts file exists in later versions (e.g., modified between v20.0.10 and v20.7.0), but no evidence confirms captureStream availability or tests in 20.0.11 changelogs. Issue #241 for Canvas support remains open with milestone for getContext, and users commonly mock Canvas contexts due to missing rendering. For real Canvas functionality, use pluggable adapters available in later versions (PR #2069, Feb 2026) or external libraries like node-canvas.

Citations:


🌐 Web query:

Vitest vi.spyOn non-existent property undefined

💡 Result:

The error "Vitest vi.spyOn non-existent property undefined" occurs when vi.spyOn(object, 'property') is called but object.property is undefined (or not a function). Vitest's vi.spyOn requires the spied property to exist on the object and be a function (or getter/setter if specified). Common causes and solutions: 1. Object or service is undefined: Ensure the object/service is properly instantiated before spying. // Wrong - service might be undefined due to missing mock providers const spy = vi.spyOn(service, 'method'); // Fix - provide mocks for dependencies const module = await Test.createTestingModule({ providers: [ DeviceSecretService, { provide: SecretManagerServiceClient, useValue: { /* mocks */ } } ] }).compile; const service = module.get(DeviceSecretService); const spy = vi.spyOn(service, 'method'); 2. Property doesn't exist on object: Verify the property name and that it's a method. // Wrong vi.spyOn(obj, 'nonExistentMethod'); // obj.nonExistentMethod === undefined // Check first console.log(obj); // Verify structure 3. ESM module exports: Import as namespace and spy after import. import * as mod from './module'; const spy = vi.spyOn(mod, 'exportedMethod'); 4. Browser mode limitations: vi.spyOn on ESM exports throws "Cannot redefine property". Use vi.mock('./module', { spy: true }) instead. 5. Spy timing: Spies only track calls made after vi.spyOn. Move spy before the code that calls the method. From Vitest docs: vi.spyOn(object, key) spies on existing methods. If the property is undefined, it fails with this error (similar to Jest's "Cannot spyOn on a primitive value; undefined given").

Citations:


🏁 Script executed:

# Check if this test has any skip markers or special handling
cat -n src/extensions/core/load3d/RecordingManager.test.ts | grep -E "skip|only|xdescribe|xit" | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

# Check if there are any error handlers around the spyOn call
cat -n src/extensions/core/load3d/RecordingManager.test.ts | sed -n '80,110p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1511


Define captureStream on the prototype before spying, since happy-dom 20.0.11 does not provide it.

vi.spyOn requires the property to already exist on the object; it cannot spy on undefined properties. Since happy-dom 20.0.11 lacks HTMLCanvasElement.prototype.captureStream, this setup will throw an error during beforeEach. Install the shim directly using Object.defineProperty or assignment before attempting to spy:

HTMLCanvasElement.prototype.captureStream = makeStream

or use vi.stubGlobal with a custom HTMLCanvasElement class that includes the method.

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

In `@src/extensions/core/load3d/RecordingManager.test.ts` around lines 90 - 97,
The test currently calls vi.spyOn on HTMLCanvasElement.prototype.captureStream
which doesn't exist in happy-dom 20.0.11; define the shim first (e.g., assign or
Object.defineProperty HTMLCanvasElement.prototype.captureStream = makeStream)
before calling vi.spyOn so the property exists, then spy or mock it as
needed—refer to HTMLCanvasElement.prototype, captureStream, makeStream, and
vi.spyOn in the RecordingManager.test setup to locate where to add the
definition.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

@@             Coverage Diff             @@
##             main   #11733       +/-   ##
===========================================
- Coverage   69.86%   52.94%   -16.93%     
===========================================
  Files        1485     1376      -109     
  Lines       84037    70445    -13592     
  Branches    22883    19629     -3254     
===========================================
- Hits        58715    37294    -21421     
- Misses      24420    32603     +8183     
+ Partials      902      548      -354     
Flag Coverage Δ
e2e ?
unit 52.94% <ø> (+0.56%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 995 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Collaborator

@dante01yoon dante01yoon left a comment

Choose a reason for hiding this comment

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

Reviewed the added Load3D unit suites against head 38fb092e5b82381d42763acc4496fd01bdb460c8.

No blocking findings from me. The tests exercise the production managers/service rather than only mocks, and I verified the canvas capture shim path locally in the same dependency set. CodeRabbit's remaining notes look like test-maintainability cleanup rather than merge blockers for this coverage PR.

Validation:

  • pnpm test:unit -- src/extensions/core/load3d/AnimationManager.test.ts src/extensions/core/load3d/CameraManager.test.ts src/extensions/core/load3d/RecordingManager.test.ts src/services/load3dService.test.ts
  • GitHub checks are green at this head.

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

Labels

size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants