Skip to content

feat(camera-info): 3D viewport widget for CreateCameraInfo node - #13741

Open
jtydhr88 wants to merge 1 commit into
mainfrom
feat/create-camera-info-preview
Open

feat(camera-info): 3D viewport widget for CreateCameraInfo node#13741
jtydhr88 wants to merge 1 commit into
mainfrom
feat/create-camera-info-preview

Conversation

@jtydhr88

Copy link
Copy Markdown
Collaborator

Summary

Vue viewport widget for the CreateCameraInfo node, bound to the CAMERA_INFO_STATE input. It renders the configured camera in a Load3D-style 3D scene so the camera can be posed directly instead of by typing numbers into the yaw/pitch/position/quaternion widgets; every drag writes back to those widgets, which stay the source of truth.

  • Editing gizmos per mode: orbit (yaw/pitch/distance rings), look_at / quaternion (position + rotation via TransformControls), plus target and roll handles.
  • Corner preview of what the subject camera sees, drawn as an extra pass after the main scene.
  • "Camera view" (look-through) renders the main viewport through the subject camera; while looking through, left-drag rotates the facing (quaternion mouse-look, look_at target-rotate, orbit revolve) and the wheel dollies (perspective) / zooms (ortho). Pointer + wheel input is coalesced to one update per animation frame.

Shared viewer changes (load3d):

  • Viewport3d becomes the reusable base viewer; createViewport3d wires only its core managers, so the camera editor can reuse it without Load3d's model-loader / HDRI / recording.
  • addPostRenderCallback: a per-frame hook to draw extra passes after the main scene (the corner preview) — overlay objects render inline with the scene and can't express a second pass.
  • Custom camera up: a camera_info built from a quaternion carries an arbitrary orientation, so its up vector is world-Y rotated by that quaternion, not world-Y. Load3D's OrbitControls forced world-Y up, so feeding in such a camera snapped the view upright and stopped matching the source. CameraManager now derives the up from the incoming quaternion and applies it, with a CameraControls toggle (setUseCustomUp) to switch back to natural Y-up for comfortable orbiting.

BE Comfy-Org/ComfyUI#14964

Screenshots (if applicable)

2026-07-15.23-31-59.mp4

@jtydhr88
jtydhr88 requested a review from a team July 17, 2026 03:43
@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

🎭 Playwright: ✅ 1716 passed, 0 failed · 1 flaky

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

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 07/19/2026, 06:10:50 PM UTC

Links

📦 Bundle: 7.94 MB gzip 🔴 +24.7 kB

Details

Summary

  • Raw size: 33.6 MB baseline 33.4 MB — 🔴 +123 kB
  • Gzip: 7.94 MB baseline 7.91 MB — 🔴 +24.7 kB
  • Brotli: 5.48 MB baseline 5.45 MB — 🔴 +23.9 kB
  • Bundles: 307 current • 302 baseline • 192 added / 187 removed

Category Glance
Other 🔴 +118 kB (12 MB) · Vendor & Third-Party 🔴 +48.9 kB (15.8 MB) · Data & Services 🟢 -45 kB (225 kB) · Utilities & Hooks 🔴 +735 B (3.41 MB) · Graph Workspace 🔴 +30 B (1.25 MB) · Panels & Settings ⚪ 0 B (551 kB) · + 5 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-Bas7veSS.js (removed) 47.3 kB 🟢 -47.3 kB 🟢 -14.1 kB 🟢 -12.2 kB
assets/index-CPYsaoLv.js (new) 47.3 kB 🔴 +47.3 kB 🔴 +14.1 kB 🔴 +12.1 kB

Status: 1 added / 1 removed

Graph Workspace — 1.25 MB (baseline 1.25 MB) • 🔴 +30 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-JpvQ0RRb.js (new) 1.25 MB 🔴 +1.25 MB 🔴 +266 kB 🔴 +200 kB
assets/GraphView-sl_CgE50.js (removed) 1.25 MB 🟢 -1.25 MB 🟢 -266 kB 🟢 -200 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-DhJ73qbG.js (new) 25 kB 🔴 +25 kB 🔴 +6.25 kB 🔴 +5.53 kB
assets/CloudSurveyView-j7Gj7o6e.js (removed) 25 kB 🟢 -25 kB 🟢 -6.25 kB 🟢 -5.53 kB
assets/OAuthConsentView-BNKbgyJY.js (new) 15 kB 🔴 +15 kB 🔴 +4.1 kB 🔴 +3.57 kB
assets/OAuthConsentView-DCd5K7Ky.js (removed) 15 kB 🟢 -15 kB 🟢 -4.1 kB 🟢 -3.58 kB
assets/CloudLoginView-_jT3YNQb.js (new) 11.4 kB 🔴 +11.4 kB 🔴 +3.07 kB 🔴 +2.69 kB
assets/CloudLoginView-BgV4iB-7.js (removed) 11.4 kB 🟢 -11.4 kB 🟢 -3.07 kB 🟢 -2.69 kB
assets/CloudSignupView-KM0Qs7wB.js (new) 9.79 kB 🔴 +9.79 kB 🔴 +2.75 kB 🔴 +2.4 kB
assets/CloudSignupView-sM_GOD0s.js (removed) 9.79 kB 🟢 -9.79 kB 🟢 -2.74 kB 🟢 -2.4 kB
assets/CloudLayoutView-BwVQ3fyq.js (removed) 9.54 kB 🟢 -9.54 kB 🟢 -2.35 kB 🟢 -2.03 kB
assets/CloudLayoutView-DS6bYJDJ.js (new) 9.54 kB 🔴 +9.54 kB 🔴 +2.35 kB 🔴 +2.03 kB
assets/UserCheckView-B4Di3gRc.js (new) 8.8 kB 🔴 +8.8 kB 🔴 +2.22 kB 🔴 +1.93 kB
assets/UserCheckView-QxlxUSpT.js (removed) 8.8 kB 🟢 -8.8 kB 🟢 -2.22 kB 🟢 -1.93 kB
assets/WidgetTextPreview-BZjxk--w.js (new) 6.13 kB 🔴 +6.13 kB 🔴 +2.17 kB 🔴 +1.92 kB
assets/WidgetTextPreview-DStJ1dq4.js (removed) 6.13 kB 🟢 -6.13 kB 🟢 -2.17 kB 🟢 -1.93 kB
assets/CloudSubscriptionRedirectView-BRp8UbKg.js (removed) 6.06 kB 🟢 -6.06 kB 🟢 -2.27 kB 🟢 -1.97 kB
assets/CloudSubscriptionRedirectView-DOoMFIeM.js (new) 6.06 kB 🔴 +6.06 kB 🔴 +2.27 kB 🔴 +1.97 kB
assets/UserSelectView-BdYoTs23.js (new) 5.54 kB 🔴 +5.54 kB 🔴 +1.99 kB 🔴 +1.73 kB
assets/UserSelectView-DDarHLXn.js (removed) 5.54 kB 🟢 -5.54 kB 🟢 -1.99 kB 🟢 -1.74 kB
assets/CloudForgotPasswordView-CMM9yQIn.js (new) 5.15 kB 🔴 +5.15 kB 🔴 +1.76 kB 🔴 +1.53 kB
assets/CloudForgotPasswordView-DR7dXGB2.js (removed) 5.15 kB 🟢 -5.15 kB 🟢 -1.76 kB 🟢 -1.54 kB
assets/CloudAuthTimeoutView-BdP3Y0A1.js (removed) 4.49 kB 🟢 -4.49 kB 🟢 -1.57 kB 🟢 -1.36 kB
assets/CloudAuthTimeoutView-CwQ6rLR7.js (new) 4.49 kB 🔴 +4.49 kB 🔴 +1.57 kB 🔴 +1.37 kB
assets/layout-CouBRdiB.js (new) 447 B 🔴 +447 B 🔴 +280 B 🔴 +231 B
assets/layout-Ds2LoCjm.js (removed) 447 B 🟢 -447 B 🟢 -283 B 🟢 -231 B
assets/WidgetTextPreview-BmVDH3mg.js (new) 131 B 🔴 +131 B 🔴 +100 B 🔴 +87 B
assets/WidgetTextPreview-W4waRBZV.js (removed) 131 B 🟢 -131 B 🟢 -100 B 🟢 -89 B

Status: 13 added / 13 removed / 1 unchanged

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-BJstoGW-.js (removed) 49.5 kB 🟢 -49.5 kB 🟢 -9.98 kB 🟢 -8.85 kB
assets/KeybindingPanel-CZ689ivr.js (new) 49.5 kB 🔴 +49.5 kB 🔴 +9.98 kB 🔴 +8.84 kB
assets/SecretsPanel-DeUEUXwl.js (removed) 31 kB 🟢 -31 kB 🟢 -7.51 kB 🟢 -6.57 kB
assets/SecretsPanel-HVRylfB5.js (new) 31 kB 🔴 +31 kB 🔴 +7.51 kB 🔴 +6.57 kB
assets/CreditsPanel-D6k3OLCq.js (removed) 16 kB 🟢 -16 kB 🟢 -4.67 kB 🟢 -4.1 kB
assets/CreditsPanel-DVY2Q6An.js (new) 16 kB 🔴 +16 kB 🔴 +4.67 kB 🔴 +4.1 kB
assets/SubscriptionPanel-BHkfsFIf.js (new) 11.5 kB 🔴 +11.5 kB 🔴 +3.64 kB 🔴 +3.2 kB
assets/SubscriptionPanel-DxuFbtu3.js (removed) 11.5 kB 🟢 -11.5 kB 🟢 -3.65 kB 🟢 -3.21 kB
assets/AboutPanel-Bo20wumr.js (new) 11.2 kB 🔴 +11.2 kB 🔴 +3.06 kB 🔴 +2.74 kB
assets/AboutPanel-CzLxJe4y.js (removed) 11.2 kB 🟢 -11.2 kB 🟢 -3.06 kB 🟢 -2.74 kB
assets/ExtensionPanel-Bbf6d7io.js (removed) 9.04 kB 🟢 -9.04 kB 🟢 -2.5 kB 🟢 -2.2 kB
assets/ExtensionPanel-P6P3vXhg.js (new) 9.04 kB 🔴 +9.04 kB 🔴 +2.5 kB 🔴 +2.2 kB
assets/ServerConfigPanel-fEVWgtoK.js (removed) 6.15 kB 🟢 -6.15 kB 🟢 -1.98 kB 🟢 -1.76 kB
assets/ServerConfigPanel-X1FyE3yk.js (new) 6.15 kB 🔴 +6.15 kB 🔴 +1.98 kB 🔴 +1.76 kB
assets/UserPanel-Ba236kC6.js (removed) 5.78 kB 🟢 -5.78 kB 🟢 -1.82 kB 🟢 -1.58 kB
assets/UserPanel-CJM71vsG.js (new) 5.78 kB 🔴 +5.78 kB 🔴 +1.82 kB 🔴 +1.58 kB
assets/refreshRemoteConfig-B07Gdkvp.js (removed) 2.97 kB 🟢 -2.97 kB 🟢 -1.27 kB 🟢 -1.11 kB
assets/refreshRemoteConfig-D4-RJvLW.js (new) 2.97 kB 🔴 +2.97 kB 🔴 +1.27 kB 🔴 +1.12 kB
assets/cloudRemoteConfig-BBbI1t8V.js (new) 990 B 🔴 +990 B 🔴 +541 B 🔴 +460 B
assets/cloudRemoteConfig-nSDfdVVw.js (removed) 990 B 🟢 -990 B 🟢 -541 B 🟢 -463 B
assets/refreshRemoteConfig-CieT5Pji.js (new) 110 B 🔴 +110 B 🔴 +89 B 🔴 +87 B
assets/refreshRemoteConfig-CZRNJDOa.js (removed) 110 B 🟢 -110 B 🟢 -89 B 🟢 -87 B

Status: 11 added / 11 removed / 15 unchanged

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SignUpForm-BNtcqq5G.js (new) 12.3 kB 🔴 +12.3 kB 🔴 +4.19 kB 🔴 +3.65 kB
assets/SignUpForm-CMk59SuD.js (removed) 12.3 kB 🟢 -12.3 kB 🟢 -4.19 kB 🟢 -3.66 kB
assets/auth-CFT4JeXm.js (removed) 3.69 kB 🟢 -3.69 kB 🟢 -1.3 kB 🟢 -1.13 kB
assets/auth-CLiYeoBu.js (new) 3.69 kB 🔴 +3.69 kB 🔴 +1.3 kB 🔴 +1.14 kB
assets/usePostAuthRedirect-CgyXip8D.js (new) 3.33 kB 🔴 +3.33 kB 🔴 +1.27 kB 🔴 +1.1 kB
assets/usePostAuthRedirect-CsOKokSl.js (removed) 3.33 kB 🟢 -3.33 kB 🟢 -1.27 kB 🟢 -1.1 kB
assets/UpdatePasswordContent-BidxmNp0.js (removed) 1.92 kB 🟢 -1.92 kB 🟢 -876 B 🟢 -764 B
assets/UpdatePasswordContent-h5DRymm3.js (new) 1.92 kB 🔴 +1.92 kB 🔴 +873 B 🔴 +763 B
assets/authStore-D68o3Hjj.js (new) 130 B 🔴 +130 B 🔴 +112 B 🔴 +110 B
assets/authStore-DODySESp.js (removed) 130 B 🟢 -130 B 🟢 -112 B 🟢 -113 B
assets/workspaceAuthStore-BOFtySxl.js (new) 110 B 🔴 +110 B 🔴 +104 B 🔴 +109 B
assets/workspaceAuthStore-BsR_lDLQ.js (removed) 110 B 🟢 -110 B 🟢 -104 B 🟢 -108 B
assets/auth-BdN89Dbm.js (new) 105 B 🔴 +105 B 🔴 +96 B 🔴 +86 B
assets/auth-D-_wa1PT.js (removed) 105 B 🟢 -105 B 🟢 -96 B 🟢 -88 B

Status: 7 added / 7 removed / 3 unchanged

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyHubPublishDialog-C5XYcBlM.js (new) 90.5 kB 🔴 +90.5 kB 🔴 +19.3 kB 🔴 +16.5 kB
assets/ComfyHubPublishDialog-CHyggu0e.js (removed) 90.5 kB 🟢 -90.5 kB 🟢 -19.3 kB 🟢 -16.5 kB
assets/useShareDialog-Bdbfo5R9.js (new) 23.7 kB 🔴 +23.7 kB 🔴 +5.58 kB 🔴 +4.97 kB
assets/useShareDialog-C8Zxnj3y.js (removed) 23.7 kB 🟢 -23.7 kB 🟢 -5.58 kB 🟢 -4.96 kB
assets/feedbackDialog-lqw-WoPn.js (removed) 4.51 kB 🟢 -4.51 kB 🟢 -1.9 kB 🟢 -1.63 kB
assets/feedbackDialog-tnMZntNq.js (new) 4.51 kB 🔴 +4.51 kB 🔴 +1.9 kB 🔴 +1.63 kB
assets/SubscriptionInactiveMemberDialog-BDHsFwsS.js (new) 2.55 kB 🔴 +2.55 kB 🔴 +1.03 kB 🔴 +887 B
assets/SubscriptionInactiveMemberDialog-BHKO1F3n.js (removed) 2.55 kB 🟢 -2.55 kB 🟢 -1.03 kB 🟢 -906 B
assets/ComfyHubPublishDialog-B-EE6OWC.js (removed) 143 B 🟢 -143 B 🟢 -105 B 🟢 -90 B
assets/ComfyHubPublishDialog-Dwv0FEPN.js (new) 143 B 🔴 +143 B 🔴 +105 B 🔴 +89 B
assets/useSubscriptionDialog-CjN2jRrZ.js (removed) 110 B 🟢 -110 B 🟢 -102 B 🟢 -88 B
assets/useSubscriptionDialog-Du0-plZB.js (new) 110 B 🔴 +110 B 🔴 +102 B 🔴 +91 B

Status: 6 added / 6 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-BvWq69HK.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.8 kB 🟢 -3.39 kB
assets/ComfyQueueButton-BZtlk1OO.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.8 kB 🔴 +3.39 kB
assets/useTerminalTabs-c6d7JiSm.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.72 kB 🔴 +3.29 kB
assets/useTerminalTabs-CAId8jM3.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.72 kB 🟢 -3.3 kB
assets/InviteMembersForm-BoyMMwaC.js (new) 7.86 kB 🔴 +7.86 kB 🔴 +2.65 kB 🔴 +2.36 kB
assets/InviteMembersForm-kB8flK6Q.js (removed) 7.86 kB 🟢 -7.86 kB 🟢 -2.65 kB 🟢 -2.34 kB
assets/TopbarBadge-BB3Wr6dO.js (new) 7.7 kB 🔴 +7.7 kB 🔴 +1.87 kB 🔴 +1.65 kB
assets/TopbarBadge-jrk-9ioE.js (removed) 7.7 kB 🟢 -7.7 kB 🟢 -1.87 kB 🟢 -1.65 kB
assets/ScrubableNumberInput-BesqD3Qz.js (new) 6.55 kB 🔴 +6.55 kB 🔴 +2.2 kB 🔴 +1.94 kB
assets/ScrubableNumberInput-DUTJHW92.js (removed) 6.55 kB 🟢 -6.55 kB 🟢 -2.2 kB 🟢 -1.94 kB
assets/toggle-group-BGwF-AF0.js (removed) 4.16 kB 🟢 -4.16 kB 🟢 -1.44 kB 🟢 -1.28 kB
assets/toggle-group-CEa9xvEq.js (new) 4.16 kB 🔴 +4.16 kB 🔴 +1.44 kB 🔴 +1.28 kB
assets/Button-BbWVu9-E.js (removed) 3.69 kB 🟢 -3.69 kB 🟢 -1.4 kB 🟢 -1.22 kB
assets/Button-C33ACncE.js (new) 3.69 kB 🔴 +3.69 kB 🔴 +1.4 kB 🔴 +1.23 kB
assets/SubscribeButton-BKgmKryF.js (new) 2.18 kB 🔴 +2.18 kB 🔴 +990 B 🔴 +874 B
assets/SubscribeButton-Dw9cN9il.js (removed) 2.18 kB 🟢 -2.18 kB 🟢 -992 B 🟢 -873 B
assets/WidgetButton-B7XqM6o1.js (new) 1.98 kB 🔴 +1.98 kB 🔴 +920 B 🔴 +816 B
assets/WidgetButton-Dwah5Y85.js (removed) 1.98 kB 🟢 -1.98 kB 🟢 -921 B 🟢 -823 B
assets/Input-nBGnrFB4.js (removed) 1.81 kB 🟢 -1.81 kB 🟢 -904 B 🟢 -789 B
assets/Input-UMq3wiN-.js (new) 1.81 kB 🔴 +1.81 kB 🔴 +902 B 🔴 +791 B
assets/CloudBadge-C1A24dqN.js (new) 1.18 kB 🔴 +1.18 kB 🔴 +594 B 🔴 +520 B
assets/CloudBadge-CT3q2r2_.js (removed) 1.18 kB 🟢 -1.18 kB 🟢 -590 B 🟢 -515 B
assets/cloudFeedbackTopbarButton-BgqPu7l_.js (new) 771 B 🔴 +771 B 🔴 +459 B 🔴 +394 B
assets/cloudFeedbackTopbarButton-kX92UUmU.js (removed) 771 B 🟢 -771 B 🟢 -459 B 🟢 -422 B
assets/ComfyQueueButton-DEiBdRHP.js (new) 128 B 🔴 +128 B 🔴 +99 B 🔴 +89 B
assets/ComfyQueueButton-DqFEbqTb.js (removed) 128 B 🟢 -128 B 🟢 -99 B 🟢 -89 B

Status: 13 added / 13 removed / 1 unchanged

Data & Services — 225 kB (baseline 270 kB) • 🟢 -45 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/load3dService-CAjaio1A.js (removed) 132 kB 🟢 -132 kB 🟢 -29.2 kB 🟢 -24.6 kB
assets/api-C7e47OiI.js (new) 89.1 kB 🔴 +89.1 kB 🔴 +24.5 kB 🔴 +21.1 kB
assets/api-Cx-vsQ9B.js (removed) 89.1 kB 🟢 -89.1 kB 🟢 -24.6 kB 🟢 -21.1 kB
assets/load3dService-B6d51Oho.js (new) 86.7 kB 🔴 +86.7 kB 🔴 +20.2 kB 🔴 +17.5 kB
assets/workflowShareService-2U1eJ6_P.js (new) 17 kB 🔴 +17 kB 🔴 +5 kB 🔴 +4.44 kB
assets/workflowShareService-7s6L2IbS.js (removed) 17 kB 🟢 -17 kB 🟢 -5 kB 🟢 -4.44 kB
assets/keybindingService-CGh4GDq6.js (new) 7.28 kB 🔴 +7.28 kB 🔴 +1.91 kB 🔴 +1.63 kB
assets/keybindingService-D7hX1M-L.js (removed) 7.28 kB 🟢 -7.28 kB 🟢 -1.91 kB 🟢 -1.63 kB
assets/releaseStore-DhUDuCPb.js (new) 6.77 kB 🔴 +6.77 kB 🔴 +2.07 kB 🔴 +1.8 kB
assets/releaseStore-gilFiJHt.js (removed) 6.77 kB 🟢 -6.77 kB 🟢 -2.07 kB 🟢 -1.8 kB
assets/dialogStore-CvR03w-_.js (removed) 5.36 kB 🟢 -5.36 kB 🟢 -1.66 kB 🟢 -1.43 kB
assets/dialogStore-ZsrI9TCj.js (new) 5.36 kB 🔴 +5.36 kB 🔴 +1.66 kB 🔴 +1.43 kB
assets/systemStatsStore-BJta2NWT.js (removed) 4.99 kB 🟢 -4.99 kB 🟢 -1.77 kB 🟢 -1.49 kB
assets/systemStatsStore-D6ydNSAe.js (new) 4.99 kB 🔴 +4.99 kB 🔴 +1.77 kB 🔴 +1.5 kB
assets/userStore-aPb17t8B.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -928 B 🟢 -822 B
assets/userStore-Ck_gqzPm.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +930 B 🔴 +819 B
assets/audioService-BNaPiu0k.js (new) 1.76 kB 🔴 +1.76 kB 🔴 +862 B 🔴 +751 B
assets/audioService-Dz8_bLi0.js (removed) 1.76 kB 🟢 -1.76 kB 🟢 -862 B 🟢 -748 B
assets/dialogService-CfvYb1E4.js (removed) 100 B 🟢 -100 B 🟢 -99 B 🟢 -101 B
assets/dialogService-kqkwBv8P.js (new) 100 B 🔴 +100 B 🔴 +99 B 🔴 +94 B
assets/settingStore-CG7FLPO6.js (new) 98 B 🔴 +98 B 🔴 +98 B 🔴 +92 B
assets/settingStore-DWmiFJRn.js (removed) 98 B 🟢 -98 B 🟢 -98 B 🟢 -89 B
assets/assetsStore-DUlcJha7.js (removed) 96 B 🟢 -96 B 🟢 -97 B 🟢 -100 B
assets/assetsStore-icfFw1G4.js (new) 96 B 🔴 +96 B 🔴 +97 B 🔴 +90 B
assets/releaseStore-BxCn1zeB.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -88 B
assets/releaseStore-BZQuzH1h.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +86 B
assets/api-DoYuQ3Em.js (new) 62 B 🔴 +62 B 🔴 +74 B 🔴 +66 B
assets/api-zxODQJKO.js (removed) 62 B 🟢 -62 B 🟢 -74 B 🟢 -66 B

Status: 14 added / 14 removed / 2 unchanged

Utilities & Hooks — 3.41 MB (baseline 3.41 MB) • 🔴 +735 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/promotionUtils-KemQtDwi.js (new) 3.05 MB 🔴 +3.05 MB 🔴 +706 kB 🔴 +533 kB
assets/promotionUtils-sNNzFTAm.js (removed) 3.05 MB 🟢 -3.05 MB 🟢 -706 kB 🟢 -532 kB
assets/useConflictDetection-Bbneu22J.js (removed) 235 kB 🟢 -235 kB 🟢 -52.3 kB 🟢 -42.6 kB
assets/useConflictDetection-C_sY4lpm.js (new) 235 kB 🔴 +235 kB 🔴 +52.3 kB 🔴 +42.6 kB
assets/useLoad3d-DPJHcnb-.js (new) 26 kB 🔴 +26 kB 🔴 +5.9 kB 🔴 +5.22 kB
assets/useLoad3d-CRvPvi71.js (removed) 25.8 kB 🟢 -25.8 kB 🟢 -5.83 kB 🟢 -5.16 kB
assets/useLoad3dViewer-BltP3S9C.js (removed) 21.3 kB 🟢 -21.3 kB 🟢 -5.01 kB 🟢 -4.39 kB
assets/useLoad3dViewer-vU9yfyeg.js (new) 21.3 kB 🔴 +21.3 kB 🔴 +5.01 kB 🔴 +4.4 kB
assets/useFeatureFlags-BicjeCUp.js (removed) 7.08 kB 🟢 -7.08 kB 🟢 -2.13 kB 🟢 -1.79 kB
assets/useFeatureFlags-W4Is1oc3.js (new) 7.08 kB 🔴 +7.08 kB 🔴 +2.13 kB 🔴 +1.8 kB
assets/useDowngradeToPersonal-CDdr3fKx.js (removed) 3 kB 🟢 -3 kB 🟢 -1.18 kB 🟢 -1.02 kB
assets/useDowngradeToPersonal-RozOk_-V.js (new) 3 kB 🔴 +3 kB 🔴 +1.18 kB 🔴 +1.04 kB
assets/useExternalLink-DVPdesS8.js (new) 2.87 kB 🔴 +2.87 kB 🔴 +1.09 kB 🔴 +945 B
assets/useExternalLink-dXt_ScVz.js (removed) 2.87 kB 🟢 -2.87 kB 🟢 -1.09 kB 🟢 -945 B
assets/downloadUtil-BKqTRZ7U.js (new) 2.45 kB 🔴 +2.45 kB 🔴 +1.12 kB 🔴 +901 B
assets/downloadUtil-BuBUZawv.js (removed) 2.45 kB 🟢 -2.45 kB 🟢 -1.12 kB 🟢 -895 B
assets/assetPreviewUtil-DA_qBOfu.js (removed) 2.41 kB 🟢 -2.41 kB 🟢 -1 kB 🟢 -875 B
assets/assetPreviewUtil-Nbw7gIh7.js (new) 2.41 kB 🔴 +2.41 kB 🔴 +1 kB 🔴 +873 B
assets/useUpstreamValue-BJtfWsLs.js (new) 2.04 kB 🔴 +2.04 kB 🔴 +791 B 🔴 +702 B
assets/useUpstreamValue-DcbAV_Ra.js (removed) 2.04 kB 🟢 -2.04 kB 🟢 -792 B 🟢 -702 B
assets/useWorkspaceTierLabel-C7BMYIb5.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +852 B 🔴 +733 B
assets/useWorkspaceTierLabel-CMkAUXcn.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -852 B 🟢 -733 B
assets/useErrorHandling-C-ETM_WX.js (removed) 1.54 kB 🟢 -1.54 kB 🟢 -648 B 🟢 -552 B
assets/useErrorHandling-CQWsMwl5.js (new) 1.54 kB 🔴 +1.54 kB 🔴 +648 B 🔴 +551 B
assets/useSessionCookie-CexIE98c.js (new) 995 B 🔴 +995 B 🔴 +517 B 🔴 +454 B
assets/useSessionCookie-D8dzf2mr.js (removed) 995 B 🟢 -995 B 🟢 -516 B 🟢 -450 B
assets/subscriptionCheckoutUtil-DmBES4U6.js (new) 905 B 🔴 +905 B 🔴 +536 B 🔴 +457 B
assets/subscriptionCheckoutUtil-huyLHFYV.js (removed) 905 B 🟢 -905 B 🟢 -537 B 🟢 -454 B
assets/useLoad3d--v-MPWw4.js (removed) 311 B 🟢 -311 B 🟢 -164 B 🟢 -148 B
assets/useLoad3d-DrYTi311.js (new) 311 B 🔴 +311 B 🔴 +164 B 🔴 +149 B
assets/SkeletonUtils-BMP6pVvS.js (removed) 133 B 🟢 -133 B 🟢 -111 B 🟢 -113 B
assets/SkeletonUtils-C35X7Mjf.js (new) 133 B 🔴 +133 B 🔴 +111 B 🔴 +110 B
assets/useSessionCookie-BGvj1CQg.js (removed) 101 B 🟢 -101 B 🟢 -86 B 🟢 -77 B
assets/useSessionCookie-BSKBteJz.js (new) 101 B 🔴 +101 B 🔴 +86 B 🔴 +72 B
assets/useFeatureFlags-CWmlOVVx.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -89 B
assets/useFeatureFlags-nrB0Mc6f.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +86 B
assets/useLoad3dViewer-2dWZryVV.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -83 B
assets/useLoad3dViewer-Ca51DF9f.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +85 B
assets/useCurrentUser-CIGgCGyB.js (removed) 96 B 🟢 -96 B 🟢 -97 B 🟢 -83 B
assets/useCurrentUser-DVJYHkkl.js (new) 96 B 🔴 +96 B 🔴 +97 B 🔴 +85 B

Status: 20 added / 20 removed / 16 unchanged

Vendor & Third-Party — 15.8 MB (baseline 15.7 MB) • 🔴 +48.9 kB

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-three-DZYUD5SW.js (new) 7.14 MB 🔴 +7.14 MB 🔴 +2.18 MB 🔴 +1.03 MB
assets/vendor-three-CAgYnvrp.js (removed) 7.09 MB 🟢 -7.09 MB 🟢 -2.18 MB 🟢 -1.02 MB
assets/vendor-other-B5ziGM4m.js (new) 2.23 MB 🔴 +2.23 MB 🔴 +476 kB 🔴 +380 kB
assets/vendor-other-xGZJ-vAL.js (removed) 2.23 MB 🟢 -2.23 MB 🟢 -476 kB 🟢 -380 kB

Status: 2 added / 2 removed / 14 unchanged

Other — 12 MB (baseline 11.9 MB) • 🔴 +118 kB

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/main-Qbl7ZWdK.js (new) 190 kB 🔴 +190 kB 🔴 +55.4 kB 🔴 +46.6 kB
assets/main-BkkxWTck.js (removed) 189 kB 🟢 -189 kB 🟢 -55.3 kB 🟢 -46.5 kB
assets/core-BlDRD-Oz.js (new) 112 kB 🔴 +112 kB 🔴 +28.7 kB 🔴 +24.3 kB
assets/core-BlL9zXNw.js (removed) 111 kB 🟢 -111 kB 🟢 -28.6 kB 🟢 -24.2 kB
assets/WidgetSelect-B6g1gpDV.js (new) 88 kB 🔴 +88 kB 🔴 +20 kB 🔴 +17.1 kB
assets/WidgetSelect-CpJpaUPN.js (removed) 88 kB 🟢 -88 kB 🟢 -20 kB 🟢 -17.1 kB
assets/Load3D-Da6OaNnu.js (removed) 68.8 kB 🟢 -68.8 kB 🟢 -11 kB 🟢 -9.5 kB
assets/Load3D-L2USSM1F.js (new) 67.6 kB 🔴 +67.6 kB 🔴 +10.9 kB 🔴 +9.39 kB
assets/CameraInfo-CseILk5G.js (new) 66 kB 🔴 +66 kB 🔴 +14.4 kB 🔴 +12.6 kB
assets/Preview3d-xV-jFmzL.js (new) 52.4 kB 🔴 +52.4 kB 🔴 +8.54 kB 🔴 +7.46 kB
assets/Preview3d-D_2o0HMd.js (removed) 50.9 kB 🟢 -50.9 kB 🟢 -8.34 kB 🟢 -7.28 kB
assets/Viewport3d-BtsYuWr0.js (new) 48.1 kB 🔴 +48.1 kB 🔴 +10.1 kB 🔴 +8.99 kB
assets/WorkspacePanelContent-CceXhhP5.js (removed) 41.9 kB 🟢 -41.9 kB 🟢 -8.77 kB 🟢 -7.75 kB
assets/WorkspacePanelContent-qguRhhqg.js (new) 41.9 kB 🔴 +41.9 kB 🔴 +8.76 kB 🔴 +7.76 kB
assets/SubscriptionRequiredDialogContentUnified-CjQldxJA.js (removed) 41.2 kB 🟢 -41.2 kB 🟢 -9.17 kB 🟢 -7.98 kB
assets/SubscriptionRequiredDialogContentUnified-CwcuUW4O.js (new) 41.2 kB 🔴 +41.2 kB 🔴 +9.17 kB 🔴 +7.98 kB
assets/SubscriptionTransitionPreviewWorkspace-Bw5g80no.js (new) 39.9 kB 🔴 +39.9 kB 🔴 +8.02 kB 🔴 +7.07 kB
assets/SubscriptionTransitionPreviewWorkspace-DdciTTGl.js (removed) 39.9 kB 🟢 -39.9 kB 🟢 -8.02 kB 🟢 -7.08 kB
assets/WidgetBoundingBoxes-CCoin-Fu.js (new) 33.8 kB 🔴 +33.8 kB 🔴 +9.2 kB 🔴 +8.16 kB
assets/WidgetBoundingBoxes-CUk7XhZl.js (removed) 33.8 kB 🟢 -33.8 kB 🟢 -9.2 kB 🟢 -8.16 kB
assets/WidgetPainter-B9WNAYjl.js (new) 32.7 kB 🔴 +32.7 kB 🔴 +7.91 kB 🔴 +7.01 kB
assets/WidgetPainter-nhge-4be.js (removed) 32.7 kB 🟢 -32.7 kB 🟢 -7.91 kB 🟢 -7.02 kB
assets/Load3dViewerContent-BwPJXCxO.js (new) 30.8 kB 🔴 +30.8 kB 🔴 +6.3 kB 🔴 +5.46 kB
assets/Load3dViewerContent-vPNd3y82.js (removed) 30.8 kB 🟢 -30.8 kB 🟢 -6.3 kB 🟢 -5.46 kB
assets/HdrViewerContent-DAy9vbwK.js (new) 29.8 kB 🔴 +29.8 kB 🔴 +8.28 kB 🔴 +7.3 kB
assets/HdrViewerContent-Bs7Xq07j.js (removed) 29.8 kB 🟢 -29.8 kB 🟢 -8.27 kB 🟢 -7.3 kB
assets/ColorPicker-CjDcqvtv.js (new) 25 kB 🔴 +25 kB 🔴 +6.54 kB 🔴 +5.77 kB
assets/ColorPicker-g2-zF83d.js (removed) 25 kB 🟢 -25 kB 🟢 -6.54 kB 🟢 -5.77 kB
assets/SubscriptionPanelContentWorkspace-Cdrz7tPz.js (new) 25 kB 🔴 +25 kB 🔴 +5.87 kB 🔴 +5.15 kB
assets/SubscriptionPanelContentWorkspace-CqHJFIud.js (removed) 25 kB 🟢 -25 kB 🟢 -5.88 kB 🟢 -5.15 kB
assets/SubscriptionRequiredDialogContentWorkspace-BrqerPYf.js (removed) 24.6 kB 🟢 -24.6 kB 🟢 -5.67 kB 🟢 -4.98 kB
assets/SubscriptionRequiredDialogContentWorkspace-Ds6aND1g.js (new) 24.6 kB 🔴 +24.6 kB 🔴 +5.67 kB 🔴 +4.99 kB
assets/SubscriptionRequiredDialogContent-67Mza-5e.js (removed) 24.4 kB 🟢 -24.4 kB 🟢 -6 kB 🟢 -5.29 kB
assets/SubscriptionRequiredDialogContent-DpKQabmR.js (new) 24.4 kB 🔴 +24.4 kB 🔴 +6 kB 🔴 +5.3 kB
assets/WidgetImageCrop-C_h82Z2x.js (new) 23.3 kB 🔴 +23.3 kB 🔴 +5.75 kB 🔴 +5.05 kB
assets/WidgetImageCrop-DdU7ReaM.js (removed) 23.3 kB 🟢 -23.3 kB 🟢 -5.76 kB 🟢 -5.05 kB
assets/load3d-DBW-F_ae.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.45 kB 🔴 +4.71 kB
assets/load3d-IboYoJrg.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.45 kB 🟢 -4.71 kB
assets/CurrentUserPopoverWorkspace-BrP3VeZW.js (removed) 20.6 kB 🟢 -20.6 kB 🟢 -4.71 kB 🟢 -4.2 kB
assets/CurrentUserPopoverWorkspace-yj4_IbPe.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +4.71 kB 🔴 +4.21 kB
assets/SignInContent-5n8xmr3m.js (removed) 20 kB 🟢 -20 kB 🟢 -5.05 kB 🟢 -4.42 kB
assets/SignInContent-JpmR82yl.js (new) 20 kB 🔴 +20 kB 🔴 +5.05 kB 🔴 +4.41 kB
assets/WidgetInputNumber-BJ49wMnq.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.83 kB 🔴 +4.29 kB
assets/WidgetInputNumber-WcHcY600.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.83 kB 🟢 -4.28 kB
assets/CreditsTile-5wVfIeZk.js (new) 17.2 kB 🔴 +17.2 kB 🔴 +4.58 kB 🔴 +4.03 kB
assets/CreditsTile-CZLTQCp4.js (removed) 17.2 kB 🟢 -17.2 kB 🟢 -4.58 kB 🟢 -4.03 kB
assets/WidgetRecordAudio-BZ6R3deu.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.63 kB 🔴 +4.13 kB
assets/WidgetRecordAudio-CiehuY-2.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.63 kB 🟢 -4.13 kB
assets/WidgetRange-B3QBCBOH.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -4.17 kB 🟢 -3.73 kB
assets/WidgetRange-BVqt6-0W.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +4.17 kB 🔴 +3.72 kB
assets/DialogHeader-BE8QtgCQ.js (removed) 15 kB 🟢 -15 kB 🟢 -4.1 kB 🟢 -3.64 kB
assets/DialogHeader-DvCJqPPl.js (new) 15 kB 🔴 +15 kB 🔴 +4.1 kB 🔴 +3.64 kB
assets/WaveAudioPlayer-CSEjFxtp.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +3.49 kB 🔴 +3.07 kB
assets/WaveAudioPlayer-D6hO1jE7.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -3.49 kB 🟢 -3.07 kB
assets/i18n-BF4lr5A8.js (removed) 12.5 kB 🟢 -12.5 kB 🟢 -3.31 kB 🟢 -2.8 kB
assets/i18n-DLe4WI32.js (new) 12.5 kB 🔴 +12.5 kB 🔴 +3.32 kB 🔴 +2.8 kB
assets/WidgetCurve-B1v97FhV.js (removed) 11.3 kB 🟢 -11.3 kB 🟢 -3.51 kB 🟢 -3.17 kB
assets/WidgetCurve-DP-UUFPF.js (new) 11.3 kB 🔴 +11.3 kB 🔴 +3.51 kB 🔴 +3.16 kB
assets/AudioPreviewPlayer-BL3zQi9p.js (new) 10.6 kB 🔴 +10.6 kB 🔴 +3.08 kB 🔴 +2.75 kB
assets/AudioPreviewPlayer-DYu7IyrA.js (removed) 10.6 kB 🟢 -10.6 kB 🟢 -3.08 kB 🟢 -2.75 kB
assets/TeamWorkspacesDialogContent-CnWSs2RK.js (new) 10.3 kB 🔴 +10.3 kB 🔴 +3 kB 🔴 +2.67 kB
assets/TeamWorkspacesDialogContent-CwF6auT7.js (removed) 10.3 kB 🟢 -10.3 kB 🟢 -3 kB 🟢 -2.66 kB
assets/SelectValue-D4ykzmcZ.js (removed) 10.2 kB 🟢 -10.2 kB 🟢 -2.48 kB 🟢 -2.19 kB
assets/SelectValue-DfWkh-mh.js (new) 10.2 kB 🔴 +10.2 kB 🔴 +2.48 kB 🔴 +2.19 kB
assets/Load3DConfiguration-BkrOw4yD.js (removed) 8.97 kB 🟢 -8.97 kB 🟢 -2.65 kB 🟢 -2.33 kB
assets/Load3DConfiguration-RccnrDT5.js (new) 8.97 kB 🔴 +8.97 kB 🔴 +2.65 kB 🔴 +2.34 kB
assets/onboardingCloudRoutes-C_DXZYtL.js (removed) 8.44 kB 🟢 -8.44 kB 🟢 -2.65 kB 🟢 -2.26 kB
assets/onboardingCloudRoutes-CH15NKmL.js (new) 8.44 kB 🔴 +8.44 kB 🔴 +2.64 kB 🔴 +2.28 kB
assets/nodeTemplates-2Wg7HyX9.js (removed) 8.33 kB 🟢 -8.33 kB 🟢 -2.88 kB 🟢 -2.54 kB
assets/nodeTemplates-CaZ2kGBu.js (new) 8.33 kB 🔴 +8.33 kB 🔴 +2.88 kB 🔴 +2.55 kB
assets/WidgetImageCompare-BbJGGScB.js (removed) 8.19 kB 🟢 -8.19 kB 🟢 -2.33 kB 🟢 -2.04 kB
assets/WidgetImageCompare-DmyPngpr.js (new) 8.19 kB 🔴 +8.19 kB 🔴 +2.33 kB 🔴 +2.04 kB
assets/NightlySurveyController-BNrRtGxW.js (removed) 7.56 kB 🟢 -7.56 kB 🟢 -2.59 kB 🟢 -2.27 kB
assets/NightlySurveyController-CbiQMSyn.js (new) 7.56 kB 🔴 +7.56 kB 🔴 +2.59 kB 🔴 +2.3 kB
assets/Popover-CFlI0lSR.js (removed) 7.52 kB 🟢 -7.52 kB 🟢 -2.72 kB 🟢 -2.39 kB
assets/Popover-DyHrU36d.js (new) 7.52 kB 🔴 +7.52 kB 🔴 +2.72 kB 🔴 +2.39 kB
assets/WidgetWithControl-BF_BGj19.js (removed) 6.36 kB 🟢 -6.36 kB 🟢 -2.59 kB 🟢 -2.29 kB
assets/WidgetWithControl-DHRH2Vud.js (new) 6.36 kB 🔴 +6.36 kB 🔴 +2.58 kB 🔴 +2.28 kB
assets/AnimationControls-Djywi30C.js (removed) 6.15 kB 🟢 -6.15 kB 🟢 -1.89 kB 🟢 -1.67 kB
assets/AnimationControls-Tr0uiucm.js (new) 6.15 kB 🔴 +6.15 kB 🔴 +1.89 kB 🔴 +1.67 kB
assets/tierBenefits-CwkX2jAf.js (new) 6.02 kB 🔴 +6.02 kB 🔴 +1.93 kB 🔴 +1.67 kB
assets/tierBenefits-DsZ7iEl1.js (removed) 6.02 kB 🟢 -6.02 kB 🟢 -1.93 kB 🟢 -1.67 kB
assets/load3dPreviewExtensions-9WFDCPjB.js (new) 5.95 kB 🔴 +5.95 kB 🔴 +1.85 kB 🔴 +1.64 kB
assets/load3dPreviewExtensions-o-6WgrEF.js (removed) 5.95 kB 🟢 -5.95 kB 🟢 -1.85 kB 🟢 -1.64 kB
assets/main-jTplWtYj.js (removed) 5.84 kB 🟢 -5.84 kB 🟢 -1.86 kB 🟢 -1.58 kB
assets/main-OaqdX8KK.js (new) 5.84 kB 🔴 +5.84 kB 🔴 +1.86 kB 🔴 +1.59 kB
assets/CancelSubscriptionDialogContent-CEyPwyjC.js (new) 5.76 kB 🔴 +5.76 kB 🔴 +1.97 kB 🔴 +1.73 kB
assets/CancelSubscriptionDialogContent-DH3u-tR0.js (removed) 5.76 kB 🟢 -5.76 kB 🟢 -1.97 kB 🟢 -1.73 kB
assets/FreeTierDialogContent-6_qQwAry.js (new) 5.25 kB 🔴 +5.25 kB 🔴 +1.76 kB 🔴 +1.55 kB
assets/FreeTierDialogContent-CTts6CIZ.js (removed) 5.25 kB 🟢 -5.25 kB 🟢 -1.76 kB 🟢 -1.55 kB
assets/CreateWorkspaceDialogContent-D18uWI80.js (removed) 5.19 kB 🟢 -5.19 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/CreateWorkspaceDialogContent-DegUsGsb.js (new) 5.19 kB 🔴 +5.19 kB 🔴 +1.82 kB 🔴 +1.58 kB
assets/CloudNotificationContent-Cg7vIlOw.js (removed) 5.11 kB 🟢 -5.11 kB 🟢 -1.79 kB 🟢 -1.54 kB
assets/CloudNotificationContent-CyW6HBA3.js (new) 5.11 kB 🔴 +5.11 kB 🔴 +1.79 kB 🔴 +1.54 kB
assets/ChangeMemberRoleDialogContent-B72SXIY8.js (new) 5.04 kB 🔴 +5.04 kB 🔴 +1.67 kB 🔴 +1.46 kB
assets/ChangeMemberRoleDialogContent-C07X0rTE.js (removed) 5.04 kB 🟢 -5.04 kB 🟢 -1.67 kB 🟢 -1.46 kB
assets/missingModelDownload-BgfoTQUi.js (new) 5.04 kB 🔴 +5.04 kB 🔴 +1.95 kB 🔴 +1.7 kB
assets/missingModelDownload-D2t-Qk0H.js (removed) 5.04 kB 🟢 -5.04 kB 🟢 -1.95 kB 🟢 -1.7 kB
assets/EditWorkspaceDialogContent-B-fYE2N3.js (removed) 5 kB 🟢 -5 kB 🟢 -1.79 kB 🟢 -1.55 kB
assets/EditWorkspaceDialogContent-CNifCoXo.js (new) 5 kB 🔴 +5 kB 🔴 +1.79 kB 🔴 +1.56 kB
assets/PaletteSwatchRow-BCBqnh5w.js (new) 4.91 kB 🔴 +4.91 kB 🔴 +1.9 kB 🔴 +1.69 kB
assets/PaletteSwatchRow-BEzWz7EZ.js (removed) 4.91 kB 🟢 -4.91 kB 🟢 -1.9 kB 🟢 -1.69 kB
assets/WidgetTextarea-CjvMUOr2.js (removed) 4.86 kB 🟢 -4.86 kB 🟢 -1.9 kB 🟢 -1.67 kB
assets/WidgetTextarea-xJlw8bIv.js (new) 4.86 kB 🔴 +4.86 kB 🔴 +1.9 kB 🔴 +1.68 kB
assets/DowngradeRemoveMembersDialogContent-CaBnhPsG.js (removed) 4.83 kB 🟢 -4.83 kB 🟢 -1.7 kB 🟢 -1.47 kB
assets/DowngradeRemoveMembersDialogContent-CRPHgjwL.js (new) 4.83 kB 🔴 +4.83 kB 🔴 +1.71 kB 🔴 +1.47 kB
assets/saveMesh-Bkb3M4uY.js (removed) 4.81 kB 🟢 -4.81 kB 🟢 -1.55 kB 🟢 -1.37 kB
assets/saveMesh-Bkq_06jv.js (new) 4.81 kB 🔴 +4.81 kB 🔴 +1.55 kB 🔴 +1.37 kB
assets/InviteMemberDialogContent-DQgmFBwv.js (new) 4.75 kB 🔴 +4.75 kB 🔴 +1.61 kB 🔴 +1.41 kB
assets/InviteMemberDialogContent-F_1R_56t.js (removed) 4.75 kB 🟢 -4.75 kB 🟢 -1.62 kB 🟢 -1.41 kB
assets/ValueControlPopover-BtOOi3ta.js (removed) 4.55 kB 🟢 -4.55 kB 🟢 -1.58 kB 🟢 -1.42 kB
assets/ValueControlPopover-D0aTphDf.js (new) 4.55 kB 🔴 +4.55 kB 🔴 +1.59 kB 🔴 +1.42 kB
assets/ApiNodesSignInContent-BEhNfaP9.js (removed) 4.14 kB 🟢 -4.14 kB 🟢 -1.34 kB 🟢 -1.18 kB
assets/ApiNodesSignInContent-VAHh9hul.js (new) 4.14 kB 🔴 +4.14 kB 🔴 +1.34 kB 🔴 +1.18 kB
assets/Slider-D0KLWtKQ.js (new) 3.92 kB 🔴 +3.92 kB 🔴 +1.45 kB 🔴 +1.26 kB
assets/Slider-DU5iB7rh.js (removed) 3.92 kB 🟢 -3.92 kB 🟢 -1.45 kB 🟢 -1.26 kB
assets/DeleteWorkspaceDialogContent-Dm1HFilV.js (new) 3.91 kB 🔴 +3.91 kB 🔴 +1.47 kB 🔴 +1.27 kB
assets/DeleteWorkspaceDialogContent-kyuqlYMB.js (removed) 3.91 kB 🟢 -3.91 kB 🟢 -1.47 kB 🟢 -1.27 kB
assets/WidgetBoundingBox-B3G8bVHz.js (removed) 3.82 kB 🟢 -3.82 kB 🟢 -1.06 kB 🟢 -940 B
assets/WidgetBoundingBox-D0iGi9bS.js (new) 3.82 kB 🔴 +3.82 kB 🔴 +1.06 kB 🔴 +929 B
assets/LeaveWorkspaceDialogContent-BY46y-8E.js (removed) 3.73 kB 🟢 -3.73 kB 🟢 -1.42 kB 🟢 -1.23 kB
assets/LeaveWorkspaceDialogContent-LbafKfsi.js (new) 3.73 kB 🔴 +3.73 kB 🔴 +1.41 kB 🔴 +1.22 kB
assets/RemoveMemberDialogContent-BGSqwSJh.js (removed) 3.71 kB 🟢 -3.71 kB 🟢 -1.38 kB 🟢 -1.19 kB
assets/RemoveMemberDialogContent-D2M9TGVc.js (new) 3.71 kB 🔴 +3.71 kB 🔴 +1.37 kB 🔴 +1.19 kB
assets/WidgetToggleSwitch-BEpwISMx.js (removed) 3.67 kB 🟢 -3.67 kB 🟢 -1.4 kB 🟢 -1.24 kB
assets/WidgetToggleSwitch-DvhrO6qH.js (new) 3.67 kB 🔴 +3.67 kB 🔴 +1.4 kB 🔴 +1.24 kB
assets/RevokeInviteDialogContent-C3_wNgtk.js (new) 3.63 kB 🔴 +3.63 kB 🔴 +1.38 kB 🔴 +1.2 kB
assets/RevokeInviteDialogContent-DLeukfWj.js (removed) 3.63 kB 🟢 -3.63 kB 🟢 -1.38 kB 🟢 -1.2 kB
assets/InviteMemberUpsellDialogContent-BQtXk1Vk.js (removed) 3.52 kB 🟢 -3.52 kB 🟢 -1.26 kB 🟢 -1.11 kB
assets/InviteMemberUpsellDialogContent-DhHAaAL8.js (new) 3.52 kB 🔴 +3.52 kB 🔴 +1.26 kB 🔴 +1.11 kB
assets/workspaceCheckoutTelemetry-BdY6ScjL.js (removed) 3.47 kB 🟢 -3.47 kB 🟢 -1.56 kB 🟢 -1.36 kB
assets/workspaceCheckoutTelemetry-BXHw5MsH.js (new) 3.47 kB 🔴 +3.47 kB 🔴 +1.56 kB 🔴 +1.36 kB
assets/Media3DTop-CUBm90sU.js (removed) 3.26 kB 🟢 -3.26 kB 🟢 -1.3 kB 🟢 -1.13 kB
assets/Media3DTop-Di0oBrIK.js (new) 3.26 kB 🔴 +3.26 kB 🔴 +1.3 kB 🔴 +1.13 kB
assets/WidgetMarkdown-0g-zwpkK.js (new) 3.12 kB 🔴 +3.12 kB 🔴 +1.29 kB 🔴 +1.14 kB
assets/WidgetMarkdown-CtKAp__7.js (removed) 3.12 kB 🟢 -3.12 kB 🟢 -1.29 kB 🟢 -1.14 kB
assets/WidgetInputText-BnLlrwQ6.js (removed) 3.06 kB 🟢 -3.06 kB 🟢 -1.29 kB 🟢 -1.16 kB
assets/WidgetInputText-CTcoJGWg.js (new) 3.06 kB 🔴 +3.06 kB 🔴 +1.29 kB 🔴 +1.16 kB
assets/GlobalToast-BbYF-M3l.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.26 kB 🔴 +1.09 kB
assets/GlobalToast-DnYSI0PF.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.26 kB 🟢 -1.09 kB
assets/MediaVideoTop-0sWo1E5D.js (removed) 2.91 kB 🟢 -2.91 kB 🟢 -1.18 kB 🟢 -1.03 kB
assets/MediaVideoTop-tvy8-NCV.js (new) 2.91 kB 🔴 +2.91 kB 🔴 +1.17 kB 🔴 +1.03 kB
assets/load3dAdvanced-Co_MWc14.js (removed) 2.87 kB 🟢 -2.87 kB 🟢 -1.13 kB 🟢 -985 B
assets/load3dAdvanced-Cz9lLT7I.js (new) 2.87 kB 🔴 +2.87 kB 🔴 +1.13 kB 🔴 +985 B
assets/ErrorPanelSurveyCta-BhzPOkHw.js (new) 2.69 kB 🔴 +2.69 kB 🔴 +1.18 kB 🔴 +1.04 kB
assets/ErrorPanelSurveyCta-Ct0C3fpb.js (removed) 2.69 kB 🟢 -2.69 kB 🟢 -1.17 kB 🟢 -1.04 kB
assets/SubscribeToRun-C--O6Yn0.js (removed) 2.45 kB 🟢 -2.45 kB 🟢 -1.07 kB 🟢 -929 B
assets/SubscribeToRun-D6c6-Crw.js (new) 2.45 kB 🔴 +2.45 kB 🔴 +1.07 kB 🔴 +929 B
assets/WidgetLayoutField-Df4IIveL.js (new) 2.43 kB 🔴 +2.43 kB 🔴 +1.05 kB 🔴 +961 B
assets/WidgetLayoutField-DvHgI3Vr.js (removed) 2.43 kB 🟢 -2.43 kB 🟢 -1.05 kB 🟢 -974 B
assets/graphHasMissingNodes-BlZ2Vmks.js (new) 1.93 kB 🔴 +1.93 kB 🔴 +903 B 🔴 +791 B
assets/graphHasMissingNodes-CY8B-U7x.js (removed) 1.93 kB 🟢 -1.93 kB 🟢 -907 B 🟢 -805 B
assets/WidgetColorPicker-B7cJsnh_.js (removed) 1.74 kB 🟢 -1.74 kB 🟢 -788 B 🟢 -699 B
assets/WidgetColorPicker-XcVdMg0H.js (new) 1.74 kB 🔴 +1.74 kB 🔴 +785 B 🔴 +700 B
assets/Loader-CG6RWlrO.js (removed) 1.69 kB 🟢 -1.69 kB 🟢 -814 B 🟢 -748 B
assets/Loader-DoGUuAA6.js (new) 1.69 kB 🔴 +1.69 kB 🔴 +812 B 🔴 +747 B
assets/WidgetColors-BA-1pHfw.js (new) 1.68 kB 🔴 +1.68 kB 🔴 +835 B 🔴 +738 B
assets/WidgetColors-DREfXkXm.js (removed) 1.68 kB 🟢 -1.68 kB 🟢 -837 B 🟢 -740 B
assets/MediaAudioTop-BfYrFs8h.js (new) 1.67 kB 🔴 +1.67 kB 🔴 +835 B 🔴 +699 B
assets/MediaAudioTop-yo7xtroJ.js (removed) 1.67 kB 🟢 -1.67 kB 🟢 -836 B 🟢 -699 B
assets/Textarea-DSTH2EAy.js (new) 1.61 kB 🔴 +1.61 kB 🔴 +842 B 🔴 +741 B
assets/Textarea-r8ROPOcJ.js (removed) 1.61 kB 🟢 -1.61 kB 🟢 -844 B 🟢 -741 B
assets/signInSchema-BJxreHen.js (new) 1.6 kB 🔴 +1.6 kB 🔴 +584 B 🔴 +549 B
assets/signInSchema-DxznaqFc.js (removed) 1.6 kB 🟢 -1.6 kB 🟢 -584 B 🟢 -507 B
assets/menuBarStyles-DPzey3AR.js (new) 1.57 kB 🔴 +1.57 kB 🔴 +612 B 🔴 +547 B
assets/VideoPlayOverlay-DqMuRfyu.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +755 B 🔴 +673 B
assets/VideoPlayOverlay-yS2I0bNI.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -761 B 🟢 -676 B
assets/nodeTypes-Du_Tvuie.js (new) 1.23 kB 🔴 +1.23 kB 🔴 +521 B 🔴 +466 B
assets/CloudRunButtonWrapper-B0VKmmoK.js (new) 1.11 kB 🔴 +1.11 kB 🔴 +547 B 🔴 +515 B
assets/CloudRunButtonWrapper-CQaFaBe0.js (removed) 1.11 kB 🟢 -1.11 kB 🟢 -549 B 🟢 -485 B
assets/cameraInfo-B-b2IkpW.js (new) 1.11 kB 🔴 +1.11 kB 🔴 +591 B 🔴 +544 B
assets/nodeTypes-CVrPWNFO.js (removed) 1.06 kB 🟢 -1.06 kB 🟢 -478 B 🟢 -428 B
assets/cloudSessionCookie-BH8zXDpH.js (removed) 991 B 🟢 -991 B 🟢 -466 B 🟢 -423 B
assets/cloudSessionCookie-BPcNx0PC.js (new) 991 B 🔴 +991 B 🔴 +464 B 🔴 +423 B
assets/cloudBadges-C-RCVWMp.js (new) 973 B 🔴 +973 B 🔴 +549 B 🔴 +469 B
assets/cloudBadges-CvIvESqS.js (removed) 973 B 🟢 -973 B 🟢 -550 B 🟢 -470 B
assets/Load3DAdvanced-B7ZuPVla.js (new) 813 B 🔴 +813 B 🔴 +455 B 🔴 +406 B
assets/Load3DAdvanced-U0tspdNB.js (removed) 813 B 🟢 -813 B 🟢 -451 B 🟢 -410 B
assets/nightlyBadges-1uDC7t-9.js (removed) 464 B 🟢 -464 B 🟢 -305 B 🟢 -256 B
assets/nightlyBadges-DwrFST3M.js (new) 464 B 🔴 +464 B 🔴 +306 B 🔴 +254 B
assets/src-C8nwZwDr.js (removed) 446 B 🟢 -446 B 🟢 -318 B 🟢 -270 B
assets/src-DrLouLeu.js (new) 446 B 🔴 +446 B 🔴 +317 B 🔴 +270 B
assets/missingModelDownload-_jhTff9K.js (new) 228 B 🔴 +228 B 🔴 +148 B 🔴 +127 B
assets/missingModelDownload-Befs-dev.js (removed) 228 B 🟢 -228 B 🟢 -151 B 🟢 -130 B
assets/SubscriptionPanelContentWorkspace-BBGpmGju.js (new) 179 B 🔴 +179 B 🔴 +117 B 🔴 +92 B
assets/SubscriptionPanelContentWorkspace-BVmv-ydP.js (removed) 179 B 🟢 -179 B 🟢 -117 B 🟢 -92 B
assets/Load3dViewerContent-DV3pPbQR.js (new) 137 B 🔴 +137 B 🔴 +103 B 🔴 +92 B
assets/Load3dViewerContent-DXQh4IYq.js (removed) 137 B 🟢 -137 B 🟢 -103 B 🟢 -102 B
assets/WidgetBoundingBox-C9AdV6AT.js (new) 131 B 🔴 +131 B 🔴 +100 B 🔴 +95 B
assets/WidgetBoundingBox-m6V_qj_3.js (removed) 131 B 🟢 -131 B 🟢 -100 B 🟢 -101 B
assets/Load3DAdvanced-CDCEnoqV.js (removed) 122 B 🟢 -122 B 🟢 -97 B 🟢 -87 B
assets/Load3DAdvanced-D4o5QWQA.js (new) 122 B 🔴 +122 B 🔴 +97 B 🔴 +94 B
assets/WidgetLegacy-B1TyvI5K.js (new) 119 B 🔴 +119 B 🔴 +108 B 🔴 +96 B
assets/WidgetLegacy-Boy8nt7K.js (removed) 119 B 🟢 -119 B 🟢 -108 B 🟢 -94 B
assets/workflowDraftStoreV2-CO-3h1KN.js (removed) 113 B 🟢 -113 B 🟢 -105 B 🟢 -116 B
assets/workflowDraftStoreV2-Cv69LhQS.js (new) 113 B 🔴 +113 B 🔴 +105 B 🔴 +107 B
assets/CameraInfo-Dyk13Ezd.js (new) 110 B 🔴 +110 B 🔴 +93 B 🔴 +91 B
assets/Load3D-0GrULp6v.js (removed) 98 B 🟢 -98 B 🟢 -89 B 🟢 -83 B
assets/Load3D-BRCBbMxV.js (new) 98 B 🔴 +98 B 🔴 +89 B 🔴 +81 B
assets/i18n-BogQhhCa.js (removed) 97 B 🟢 -97 B 🟢 -92 B 🟢 -87 B
assets/i18n-BoguUDt2.js (new) 97 B 🔴 +97 B 🔴 +92 B 🔴 +85 B
assets/changeTracker-2NyTFuwM.js (removed) 93 B 🟢 -93 B 🟢 -95 B 🟢 -83 B
assets/changeTracker-BkQSm6oQ.js (new) 93 B 🔴 +93 B 🔴 +95 B 🔴 +84 B

Status: 104 added / 99 removed / 63 unchanged

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 44.6 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 57.6 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 73.2 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 54.7 MB heap
large-graph-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 61.6 MB heap
large-graph-pan: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 53.6 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 59.5 MB heap
minimap-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 63.5 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 55.4 MB heap
subgraph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 59.1 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 48.1 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 153ms TBT · 69.6 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 73.6 MB heap
vue-large-graph-idle: · 57.1 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 154.9 MB heap
vue-large-graph-pan: · 57.1 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 36ms TBT · 155.1 MB heap
workflow-execution: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 50.1 MB heap

⚠️ 4 regressions detected

Show regressions
Metric Baseline PR (median) Δ Sig
canvas-idle: task duration 474ms 523ms +10% ⚠️ z=4.1
large-graph-pan: task duration 1168ms 1202ms +3% ⚠️ z=2.8
minimap-idle: task duration 602ms 667ms +11% ⚠️ z=2.9
subgraph-idle: task duration 438ms 464ms +6% ⚠️ z=3.0
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 +0%
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 9ms 11ms +24% z=0.5
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 9 8 -11% z=-5.6
canvas-idle: task duration 474ms 523ms +10% ⚠️ z=4.1
canvas-idle: script duration 21ms 23ms +14% z=-0.8
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: heap used 69.3 MB 44.6 MB -36%
canvas-idle: DOM nodes 18 -265 -1569% z=-225.1
canvas-idle: event listeners 4 -132 -3400% z=-30.3
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 4ms -7% z=-0.4
canvas-mouse-sweep: style recalc duration 41ms 42ms +1% z=-0.3
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 75 75 -1% z=-1.8
canvas-mouse-sweep: task duration 860ms 846ms -2% z=-0.3
canvas-mouse-sweep: script duration 132ms 129ms -2% z=-1.0
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: heap used 48.4 MB 57.6 MB +19%
canvas-mouse-sweep: DOM nodes -266 -103 -61% z=-63.9
canvas-mouse-sweep: event listeners -133 -65 -52% z=-17.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 1ms +8% z=1.2
canvas-zoom-sweep: style recalc duration 18ms 20ms +7% z=0.4
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 351ms 372ms +6% z=1.9
canvas-zoom-sweep: script duration 23ms 26ms +12% z=-0.3
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: heap used 72.4 MB 73.2 MB +1%
canvas-zoom-sweep: DOM nodes 77 78 +1% z=-2.2
canvas-zoom-sweep: event listeners 19 19 +0% z=-0.9
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 9ms 11ms +18% z=1.5
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 12 12 +0% z=-2.2
dom-widget-clipping: task duration 370ms 383ms +3% z=1.1
dom-widget-clipping: script duration 62ms 64ms +3% z=-1.2
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: heap used 54.5 MB 54.7 MB +0%
dom-widget-clipping: DOM nodes 20 20 +0% z=-1.5
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 -0%
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 9ms 10ms +8% z=-2.1
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 9 10 +6% z=-6.7
large-graph-idle: task duration 615ms 644ms +5% z=1.9
large-graph-idle: script duration 108ms 110ms +2% z=0.7
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: heap used 61.5 MB 61.6 MB +0%
large-graph-idle: DOM nodes -268 -266 -1% z=-321.6
large-graph-idle: event listeners -129 -129 +0% z=-25.4
large-graph-pan: avg frame time 17ms 17ms -0% z=-0.2
large-graph-pan: p95 frame time 17ms 17ms -0%
large-graph-pan: layout duration 0ms 0ms +0%
large-graph-pan: style recalc duration 15ms 17ms +14% z=-0.7
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 1168ms 1202ms +3% ⚠️ z=2.8
large-graph-pan: script duration 406ms 413ms +2% z=0.3
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: heap used 58.6 MB 53.6 MB -8%
large-graph-pan: DOM nodes -270 -270 -0% z=-174.9
large-graph-pan: event listeners -129 -130 +1% z=-162.9
large-graph-zoom: avg frame time 17ms 17ms +0%
large-graph-zoom: p95 frame time 17ms 17ms -0%
large-graph-zoom: layout duration 8ms 8ms +2%
large-graph-zoom: style recalc duration 16ms 17ms +4%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 65 66 +1%
large-graph-zoom: task duration 1432ms 1450ms +1%
large-graph-zoom: script duration 509ms 520ms +2%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: heap used 55.8 MB 59.5 MB +7%
large-graph-zoom: DOM nodes -273 -274 +0%
large-graph-zoom: event listeners -133 -132 -1%
minimap-idle: avg frame time 17ms 17ms -0% z=0.1
minimap-idle: p95 frame time 17ms 17ms -0%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 9ms 9ms -3% z=-1.3
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 8 8 +0% z=-2.3
minimap-idle: task duration 602ms 667ms +11% ⚠️ z=2.9
minimap-idle: script duration 106ms 116ms +9% z=1.8
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: heap used 63.2 MB 63.5 MB +0%
minimap-idle: DOM nodes -267 -268 +0% z=-209.9
minimap-idle: event listeners -129 -129 +0% z=-202.3
subgraph-dom-widget-clipping: avg frame time 17ms 17ms +0% z=0.1
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 12ms 14ms +21% z=1.6
subgraph-dom-widget-clipping: layout count 0 0 +0%
subgraph-dom-widget-clipping: style recalc count 48 48 -1% z=-0.8
subgraph-dom-widget-clipping: task duration 378ms 406ms +7% z=1.5
subgraph-dom-widget-clipping: script duration 120ms 130ms +8% z=0.2
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: heap used 54.9 MB 55.4 MB +1%
subgraph-dom-widget-clipping: DOM nodes 22 21 -5% z=-1.1
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 +0%
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 11ms 10ms -13% z=-0.7
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 11 11 -5% z=-0.6
subgraph-idle: task duration 438ms 464ms +6% ⚠️ z=3.0
subgraph-idle: script duration 18ms 22ms +23% z=0.5
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: heap used 45.5 MB 59.1 MB +30%
subgraph-idle: DOM nodes -264 -122 -54% z=-96.2
subgraph-idle: event listeners -133 -64 -52% variance too high
subgraph-mouse-sweep: avg frame time 17ms 17ms +0% z=0.8
subgraph-mouse-sweep: p95 frame time 17ms 17ms -0%
subgraph-mouse-sweep: layout duration 5ms 4ms -12% z=-0.7
subgraph-mouse-sweep: style recalc duration 42ms 40ms -4% z=-0.7
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 77 77 -1% z=-1.9
subgraph-mouse-sweep: task duration 778ms 767ms -1% z=0.0
subgraph-mouse-sweep: script duration 100ms 98ms -2% z=-0.4
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: heap used 48.0 MB 48.1 MB +0%
subgraph-mouse-sweep: DOM nodes -261 -265 +1% z=-148.3
subgraph-mouse-sweep: event listeners -133 -133 +0% variance too high
subgraph-transition-enter: avg frame time 17ms 17ms +0%
subgraph-transition-enter: p95 frame time 17ms 17ms +0%
subgraph-transition-enter: layout duration 13ms 14ms +3%
subgraph-transition-enter: style recalc duration 31ms 32ms +4%
subgraph-transition-enter: layout count 14 15 +7%
subgraph-transition-enter: style recalc count 18 19 +6%
subgraph-transition-enter: task duration 811ms 852ms +5%
subgraph-transition-enter: script duration 30ms 37ms +24%
subgraph-transition-enter: TBT 135ms 153ms +13%
subgraph-transition-enter: heap used 93.3 MB 69.6 MB -25%
subgraph-transition-enter: DOM nodes 13673 13673 +0%
subgraph-transition-enter: event listeners 2371 2371 +0%
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 38ms 41ms +7%
viewport-pan-sweep: layout count 0 0 +0%
viewport-pan-sweep: style recalc count 249 250 +0%
viewport-pan-sweep: task duration 4009ms 4310ms +8%
viewport-pan-sweep: script duration 1291ms 1390ms +8%
viewport-pan-sweep: TBT 0ms 0ms +0%
viewport-pan-sweep: heap used 71.3 MB 73.6 MB +3%
viewport-pan-sweep: DOM nodes -269 -266 -1%
viewport-pan-sweep: event listeners -115 -113 -2%
vue-large-graph-idle: avg frame time 17ms 17ms +2%
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 13223ms 13450ms +2%
vue-large-graph-idle: script duration 579ms 554ms -4%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: heap used 163.5 MB 154.9 MB -5%
vue-large-graph-idle: DOM nodes -8311 -8311 +0%
vue-large-graph-idle: event listeners -16386 -16385 -0%
vue-large-graph-pan: avg frame time 17ms 17ms +2%
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 14ms 15ms +6%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 83 88 +6%
vue-large-graph-pan: task duration 16019ms 16184ms +1%
vue-large-graph-pan: script duration 876ms 893ms +2%
vue-large-graph-pan: TBT 61ms 36ms -42%
vue-large-graph-pan: heap used 160.9 MB 155.1 MB -4%
vue-large-graph-pan: DOM nodes -8311 -8311 +0%
vue-large-graph-pan: event listeners -16380 -16381 +0%
workflow-execution: avg frame time 17ms 17ms -0% z=0.1
workflow-execution: p95 frame time 17ms 17ms +0%
workflow-execution: layout duration 0ms 1ms +176% z=-4.2
workflow-execution: style recalc duration 19ms 23ms +23% z=-0.6
workflow-execution: layout count 2 3 +25% z=-4.5
workflow-execution: style recalc count 13 15 +12% z=-1.6
workflow-execution: task duration 120ms 124ms +3% z=0.1
workflow-execution: script duration 10ms 10ms +1% z=-6.3
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: heap used 49.3 MB 50.1 MB +2%
workflow-execution: DOM nodes 119 135 +13% z=-3.7
workflow-execution: event listeners 65 67 +3% z=3.5
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-07-19T18:20:38.005Z",
  "gitSha": "228ceefdd64807923cab311c51449f554ded1dfb",
  "branch": "feat/create-camera-info-preview",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2094.2469999999958,
      "styleRecalcs": 6,
      "styleRecalcDurationMs": 6.339999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 506.85,
      "heapDeltaBytes": -21950060,
      "heapUsedBytes": 46966040,
      "domNodes": -265,
      "jsHeapTotalBytes": 19275776,
      "scriptDurationMs": 20.334,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-idle",
      "durationMs": 2087.6380000000268,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 16.118000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 539.38,
      "heapDeltaBytes": -22517248,
      "heapUsedBytes": 46494324,
      "domNodes": -264,
      "jsHeapTotalBytes": 19275776,
      "scriptDurationMs": 26.561,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1814.7789999999873,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 38.777,
      "layouts": 12,
      "layoutDurationMs": 3.19,
      "taskDurationMs": 789.721,
      "heapDeltaBytes": -1282300,
      "heapUsedBytes": 67548836,
      "domNodes": 60,
      "jsHeapTotalBytes": 20983808,
      "scriptDurationMs": 124.906,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1876.2189999999919,
      "styleRecalcs": 74,
      "styleRecalcDurationMs": 44.543,
      "layouts": 12,
      "layoutDurationMs": 3.8850000000000002,
      "taskDurationMs": 902.2110000000001,
      "heapDeltaBytes": -15867976,
      "heapUsedBytes": 53288452,
      "domNodes": -266,
      "jsHeapTotalBytes": 21110784,
      "scriptDurationMs": 133.415,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1741.591000000028,
      "styleRecalcs": 30,
      "styleRecalcDurationMs": 19.576,
      "layouts": 6,
      "layoutDurationMs": 0.7549999999999998,
      "taskDurationMs": 364.80899999999997,
      "heapDeltaBytes": 7807456,
      "heapUsedBytes": 76783148,
      "domNodes": 77,
      "jsHeapTotalBytes": 19410944,
      "scriptDurationMs": 25.755,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1756.5610000000333,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 20.067,
      "layouts": 6,
      "layoutDurationMs": 0.637,
      "taskDurationMs": 378.51599999999996,
      "heapDeltaBytes": 7748904,
      "heapUsedBytes": 76687804,
      "domNodes": 78,
      "jsHeapTotalBytes": 19673088,
      "scriptDurationMs": 26.428,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 582.7369999999803,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 10.088999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 371.53999999999996,
      "heapDeltaBytes": -11688868,
      "heapUsedBytes": 57402928,
      "domNodes": 20,
      "jsHeapTotalBytes": 19935232,
      "scriptDurationMs": 61.402,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 612.2879999999782,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 12.095,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 393.79900000000004,
      "heapDeltaBytes": -11617768,
      "heapUsedBytes": 57407736,
      "domNodes": 20,
      "jsHeapTotalBytes": 19935232,
      "scriptDurationMs": 66.636,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2019.6109999999976,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 9.305000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 577.067,
      "heapDeltaBytes": 4879500,
      "heapUsedBytes": 64620048,
      "domNodes": -267,
      "jsHeapTotalBytes": 4665344,
      "scriptDurationMs": 103.17699999999999,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2026.9539999999893,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.667,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 710.0840000000002,
      "heapDeltaBytes": 5107140,
      "heapUsedBytes": 64643880,
      "domNodes": -265,
      "jsHeapTotalBytes": 4665344,
      "scriptDurationMs": 116.33500000000001,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2132.738999999958,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 15.363999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1162.3600000000001,
      "heapDeltaBytes": -4016124,
      "heapUsedBytes": 56413472,
      "domNodes": -270,
      "jsHeapTotalBytes": 5451776,
      "scriptDurationMs": 401.05199999999996,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2196.9119999999975,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 18.077999999999996,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1242.575,
      "heapDeltaBytes": -4763876,
      "heapUsedBytes": 56038140,
      "domNodes": -269,
      "jsHeapTotalBytes": 5451776,
      "scriptDurationMs": 425.145,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3182.0319999999924,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 16.845000000000002,
      "layouts": 60,
      "layoutDurationMs": 7.797,
      "taskDurationMs": 1406.113,
      "heapDeltaBytes": -1799492,
      "heapUsedBytes": 60009208,
      "domNodes": -273,
      "jsHeapTotalBytes": 8073216,
      "scriptDurationMs": 507.629,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3297.582000000034,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 17.318,
      "layouts": 60,
      "layoutDurationMs": 8.008,
      "taskDurationMs": 1494.52,
      "heapDeltaBytes": 2399608,
      "heapUsedBytes": 64712876,
      "domNodes": -274,
      "jsHeapTotalBytes": 7811072,
      "scriptDurationMs": 531.626,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2008.813000000032,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 8.495,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 660.5989999999999,
      "heapDeltaBytes": 4755776,
      "heapUsedBytes": 66127376,
      "domNodes": -267,
      "jsHeapTotalBytes": 5451776,
      "scriptDurationMs": 119.149,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2030.3299999999354,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 8.528999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 672.811,
      "heapDeltaBytes": 5548084,
      "heapUsedBytes": 67078828,
      "domNodes": -269,
      "jsHeapTotalBytes": 5451776,
      "scriptDurationMs": 112.02900000000001,
      "eventListeners": -129,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 609.4679999999926,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 12.862,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 395.32800000000003,
      "heapDeltaBytes": -10757044,
      "heapUsedBytes": 58264720,
      "domNodes": 22,
      "jsHeapTotalBytes": 20983808,
      "scriptDurationMs": 126.684,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 618.0650000000014,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 15.581,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 417.00200000000007,
      "heapDeltaBytes": -11160564,
      "heapUsedBytes": 57959152,
      "domNodes": 20,
      "jsHeapTotalBytes": 20983808,
      "scriptDurationMs": 132.48299999999998,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2039.1500000000065,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.927,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 499.895,
      "heapDeltaBytes": -18099252,
      "heapUsedBytes": 51025164,
      "domNodes": -266,
      "jsHeapTotalBytes": 19275776,
      "scriptDurationMs": 23.016000000000002,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2018.0030000000215,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.802999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 428.52899999999994,
      "heapDeltaBytes": 4038984,
      "heapUsedBytes": 72987776,
      "domNodes": 22,
      "jsHeapTotalBytes": 19673088,
      "scriptDurationMs": 20.058,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1689.799999999991,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 37.278,
      "layouts": 16,
      "layoutDurationMs": 4.468,
      "taskDurationMs": 745.13,
      "heapDeltaBytes": -19014096,
      "heapUsedBytes": 50115568,
      "domNodes": -265,
      "jsHeapTotalBytes": 19537920,
      "scriptDurationMs": 96.82,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1740.0569999999789,
      "styleRecalcs": 78,
      "styleRecalcDurationMs": 42.903000000000006,
      "layouts": 16,
      "layoutDurationMs": 4.493,
      "taskDurationMs": 789.632,
      "heapDeltaBytes": -18380656,
      "heapUsedBytes": 50764960,
      "domNodes": -264,
      "jsHeapTotalBytes": 20586496,
      "scriptDurationMs": 99.929,
      "eventListeners": -133,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 1199.2819999999824,
      "styleRecalcs": 19,
      "styleRecalcDurationMs": 31.686000000000007,
      "layouts": 15,
      "layoutDurationMs": 13.661999999999997,
      "taskDurationMs": 852.2030000000001,
      "heapDeltaBytes": 44060,
      "heapUsedBytes": 72940996,
      "domNodes": 13673,
      "jsHeapTotalBytes": 15204352,
      "scriptDurationMs": 37.242999999999995,
      "eventListeners": 2371,
      "totalBlockingTimeMs": 153,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8159.999999999968,
      "styleRecalcs": 250,
      "styleRecalcDurationMs": 41.751999999999995,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4139.603,
      "heapDeltaBytes": 16502872,
      "heapUsedBytes": 77934072,
      "domNodes": -264,
      "jsHeapTotalBytes": 7491584,
      "scriptDurationMs": 1307.4699999999998,
      "eventListeners": -113,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8178.574000000026,
      "styleRecalcs": 250,
      "styleRecalcDurationMs": 40.701,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4479.986999999999,
      "heapDeltaBytes": 17018576,
      "heapUsedBytes": 76358000,
      "domNodes": -267,
      "jsHeapTotalBytes": 8015872,
      "scriptDurationMs": 1471.9119999999998,
      "eventListeners": -113,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 13442.434999999989,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 13425.333999999999,
      "heapDeltaBytes": -71531148,
      "heapUsedBytes": 154291372,
      "domNodes": -8311,
      "jsHeapTotalBytes": -12783616,
      "scriptDurationMs": 555.2550000000001,
      "eventListeners": -16382,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.219999999999953,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 13490.035000000034,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 13474.231,
      "heapDeltaBytes": -36911036,
      "heapUsedBytes": 170603284,
      "domNodes": -8311,
      "jsHeapTotalBytes": -9895936,
      "scriptDurationMs": 553.356,
      "eventListeners": -16388,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 15955.720999999983,
      "styleRecalcs": 83,
      "styleRecalcDurationMs": 14.085999999999988,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 15930.654,
      "heapDeltaBytes": -41911804,
      "heapUsedBytes": 167806000,
      "domNodes": -8311,
      "jsHeapTotalBytes": -12259328,
      "scriptDurationMs": 852.0840000000001,
      "eventListeners": -16382,
      "totalBlockingTimeMs": 21,
      "frameDurationMs": 17.220000000000073,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 16461.150999999973,
      "styleRecalcs": 93,
      "styleRecalcDurationMs": 15.274999999999983,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16437.497,
      "heapDeltaBytes": -53295824,
      "heapUsedBytes": 157373368,
      "domNodes": -8311,
      "jsHeapTotalBytes": -13832192,
      "scriptDurationMs": 933.5749999999999,
      "eventListeners": -16380,
      "totalBlockingTimeMs": 50,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "workflow-execution",
      "durationMs": 467.2640000000001,
      "styleRecalcs": 17,
      "styleRecalcDurationMs": 23.378999999999998,
      "layouts": 3,
      "layoutDurationMs": 1.392,
      "taskDurationMs": 128.34699999999998,
      "heapDeltaBytes": -15743664,
      "heapUsedBytes": 52502724,
      "domNodes": 154,
      "jsHeapTotalBytes": 6565888,
      "scriptDurationMs": 11.202,
      "eventListeners": 67,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "workflow-execution",
      "durationMs": 484.5189999999775,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 22.272999999999996,
      "layouts": 2,
      "layoutDurationMs": 0.47400000000000003,
      "taskDurationMs": 120.49100000000001,
      "heapDeltaBytes": -15860172,
      "heapUsedBytes": 52523128,
      "domNodes": 115,
      "jsHeapTotalBytes": 6041600,
      "scriptDurationMs": 9.345,
      "eventListeners": 67,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    }
  ]
}

