perf(viz): lazy-load SignalR via dynamic import (−54 KB main bundle) - #82
Conversation
The main bundle has been creeping up against the 800 KiB cap (98.8 % after PR #79) and the next non-trivial feature would have failed CI. Defer the `@microsoft/signalr` runtime until `start()` runs. - Replace eager `import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr'` with `import type { HubConnection }` (type-only, zero runtime cost). - Make `connection` a module-scoped `let` initialised inside `start()`, via `await import('@microsoft/signalr')`. First start triggers a separate chunk fetch; subsequent reconnects reuse the cached module AND the same `HubConnection` instance. - Wrap handler registration in `_wireConnection(c)` so it runs once after the lazy build, before the first `connection.start()` call. Bundle delta: main: 809.0 → 753.6 KB (−55 KB; 98.8 % → 92.0 % of cap) esm: — → 55.3 KB (new lazy chunk for SignalR) sensor: 20.5 → 20.5 KB (unchanged) The first paint no longer blocks on parsing 55 KB of SignalR; the ~50 ms chunk fetch overlaps with the existing async boot path (`_autoSpawnIfEmpty`, terrain loading) so user-visible startup time is unchanged in practice. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughSignalR initialization has been refactored from eager loading at module startup to lazy loading on first Changes
Sequence Diagram(s)sequenceDiagram
participant Client as App Client
participant Module as SignalR Module
participant Hub as HubConnection
Note over Client,Hub: Previous (Eager Initialization)
Client->>Module: import at module load
Module-->>Client: SignalR library
Client->>Hub: new HubConnectionBuilder().build()
Hub-->>Client: connection instance
Note over Client: Connection ready but unused until start()
Note over Client,Hub: New (Lazy Initialization)
Client->>Client: Module loads, connection = null
Client->>Client: start() called
Client->>Module: dynamic import('@microsoft/signalr')
Module-->>Client: SignalR library
Client->>Hub: new HubConnectionBuilder().build()
Hub-->>Client: connection instance
Client->>Client: _wireConnection(connection)
Note over Client: Event handlers wired
Client->>Hub: connect()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Review rate limit: 0/1 reviews remaining, refill in 44 minutes and 53 seconds.Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/ResQ.Viz.Web/client/app.ts`:
- Around line 743-748: The project currently uses SignalR client code
(HubConnectionBuilder, LogLevel and the connection creation logic) that is fine,
but package.json declares "@microsoft/signalr": "^10.0.0"; update
src/ResQ.Viz.Web/package.json to set "@microsoft/signalr": "^8.0.0" to comply
with the TypeScript client requirement, then reinstall dependencies (npm/Yarn
install) so the HubConnectionBuilder/LogLevel imports resolve against SignalR
8.x.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6bd3c2bf-1eaf-4a8e-a578-a0095d9d2ac3
📒 Files selected for processing (1)
src/ResQ.Viz.Web/client/app.ts
There was a problem hiding this comment.
Code Review
This pull request implements lazy loading for the SignalR runtime in the web client to improve initial load performance and reduce the main bundle size. The @microsoft/signalr dependency is now dynamically imported within the start function, and connection event handlers have been refactored into a _wireConnection helper. I have no feedback to provide.
CodeRabbit flagged this PR for declaring `@microsoft/signalr` ^10 because CLAUDE.md still said the project uses signalr 8. The package was bumped to 10 alongside the .NET 10 backend (older PR); CLAUDE.md just wasn't updated. Bring the line into sync with reality and note that signalr now ships as a lazy chunk per this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surfaces the LosQueryStats added in PRs #78/#79 in a small dev/audit panel. Lets an operator confirm the WebGPU sensor primitive is healthy without dropping into devtools — useful both for the 2026-05-12 audit routine (P2 #5) and during dev when tuning ring sizing or world extents. - New `client/sensorStatsOverlay.ts` — `SensorStatsOverlay` class builds a fixed bottom-left panel, hidden by default. Press 'i' (KeyI; ignored when typing in inputs) to toggle. - Two sections (mesh-link, lidar) showing totalQueries / totalRays / peakSlotDepth / raysOutsideWorld. Cells colour amber when peak slot depth > 1 (callers queueing) or outside-AABB > 0 (sim is outside the brick map's coverage; project memory documents the 4 km terrain vs 1 km sensor world gap). - `update()` is called once per `ReceiveFrame`; short-circuits when the panel is hidden, so the closed-overlay cost is one boolean. - Falls back gracefully when `getSensorContext()` returns null (no-WebGPU browser, init failure) — the panel just shows "offline". Bundle: 809.0 → 811.3 KB (+2.3 KB; 99.0 % of the 800 KiB cap). Lands much more comfortably on top of #82 (defer-signalr), which would take this combined to ~756 KB. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(viz): sensor-stats overlay (toggle with 'i') Surfaces the LosQueryStats added in PRs #78/#79 in a small dev/audit panel. Lets an operator confirm the WebGPU sensor primitive is healthy without dropping into devtools — useful both for the 2026-05-12 audit routine (P2 #5) and during dev when tuning ring sizing or world extents. - New `client/sensorStatsOverlay.ts` — `SensorStatsOverlay` class builds a fixed bottom-left panel, hidden by default. Press 'i' (KeyI; ignored when typing in inputs) to toggle. - Two sections (mesh-link, lidar) showing totalQueries / totalRays / peakSlotDepth / raysOutsideWorld. Cells colour amber when peak slot depth > 1 (callers queueing) or outside-AABB > 0 (sim is outside the brick map's coverage; project memory documents the 4 km terrain vs 1 km sensor world gap). - `update()` is called once per `ReceiveFrame`; short-circuits when the panel is hidden, so the closed-overlay cost is one boolean. - Falls back gracefully when `getSensorContext()` returns null (no-WebGPU browser, init failure) — the panel just shows "offline". Bundle: 809.0 → 811.3 KB (+2.3 KB; 99.0 % of the 800 KiB cap). Lands much more comfortably on top of #82 (defer-signalr), which would take this combined to ~756 KB. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(viz): tighten overlay keybind + align CSS with design tokens Address Gemini review on PR #83: - Keybind: extend the input-bail list to cover SELECT (the operator may type 'i' to jump-search options), and require no shift key (uppercase 'I' shouldn't toggle a dev panel from within prose typed elsewhere). - CSS: replace `--fg` / `--muted` with the project's canonical `--text` / `--text-muted` (defined in `:root` lines 18-36); fix the `--warning` fallback to the palette colour `#d29922`. - Add `body.investor-mode .sensor-stats-overlay { display: none }` so the panel disappears during cinematic recordings — matches the visibility rule for the event log and telemetry strip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
The main bundle has been creeping against the 800 KiB cap (98.8% after #79). The next non-trivial main-bundle feature would have failed CI. Defer
@microsoft/signalruntilstart()runs.Bundle delta
Mechanics
import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr'→import type { HubConnection }(type-only, zero runtime cost).connectionis a module-scopedlet HubConnection | null, initialized insidestart()viaawait import('@microsoft/signalr'). Subsequent reconnects reuse the cached module AND the sameHubConnectioninstance._wireConnection(c)so it runs once after the lazy build.Cost
~50 ms one-time chunk fetch on first
start(). Overlaps with the existing async boot path (_autoSpawnIfEmpty, terrain loading) so user-visible startup time is unchanged in practice.Test plan
npm run buildpasses; main bundle 753.6 KB, well under 819.2 KB captsc --noEmitclean/and confirm SignalR connection comes up; check Network tab for the deferredesm-*.jschunk🤖 Generated with Claude Code
Summary by CodeRabbit