App startup with on-demand panels

huggingface/agent-manager · issue #133 · production builds of main@ef08e84 (before) and perf/lazy-panels-133 (after), same fixture fleet, same Chromium 153.0.8010.12, same machine, 5 cold visits per cell, medians (min–max).

Initial JS, cold Overview795 kB → 697 kB-12% · gzip 234 kB → 206 kB
Reader usable, mobile-like6029 → 5515 ms-9%
Reader usable, desktop (within run-to-run noise)209 → 200 ms195–277 → 196–354
Overview usable, mobile-like5250 → 4760 ms-9%

Desktop · 1280×800 · no throttling

Cold visit: fresh browser profile, HTTP cache disabled, the remembered selection seeded in storage. nav = sidebar lists sessions; view = the selected view's first useful interaction; script = V8 ScriptDuration (parse, compile and run of all JS so far) at that moment — this Chromium reports V8CompileDuration as 0, so it is not shown separately; JS = script requests and bytes on the wire until then (the server does not compress; gzip computed from the same files).

scenarioJS requestsJS bytesgzipscript msnav msview ms
Overview (cold)1 → 1795 kB → 697 kB
-12%
234 kB → 206 kB
-12%
83 → 57
-31%
167 → 128
126–204 → 113–153
167 → 128
-23% · 126–205 → 113–153
Restored Reader session1 → 1795 kB → 697 kB
-12%
234 kB → 206 kB
-12%
110 → 109
-2%
94 → 84
87–132 → 79–199
209 → 200
-4% · 195–277 → 196–354
Restored empty session (composer)1 → 1795 kB → 697 kB
-12%
234 kB → 206 kB
-12%
97 → 66
-32%
110 → 80
102–122 → 78–90
224 → 154
-31% · 194–274 → 151–163
Restored Files pane1 → 2795 kB → 729 kB
-8%
234 kB → 216 kB
-8%
94 → 76
-20%
105 → 81
83–129 → 77–87
225 → 164
-27% · 186–274 → 159–176
Restored Trace pane1 → 2795 kB → 709 kB
-11%
234 kB → 210 kB
-10%
188 → 108
-42%
146 → 79
140–239 → 76–136
387 → 188
-51% · 277–443 → 181–291
Restored group: Files + Trace1 → 3795 kB → 741 kB
-7%
234 kB → 221 kB
-6%
178 → 136
-23%
146 → 96
84–212 → 77–125
336 → 235
-30% · 214–486 → 215–308

desktop: opening a deferred panel from a warm Overview (ms, click → first useful content)

panelfirst open, beforefirst open, after (chunk on the wire)reopen, beforereopen, after (module cached)
Settings (General)108 (98–138)116 (83–123)35 (34–44)35 (35–43)
Settings › Usage68 (61–1838)75 (66–1831)73 (58–75)74 (57–74)
Settings › API log62 (50–65)70 (54–72)67 (65–115)64 (61–116)
Settings › Skills41 (37–42)58 (55–60)41 (39–43)42 (39–44)
Settings › Cron49 (45–50)67 (53–72)38 (37–39)38 (37–45)
Files pane68 (62–77)57 (54–116)56 (52–73)54 (52–58)
Trace pane99 (93–125)119 (117–123)63 (62–68)62 (46–66)

A panel counts as open at its first useful content — for Usage a populated trace row, for the API log a real row, for the others their form and controls — so these numbers include each panel’s own API round trips; the frame and its skeletons appear earlier and are not counted. "After" adds one chunk request on top; the difference between the columns is the real cost moved from startup to first open.

Mobile-like · 390×844 · 4× CPU slowdown · 1.6 Mbps down / 150 ms RTT

Cold visit: fresh browser profile, HTTP cache disabled, the remembered selection seeded in storage. nav = sidebar lists sessions; view = the selected view's first useful interaction; script = V8 ScriptDuration (parse, compile and run of all JS so far) at that moment — this Chromium reports V8CompileDuration as 0, so it is not shown separately; JS = script requests and bytes on the wire until then (the server does not compress; gzip computed from the same files).

scenarioJS requestsJS bytesgzipscript msnav msview ms
Overview (cold)1 → 1795 kB → 697 kB
-12%
234 kB → 206 kB
-12%
249 → 257
+3%
5250 → 4760
5240–5260 → 4728–4760
5250 → 4760
-9% · 5240–5260 → 4728–4760
Restored Reader session1 → 1795 kB → 697 kB
-12%
234 kB → 206 kB
-12%
482 → 492
+2%
5244 → 4733
5234–5259 → 4723–4800
6029 → 5515
-9% · 6014–6060 → 5500–5573
Restored empty session (composer)1 → 1795 kB → 697 kB
-12%
234 kB → 206 kB
-12%
324 → 304
-6%
5244 → 4732
5243–5276 → 4727–4749
5646 → 5121
-9% · 5623–5671 → 5103–5161
Restored Files pane1 → 2795 kB → 729 kB
-8%
234 kB → 216 kB
-8%
365 → 356
-3%
5250 → 4736
5238–5264 → 4730–4763
5905 → 5739
-3% · 5889–5917 → 5736–5774
Restored Trace pane1 → 2795 kB → 709 kB
-11%
234 kB → 210 kB
-10%
690 → 622
-10%
5257 → 4734
5250–5266 → 4718–4745
6014 → 5667
-6% · 6006–6037 → 5663–5692
Restored group: Files + Trace1 → 2795 kB → 729 kB
-8%
234 kB → 216 kB
-8%
355 → 354
-0%
5254 → 4741
5233–5298 → 4727–4761
5907 → 5750
-3% · 5876–5947 → 5723–5781

mobile: opening a deferred panel from a warm Overview (ms, click → first useful content)

panelfirst open, beforefirst open, after (chunk on the wire)reopen, beforereopen, after (module cached)
Settings (General)262 (193–273)948 (937–989)84 (69–94)83 (82–86)
Settings › Usage916 (898–966)875 (847–885)865 (381–869)865 (861–867)
Settings › API log314 (290–345)838 (835–862)332 (322–345)332 (253–337)
Settings › Skills53 (49–71)224 (219–239)46 (40–52)46 (41–65)
Settings › Cron127 (114–132)327 (317–365)92 (88–108)94 (76–96)

A panel counts as open at its first useful content — for Usage a populated trace row, for the API log a real row, for the others their form and controls — so these numbers include each panel’s own API round trips; the frame and its skeletons appear earlier and are not counted. "After" adds one chunk request on top; the difference between the columns is the real cost moved from startup to first open.

What moved out of the entry chunk

chunkbeforeaftergzip after
entry (index-BlE9PEIk.js → index-lCObQ1kp.js)795 kB697 kB (-12%)206 kB (was 234 kB)
FilesPane-BVsT0rP4.js on demand—31 kB11 kB
SettingsView-BYVs4rU2.js on demand—24 kB7.4 kB
ApiLog-CWXJkFFk.js on demand—15 kB5.8 kB
CronSettings-BGxKiLar.js on demand—12 kB4.2 kB
TracePane-3a5Oub0k.js on demand—12 kB4.4 kB
UsagePanel-DcQgRv7A.js on demand—5.6 kB2.0 kB
SkillsEditor-BuQe52yR.js on demand—3.6 kB1.4 kB

Unchanged and deliberately kept: the CodeMirror core/languages chunks and the PDF chunks were already dynamic. The entry still carries xterm, react-dom, marked and dompurify — the terminal/Reader pane needs them and they are out of scope here.

Conditions

Regression budgets (from these runs)

Independent reproduction

The PR reviewer rebuilt both SHAs and reran the harness with the same settings on the same box (load 5.2–7.7): mobile-like Overview 5246 → 4752 ms, restored Reader 6028 → 5535 ms, Files 5880 → 5743, Trace 5995 → 5662, mixed group 5886 → 5758 — the same gain, outside the sample ranges. Desktop was flat there (Overview 88 → 81 ms, Reader 195 → 195, mixed group 195 → 220), so the desktop cells on this page should be read as noise around zero, and the improvement described as less to download and wait for at startup — not a CPU speedup: V8 script time is broadly flat for Overview/Reader in both runs. The first-open trade reproduced too (mobile Settings 199 → 935 ms, API log 104 → 855; warm 90 → 85 and 76 → 73).