@github-actions

Copy link
Copy Markdown

🎨 Storybook: 🚧 Building...

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The pull request adds a camera-info widget with interactive Three.js overlays, orbit and transform gizmos, look-through editing, widget synchronization, viewport render callbacks, and custom camera-up controls.

Changes

Camera info and camera controls

Layer / File(s) Summary
Camera state and transformation math
src/extensions/core/cameraInfo/types.ts, widgetBridge.ts, cameraTransform.ts, lookThroughDragMath.ts, handles/*Math.ts
Adds camera state contracts, validated widget serialization, subject transforms, orbit and roll calculations, look-through rotation/dolly behavior, and focused unit tests.
Three.js overlay and interactive viewport
src/extensions/core/cameraInfo/CameraInfoOverlay.ts, CameraInfoViewport.ts, handles/*
Adds subject-camera previews, orbit/roll/target/camera gizmos, pointer picking, drag interactions, look-through mode, and lifecycle cleanup.
Viewport rendering infrastructure
src/extensions/core/load3d/Viewport3d.ts, createViewport3d.ts, related tests
Adds pre-render and post-render callback registries, viewport construction helpers, callback dispatch tests, and custom-up delegation.
Vue widget integration
src/components/cameraInfo/*, src/composables/useCameraInfo.ts, src/extensions/core/cameraInfo.ts, src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts, src/extensions/core/load3dLazy.ts
Registers the camera-info widget, connects Vue controls to viewport state, synchronizes LiteGraph widgets, and manages initialization, cleanup, localization, and lazy loading.
Custom camera-up controls
src/extensions/core/load3d/CameraManager.ts, src/components/load3d/controls/CameraControls.vue, src/components/load3d/Load3DControls.vue, src/composables/useLoad3d.ts, src/extensions/core/load3d/interfaces.ts
Adds custom-up state handling, manager and configuration APIs, UI toggles, event propagation, and load3d synchronization.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CameraInfoVue
  participant useCameraInfo
  participant CameraInfoViewport
  participant CameraInfoOverlay
  participant LiteGraphWidgets

  CameraInfoVue->>useCameraInfo: initialize(container)
  useCameraInfo->>LiteGraphWidgets: read camera state
  useCameraInfo->>CameraInfoViewport: create viewport
  CameraInfoViewport->>CameraInfoOverlay: apply subject camera state
  CameraInfoVue->>useCameraInfo: setLookThrough / setGizmosVisible
  useCameraInfo->>CameraInfoViewport: update interaction state
  CameraInfoViewport->>useCameraInfo: emit field updates
  useCameraInfo->>LiteGraphWidgets: write changed values
Loading

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
End-To-End Regression Coverage For Fixes ❓ Inconclusive PR file list is present, but the PR title/commit subjects are missing, so the required bug-fix-language check can’t be verified. Please provide the PR title and commit subjects (or confirm none use fix/fixes/bugfix/hotfix language) so the regression-test rule can be assessed.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: a 3D viewport widget for CreateCameraInfo.
Description check ✅ Passed The description covers the main feature, behavior, and shared Load3D changes, though it doesn't follow the template's section structure exactly.
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.
Adr Compliance For Entity/Litegraph Changes ✅ Passed No changed files are under src/lib/litegraph/, src/ecs/, or core graph-entity paths; the PR only adds camera-info/load3d UI and extension code, so ADR check is not applicable.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/create-camera-info-preview

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

❤️ Share

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

@jtydhr88

Copy link
Copy Markdown
Collaborator Author

Note: there's a known bug where the node height resets to its default value after a refresh. This comes from dynamicInput, isn't specific to this PR, and try to fix in another PR #13456 .

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 14

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/cameraInfo/CameraInfo.vue`:
- Around line 165-190: Update the mode-change handling near
transformGizmoOptions so transformGizmoMode is reset to the supported fallback
when the current selection becomes disabled, such as switching from
quaternion/camera-rotate to orbit. Reconcile the selection against the computed
enabled options and preserve valid selections; ensure the viewport receives the
fallback rather than retaining an invalid gizmo mode.

In `@src/composables/useCameraInfo.ts`:
- Around line 69-72: Update the initialization error handling in useCameraInfo
to obtain the user-facing alert text through vue-i18n instead of hardcoding it.
Add the corresponding translation entry to src/locales/en/main.json and pass the
translated message to useToastStore().addAlert, while leaving the console error
unchanged.
- Around line 53-79: Update cleanup and initialization around wrappedSet,
unwireWidgets, and wireNodeMouseStatus to fully unwind prior hooks before
reinitializing. Remove each unwired widget from wrappedSet, retain the original
node mouse handlers so cleanup restores them, and clean any existing or
partially initialized viewport before creating a new one in initialize.

In `@src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts`:
- Around line 90-109: Update the “camera type swap” test to assert that the
rebuilt CameraHelper references the newly active camera returned by
getSubjectCamera(), rather than only checking that its object identity differs
from the initial helper. Keep the behavior-focused assertions and avoid
change-detector or implementation-identity checks.

In `@src/extensions/core/cameraInfo/CameraInfoViewport.ts`:
- Around line 3-31: Separate type-only imports in CameraInfoViewport.ts: import
Viewport3d, CameraHandle, OrbitHandles, and other runtime values normally, while
importing Load3DOptions, CameraHandleMode, CameraHandleTransform,
OrbitHandleType, and camera-info types through dedicated import type
declarations. In CameraHandle.test.ts, keep the CameraHandle value import
separate from dedicated type imports for CameraHandleMode and
CameraHandleTransform.
- Around line 481-484: Update CameraInfoViewport.onPostRender so it never
synchronously calls viewport.forceRender from the post-render hook. Move
fitSubjectAspect synchronization before the main render, or defer the required
follow-up render until the current frame completes, while preserving
aspect-change handling and avoiding re-entry into Viewport3d.renderView.

In `@src/extensions/core/cameraInfo/cameraTransform.test.ts`:
- Around line 129-143: The roll test should verify the actual rotation behavior
rather than only checking that the quaternion changed. Update the “rotates
around the view axis without moving the camera position” test to assert that the
forward direction remains unchanged and that the up direction rotates by exactly
45 degrees around that forward axis, using deterministic vector comparisons.

In `@src/extensions/core/cameraInfo/handles/handlePicking.test.ts`:
- Around line 67-78: Adjust the test for pickHandleAtPointer so the ray does not
intersect either handle directly, forcing the screen-space fallback
nearest-candidate branch while keeping both handles within tolerance. Preserve
the assertion that the closer projected handle, “yaw”, is selected.

In `@src/extensions/core/cameraInfo/handles/rollDragMath.ts`:
- Around line 15-24: Update the roll orientation setup around the backward
vector normalization to detect coincident cameraPos and target positions and
choose a stable nonzero fallback direction before computing right and up.
Preserve the existing orientation behavior for distinct positions, and add a
regression test covering coincident inputs to verify the roll drag math remains
nondegenerate.

In `@src/extensions/core/cameraInfo/lookThroughDragMath.ts`:
- Around line 199-204: Update the look-at dolly calculation in the visible
drag-math flow to clamp nextDistance to both MIN_DISTANCE and the shared
maximum-distance constant used by orbit dolly and handle dragging. Preserve the
existing exponential factor and ensure the clamped distance is used when
computing the next position.
- Around line 131-139: Update the quaternion rotation logic near yawQ and pitchQ
so yaw uses the camera’s quaternion-derived local up axis rather than global Y,
and apply both rotations in local space instead of premultiplying yawQ. Preserve
normalization and add a regression covering horizontal movement from a rolled
starting quaternion.

In `@src/extensions/core/cameraInfo/widgetBridge.ts`:
- Around line 34-36: Update num in widgetBridge.ts to apply camera-specific
domain bounds when reading zoom, FOV, distance, and pitch, matching the limits
enforced by the drag controls. Keep finite numeric values only when they fall
within each field’s supported range; otherwise return the provided fallback
before values reach Three.js calculations.
- Around line 1-6: Separate type-only imports from value imports across the
camera info files: update src/extensions/core/cameraInfo/widgetBridge.ts lines
1-6 and src/extensions/core/cameraInfo/CameraInfoOverlay.ts lines 5-10 to use
dedicated import type statements, and update
src/extensions/core/cameraInfo/cameraTransform.test.ts lines 3-4,
src/extensions/core/cameraInfo/lookThroughDragMath.test.ts lines 4-5,
src/extensions/core/cameraInfo/widgetBridge.test.ts lines 3-8, and
src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts lines 4-5 so
CameraInfoState or NodeWithWidgets is imported separately from value symbols.

In `@src/extensions/core/load3d/Viewport3d.ts`:
- Around line 198-210: Update runPostRenderCallbacks to iterate over a stable
snapshot of postRenderCallbacks rather than the live array, so disposals during
dispatch do not skip callbacks and newly registered callbacks wait until the
next render.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 6c3d8342-c4a2-4b59-a099-69711fb2fb37

📥 Commits

Reviewing files that changed from the base of the PR and between 99674df and e893bf0.

📒 Files selected for processing (42)
  • src/components/cameraInfo/CameraInfo.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/CameraControls.vue
  • src/composables/useCameraInfo.test.ts
  • src/composables/useCameraInfo.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/cameraInfo.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.test.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.ts
  • src/extensions/core/cameraInfo/cameraTransform.test.ts
  • src/extensions/core/cameraInfo/cameraTransform.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.test.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.test.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.test.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.ts
  • src/extensions/core/cameraInfo/handles/TargetHandle.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.test.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.ts
  • src/extensions/core/cameraInfo/handles/types.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.test.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.ts
  • src/extensions/core/cameraInfo/types.ts
  • src/extensions/core/cameraInfo/widgetBridge.test.ts
  • src/extensions/core/cameraInfo/widgetBridge.ts
  • src/extensions/core/load3d/CameraManager.ts
  • src/extensions/core/load3d/Load3d.test.ts
  • src/extensions/core/load3d/Viewport3d.test.ts
  • src/extensions/core/load3d/Viewport3d.ts
  • src/extensions/core/load3d/createViewport3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/nodeTypes.ts
  • src/extensions/core/load3dLazy.ts
  • src/locales/en/main.json
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
💤 Files with no reviewable changes (1)
  • src/extensions/core/load3d/Viewport3d.test.ts

Comment thread src/components/cameraInfo/CameraInfo.vue
Comment thread src/composables/useCameraInfo.ts
Comment thread src/composables/useCameraInfo.ts
Comment thread src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts
Comment thread src/extensions/core/cameraInfo/CameraInfoViewport.ts Outdated
Comment thread src/extensions/core/cameraInfo/lookThroughDragMath.ts
Comment thread src/extensions/core/cameraInfo/lookThroughDragMath.ts
Comment thread src/extensions/core/cameraInfo/widgetBridge.ts Outdated
Comment thread src/extensions/core/cameraInfo/widgetBridge.ts
Comment thread src/extensions/core/load3d/Viewport3d.ts
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.12168% with 135 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...c/extensions/core/cameraInfo/CameraInfoViewport.ts 81.93% 71 Missing ⚠️
src/extensions/core/load3d/createViewport3d.ts 0.00% 17 Missing ⚠️
src/extensions/core/cameraInfo.ts 7.69% 12 Missing ⚠️
...extensions/core/cameraInfo/handles/OrbitHandles.ts 92.52% 8 Missing ⚠️
src/components/load3d/controls/CameraControls.vue 25.00% 6 Missing ⚠️
src/components/cameraInfo/CameraInfo.vue 92.95% 5 Missing ⚠️
...c/extensions/core/cameraInfo/handles/RollHandle.ts 92.30% 5 Missing ⚠️
src/composables/useLoad3d.ts 20.00% 4 Missing ⚠️
src/composables/useCameraInfo.ts 96.05% 3 Missing ⚠️
...rc/extensions/core/cameraInfo/CameraInfoOverlay.ts 98.05% 2 Missing ⚠️
... and 2 more
@@            Coverage Diff             @@
##             main   #13741      +/-   ##
==========================================
+ Coverage   78.78%   78.94%   +0.16%     
==========================================
  Files        1683     1700      +17     
  Lines       95054    96251    +1197     
  Branches    34410    34673     +263     
==========================================
+ Hits        74885    75983    +1098     
- Misses      19778    19882     +104     
+ Partials      391      386       -5     
Flag Coverage Δ
unit 67.68% <89.12%> (+0.36%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/components/load3d/Load3DControls.vue 95.14% <100.00%> (+0.09%) ⬆️
src/extensions/core/cameraInfo/cameraTransform.ts 100.00% <100.00%> (ø)
...extensions/core/cameraInfo/handles/CameraHandle.ts 100.00% <100.00%> (ø)
...extensions/core/cameraInfo/handles/TargetHandle.ts 100.00% <100.00%> (ø)
...xtensions/core/cameraInfo/handles/handlePicking.ts 100.00% <100.00%> (ø)
...xtensions/core/cameraInfo/handles/orbitDragMath.ts 100.00% <100.00%> (ø)
...extensions/core/cameraInfo/handles/rollDragMath.ts 100.00% <100.00%> (ø)
.../extensions/core/cameraInfo/lookThroughDragMath.ts 100.00% <100.00%> (ø)
src/extensions/core/cameraInfo/types.ts 100.00% <100.00%> (ø)
src/extensions/core/cameraInfo/widgetBridge.ts 100.00% <100.00%> (ø)
... and 15 more

... and 14 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.

@jtydhr88
jtydhr88 force-pushed the feat/create-camera-info-preview branch from e893bf0 to 32b51dc Compare July 18, 2026 03:33

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 8

♻️ Duplicate comments (1)
src/composables/useCameraInfo.ts (1)

59-63: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Clean up before returning for a missing node.

If nodeRef becomes null after initialization, Line 61 returns before removing the existing viewport or restoring the old node’s callbacks. Move cleanup before the guard and add a null-transition regression test.

Proposed fix
 const initialize = (container: HTMLElement): void => {
+  if (viewport) cleanup()
   const raw = toRaw(node.value)
   if (!raw || !container) return
-  if (viewport) cleanup()

As per coding guidelines, implement proper cleanup and error handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/composables/useCameraInfo.ts` around lines 59 - 63, Update initialize in
useCameraInfo so cleanup runs before the missing-node guard, ensuring an
existing viewport and the previous node’s callbacks are released when nodeRef
becomes null. Preserve the early return after cleanup, and add a regression test
covering the transition from an initialized node to a null node.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/load3d/controls/CameraControls.vue`:
- Around line 32-41: Update the icon class composition in the CameraControls
template to replace the font-size utility text-lg with the appropriate size-*
utility, while preserving the existing icon selection and text-base-foreground
classes.

In `@src/extensions/core/cameraInfo/CameraInfoViewport.test.ts`:
- Around line 343-362: Extend the camera handle change tests around
CameraInfoViewport to cover quaternion rotation mode: select the camera-rotate
mode, dispatch objectChange on controlsOf(viewport.cameraHandle), and verify
onHandleDrag is called for mode.quat_x, mode.quat_y, mode.quat_z, and
mode.quat_w with numeric values. Preserve the existing translation-mode
coverage.

In `@src/extensions/core/cameraInfo/CameraInfoViewport.ts`:
- Around line 175-185: Update setLookThrough in CameraInfoViewport to save the
controls’ prior enabled state and disable standard controls when look-through is
activated. Restore that saved state when look-through is deactivated, ensuring
custom mouse-look input cannot simultaneously move the existing orbit controls.
- Around line 214-226: Update the handle visibility logic around wantTarget and
wantCamera so both targetHandle and cameraHandle are also gated by gizmosOn.
Preserve the existing transform mode applicability checks, but ensure selecting
“Hide gizmos” makes these handles invisible.

In `@src/extensions/core/cameraInfo/handles/CameraHandle.test.ts`:
- Around line 46-72: Extend the CameraHandle tests to cover emitted drag
behavior by dispatching objectChange and dragging-changed events on the handle’s
controlled object. Assert that the resulting callback emits the updated
transform, current mode, and dragging boolean, while preserving the existing
state and proxy tests.

In `@src/extensions/core/load3d/CameraManager.ts`:
- Around line 177-179: Update the toggleCamera method to copy the current
camera’s up vector to the newly created camera alongside position and rotation,
preserving custom-up orientation and allowing subsequent setUseCustomUp behavior
to remain consistent.

In `@src/extensions/core/load3d/Viewport3d.test.ts`:
- Around line 480-518: Extend the “render callback dispatch” tests to exercise
the real render pipeline through renderView(), recording events from a
pre-render callback, the main scene-render step, and a post-render callback.
Assert the ordering is exactly pre → main scene → post, without directly
invoking runPreRenderCallbacks or runPostRenderCallbacks and without adding
change-detector coverage.

In `@src/locales/en/main.json`:
- Line 2270: Update the failedToInitializeCameraInfoViewer locale message to
include the recovery step “Try refreshing the node,” while preserving the
existing initialization failure context.

---

Duplicate comments:
In `@src/composables/useCameraInfo.ts`:
- Around line 59-63: Update initialize in useCameraInfo so cleanup runs before
the missing-node guard, ensuring an existing viewport and the previous node’s
callbacks are released when nodeRef becomes null. Preserve the early return
after cleanup, and add a regression test covering the transition from an
initialized node to a null node.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 8c572bbe-8769-4a93-a4a1-0e077d6f9930

📥 Commits

Reviewing files that changed from the base of the PR and between e893bf0 and 32b51dc.

📒 Files selected for processing (45)
  • src/components/cameraInfo/CameraInfo.test.ts
  • src/components/cameraInfo/CameraInfo.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/CameraControls.vue
  • src/composables/useCameraInfo.test.ts
  • src/composables/useCameraInfo.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/cameraInfo.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.test.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.ts
  • src/extensions/core/cameraInfo/cameraTransform.test.ts
  • src/extensions/core/cameraInfo/cameraTransform.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.test.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.test.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.test.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.ts
  • src/extensions/core/cameraInfo/handles/TargetHandle.test.ts
  • src/extensions/core/cameraInfo/handles/TargetHandle.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.test.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.ts
  • src/extensions/core/cameraInfo/handles/types.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.test.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.ts
  • src/extensions/core/cameraInfo/types.ts
  • src/extensions/core/cameraInfo/widgetBridge.test.ts
  • src/extensions/core/cameraInfo/widgetBridge.ts
  • src/extensions/core/load3d/CameraManager.test.ts
  • src/extensions/core/load3d/CameraManager.ts
  • src/extensions/core/load3d/Load3d.test.ts
  • src/extensions/core/load3d/Viewport3d.test.ts
  • src/extensions/core/load3d/Viewport3d.ts
  • src/extensions/core/load3d/createViewport3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/nodeTypes.ts
  • src/extensions/core/load3dLazy.ts
  • src/locales/en/main.json
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts

Comment thread src/components/load3d/controls/CameraControls.vue
Comment thread src/extensions/core/cameraInfo/CameraInfoViewport.ts
Comment thread src/extensions/core/cameraInfo/CameraInfoViewport.ts
Comment thread src/extensions/core/cameraInfo/handles/CameraHandle.test.ts
Comment thread src/extensions/core/load3d/CameraManager.ts
Comment thread src/extensions/core/load3d/Viewport3d.test.ts
Comment thread src/locales/en/main.json Outdated
@jtydhr88
jtydhr88 force-pushed the feat/create-camera-info-preview branch from 32b51dc to 2162a93 Compare July 18, 2026 12:58

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/composables/useCameraInfo.test.ts`:
- Around line 140-145: Remove the “ignores toolbar actions before
initialization” test from the useCameraInfo test suite, including its
optional-chaining no-throw assertion; retain tests that verify observable camera
behavior instead.

In `@src/extensions/core/cameraInfo/CameraInfoOverlay.ts`:
- Around line 110-119: Update CameraInfoOverlay.dispose() to detach
referenceGroup, subjectPerspective, and subjectOrthographic from the scene
before disposing their resources. Preserve the existing idempotency guard and
cleanup behavior, ensuring a reused scene cannot traverse these disposed
objects.

In `@src/extensions/core/cameraInfo/CameraInfoViewport.test.ts`:
- Around line 351-370: Update the camera-handle change tests around
CameraInfoViewport to assert the complete callback contract: verify position_x,
position_y, and position_z, plus target_x, target_y, and target_z, with numeric
values for each emitted coordinate. Cover both the position and target callback
cases so missing y/z widget writes fail the tests.

In `@src/extensions/core/cameraInfo/CameraInfoViewport.ts`:
- Around line 307-310: Update CameraInfoViewport.updatePointer to convert
pointer coordinates through viewport.clientPointToNdc() instead of calculating
NDC from the full canvas bounds. When fitting the subject/look-through camera,
use Viewport3d’s effective letterboxed render-viewport aspect rather than the
canvas aspect, ensuring both picking and projection calculations match the
rendered sub-viewport.

In `@src/extensions/core/cameraInfo/cameraTransform.test.ts`:
- Around line 70-79: Extend the “uses explicit position” test around
computeSubjectTransform to verify orientation as well as position. Use a
non-axis-aligned lookAt target, transform the camera’s local -Z direction by the
returned quaternion, and assert the resulting direction numerically points
toward the target while preserving the existing position assertion.

In `@src/extensions/core/cameraInfo/handles/OrbitHandles.test.ts`:
- Around line 33-63: Add a nonzero-pitch test case for OrbitHandles.update() and
validate the world-space positions of both the pitch and distance handles
against the expected camera direction, including deterministic edge/permutation
coverage for the pitch branch. Keep assertions focused on resulting geometry
rather than implementation details.
- Around line 84-89: Remove the child mesh visibility assertion from the test
“isVisible reflects the root group, not individual handle meshes” in
OrbitHandles.test.ts. Keep the handles.update setup and the handles.isVisible()
expectation, which validates the observable OrbitHandles behavior without
asserting Three.js child-visibility internals.

In `@src/extensions/core/cameraInfo/handles/rollDragMath.test.ts`:
- Around line 18-21: Expand the rollBasis tests in rollDragMath.test.ts to fully
validate the straight-down fallback by asserting its complete orthonormal basis,
not just right.x. Add a corresponding straight-up camera case and assert the
complete expected basis there as well, covering both vertical-pole fallback
directions and their numerical geometry.

In `@src/extensions/core/cameraInfo/handles/RollHandle.test.ts`:
- Around line 54-77: Add a deterministic test in the roll-handle positioning
suite that sets roll to 90 degrees and verifies the pickable handle’s world
position relative to the target and camera basis. Exercise the handle-position
calculation used by the roll handle, such as through the existing handle.update
flow, and assert the resulting coordinates numerically rather than only checking
the default target placement.

In `@src/extensions/core/cameraInfo/lookThroughDragMath.test.ts`:
- Around line 82-105: Strengthen the quaternion coverage in the
rotateSubjectByDrag tests around rotateSubjectByDrag by adding deterministic
yaw-only and pitch-only cases that assert the resulting transformed forward and
up vectors. Include a rolled starting quaternion to verify yaw rotates around
world Y, and cover the relevant axis/sign permutations so incorrect axis
selection or multiplication order fails while preserving the existing
unit-quaternion assertions.

In `@src/extensions/core/cameraInfo/widgetBridge.test.ts`:
- Around line 20-43: Update the test for readStateFromWidgets to set camera_type
to 'orthographic' and assert that state.cameraType is 'orthographic'. Keep the
existing widget values and assertions unchanged so the test exercises the
non-default camera-type behavior.

In `@src/extensions/core/load3d/Viewport3d.ts`:
- Around line 202-216: Update addPreRenderCallback and addPostRenderCallback to
store a unique registration wrapper rather than the raw callback, and have each
returned disposer remove only that wrapper. Ensure repeated disposer calls are
idempotent and do not remove another registration of the same callback.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 8a1cbf2f-7497-4b51-8bdd-39aa8182c66f

📥 Commits

Reviewing files that changed from the base of the PR and between 32b51dc and 2162a93.

📒 Files selected for processing (45)
  • src/components/cameraInfo/CameraInfo.test.ts
  • src/components/cameraInfo/CameraInfo.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/CameraControls.vue
  • src/composables/useCameraInfo.test.ts
  • src/composables/useCameraInfo.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/cameraInfo.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.test.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.ts
  • src/extensions/core/cameraInfo/cameraTransform.test.ts
  • src/extensions/core/cameraInfo/cameraTransform.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.test.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.test.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.test.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.ts
  • src/extensions/core/cameraInfo/handles/TargetHandle.test.ts
  • src/extensions/core/cameraInfo/handles/TargetHandle.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.test.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.ts
  • src/extensions/core/cameraInfo/handles/types.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.test.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.ts
  • src/extensions/core/cameraInfo/types.ts
  • src/extensions/core/cameraInfo/widgetBridge.test.ts
  • src/extensions/core/cameraInfo/widgetBridge.ts
  • src/extensions/core/load3d/CameraManager.test.ts
  • src/extensions/core/load3d/CameraManager.ts
  • src/extensions/core/load3d/Load3d.test.ts
  • src/extensions/core/load3d/Viewport3d.test.ts
  • src/extensions/core/load3d/Viewport3d.ts
  • src/extensions/core/load3d/createViewport3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/nodeTypes.ts
  • src/extensions/core/load3dLazy.ts
  • src/locales/en/main.json
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts

Comment thread src/composables/useCameraInfo.test.ts Outdated
Comment thread src/extensions/core/cameraInfo/CameraInfoOverlay.ts
Comment thread src/extensions/core/cameraInfo/CameraInfoViewport.test.ts
Comment thread src/extensions/core/cameraInfo/CameraInfoViewport.ts
Comment thread src/extensions/core/cameraInfo/cameraTransform.test.ts
Comment thread src/extensions/core/cameraInfo/handles/rollDragMath.test.ts Outdated
Comment thread src/extensions/core/cameraInfo/handles/RollHandle.test.ts
Comment thread src/extensions/core/cameraInfo/lookThroughDragMath.test.ts
Comment thread src/extensions/core/cameraInfo/widgetBridge.test.ts
Comment thread src/extensions/core/load3d/Viewport3d.ts
@jtydhr88
jtydhr88 force-pushed the feat/create-camera-info-preview branch from 2162a93 to f64c8fb Compare July 19, 2026 11:53

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/extensions/core/load3d/Viewport3d.test.ts (1)

442-517: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider exporting LetterboxDimmer for test reuse instead of re-declaring its shape.

DimmerInternals.letterboxDimmer duplicates the private LetterboxDimmer type from Viewport3d.ts. Exporting it would let the test import the real type and avoid type drift if the shape changes.

♻️ Proposed fix
-type LetterboxDimmer = {
+export type LetterboxDimmer = {
   scene: THREE.Scene
   camera: THREE.OrthographicCamera
   geometry: THREE.PlaneGeometry
   material: THREE.MeshBasicMaterial
 }

Then in the test: import type { LetterboxDimmer } from '@/extensions/core/load3d/Viewport3d' and use it directly in DimmerInternals.

Based on learnings: "avoid duplicating interface/type definitions... Import real type definitions from the component modules under test."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/extensions/core/load3d/Viewport3d.test.ts` around lines 442 - 517, Export
the existing LetterboxDimmer type from Viewport3d.ts, then update the test to
import it and use it for DimmerInternals.letterboxDimmer instead of redeclaring
the scene, camera, geometry, and material shape; leave the other test-only
internals unchanged.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/composables/useCameraInfo.ts`:
- Around line 59-80: Update the catch block in initialize so any
CameraInfoViewport constructed before wireWidgetsToOverlay or
wireNodeMouseStatus fails is disposed via cleanup(). Preserve the existing error
logging and toast behavior while ensuring partial initialization cannot leave
viewport resources active.

In `@src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts`:
- Around line 106-121: Update the camera-type swap test around applyState and
the newHelper lookup to assert that the scene contains exactly one
THREE.CameraHelper after switching to orthographic mode, while preserving the
existing active-camera and helper-target assertions.

In `@src/extensions/core/cameraInfo/handles/CameraHandle.test.ts`:
- Around line 53-55: Update the tests for CameraHandle visibility, including the
cases around “starts hidden with controls disabled” and “controls.enabled
together,” to assert controls().enabled alongside isVisible(). Use the existing
controls() helper so both visibility and control-enabled state are verified.

In `@src/extensions/core/cameraInfo/handles/OrbitHandles.test.ts`:
- Around line 33-45: Strengthen the yaw-handle assertion in the test around
handles.update by verifying yawHandle.position.z is close to the expected
RING_RADIUS at yaw=0, while retaining the x-position assertion. Replace the
sign-only z check with a magnitude check that directly validates the ring
placement.

In `@src/extensions/core/cameraInfo/handles/OrbitHandles.ts`:
- Around line 35-61: Extract the shared handle/glow mesh builders and constants
into a common handle-visuals module. Update
src/extensions/core/cameraInfo/handles/OrbitHandles.ts lines 35-61 to import and
use the shared makeHandle, makeGlow, BASE_GLOW_OPACITY, HOVER_GLOW_OPACITY,
HOVER_SCALE, HANDLE_RADIUS, and GLOW_RADIUS symbols; apply the same replacement
to src/extensions/core/cameraInfo/handles/RollHandle.ts lines 19-32, removing
its duplicated builders and glow construction.

In `@src/extensions/core/cameraInfo/handles/rollDragMath.test.ts`:
- Around line 18-35: Strengthen both vertical-pole fallback tests for rollBasis
by asserting the exact deterministic up vector: straight-down must produce (0,
0, -1), and straight-up must produce (0, 0, 1). Add the missing right.x
assertion to the straight-up test while preserving the existing length and
orthogonality checks.

---

Outside diff comments:
In `@src/extensions/core/load3d/Viewport3d.test.ts`:
- Around line 442-517: Export the existing LetterboxDimmer type from
Viewport3d.ts, then update the test to import it and use it for
DimmerInternals.letterboxDimmer instead of redeclaring the scene, camera,
geometry, and material shape; leave the other test-only internals unchanged.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: a73a55da-cb4c-4c3d-b0a9-de10b1ef9b70

📥 Commits

Reviewing files that changed from the base of the PR and between 2162a93 and f64c8fb.

📒 Files selected for processing (45)
  • src/components/cameraInfo/CameraInfo.test.ts
  • src/components/cameraInfo/CameraInfo.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/CameraControls.vue
  • src/composables/useCameraInfo.test.ts
  • src/composables/useCameraInfo.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/cameraInfo.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.test.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.ts
  • src/extensions/core/cameraInfo/cameraTransform.test.ts
  • src/extensions/core/cameraInfo/cameraTransform.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.test.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.test.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.test.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.ts
  • src/extensions/core/cameraInfo/handles/TargetHandle.test.ts
  • src/extensions/core/cameraInfo/handles/TargetHandle.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.test.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.ts
  • src/extensions/core/cameraInfo/handles/types.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.test.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.ts
  • src/extensions/core/cameraInfo/types.ts
  • src/extensions/core/cameraInfo/widgetBridge.test.ts
  • src/extensions/core/cameraInfo/widgetBridge.ts
  • src/extensions/core/load3d/CameraManager.test.ts
  • src/extensions/core/load3d/CameraManager.ts
  • src/extensions/core/load3d/Load3d.test.ts
  • src/extensions/core/load3d/Viewport3d.test.ts
  • src/extensions/core/load3d/Viewport3d.ts
  • src/extensions/core/load3d/createViewport3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/nodeTypes.ts
  • src/extensions/core/load3dLazy.ts
  • src/locales/en/main.json
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts

Comment thread src/composables/useCameraInfo.ts
Comment thread src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts
Comment thread src/extensions/core/cameraInfo/handles/CameraHandle.test.ts
Comment on lines +33 to +45
it('positions the yaw handle on the +Z side of the ring at yaw=0', () => {
handles.update({
...DEFAULT_CAMERA_INFO_STATE,
mode: 'orbit',
orbit: { yaw: 0, pitch: 0, distance: 5 }
})

const yawHandle = handles
.pickableMeshes()
.find((m) => m.userData.handleType === 'yaw')!
expect(yawHandle.position.x).toBeCloseTo(0)
expect(yawHandle.position.z).toBeGreaterThan(0)
})

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.

🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Strengthen the yaw-handle position assertion.

Only the sign of z is checked; the expected magnitude (RING_RADIUS) is never verified, so a scaling regression in the yaw-handle placement would slip through.

🧪 Proposed tightening
     expect(yawHandle.position.x).toBeCloseTo(0)
-    expect(yawHandle.position.z).toBeGreaterThan(0)
+    expect(yawHandle.position.z).toBeCloseTo(1.5)

As per path instructions, ensure new geometry behaviors have meaningful assertions rather than weak change-detector-style checks.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('positions the yaw handle on the +Z side of the ring at yaw=0', () => {
handles.update({
...DEFAULT_CAMERA_INFO_STATE,
mode: 'orbit',
orbit: { yaw: 0, pitch: 0, distance: 5 }
})
const yawHandle = handles
.pickableMeshes()
.find((m) => m.userData.handleType === 'yaw')!
expect(yawHandle.position.x).toBeCloseTo(0)
expect(yawHandle.position.z).toBeGreaterThan(0)
})
it('positions the yaw handle on the +Z side of the ring at yaw=0', () => {
handles.update({
...DEFAULT_CAMERA_INFO_STATE,
mode: 'orbit',
orbit: { yaw: 0, pitch: 0, distance: 5 }
})
const yawHandle = handles
.pickableMeshes()
.find((m) => m.userData.handleType === 'yaw')!
expect(yawHandle.position.x).toBeCloseTo(0)
expect(yawHandle.position.z).toBeCloseTo(1.5)
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/extensions/core/cameraInfo/handles/OrbitHandles.test.ts` around lines 33
- 45, Strengthen the yaw-handle assertion in the test around handles.update by
verifying yawHandle.position.z is close to the expected RING_RADIUS at yaw=0,
while retaining the x-position assertion. Replace the sign-only z check with a
magnitude check that directly validates the ring placement.

Source: Path instructions

Comment thread src/extensions/core/cameraInfo/handles/OrbitHandles.ts
Comment thread src/extensions/core/cameraInfo/handles/rollDragMath.test.ts
@jtydhr88
jtydhr88 force-pushed the feat/create-camera-info-preview branch from f64c8fb to d167e6a Compare July 19, 2026 15:30

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/composables/useCameraInfo.test.ts`:
- Around line 86-234: Restore a test in the useCameraInfo suite covering toolbar
actions before initialization: call setGizmosVisible, setTransformGizmoMode, and
setLookThrough without invoking initialize, then assert instances remains empty
to preserve the lazy-initialization guard.

In `@src/extensions/core/cameraInfo/CameraInfoViewport.test.ts`:
- Around line 386-388: Update the quaternion assertion in the camera drag test
around controlsOf(viewport.cameraHandle) to iterate over x, y, z, and w,
asserting onHandleDrag is called with each corresponding mode.quat_${axis} key
and a numeric value. Preserve the existing event dispatch and ensure all four
quaternion component emits are covered.

In `@src/extensions/core/cameraInfo/handles/CameraHandle.test.ts`:
- Around line 62-68: Update the setMode test around CameraHandle.setMode to
assert the underlying TransformControls instance’s mode after switching between
rotate and translate, rather than relying on handle.getMode(). Preserve coverage
of both mode transitions and verify the observable controls behavior.

In `@src/extensions/core/cameraInfo/handles/OrbitHandles.ts`:
- Around line 257-258: Change the module-level pure helper vec from a const
arrow function to a function declaration, preserving its Vector3Like input and
THREE.Vector3 output behavior. Leave buildArcCurve, makeHandle, and makeGlow
unchanged.

In `@src/extensions/core/cameraInfo/handles/RollHandle.ts`:
- Around line 112-118: Remove the redundant cameraVec object literals in both
update() and dragPlane(), and pass the cameraPos returned by
computeSubjectTransform(state) directly to rollBasis(). Preserve the existing
basis calculations and behavior.

In `@src/extensions/core/cameraInfo/lookThroughDragMath.test.ts`:
- Around line 185-209: Add a null-edge-case test for dollySubjectByWheel in
look_at mode, using stateWith to set lookAt.position equal to target and
asserting the result is null. Place it alongside the existing look_at dolly
tests and mirror the established null-path coverage pattern from
rotateSubjectByDrag.

In `@src/extensions/core/load3d/CameraManager.ts`:
- Around line 202-220: Update setCameraState so applying a state quaternion does
not unconditionally re-enable custom-up after setUseCustomUp(false). Respect the
existing usingCustomUp state when deciding whether to apply the
quaternion-derived activeCamera.up, customUp, and cameraUpStateChange update,
preserving explicit user-disabled custom-up behavior.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 62f69acd-fea0-4971-be2c-4a71e1f961c1

📥 Commits

Reviewing files that changed from the base of the PR and between f64c8fb and d167e6a.

📒 Files selected for processing (45)
  • src/components/cameraInfo/CameraInfo.test.ts
  • src/components/cameraInfo/CameraInfo.vue
  • src/components/load3d/Load3DControls.vue
  • src/components/load3d/controls/CameraControls.vue
  • src/composables/useCameraInfo.test.ts
  • src/composables/useCameraInfo.ts
  • src/composables/useLoad3d.ts
  • src/extensions/core/cameraInfo.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.test.ts
  • src/extensions/core/cameraInfo/CameraInfoOverlay.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.test.ts
  • src/extensions/core/cameraInfo/CameraInfoViewport.ts
  • src/extensions/core/cameraInfo/cameraTransform.test.ts
  • src/extensions/core/cameraInfo/cameraTransform.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.test.ts
  • src/extensions/core/cameraInfo/handles/CameraHandle.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.test.ts
  • src/extensions/core/cameraInfo/handles/OrbitHandles.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.test.ts
  • src/extensions/core/cameraInfo/handles/RollHandle.ts
  • src/extensions/core/cameraInfo/handles/TargetHandle.test.ts
  • src/extensions/core/cameraInfo/handles/TargetHandle.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.test.ts
  • src/extensions/core/cameraInfo/handles/handlePicking.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/orbitDragMath.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.test.ts
  • src/extensions/core/cameraInfo/handles/rollDragMath.ts
  • src/extensions/core/cameraInfo/handles/types.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.test.ts
  • src/extensions/core/cameraInfo/lookThroughDragMath.ts
  • src/extensions/core/cameraInfo/types.ts
  • src/extensions/core/cameraInfo/widgetBridge.test.ts
  • src/extensions/core/cameraInfo/widgetBridge.ts
  • src/extensions/core/load3d/CameraManager.test.ts
  • src/extensions/core/load3d/CameraManager.ts
  • src/extensions/core/load3d/Load3d.test.ts
  • src/extensions/core/load3d/Viewport3d.test.ts
  • src/extensions/core/load3d/Viewport3d.ts
  • src/extensions/core/load3d/createViewport3d.ts
  • src/extensions/core/load3d/interfaces.ts
  • src/extensions/core/load3d/nodeTypes.ts
  • src/extensions/core/load3dLazy.ts
  • src/locales/en/main.json
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts

Comment on lines +86 to +234
describe('useCameraInfo', () => {
it('constructs the viewport from widget state and exposes the mode', () => {
const node = makeNode({ mode: 'look_at', 'mode.distance': 7 })
const container = document.createElement('div')
const camera = useCameraInfo(nodeRef(node))

camera.initialize(container)

expect(ViewportMock).toHaveBeenCalledOnce()
const [ctorContainer, initialState] = instances[0].ctorArgs as [
HTMLElement,
{ mode: string; orbit: { distance: number } }
]
expect(ctorContainer).toBe(container)
expect(initialState.mode).toBe('look_at')
expect(initialState.orbit.distance).toBe(7)
expect(camera.mode.value).toBe('look_at')
})

it('does nothing when the node is null', () => {
const camera = useCameraInfo(ref(null))
camera.initialize(document.createElement('div'))

expect(ViewportMock).not.toHaveBeenCalled()
})

it('alerts and does not throw when the viewport fails to construct', () => {
ViewportMock.mockImplementationOnce(() => {
throw new Error('webgl unavailable')
})
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
const camera = useCameraInfo(nodeRef(makeNode({ mode: 'orbit' })))

expect(() => camera.initialize(document.createElement('div'))).not.toThrow()
expect(addAlert).toHaveBeenCalledOnce()

consoleError.mockRestore()
})

it('forwards toolbar actions to the viewport', () => {
const camera = useCameraInfo(nodeRef(makeNode({ mode: 'orbit' })))
camera.initialize(document.createElement('div'))

camera.setGizmosVisible(false)
camera.setTransformGizmoMode('camera-rotate')
camera.setLookThrough(true)

expect(instances[0].setGizmosVisible).toHaveBeenCalledWith(false)
expect(instances[0].setTransformGizmoMode).toHaveBeenCalledWith(
'camera-rotate'
)
expect(instances[0].setLookThrough).toHaveBeenCalledWith(true)
})

it('re-applies state to the viewport when a widget changes, keeping the original callback', () => {
const node = makeNode({ mode: 'orbit', target_x: 0 })
const original = vi.fn()
widget(node, 'target_x').callback = original
const camera = useCameraInfo(nodeRef(node))
camera.initialize(document.createElement('div'))

const targetX = widget(node, 'target_x')
targetX.value = 3
targetX.callback!(3)

expect(original).toHaveBeenCalledWith(3)
const applied = instances[0].applyState.mock.lastCall?.[0] as {
target: { x: number }
}
expect(applied.target.x).toBe(3)
})

it('updates the mode ref when the mode widget changes', () => {
const node = makeNode({ mode: 'orbit' })
const camera = useCameraInfo(nodeRef(node))
camera.initialize(document.createElement('div'))

const modeWidget = widget(node, 'mode')
modeWidget.value = 'quaternion'
modeWidget.callback!('quaternion')

expect(camera.mode.value).toBe('quaternion')
})

it('routes node hover into the viewport status flags', () => {
const node = makeNode({ mode: 'orbit' })
const camera = useCameraInfo(nodeRef(node))
camera.initialize(document.createElement('div'))

camera.handleMouseEnter()
camera.handleMouseLeave()
node.onMouseEnter?.()

expect(instances[0].viewport.updateStatusMouseOnScene).toHaveBeenCalledWith(
true
)
expect(instances[0].viewport.updateStatusMouseOnScene).toHaveBeenCalledWith(
false
)
expect(instances[0].viewport.updateStatusMouseOnNode).toHaveBeenCalledWith(
true
)
})

it('removes the viewport and restores widget callbacks on cleanup', () => {
const node = makeNode({ mode: 'orbit', target_x: 0 })
const original = vi.fn()
widget(node, 'target_x').callback = original
const camera = useCameraInfo(nodeRef(node))
camera.initialize(document.createElement('div'))

camera.cleanup()

expect(instances[0].remove).toHaveBeenCalledOnce()
expect(widget(node, 'target_x').callback).toBe(original)
})

it('restores the node mouse handlers on cleanup', () => {
const node = makeNode({ mode: 'orbit' })
const originalEnter = vi.fn()
const originalLeave = vi.fn()
node.onMouseEnter = originalEnter
node.onMouseLeave = originalLeave
const camera = useCameraInfo(nodeRef(node))
camera.initialize(document.createElement('div'))

expect(node.onMouseEnter).not.toBe(originalEnter)

camera.cleanup()

expect(node.onMouseEnter).toBe(originalEnter)
expect(node.onMouseLeave).toBe(originalLeave)
})

it('re-wires widgets on a second initialize after cleanup', () => {
const node = makeNode({ mode: 'orbit', target_x: 0 })
const camera = useCameraInfo(nodeRef(node))
camera.initialize(document.createElement('div'))
camera.cleanup()
camera.initialize(document.createElement('div'))

const targetX = widget(node, 'target_x')
targetX.value = 5
targetX.callback!(5)

expect(instances).toHaveLength(2)
expect(instances[1].applyState).toHaveBeenCalled()
})
})

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Restore the "toolbar actions before initialize()" regression test.

Based on learnings, a prior discussion on this exact file concluded that the test asserting instances stays empty when setGizmosVisible/setTransformGizmoMode/setLookThrough are called before camera.initialize() should be retained, since it verifies the lazy-initialization contract (not just a no-throw check). That test no longer appears in this revision — the line range it previously occupied (140-145) now holds an unrelated test. Please restore coverage for this guard, e.g.:

it('ignores toolbar actions before initialization', () => {
  const camera = useCameraInfo(nodeRef(makeNode({ mode: 'orbit' })))
  camera.setGizmosVisible(false)
  camera.setTransformGizmoMode('camera-rotate')
  camera.setLookThrough(true)
  expect(instances).toHaveLength(0)
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/composables/useCameraInfo.test.ts` around lines 86 - 234, Restore a test
in the useCameraInfo suite covering toolbar actions before initialization: call
setGizmosVisible, setTransformGizmoMode, and setLookThrough without invoking
initialize, then assert instances remains empty to preserve the
lazy-initialization guard.

Source: Learnings

Comment on lines +386 to +388
controlsOf(viewport.cameraHandle).dispatchEvent({ type: 'objectChange' })

expect(onHandleDrag).toHaveBeenCalledWith('mode.quat_w', expect.any(Number))

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert all four quaternion components, not just quat_w.

This mirrors the position/target tests (which already loop over x/y/z). As written, a regression that drops the mode.quat_x/y/z widget writes in handleCameraDrag would still pass. Loop over ['x','y','z','w'] and assert each mode.quat_${axis} emit.

As per path instructions, tests must cover the full behavioral contract for new logic.

💚 Proposed fix
-    expect(onHandleDrag).toHaveBeenCalledWith('mode.quat_w', expect.any(Number))
+    for (const axis of ['x', 'y', 'z', 'w']) {
+      expect(onHandleDrag).toHaveBeenCalledWith(
+        `mode.quat_${axis}`,
+        expect.any(Number)
+      )
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
controlsOf(viewport.cameraHandle).dispatchEvent({ type: 'objectChange' })
expect(onHandleDrag).toHaveBeenCalledWith('mode.quat_w', expect.any(Number))
controlsOf(viewport.cameraHandle).dispatchEvent({ type: 'objectChange' })
for (const axis of ['x', 'y', 'z', 'w']) {
expect(onHandleDrag).toHaveBeenCalledWith(
`mode.quat_${axis}`,
expect.any(Number)
)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/extensions/core/cameraInfo/CameraInfoViewport.test.ts` around lines 386 -
388, Update the quaternion assertion in the camera drag test around
controlsOf(viewport.cameraHandle) to iterate over x, y, z, and w, asserting
onHandleDrag is called with each corresponding mode.quat_${axis} key and a
numeric value. Preserve the existing event dispatch and ensure all four
quaternion component emits are covered.

Source: Path instructions

Comment on lines +62 to +68
it('setMode switches translate <-> rotate', () => {
handle.setMode('rotate')
expect(handle.getMode()).toBe('rotate')

handle.setMode('translate')
expect(handle.getMode()).toBe('translate')
})

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.

🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the underlying TransformControls mode, not just the wrapper's mirrored field.

handle.getMode() only echoes this.mode; it doesn't verify that controls.setMode() actually propagated to the real TransformControls instance. A regression that updates the local field but breaks the call to this.controls.setMode(mode) would pass this test.

🧪 Proposed fix
   it('setMode switches translate <-> rotate', () => {
     handle.setMode('rotate')
     expect(handle.getMode()).toBe('rotate')
+    expect(controls().mode).toBe('rotate')

     handle.setMode('translate')
     expect(handle.getMode()).toBe('translate')
+    expect(controls().mode).toBe('translate')
   })

As per path instructions, avoid change-detector tests that assert implementation state instead of observable side effects.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('setMode switches translate <-> rotate', () => {
handle.setMode('rotate')
expect(handle.getMode()).toBe('rotate')
handle.setMode('translate')
expect(handle.getMode()).toBe('translate')
})
it('setMode switches translate <-> rotate', () => {
handle.setMode('rotate')
expect(handle.getMode()).toBe('rotate')
expect(controls().mode).toBe('rotate')
handle.setMode('translate')
expect(handle.getMode()).toBe('translate')
expect(controls().mode).toBe('translate')
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/extensions/core/cameraInfo/handles/CameraHandle.test.ts` around lines 62
- 68, Update the setMode test around CameraHandle.setMode to assert the
underlying TransformControls instance’s mode after switching between rotate and
translate, rather than relying on handle.getMode(). Preserve coverage of both
mode transitions and verify the observable controls behavior.

Source: Path instructions

Comment on lines +257 to +258
const vec = (v: THREE.Vector3Like): THREE.Vector3 =>
new THREE.Vector3(v.x, v.y, v.z)

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.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a function declaration for the pure vec helper.

vec is a pure, non-callback helper but is defined as a const arrow function. Based on learnings, this repo prefers function declarations for pure functions (all other module-level helpers here — buildArcCurve, makeHandle, makeGlow — already follow this convention).

♻️ Proposed fix
-const vec = (v: THREE.Vector3Like): THREE.Vector3 =>
-  new THREE.Vector3(v.x, v.y, v.z)
+function vec(v: THREE.Vector3Like): THREE.Vector3 {
+  return new THREE.Vector3(v.x, v.y, v.z)
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const vec = (v: THREE.Vector3Like): THREE.Vector3 =>
new THREE.Vector3(v.x, v.y, v.z)
function vec(v: THREE.Vector3Like): THREE.Vector3 {
return new THREE.Vector3(v.x, v.y, v.z)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/extensions/core/cameraInfo/handles/OrbitHandles.ts` around lines 257 -
258, Change the module-level pure helper vec from a const arrow function to a
function declaration, preserving its Vector3Like input and THREE.Vector3 output
behavior. Leave buildArcCurve, makeHandle, and makeGlow unchanged.

Source: Learnings

Comment thread src/extensions/core/cameraInfo/handles/RollHandle.ts Outdated
Comment on lines +185 to +209
it('look_at: moves the position toward the target, preserving direction', () => {
const state = stateWith({
mode: 'look_at',
lookAt: { position: { x: 0, y: 0, z: 5 } },
target: { x: 0, y: 0, z: 0 }
})
const result = dollySubjectByWheel(state, -100)
const pos = result!.nextState.lookAt.position

expect(pos.z).toBeGreaterThan(0)
expect(pos.z).toBeLessThan(5)
expect(pos.x).toBeCloseTo(0)
expect(pos.y).toBeCloseTo(0)
})

it('look_at: clamps distance to the shared maximum on scroll-out', () => {
const state = stateWith({
mode: 'look_at',
lookAt: { position: { x: 0, y: 0, z: 5 } },
target: { x: 0, y: 0, z: 0 }
})
const pos = dollySubjectByWheel(state, 100000)!.nextState.lookAt.position

expect(Math.hypot(pos.x, pos.y, pos.z)).toBeCloseTo(100)
})

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.

🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing null-edge-case test for dollySubjectByWheel in look_at mode.

dollyLookAt returns null when camera position coincides with the target (distance < 1e-6), mirroring the already-tested null path in rotateSubjectByDrag - look_at (Line 71-79). That branch has no coverage here.

🧪 Proposed test addition
   it('look_at: clamps distance to the shared maximum on scroll-out', () => {
     ...
   })
+
+  it('look_at: returns null when the camera sits on the target', () => {
+    const state = stateWith({
+      mode: 'look_at',
+      lookAt: { position: { x: 1, y: 1, z: 1 } },
+      target: { x: 1, y: 1, z: 1 }
+    })
+
+    expect(dollySubjectByWheel(state, -100)).toBeNull()
+  })

As per path instructions, "ensuring edge cases/error/null scenarios are covered where behavior exists."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('look_at: moves the position toward the target, preserving direction', () => {
const state = stateWith({
mode: 'look_at',
lookAt: { position: { x: 0, y: 0, z: 5 } },
target: { x: 0, y: 0, z: 0 }
})
const result = dollySubjectByWheel(state, -100)
const pos = result!.nextState.lookAt.position
expect(pos.z).toBeGreaterThan(0)
expect(pos.z).toBeLessThan(5)
expect(pos.x).toBeCloseTo(0)
expect(pos.y).toBeCloseTo(0)
})
it('look_at: clamps distance to the shared maximum on scroll-out', () => {
const state = stateWith({
mode: 'look_at',
lookAt: { position: { x: 0, y: 0, z: 5 } },
target: { x: 0, y: 0, z: 0 }
})
const pos = dollySubjectByWheel(state, 100000)!.nextState.lookAt.position
expect(Math.hypot(pos.x, pos.y, pos.z)).toBeCloseTo(100)
})
it('look_at: moves the position toward the target, preserving direction', () => {
const state = stateWith({
mode: 'look_at',
lookAt: { position: { x: 0, y: 0, z: 5 } },
target: { x: 0, y: 0, z: 0 }
})
const result = dollySubjectByWheel(state, -100)
const pos = result!.nextState.lookAt.position
expect(pos.z).toBeGreaterThan(0)
expect(pos.z).toBeLessThan(5)
expect(pos.x).toBeCloseTo(0)
expect(pos.y).toBeCloseTo(0)
})
it('look_at: clamps distance to the shared maximum on scroll-out', () => {
const state = stateWith({
mode: 'look_at',
lookAt: { position: { x: 0, y: 0, z: 5 } },
target: { x: 0, y: 0, z: 0 }
})
const pos = dollySubjectByWheel(state, 100000)!.nextState.lookAt.position
expect(Math.hypot(pos.x, pos.y, pos.z)).toBeCloseTo(100)
})
it('look_at: returns null when the camera sits on the target', () => {
const state = stateWith({
mode: 'look_at',
lookAt: { position: { x: 1, y: 1, z: 1 } },
target: { x: 1, y: 1, z: 1 }
})
expect(dollySubjectByWheel(state, -100)).toBeNull()
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/extensions/core/cameraInfo/lookThroughDragMath.test.ts` around lines 185
- 209, Add a null-edge-case test for dollySubjectByWheel in look_at mode, using
stateWith to set lookAt.position equal to target and asserting the result is
null. Place it alongside the existing look_at dolly tests and mirror the
established null-path coverage pattern from rotateSubjectByDrag.

Source: Path instructions

Comment thread src/extensions/core/load3d/CameraManager.ts

@christian-byrne christian-byrne left a comment

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.

Can you follow domain-driven organization of code modules and files rather than technical layers like sr/ccomponents and src/composables, etc.?

See

description: Reviews whether new code is placed in the right domain/layer and follows domain-driven structure principles

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

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants