Phase 2 of the review: browse the whole session instead of only the rolling
window, diagnose a calibration from the recorded points, and provide the
reviewed two-panel layout.
What
- SessionStore gains extent(), visible(t0, t1, max_points) and
statistics(t0, t1, unit). visible() walks only the blocks that overlap the
window and decimates by taking each bucket's minimum and maximum, so a spike
survives decimation and browsing a long session does not copy all of it.
- ForcePlotsPanel gains follow/browse modes, a whole-session overview strip
with the current window, pan and wheel zoom, a rubber-band zoom toggle,
a shared time cursor across all three channels, and a selection readout
(duration, valid samples, per-axis peak/mean/std).
- CalibrationPoint records a bounded per-axis trace, an excluded flag, a
validation flag and its fit residual. CalibrationRecorder gains
set_excluded/set_validation/fit; the fit is a least-squares line over the
non-excluded, non-validation points with per-point residuals and an
independent validation error.
- The calibration table becomes the reviewed diagnostic view
(reference load | FN | FX | FY | stability | state) with the residual shown
next to the loaded axis, plus exclude/restore and mark-validation actions and
a fit summary line. Double clicking a row opens the point's three-axis trace.
- Add a layout preset: free layout or 2D mode (depth map on the left, force
charts on the right, calibration folded away), persisted in preferences.
Why
- Browsing is a display pause, not a capture pause: the record channel keeps
running while the charts show an older window.
- Excluding a point removes it from the fit and from the exported calibration
but keeps it on record, so the evidence for the decision is not destroyed.
- The non-loaded axes are recorded but never given a reference load: their true
load is unknown during a single-axis calibration, so it stays empty.
Verification
- shared/tests/test_record_channel.py grows to 18 cases, covering extent,
peak-preserving decimation, windowed reads, inverted/empty windows, unit-aware
statistics and the time origin.
- tools/ui_check.py gains "session browsing" and "layout preset" sections
(223 checks, 0 failures overall).
- apps/studio/tests/test_ui_smoke.py covers the same invariants.
Not in this change: synchronised playback of video, depth and curves. It needs
a decision on how depth is stored for replay (per-frame PNG, reduced-rate PNG,
or offline recomputation); the cursor plumbing (cursor_moved / set_cursor) is
already in place for it.