Skip to content

Forward scroll unless focused#6597

Merged
jtydhr88 merged 5 commits intomainfrom
bl-visual-impala-358
Feb 1, 2026
Merged

Forward scroll unless focused#6597
jtydhr88 merged 5 commits intomainfrom
bl-visual-impala-358

Conversation

@benceruleanlu
Copy link
Member

@benceruleanlu benceruleanlu commented Nov 5, 2025

Summary

Forward wheel events to the canvas unless a wheel-capturing element is focused, and ensure the Load3D scene becomes focusable on pointer interaction so its wheel zoom/pan works after the user clicks into it.

Changes

  • What: gate wheel forwarding on focused capture elements; focus the Load3D scene container on pointerdown to opt into wheel capture.
  • Dependencies: none

Review Focus

  • Validate wheel forwarding behavior across focusable inputs vs. non-focusable capture zones.
  • Confirm Load3D zoom/pan only captures wheel after a user click (canvas pan should still work when merely hovering).

Screenshots (if applicable)

N/A

@github-actions
Copy link

github-actions bot commented Nov 5, 2025

🎭 Playwright Tests: ✅ Passed

Results: 507 passed, 0 failed, 0 flaky, 8 skipped (Total: 515)

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

@github-actions
Copy link

github-actions bot commented Nov 5, 2025

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 01/30/2026, 05:37:24 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Nov 5, 2025

Bundle Size Report

Summary

  • Raw size: 22.1 MB baseline 22.1 MB — 🔴 +244 B
  • Gzip: 4.61 MB baseline 4.61 MB — 🔴 +162 B
  • Brotli: 3.42 MB baseline 3.42 MB — 🔴 +99 B
  • Bundles: 173 current • 173 baseline • 81 added / 81 removed

Category Glance
Data & Services 🔴 +384 B (2.71 MB) · Other 🟢 -132 B (7.1 MB) · Panels & Settings 🟢 -8 B (471 kB) · Vendor & Third-Party ⚪ 0 B (10.7 MB) · Graph Workspace ⚪ 0 B (974 kB) · Views & Navigation ⚪ 0 B (80.7 kB) · + 5 more

Per-category breakdown
App Entry Points — 26 kB (baseline 26 kB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-0o8eSuaX.js (removed) 26 kB 🟢 -26 kB 🟢 -7.5 kB 🟢 -6.61 kB
assets/index-C1YlY5WB.js (new) 26 kB 🔴 +26 kB 🔴 +7.51 kB 🔴 +6.6 kB

Status: 1 added / 1 removed

Graph Workspace — 974 kB (baseline 974 kB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-BbQqs2Ly.js (removed) 974 kB 🟢 -974 kB 🟢 -197 kB 🟢 -149 kB
assets/GraphView-BBVhLDcE.js (new) 974 kB 🔴 +974 kB 🔴 +197 kB 🔴 +149 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-B8LkFGIm.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -3.61 kB 🟢 -3.05 kB
assets/CloudSurveyView-BZC2WtqJ.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +3.61 kB 🔴 +3.05 kB
assets/CloudLoginView-BYlKVm36.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.09 kB 🔴 +2.72 kB
assets/CloudLoginView-C9R0_Ugu.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.09 kB 🟢 -2.71 kB
assets/UserCheckView-CLwAHgYQ.js (removed) 10.5 kB 🟢 -10.5 kB 🟢 -2.44 kB 🟢 -2.13 kB
assets/UserCheckView-D5jrT3nL.js (new) 10.5 kB 🔴 +10.5 kB 🔴 +2.44 kB 🔴 +2.13 kB
assets/CloudLayoutView-B8qEuUUq.js (new) 8.54 kB 🔴 +8.54 kB 🔴 +2.24 kB 🔴 +1.96 kB
assets/CloudLayoutView-BB2Oum5f.js (removed) 8.54 kB 🟢 -8.54 kB 🟢 -2.24 kB 🟢 -1.96 kB
assets/CloudSignupView-B12ldmFX.js (new) 8.18 kB 🔴 +8.18 kB 🔴 +2.32 kB 🔴 +2.02 kB
assets/CloudSignupView-B3qZgE2h.js (removed) 8.18 kB 🟢 -8.18 kB 🟢 -2.32 kB 🟢 -2.02 kB
assets/CloudForgotPasswordView-B1msQ3bg.js (removed) 6.26 kB 🟢 -6.26 kB 🟢 -1.93 kB 🟢 -1.69 kB
assets/CloudForgotPasswordView-CqUTypTc.js (new) 6.26 kB 🔴 +6.26 kB 🔴 +1.93 kB 🔴 +1.69 kB
assets/UserSelectView-D0PLhTti.js (removed) 5.28 kB 🟢 -5.28 kB 🟢 -1.76 kB 🟢 -1.57 kB
assets/UserSelectView-D6YXk3hK.js (new) 5.28 kB 🔴 +5.28 kB 🔴 +1.76 kB 🔴 +1.57 kB
assets/CloudSubscriptionRedirectView-C3ego9gb.js (new) 5.27 kB 🔴 +5.27 kB 🔴 +1.73 kB 🔴 +1.54 kB
assets/CloudSubscriptionRedirectView-DPadqUKi.js (removed) 5.27 kB 🟢 -5.27 kB 🟢 -1.73 kB 🟢 -1.54 kB
assets/CloudAuthTimeoutView-BevAbWnL.js (removed) 5.24 kB 🟢 -5.24 kB 🟢 -1.7 kB 🟢 -1.48 kB
assets/CloudAuthTimeoutView-fH2I-RnN.js (new) 5.24 kB 🔴 +5.24 kB 🔴 +1.7 kB 🔴 +1.48 kB
assets/CloudSorryContactSupportView-cm9oKn4s.js 1.97 kB 1.97 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/layout-CUzumK-h.js 500 B 500 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 9 added / 9 removed

Panels & Settings — 471 kB (baseline 471 kB) • 🟢 -8 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WorkspacePanel-22RIfqld.js (removed) 29.8 kB 🟢 -29.8 kB 🟢 -5.89 kB 🟢 -5.14 kB
assets/WorkspacePanel-CwFi9kOE.js (new) 29.8 kB 🔴 +29.8 kB 🔴 +5.89 kB 🔴 +5.15 kB
assets/LegacyCreditsPanel-BfXUm7WU.js (new) 23.8 kB 🔴 +23.8 kB 🔴 +5.94 kB 🔴 +5.22 kB
assets/LegacyCreditsPanel-DSHi1D4z.js (removed) 23.8 kB 🟢 -23.8 kB 🟢 -5.94 kB 🟢 -5.22 kB
assets/SubscriptionPanel-D3Y3vQ3_.js (new) 21 kB 🔴 +21 kB 🔴 +5.05 kB 🔴 +4.44 kB
assets/SubscriptionPanel-DpViEyFm.js (removed) 21 kB 🟢 -21 kB 🟢 -5.04 kB 🟢 -4.44 kB
assets/KeybindingPanel-BC9REFDk.js (new) 14.3 kB 🔴 +14.3 kB 🔴 +3.76 kB 🔴 +3.34 kB
assets/KeybindingPanel-Cqh2zhF5.js (removed) 14.3 kB 🟢 -14.3 kB 🟢 -3.76 kB 🟢 -3.34 kB
assets/AboutPanel-DRDUOK8Y.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.68 kB 🔴 +2.43 kB
assets/AboutPanel-nAPeX13O.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.68 kB 🟢 -2.43 kB
assets/ExtensionPanel-BN17h4C0.js (removed) 10.2 kB 🟢 -10.2 kB 🟢 -2.71 kB 🟢 -2.4 kB
assets/ExtensionPanel-DpsRXSXX.js (new) 10.2 kB 🔴 +10.2 kB 🔴 +2.71 kB 🔴 +2.4 kB
assets/ServerConfigPanel-CoWLDDdT.js (new) 7.23 kB 🔴 +7.23 kB 🔴 +2.17 kB 🔴 +1.94 kB
assets/ServerConfigPanel-Drtk3Cj9.js (removed) 7.23 kB 🟢 -7.23 kB 🟢 -2.17 kB 🟢 -1.94 kB
assets/UserPanel-CX_ZP5HA.js (removed) 6.58 kB 🟢 -6.58 kB 🟢 -1.91 kB 🟢 -1.68 kB
assets/UserPanel-CyGqt86Y.js (new) 6.58 kB 🔴 +6.58 kB 🔴 +1.91 kB 🔴 +1.68 kB
assets/refreshRemoteConfig-BvAnKw7S.js (removed) 1.31 kB 🟢 -1.31 kB 🟢 -571 B 🟢 -496 B
assets/refreshRemoteConfig-o6aKxink.js (new) 1.31 kB 🔴 +1.31 kB 🔴 +573 B 🔴 +502 B
assets/config-CQmXnrXP.js (removed) 1.16 kB 🟢 -1.16 kB 🟢 -609 B 🟢 -539 B
assets/config-BK-qZSAW.js (new) 1.15 kB 🔴 +1.15 kB 🔴 +606 B 🔴 +532 B
assets/cloudRemoteConfig-BpfcOE1q.js (removed) 1.11 kB 🟢 -1.11 kB 🟢 -509 B 🟢 -439 B
assets/cloudRemoteConfig-D_YCZKxA.js (new) 1.11 kB 🔴 +1.11 kB 🔴 +509 B 🔴 +437 B
assets/refreshRemoteConfig-Bckc2EMm.js (removed) 169 B 🟢 -169 B 🟢 -108 B 🟢 -102 B
assets/refreshRemoteConfig-DEfKTx9L.js (new) 169 B 🔴 +169 B 🔴 +108 B 🔴 +103 B
assets/remoteConfig-B0mlVvm7.js 788 B 788 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-2UNjEj6k.js 32.9 kB 32.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-B2OMGvh7.js 31.2 kB 31.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BcujOfpn.js 29.6 kB 29.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BI09_t23.js 29.4 kB 29.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BKamuseh.js 25.8 kB 25.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BlTun9tZ.js 26.4 kB 26.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CZ62uO3e.js 30.2 kB 30.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DaK-NByz.js 35.2 kB 35.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DaS3cSXp.js 39.4 kB 39.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DWbMuaAa.js 32 kB 32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-S7pA60Hj.js 30.4 kB 30.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 12 added / 12 removed

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

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/auth-D3luOwL2.js (removed) 3.54 kB 🟢 -3.54 kB 🟢 -1.24 kB 🟢 -1.07 kB
assets/auth-DECvX7De.js (new) 3.54 kB 🔴 +3.54 kB 🔴 +1.24 kB 🔴 +1.06 kB
assets/firebaseAuthStore-DBrMEGg6.js (new) 217 B 🔴 +217 B 🔴 +138 B 🔴 +121 B
assets/firebaseAuthStore-vCBMphFP.js (removed) 217 B 🟢 -217 B 🟢 -138 B 🟢 -121 B
assets/auth-BPMO3atV.js (removed) 178 B 🟢 -178 B 🟢 -142 B 🟢 -143 B
assets/auth-CAjvTPKR.js (new) 178 B 🔴 +178 B 🔴 +142 B 🔴 +136 B

Status: 3 added / 3 removed

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

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-CKUzyzhY.js (new) 2.71 kB 🔴 +2.71 kB 🔴 +1.29 kB 🔴 +1.14 kB
assets/useSubscriptionDialog-nhvd3Qi8.js (removed) 2.71 kB 🟢 -2.71 kB 🟢 -1.28 kB 🟢 -1.15 kB
assets/useSubscriptionDialog-1nPIsiTW.js (removed) 179 B 🟢 -179 B 🟢 -110 B 🟢 -101 B
assets/useSubscriptionDialog-Bv-L9ek1.js (new) 179 B 🔴 +179 B 🔴 +110 B 🔴 +98 B

Status: 2 added / 2 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-B7AzI4p_.js (removed) 9.52 kB 🟢 -9.52 kB 🟢 -2.69 kB 🟢 -2.42 kB
assets/ComfyQueueButton-DYjQvuRZ.js (new) 9.52 kB 🔴 +9.52 kB 🔴 +2.69 kB 🔴 +2.41 kB
assets/SubscribeButton-BgfUkRKE.js (new) 4.63 kB 🔴 +4.63 kB 🔴 +1.57 kB 🔴 +1.39 kB
assets/SubscribeButton-CgElFdpH.js (removed) 4.63 kB 🟢 -4.63 kB 🟢 -1.57 kB 🟢 -1.39 kB
assets/cloudFeedbackTopbarButton-8k2zfTrI.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +675 B 🔴 +572 B
assets/cloudFeedbackTopbarButton-BFQoPxBi.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -675 B 🟢 -573 B
assets/ComfyQueueButton-DhH2bjjk.js (removed) 181 B 🟢 -181 B 🟢 -118 B 🟢 -124 B
assets/ComfyQueueButton-zAR3JA9j.js (new) 181 B 🔴 +181 B 🔴 +118 B 🔴 +110 B
assets/Button-DbRyW27H.js 3.82 kB 3.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/CloudBadge-DdpkQvvD.js 1.85 kB 1.85 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/TopbarBadge-BFw4kSAY.js 8.36 kB 8.36 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/UserAvatar-D80lITos.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-CVau1vM3.js 2.41 kB 2.41 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 4 added / 4 removed

Data & Services — 2.71 MB (baseline 2.71 MB) • 🔴 +384 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-Br3bz8rg.js (new) 2.01 MB 🔴 +2.01 MB 🔴 +425 kB 🔴 +324 kB
assets/dialogService-C0jZxvrW.js (removed) 2.01 MB 🟢 -2.01 MB 🟢 -425 kB 🟢 -324 kB
assets/api-DZ5XR9hH.js (new) 675 kB 🔴 +675 kB 🔴 +149 kB 🔴 +119 kB
assets/api-0_xJMVsr.js (removed) 675 kB 🟢 -675 kB 🟢 -149 kB 🟢 -119 kB
assets/releaseStore-DBgSzUS5.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.4 kB 🔴 +2.12 kB
assets/releaseStore-Msq5KIWQ.js (removed) 8.91 kB 🟢 -8.91 kB 🟢 -2.4 kB 🟢 -2.12 kB
assets/keybindingService-DyZNHzol.js (removed) 6.74 kB 🟢 -6.74 kB 🟢 -1.75 kB 🟢 -1.53 kB
assets/keybindingService-g-uCVo7a.js (new) 6.74 kB 🔴 +6.74 kB 🔴 +1.75 kB 🔴 +1.52 kB
assets/bootstrapStore-BL_vrhCJ.js (new) 2.69 kB 🔴 +2.69 kB 🔴 +1.03 kB 🔴 +963 B
assets/bootstrapStore-CT9MfHnC.js (removed) 2.69 kB 🟢 -2.69 kB 🟢 -1.03 kB 🟢 -967 B
assets/userStore-Bbs7yE1D.js (removed) 2.16 kB 🟢 -2.16 kB 🟢 -811 B 🟢 -724 B
assets/userStore-DNIn3ABz.js (new) 2.16 kB 🔴 +2.16 kB 🔴 +815 B 🔴 +725 B
assets/audioService-C4ihUw-I.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -929 B 🟢 -808 B
assets/audioService-jGyqji6y.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +931 B 🔴 +808 B
assets/releaseStore-DL0as-4e.js (new) 140 B 🔴 +140 B 🔴 +106 B 🔴 +103 B
assets/releaseStore-RM6-Y5p0.js (removed) 140 B 🟢 -140 B 🟢 -106 B 🟢 -110 B
assets/serverConfigStore-DOoqLe5c.js 2.64 kB 2.64 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 8 added / 8 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useErrorHandling-BJ4-7oLQ.js (removed) 5.21 kB 🟢 -5.21 kB 🟢 -1.53 kB 🟢 -1.34 kB
assets/useErrorHandling-D57Tn_x0.js (new) 5.21 kB 🔴 +5.21 kB 🔴 +1.53 kB 🔴 +1.35 kB
assets/useWorkspaceUI-8QKiJEcj.js (new) 3.42 kB 🔴 +3.42 kB 🔴 +976 B 🔴 +840 B
assets/useWorkspaceUI-u-o6kaJ8.js (removed) 3.42 kB 🟢 -3.42 kB 🟢 -974 B 🟢 -843 B
assets/useSubscriptionActions-C_-bV1aq.js (removed) 2.22 kB 🟢 -2.22 kB 🟢 -867 B 🟢 -763 B
assets/useSubscriptionActions-DzCadnEX.js (new) 2.22 kB 🔴 +2.22 kB 🔴 +869 B 🔴 +763 B
assets/subscriptionCheckoutUtil-CDQ8THBd.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -871 B 🟢 -771 B
assets/subscriptionCheckoutUtil-DINPif9a.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +872 B 🔴 +766 B
assets/useSubscriptionCredits-Chj6K3LE.js (removed) 1.39 kB 🟢 -1.39 kB 🟢 -600 B 🟢 -529 B
assets/useSubscriptionCredits-DxGZKS6H.js (new) 1.39 kB 🔴 +1.39 kB 🔴 +599 B 🔴 +531 B
assets/audioUtils-BVnn8zRo.js (new) 970 B 🔴 +970 B 🔴 +546 B 🔴 +486 B
assets/audioUtils-CJEpTYmi.js (removed) 970 B 🟢 -970 B 🟢 -547 B 🟢 -459 B
assets/useCurrentUser-_XKMqbeH.js (removed) 145 B 🟢 -145 B 🟢 -114 B 🟢 -104 B
assets/useCurrentUser-B8XYp6Mj.js (new) 145 B 🔴 +145 B 🔴 +114 B 🔴 +102 B
assets/_plugin-vue_export-helper-DuK_Fly3.js 467 B 467 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/colorUtil-DfMUHmsF.js 7.2 kB 7.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/markdownRendererUtil-DM9z_tTX.js 1.78 kB 1.78 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/tailwindUtil-BWBAZ7f9.js 488 B 488 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 7 added / 7 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-DHGfk3hn.js 408 kB 408 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-B3TsI6ya.js 4.1 MB 4.1 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-4Jj8eU28.js 3.04 MB 3.04 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-reka-ui-aCG649nF.js 263 kB 263 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-CERwhPwK.js 1.83 MB 1.83 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-BxrEVL6s.js 650 kB 650 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-Dwii0E-t.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-IX6P8SWv.js 398 kB 398 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 7.1 MB (baseline 7.1 MB) • 🟢 -132 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/core-Bpml2FxC.js (removed) 180 kB 🟢 -180 kB 🟢 -43.3 kB 🟢 -36.2 kB
assets/core-D3FiCL14.js (new) 180 kB 🔴 +180 kB 🔴 +43.3 kB 🔴 +36.1 kB
assets/WidgetSelect-CAtMnQpb.js (removed) 52.2 kB 🟢 -52.2 kB 🟢 -11.5 kB 🟢 -10 kB
assets/WidgetSelect-MWwmHoMC.js (new) 52.2 kB 🔴 +52.2 kB 🔴 +11.5 kB 🔴 +10 kB
assets/Load3DControls-D33RU892.js (new) 35.9 kB 🔴 +35.9 kB 🔴 +5.87 kB 🔴 +5.08 kB
assets/Load3DControls-visAYFcK.js (removed) 35.9 kB 🟢 -35.9 kB 🟢 -5.87 kB 🟢 -5.09 kB
assets/SubscriptionRequiredDialogContent-DwBYjYJD.js (new) 28.7 kB 🔴 +28.7 kB 🔴 +6.79 kB 🔴 +5.91 kB
assets/SubscriptionRequiredDialogContent-NC2Wm-qi.js (removed) 28.7 kB 🟢 -28.7 kB 🟢 -6.79 kB 🟢 -5.92 kB
assets/CurrentUserPopoverWorkspace-5dxlCOXZ.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +4.99 kB 🔴 +4.43 kB
assets/CurrentUserPopoverWorkspace-IK21Y1TX.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -4.99 kB 🟢 -4.42 kB
assets/Load3D-CnQ3tohE.js (new) 19.3 kB 🔴 +19.3 kB 🔴 +4.42 kB 🔴 +3.88 kB
assets/Load3D-DM6Rqpu-.js (removed) 19.2 kB 🟢 -19.2 kB 🟢 -4.38 kB 🟢 -3.84 kB
assets/WidgetInputNumber-C1zzD6EW.js (removed) 18.3 kB 🟢 -18.3 kB 🟢 -4.53 kB 🟢 -4.03 kB
assets/WidgetInputNumber-DcAqRvpE.js (new) 18.3 kB 🔴 +18.3 kB 🔴 +4.53 kB 🔴 +4.03 kB
assets/WidgetRecordAudio-CNAWyAke.js (removed) 18.3 kB 🟢 -18.3 kB 🟢 -4.97 kB 🟢 -4.44 kB
assets/WidgetRecordAudio-nncBGxAS.js (new) 18.3 kB 🔴 +18.3 kB 🔴 +4.97 kB 🔴 +4.44 kB
assets/SubscriptionPanelContentWorkspace-D_0MeVoR.js (removed) 18.2 kB 🟢 -18.2 kB 🟢 -4.47 kB 🟢 -3.9 kB
assets/SubscriptionPanelContentWorkspace-IdptCDZv.js (new) 18.2 kB 🔴 +18.2 kB 🔴 +4.48 kB 🔴 +3.9 kB
assets/WidgetImageCrop-B8vN0f5F.js (removed) 17.1 kB 🟢 -17.1 kB 🟢 -4.14 kB 🟢 -3.63 kB
assets/WidgetImageCrop-BJTM-1eL.js (new) 17.1 kB 🔴 +17.1 kB 🔴 +4.13 kB 🔴 +3.63 kB
assets/PanelTemplate-2JgpsoeK.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -5.45 kB 🟢 -4.8 kB
assets/PanelTemplate-B83afn5M.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +5.46 kB 🔴 +4.79 kB
assets/AudioPreviewPlayer-Brfeorqe.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.97 kB 🟢 -2.65 kB
assets/AudioPreviewPlayer-DZclgP99.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.97 kB 🔴 +2.66 kB
assets/InviteMemberDialogContent-BJUKnLHz.js (new) 8.36 kB 🔴 +8.36 kB 🔴 +2.5 kB 🔴 +2.17 kB
assets/InviteMemberDialogContent-Cns2g9Rm.js (removed) 8.36 kB 🟢 -8.36 kB 🟢 -2.5 kB 🟢 -2.17 kB
assets/WidgetWithControl-BhNk_cqm.js (removed) 8.07 kB 🟢 -8.07 kB 🟢 -2.68 kB 🟢 -2.41 kB
assets/WidgetWithControl-CNfNsPNW.js (new) 8.07 kB 🔴 +8.07 kB 🔴 +2.68 kB 🔴 +2.42 kB
assets/CreateWorkspaceDialogContent-4vgWaPXD.js (new) 5.93 kB 🔴 +5.93 kB 🔴 +1.92 kB 🔴 +1.68 kB
assets/CreateWorkspaceDialogContent-Bg3B71vi.js (removed) 5.93 kB 🟢 -5.93 kB 🟢 -1.92 kB 🟢 -1.68 kB
assets/EditWorkspaceDialogContent--1tYmSZD.js (new) 5.7 kB 🔴 +5.7 kB 🔴 +1.88 kB 🔴 +1.64 kB
assets/EditWorkspaceDialogContent-BUzR5x5W.js (removed) 5.7 kB 🟢 -5.7 kB 🟢 -1.88 kB 🟢 -1.65 kB
assets/ValueControlPopover-2FjUGycj.js (new) 5.17 kB 🔴 +5.17 kB 🔴 +1.69 kB 🔴 +1.5 kB
assets/ValueControlPopover-DdL0wMpV.js (removed) 5.17 kB 🟢 -5.17 kB 🟢 -1.69 kB 🟢 -1.5 kB
assets/DeleteWorkspaceDialogContent-Do8LHd_w.js (removed) 4.59 kB 🟢 -4.59 kB 🟢 -1.56 kB 🟢 -1.35 kB
assets/DeleteWorkspaceDialogContent-MvpuR62w.js (new) 4.59 kB 🔴 +4.59 kB 🔴 +1.56 kB 🔴 +1.35 kB
assets/LeaveWorkspaceDialogContent-BC02_pak.js (new) 4.41 kB 🔴 +4.41 kB 🔴 +1.5 kB 🔴 +1.3 kB
assets/LeaveWorkspaceDialogContent-C5ayjfdf.js (removed) 4.41 kB 🟢 -4.41 kB 🟢 -1.5 kB 🟢 -1.31 kB
assets/RemoveMemberDialogContent-DakcgF40.js (new) 4.38 kB 🔴 +4.38 kB 🔴 +1.45 kB 🔴 +1.27 kB
assets/RemoveMemberDialogContent-jR-phpTt.js (removed) 4.38 kB 🟢 -4.38 kB 🟢 -1.45 kB 🟢 -1.27 kB
assets/RevokeInviteDialogContent-B_NRigYz.js (removed) 4.29 kB 🟢 -4.29 kB 🟢 -1.47 kB 🟢 -1.29 kB
assets/RevokeInviteDialogContent-BMkqjHCw.js (new) 4.29 kB 🔴 +4.29 kB 🔴 +1.47 kB 🔴 +1.29 kB
assets/GlobalToast-Bfq1l41B.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.1 kB 🟢 -941 B
assets/GlobalToast-BxHGzM0i.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.1 kB 🔴 +944 B
assets/SubscribeToRun-BNX1ZG14.js (removed) 2.96 kB 🟢 -2.96 kB 🟢 -1.15 kB 🟢 -1.01 kB
assets/SubscribeToRun-CLCe1Bpv.js (new) 2.96 kB 🔴 +2.96 kB 🔴 +1.15 kB 🔴 +1.01 kB
assets/cloudSessionCookie-CvAww9fH.js (new) 2.94 kB 🔴 +2.94 kB 🔴 +931 B 🔴 +798 B
assets/cloudSessionCookie-DwHfnsk8.js (removed) 2.94 kB 🟢 -2.94 kB 🟢 -930 B 🟢 -800 B
assets/BaseViewTemplate-B2Ree9mF.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -1.04 kB 🟢 -941 B
assets/BaseViewTemplate-D-EaVu3M.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +1.04 kB 🔴 +949 B
assets/CloudRunButtonWrapper-BFaH7gG2.js (new) 1.79 kB 🔴 +1.79 kB 🔴 +645 B 🔴 +569 B
assets/CloudRunButtonWrapper-CPvudAWU.js (removed) 1.79 kB 🟢 -1.79 kB 🟢 -646 B 🟢 -597 B
assets/cloudBadges-b1X8Ke6t.js (new) 1.08 kB 🔴 +1.08 kB 🔴 +536 B 🔴 +473 B
assets/cloudBadges-CLORO5-B.js (removed) 1.08 kB 🟢 -1.08 kB 🟢 -537 B 🟢 -498 B
assets/graphHasMissingNodes-a2fZUUm6.js (new) 1.06 kB 🔴 +1.06 kB 🔴 +462 B 🔴 +418 B
assets/graphHasMissingNodes-BaIjF0fM.js (removed) 1.06 kB 🟢 -1.06 kB 🟢 -461 B 🟢 -419 B
assets/cloudSubscription-CdjFYj--.js (removed) 976 B 🟢 -976 B 🟢 -464 B 🟢 -404 B
assets/cloudSubscription-CPNDSvoF.js (new) 976 B 🔴 +976 B 🔴 +464 B 🔴 +402 B
assets/nightlyBadges-Cdwp25W3.js (new) 595 B 🔴 +595 B 🔴 +356 B 🔴 +308 B
assets/nightlyBadges-wXTaIV9p.js (removed) 595 B 🟢 -595 B 🟢 -356 B 🟢 -309 B
assets/SubscriptionPanelContentWorkspace-BXVH_z76.js (new) 266 B 🔴 +266 B 🔴 +136 B 🔴 +125 B
assets/SubscriptionPanelContentWorkspace-CFHGZeNa.js (removed) 266 B 🟢 -266 B 🟢 -136 B 🟢 -125 B
assets/WidgetInputNumber-DxdSVo7u.js (removed) 186 B 🟢 -186 B 🟢 -119 B 🟢 -110 B
assets/WidgetInputNumber-rAne7rWK.js (new) 186 B 🔴 +186 B 🔴 +119 B 🔴 +109 B
assets/WidgetLegacy-B3Z7mXij.js (new) 164 B 🔴 +164 B 🔴 +125 B 🔴 +109 B
assets/WidgetLegacy-DyaDuMR_.js (removed) 164 B 🟢 -164 B 🟢 -125 B 🟢 -113 B
assets/Load3D-B3vk_0IY.js (new) 131 B 🔴 +131 B 🔴 +107 B 🔴 +112 B
assets/Load3D-CgfyUqU3.js (removed) 131 B 🟢 -131 B 🟢 -107 B 🟢 -113 B
assets/auto-DWs2ctGL.js 1.73 kB 1.73 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BEw5ErI4.js 18.5 kB 18.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BGeHkplA.js 17.9 kB 17.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BV0l36Iz.js 17.2 kB 17.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-C_Y3D6Cn.js 17.8 kB 17.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-C6piRza5.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Cf8Zq1td.js 18.8 kB 18.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CiziP3Xs.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-D1595tOr.js 19.3 kB 19.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DXauvccL.js 20.6 kB 20.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-P5QCEfZc.js 18 kB 18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-WbYP_D61.js 17 kB 17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-DKbrI9yU.js 500 kB 500 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/i18n-Do-i5KgH.js 188 B 188 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/LazyImage-ooHoQZNd.js 14.1 kB 14.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BHtk4Fg_.js 174 kB 174 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BMSlgLcp.js 155 kB 155 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BQCWi9e4.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CJicmTR7.js 113 kB 113 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CNOkBy-u.js 126 kB 126 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CySb1R5_.js 151 kB 151 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-D0g10ZKf.js 131 kB 131 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DMUPIFMF.js 133 kB 133 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DpsGU4si.js 126 kB 126 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Dz6IPJXM.js 144 kB 144 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-lrEzMywH.js 128 kB 128 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Media3DTop-DUmUhXD6.js 2.38 kB 2.38 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-CD66_Mw_.js 2 kB 2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-D5feRGXX.js 2.34 kB 2.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-De3MzVmp.js 2.82 kB 2.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/mixpanel.module-DLR992B1.js 143 B 143 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-A7pvB7zM.js 370 kB 370 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BIVjUijC.js 345 kB 345 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Chkn0HaI.js 343 kB 343 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CK_6GHao.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CToVAwnT.js 373 kB 373 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DDabdWgx.js 417 kB 417 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DgvJyE3d.js 386 kB 386 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DjxaeFt_.js 416 kB 416 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DslnWEGg.js 377 kB 377 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-EPAM3kwk.js 373 kB 373 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-NrulhNyH.js 366 kB 366 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/previousFullPath-CmezY7As.js 838 B 838 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/rolldown-runtime-cVp-94Rc.js 1.96 kB 1.96 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Slider-C87scEAV.js 4.21 kB 4.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widget-BJiJuR5i.js 518 B 518 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-CUtab2CB.js 4.71 kB 4.71 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetBoundingBox-D79nBMxa.js 186 B 186 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-CiXfBVBH.js 2.79 kB 2.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetColorPicker-BEfQQjV6.js 3.71 kB 3.71 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetGalleria-jm_gz6R2.js 4.57 kB 4.57 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-DnZ84Utk.js 3.79 kB 3.79 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetInputText-WCHoBOIV.js 2.58 kB 2.58 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField-BHrU_4qY.js 2.7 kB 2.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetMarkdown-3U-WuCE_.js 3.49 kB 3.49 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-ERx8czR8.js 1.31 kB 1.31 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetTextarea-BXRA46js.js 3.87 kB 3.87 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-DYS14Ar3.js 3.26 kB 3.26 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetTypes-KPj-zM0O.js 573 B 573 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 34 added / 34 removed

@benceruleanlu
Copy link
Member Author

@DrJKL did you want to take over this? I can open if needed, or feel free to let me know if you opened your own PR

I didn't open this because I wanted to add some tests for this since it touches a pretty hot path, but I tested manually and it works.

## Summary
Adds test coverage for the fix in #6597 (GitHub issue #6443).

## Changes
Adds 2 new tests to `useCanvasInteractions.test.ts`:
- **should forward wheel events to canvas when capture element is NOT
focused** - verifies zoom works when hovering over unfocused inputs
- **should NOT forward wheel events when capture element IS focused** -
verifies focused text areas still capture scroll events

## Testing
All 9 tests pass:
```
✓ src/renderer/core/canvas/useCanvasInteractions.test.ts (9 tests) 9ms
```

## Context
PR #6597 by @benceruleanlu implements the fix but is marked as draft due
to missing test coverage. This PR adds that coverage to help get the fix
merged.

Fixes #6443 (together with #6597)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8458-test-add-tests-for-focus-dependent-wheel-capture-behavior-2f86d73d365081ee937ed4c9f3f6532f)
by [Unito](https://www.unito.io)

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

Wheel event forwarding was made conditional: wheel events from elements marked data-capture-wheel are only forwarded to the canvas when they are not focused, or when in "standard" navigation mode with Ctrl/Meta held; focused capture elements block forwarding.

Changes

Cohort / File(s) Summary
Canvas interaction core
src/renderer/core/canvas/useCanvasInteractions.ts, src/renderer/core/canvas/useCanvasInteractions.test.ts
Added helpers wheelCapturedByFocusedElement() and shouldForwardWheelEvent(); updated handleWheel() and forwardEventToCanvas() to gate wheel forwarding based on focus and navigation mode. Tests added for focused/unfocused capture elements and ctrl+wheel behavior in standard mode.
3D scene focus handling
src/components/load3d/Load3DScene.vue
Made scene container focusable (tabindex="-1") and added pointerdown handler to focus the container (in addition to stopping propagation).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CaptureElement
    participant Interactions as useCanvasInteractions
    participant Settings as useSettingStore
    participant Canvas

    User->>CaptureElement: wheel event
    CaptureElement->>Interactions: emit WheelEvent
    Interactions->>Interactions: wheelCapturedByFocusedElement(event)?
    alt wheel captured by focused element
        Interactions->>Settings: get navigation mode
        Settings-->>Interactions: "legacy" / "standard"
        alt standard + Ctrl/Meta held
            Interactions->>Canvas: forward wheel (preventDefault & stopPropagation)
        else legacy or no modifier
            Interactions-->>CaptureElement: do not forward
        end
    else not captured by focused element
        Interactions->>Canvas: forward wheel (preventDefault & stopPropagation)
    end
Loading

Suggested reviewers

  • DrJKL
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bl-visual-impala-358

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

❤️ Share

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

@christian-byrne
Copy link
Contributor

Did you want someone else to take this still?

@benceruleanlu
Copy link
Member Author

Did you want someone else to take this still?

I got it

@benceruleanlu benceruleanlu marked this pull request as ready for review January 30, 2026 05:13
@benceruleanlu benceruleanlu requested a review from a team as a code owner January 30, 2026 05:13
Copilot AI review requested due to automatic review settings January 30, 2026 05:13
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jan 30, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves the two-finger panning/scroll UX over input widgets by forwarding wheel events to the canvas unless the widget element is actively focused. Previously, any element with data-capture-wheel="true" would always capture wheel events regardless of focus state.

Changes:

  • Introduced wheelCapturedByFocusedElement() to check both the presence of a capture element AND its focus state
  • Refactored handleWheel() and forwardEventToCanvas() to respect the new focus-based capture logic
  • Added comprehensive test coverage for the new focus-based wheel event forwarding behavior

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/renderer/core/canvas/useCanvasInteractions.ts Implements focus-aware wheel event capture logic with new helper functions and updated event forwarding
src/renderer/core/canvas/useCanvasInteractions.test.ts Adds test cases for focus-based wheel event forwarding in both legacy and standard navigation modes
Comments suppressed due to low confidence (1)

src/renderer/core/canvas/useCanvasInteractions.ts:68

  • There appears to be a logic issue in handleWheel for standard navigation mode. When an element with data-capture-wheel is NOT focused, the shouldForwardWheelEvent check passes (line 53), but then the event is not forwarded because it doesn't match either the Ctrl+wheel condition (line 56) or the legacy mode condition (line 62). This means regular wheel events over unfocused capture elements won't be forwarded to the canvas in standard mode, which contradicts the PR's goal of forwarding wheel events when the element is not focused. Consider adding logic to forward the event in standard mode when shouldForwardWheelEvent returns true but Ctrl/Meta is not pressed.
  const handleWheel = (event: WheelEvent) => {
    if (!shouldForwardWheelEvent(event)) return

    // In standard mode, Ctrl+wheel should go to canvas for zoom
    if (isStandardNavMode.value && (event.ctrlKey || event.metaKey)) {
      forwardEventToCanvas(event)
      return
    }

    // In legacy mode, all wheel events go to canvas for zoom
    if (!isStandardNavMode.value) {
      forwardEventToCanvas(event)
      return
    }

    // Otherwise, let the component handle it normally
  }

Comment on lines +203 to +224
it('should forward ctrl+wheel to canvas when capture element IS focused in standard mode', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('standard')

const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
const textarea = document.createElement('textarea')
captureElement.appendChild(textarea)
document.body.appendChild(captureElement)
textarea.focus()

const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent(true)
Object.defineProperty(mockEvent, 'target', { value: textarea })

handleWheel(mockEvent)

expect(mockEvent.preventDefault).toHaveBeenCalled()
expect(mockEvent.stopPropagation).toHaveBeenCalled()

document.body.removeChild(captureElement)
})
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The test only checks ctrlKey but the implementation also supports metaKey (for macOS Command key). Consider adding a test case for metaKey to ensure both keyboard modifiers work correctly on different platforms.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +103
// Honor wheel capture only when the element is focused
if (event instanceof WheelEvent && !shouldForwardWheelEvent(event)) return
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The forwardEventToCanvas function has new wheel event capture logic but lacks dedicated test coverage. While handleWheel tests indirectly exercise this path, consider adding explicit tests for forwardEventToCanvas to verify it properly respects the focus-based wheel capture when called directly (not just through handleWheel).

Copilot uses AI. Check for mistakes.
Comment on lines +158 to +224
it('should forward wheel events to canvas when capture element is NOT focused', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('legacy')

const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
const textarea = document.createElement('textarea')
captureElement.appendChild(textarea)
document.body.appendChild(captureElement)

const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent()
Object.defineProperty(mockEvent, 'target', { value: textarea })

handleWheel(mockEvent)

expect(mockEvent.preventDefault).toHaveBeenCalled()
expect(mockEvent.stopPropagation).toHaveBeenCalled()

document.body.removeChild(captureElement)
})

it('should NOT forward wheel events when capture element IS focused', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('legacy')

const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
const textarea = document.createElement('textarea')
captureElement.appendChild(textarea)
document.body.appendChild(captureElement)
textarea.focus()

const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent()
Object.defineProperty(mockEvent, 'target', { value: textarea })

handleWheel(mockEvent)

expect(mockEvent.preventDefault).not.toHaveBeenCalled()
expect(mockEvent.stopPropagation).not.toHaveBeenCalled()

document.body.removeChild(captureElement)
})

it('should forward ctrl+wheel to canvas when capture element IS focused in standard mode', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('standard')

const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
const textarea = document.createElement('textarea')
captureElement.appendChild(textarea)
document.body.appendChild(captureElement)
textarea.focus()

const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent(true)
Object.defineProperty(mockEvent, 'target', { value: textarea })

handleWheel(mockEvent)

expect(mockEvent.preventDefault).toHaveBeenCalled()
expect(mockEvent.stopPropagation).toHaveBeenCalled()

document.body.removeChild(captureElement)
})
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

Missing test coverage for standard navigation mode with a regular wheel event (no Ctrl/Meta) when the capture element is NOT focused. This scenario should test whether the wheel event is properly forwarded to the canvas for panning, which is a key part of the PR's goal.

Copilot uses AI. Check for mistakes.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae84b22e43

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +44 to +46
const shouldForwardWheelEvent = (event: WheelEvent): boolean =>
!wheelCapturedByFocusedElement(event) ||
(isStandardNavMode.value && (event.ctrlKey || event.metaKey))

Choose a reason for hiding this comment

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

P2 Badge Avoid forwarding wheel from non-focusable capture zones

Because shouldForwardWheelEvent now returns true whenever the capture element is not focused, any data-capture-wheel element that can’t receive focus will always have its wheel events intercepted by the capture-phase listener (@wheel.capture="canvasInteractions.forwardEventToCanvas" in GraphCanvas.vue). That means the original target never sees the wheel event (preventDefault + stopPropagation happen in capture), so widgets that rely on wheel without focus will stop working. A concrete example is Load3DScene.vue, which marks the container with data-capture-wheel but doesn’t make it focusable; after this change, wheel zoom/pan in the 3D viewer will never fire unless the element is made focusable or the capture logic treats non-focusable elements as captured.

Useful? React with 👍 / 👎.

Copy link
Member Author

Choose a reason for hiding this comment

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

Very true.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it's actually incredible that ChatGPT was the one that came up with the best review comment here. This is actually something that's insane. I don't know how it managed to catch this one where like CodeRabbit and Copilot both missed it

Copy link
Member Author

Choose a reason for hiding this comment

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

So basically the behavior I'm leaning towards right now is that the user would need to click on "Load3D Scene" for them to be able to pan it. I think that's perfectly reasonable because that's really the only solution that lets us let the user pan across the graph without having their pan be stopped in his tracks the second it manages to go over the "Load3D Scene" widget. I think that's reasonable and I made that change

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, how does it feel to use? Maybe we can focus the 3D scene when clicking the node.

Copy link
Member Author

Choose a reason for hiding this comment

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

It feels fine? Then deselection would open another discussion. And I think that complicates things by introducing widget specific exceptions to a global rule.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/renderer/core/canvas/useCanvasInteractions.test.ts`:
- Around line 158-224: The tests in useCanvasInteractions.test.ts are creating
plain objects cast to WheelEvent so event instanceof WheelEvent is false and the
wheel-specific code path in useCanvasInteractions.handleWheel isn't exercised;
update the createMockWheelEvent helper to construct real WheelEvent instances
(e.g., new WheelEvent('wheel', { ctrlKey, metaKey })) and spy on
preventDefault/stopPropagation (or assert defaultPrevented/cancelBubble) so
handleWheel behavior is actually covered when calling
useCanvasInteractions().handleWheel.

In `@src/renderer/core/canvas/useCanvasInteractions.ts`:
- Around line 29-46: Convert the two helper function expressions to function
declarations: change the const wheelCapturedByFocusedElement = (event:
WheelEvent): boolean => { ... } into function
wheelCapturedByFocusedElement(event: WheelEvent): boolean { ... } and change the
const shouldForwardWheelEvent = (event: WheelEvent): boolean => ... into
function shouldForwardWheelEvent(event: WheelEvent): boolean { return ... } so
they follow the repo guideline for pure helpers while preserving existing
behavior and references.

Comment on lines +158 to +224
it('should forward wheel events to canvas when capture element is NOT focused', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('legacy')

const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
const textarea = document.createElement('textarea')
captureElement.appendChild(textarea)
document.body.appendChild(captureElement)

const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent()
Object.defineProperty(mockEvent, 'target', { value: textarea })

handleWheel(mockEvent)

expect(mockEvent.preventDefault).toHaveBeenCalled()
expect(mockEvent.stopPropagation).toHaveBeenCalled()

document.body.removeChild(captureElement)
})

it('should NOT forward wheel events when capture element IS focused', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('legacy')

const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
const textarea = document.createElement('textarea')
captureElement.appendChild(textarea)
document.body.appendChild(captureElement)
textarea.focus()

const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent()
Object.defineProperty(mockEvent, 'target', { value: textarea })

handleWheel(mockEvent)

expect(mockEvent.preventDefault).not.toHaveBeenCalled()
expect(mockEvent.stopPropagation).not.toHaveBeenCalled()

document.body.removeChild(captureElement)
})

it('should forward ctrl+wheel to canvas when capture element IS focused in standard mode', () => {
const { get } = useSettingStore()
vi.mocked(get).mockReturnValue('standard')

const captureElement = document.createElement('div')
captureElement.setAttribute('data-capture-wheel', 'true')
const textarea = document.createElement('textarea')
captureElement.appendChild(textarea)
document.body.appendChild(captureElement)
textarea.focus()

const { handleWheel } = useCanvasInteractions()
const mockEvent = createMockWheelEvent(true)
Object.defineProperty(mockEvent, 'target', { value: textarea })

handleWheel(mockEvent)

expect(mockEvent.preventDefault).toHaveBeenCalled()
expect(mockEvent.stopPropagation).toHaveBeenCalled()

document.body.removeChild(captureElement)
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use real WheelEvent instances so the instanceof paths are exercised.

The new tests use a plain object cast to WheelEvent, so event instanceof WheelEvent is false and the wheel-specific forwarding path isn’t actually exercised. Consider constructing real WheelEvent instances and spying on preventDefault/stopPropagation (or assert defaultPrevented/cancelBubble) to ensure the behavior is covered.

✅ Example helper update (uses real WheelEvent)
function createMockWheelEvent(ctrlKey = false, metaKey = false): WheelEvent {
  const event = new WheelEvent('wheel', { ctrlKey, metaKey })
  vi.spyOn(event, 'preventDefault')
  vi.spyOn(event, 'stopPropagation')
  return event
}

Based on learnings: Aim for behavioral coverage of critical and new features.

🤖 Prompt for AI Agents
In `@src/renderer/core/canvas/useCanvasInteractions.test.ts` around lines 158 -
224, The tests in useCanvasInteractions.test.ts are creating plain objects cast
to WheelEvent so event instanceof WheelEvent is false and the wheel-specific
code path in useCanvasInteractions.handleWheel isn't exercised; update the
createMockWheelEvent helper to construct real WheelEvent instances (e.g., new
WheelEvent('wheel', { ctrlKey, metaKey })) and spy on
preventDefault/stopPropagation (or assert defaultPrevented/cancelBubble) so
handleWheel behavior is actually covered when calling
useCanvasInteractions().handleWheel.

Comment on lines +29 to +46
/**
* Returns true if the wheel event target is inside an element that should
* capture wheel events AND that element (or a descendant) currently has focus.
*
* This allows two-finger panning to continue over inputs until the user has
* actively focused the widget, at which point the widget can consume scroll.
*/
const wheelCapturedByFocusedElement = (event: WheelEvent): boolean => {
const target = event.target as HTMLElement | null
const captureElement = target?.closest('[data-capture-wheel="true"]')
const active = document.activeElement as Element | null

return !!(captureElement && active && captureElement.contains(active))
}

const shouldForwardWheelEvent = (event: WheelEvent): boolean =>
!wheelCapturedByFocusedElement(event) ||
(isStandardNavMode.value && (event.ctrlKey || event.metaKey))
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Prefer function declarations for the new pure helpers.

Both helpers are pure and can be declared as functions to match the repo guideline.
[replacements below keep behavior unchanged]

♻️ Suggested refactor
-  const wheelCapturedByFocusedElement = (event: WheelEvent): boolean => {
+  function wheelCapturedByFocusedElement(event: WheelEvent): boolean {
     const target = event.target as HTMLElement | null
     const captureElement = target?.closest('[data-capture-wheel="true"]')
     const active = document.activeElement as Element | null

     return !!(captureElement && active && captureElement.contains(active))
   }

-  const shouldForwardWheelEvent = (event: WheelEvent): boolean =>
-    !wheelCapturedByFocusedElement(event) ||
-    (isStandardNavMode.value && (event.ctrlKey || event.metaKey))
+  function shouldForwardWheelEvent(event: WheelEvent): boolean {
+    return (
+      !wheelCapturedByFocusedElement(event) ||
+      (isStandardNavMode.value && (event.ctrlKey || event.metaKey))
+    )
+  }

As per coding guidelines: Do not use function expressions if it is possible to use function declarations instead.

📝 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
/**
* Returns true if the wheel event target is inside an element that should
* capture wheel events AND that element (or a descendant) currently has focus.
*
* This allows two-finger panning to continue over inputs until the user has
* actively focused the widget, at which point the widget can consume scroll.
*/
const wheelCapturedByFocusedElement = (event: WheelEvent): boolean => {
const target = event.target as HTMLElement | null
const captureElement = target?.closest('[data-capture-wheel="true"]')
const active = document.activeElement as Element | null
return !!(captureElement && active && captureElement.contains(active))
}
const shouldForwardWheelEvent = (event: WheelEvent): boolean =>
!wheelCapturedByFocusedElement(event) ||
(isStandardNavMode.value && (event.ctrlKey || event.metaKey))
/**
* Returns true if the wheel event target is inside an element that should
* capture wheel events AND that element (or a descendant) currently has focus.
*
* This allows two-finger panning to continue over inputs until the user has
* actively focused the widget, at which point the widget can consume scroll.
*/
function wheelCapturedByFocusedElement(event: WheelEvent): boolean {
const target = event.target as HTMLElement | null
const captureElement = target?.closest('[data-capture-wheel="true"]')
const active = document.activeElement as Element | null
return !!(captureElement && active && captureElement.contains(active))
}
function shouldForwardWheelEvent(event: WheelEvent): boolean {
return (
!wheelCapturedByFocusedElement(event) ||
(isStandardNavMode.value && (event.ctrlKey || event.metaKey))
)
}
🤖 Prompt for AI Agents
In `@src/renderer/core/canvas/useCanvasInteractions.ts` around lines 29 - 46,
Convert the two helper function expressions to function declarations: change the
const wheelCapturedByFocusedElement = (event: WheelEvent): boolean => { ... }
into function wheelCapturedByFocusedElement(event: WheelEvent): boolean { ... }
and change the const shouldForwardWheelEvent = (event: WheelEvent): boolean =>
... into function shouldForwardWheelEvent(event: WheelEvent): boolean { return
... } so they follow the repo guideline for pure helpers while preserving
existing behavior and references.

Copy link
Contributor

@christian-byrne christian-byrne left a comment

Choose a reason for hiding this comment

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

LGTM, at least we can test how this feels

@jtydhr88 jtydhr88 merged commit 4e20b75 into main Feb 1, 2026
27 checks passed
@jtydhr88 jtydhr88 deleted the bl-visual-impala-358 branch February 1, 2026 14:32
@dosubot
Copy link

dosubot bot commented Feb 1, 2026

Related Documentation

No published documentation to review for changes on this repository.

Write your first living document

How did I do? Any feedback?  Join Discord

christian-byrne added a commit that referenced this pull request Feb 14, 2026
Remove `disabled` attribute from read-only preview textareas — disabled
elements cannot receive focus, so the focus-gated wheel capture logic
(#6597) always forwards wheel events to the canvas instead of allowing
scroll.

Add `data-capture-wheel` and `tabindex` to WidgetMarkdown display div
so rendered markdown content can also capture wheel events when focused.

Fixes COM-14812

Amp-Thread-ID: https://ampcode.com/threads/T-019c5a22-0e4c-7217-b022-49f9c86b8790
christian-byrne added a commit that referenced this pull request Feb 20, 2026
Remove `disabled` attribute from read-only preview textareas — disabled
elements cannot receive focus, so the focus-gated wheel capture logic
(#6597) always forwards wheel events to the canvas instead of allowing
scroll.

Add `data-capture-wheel` and `tabindex` to WidgetMarkdown display div
so rendered markdown content can also capture wheel events when focused.

Fixes COM-14812

Amp-Thread-ID: https://ampcode.com/threads/T-019c5a22-0e4c-7217-b022-49f9c86b8790
christian-byrne added a commit that referenced this pull request Feb 20, 2026
## Summary

Restore mouse-wheel scrolling for read-only preview widgets (PreviewAny
plaintext and markdown modes), broken by the focus-gated wheel capture
in #6597.

## Changes

- **What**: Remove `disabled` attribute from read-only textareas (keep
`readonly`) so they can receive focus and capture wheel events. Add
`data-capture-wheel` and `tabindex` to WidgetMarkdown display div.
- **Root cause**: `disabled` elements cannot receive focus in browsers.
The focus-gated `wheelCapturedByFocusedElement()` from #6597 always
evaluated to false for disabled textareas, forwarding all wheel events
to the canvas.

## Review Focus

- Verify that removing `disabled` while keeping `readonly` does not
allow unintended editing
- Confirm `tabindex="0"` on the markdown display div does not cause
unexpected tab-order issues
- Ensure trackpad panning over unfocused widgets (#6523) still works
correctly

Fixes COM-14812

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8863-fix-restore-mouse-wheel-scrolling-in-preview-as-text-outputs-3076d73d365081719bf5e453235bb2b5)
by [Unito](https://www.unito.io)
github-actions bot pushed a commit that referenced this pull request Feb 20, 2026
## Summary

Restore mouse-wheel scrolling for read-only preview widgets (PreviewAny
plaintext and markdown modes), broken by the focus-gated wheel capture
in #6597.

## Changes

- **What**: Remove `disabled` attribute from read-only textareas (keep
`readonly`) so they can receive focus and capture wheel events. Add
`data-capture-wheel` and `tabindex` to WidgetMarkdown display div.
- **Root cause**: `disabled` elements cannot receive focus in browsers.
The focus-gated `wheelCapturedByFocusedElement()` from #6597 always
evaluated to false for disabled textareas, forwarding all wheel events
to the canvas.

## Review Focus

- Verify that removing `disabled` while keeping `readonly` does not
allow unintended editing
- Confirm `tabindex="0"` on the markdown display div does not cause
unexpected tab-order issues
- Ensure trackpad panning over unfocused widgets (#6523) still works
correctly

Fixes COM-14812

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8863-fix-restore-mouse-wheel-scrolling-in-preview-as-text-outputs-3076d73d365081719bf5e453235bb2b5)
by [Unito](https://www.unito.io)
github-actions bot pushed a commit that referenced this pull request Feb 20, 2026
## Summary

Restore mouse-wheel scrolling for read-only preview widgets (PreviewAny
plaintext and markdown modes), broken by the focus-gated wheel capture
in #6597.

## Changes

- **What**: Remove `disabled` attribute from read-only textareas (keep
`readonly`) so they can receive focus and capture wheel events. Add
`data-capture-wheel` and `tabindex` to WidgetMarkdown display div.
- **Root cause**: `disabled` elements cannot receive focus in browsers.
The focus-gated `wheelCapturedByFocusedElement()` from #6597 always
evaluated to false for disabled textareas, forwarding all wheel events
to the canvas.

## Review Focus

- Verify that removing `disabled` while keeping `readonly` does not
allow unintended editing
- Confirm `tabindex="0"` on the markdown display div does not cause
unexpected tab-order issues
- Ensure trackpad panning over unfocused widgets (#6523) still works
correctly

Fixes COM-14812

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8863-fix-restore-mouse-wheel-scrolling-in-preview-as-text-outputs-3076d73d365081719bf5e453235bb2b5)
by [Unito](https://www.unito.io)
huntcsg pushed a commit that referenced this pull request Feb 21, 2026
## Summary

Restore mouse-wheel scrolling for read-only preview widgets (PreviewAny
plaintext and markdown modes), broken by the focus-gated wheel capture
in #6597.

## Changes

- **What**: Remove `disabled` attribute from read-only textareas (keep
`readonly`) so they can receive focus and capture wheel events. Add
`data-capture-wheel` and `tabindex` to WidgetMarkdown display div.
- **Root cause**: `disabled` elements cannot receive focus in browsers.
The focus-gated `wheelCapturedByFocusedElement()` from #6597 always
evaluated to false for disabled textareas, forwarding all wheel events
to the canvas.

## Review Focus

- Verify that removing `disabled` while keeping `readonly` does not
allow unintended editing
- Confirm `tabindex="0"` on the markdown display div does not cause
unexpected tab-order issues
- Ensure trackpad panning over unfocused widgets (#6523) still works
correctly

Fixes COM-14812

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8863-fix-restore-mouse-wheel-scrolling-in-preview-as-text-outputs-3076d73d365081719bf5e453235bb2b5)
by [Unito](https://www.unito.io)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants