Commit Graph
2 Commits
Author SHA1 Message Date
ChuckandClaude Sonnet 5.5 1bcb524fc3 feat(scroll): report a panel that cannot reach its refresh cap, and suggest one it can hold (#759)
* feat(scroll): report a panel that cannot reach its refresh cap, and suggest one it can hold

Scroll speeds are solved against display.hardware.limit_refresh_rate_hz,
which is only a ceiling. A panel that cannot reach it still moves whole
pixels per frame, but every scroll runs slow by the shortfall and the
"smooth" ladder is the cap's, not the panel's. A user rig (Pi 4, 2x128x64,
adafruit-hat-pwm, pwm_bits 9, gpio_slowdown 5) measured 107.6-113.1 Hz under
a 120 Hz cap: 60 px/s ran at 55, and nothing said why.

- scroll_config: refresh_shortfall() (more than 3% under the planned rate),
  holdable_cap() (a multiple of 10, 5% under the measurement, since the
  measurement is the fast end of an uncapped panel's drift), and
  describe_refresh_shortfall().
- FrameTimingRecorder.plan_refresh(): once the measured period has held for
  three trusted windows, a shortfall is logged once as a warning naming the
  cap to use. DisplayManager calls it only for a real panel, not the
  emulator or the fallback canvas. The stats file records
  planned_refresh_hz (additive).
- GET /api/v3/config/refresh-rate, plus a hint under the Display tab's
  Limit Refresh Rate field (js/pages/display.js) with a button that fills in
  the suggested cap.
- _panel_refresh_hz (behind the Vegas slider's advice) ignores a measurement
  written under a different cap, so a changed cap stops being advised from
  the old rate before the display restarts.

Verified on ledpi with a temporary 200 Hz cap: the warning logged about a
minute after the restart ("about 132 Hz ... Set Limit Refresh Rate to
120 Hz"), the endpoint returned the same shortfall, and the Display tab
showed the hint; its button filled in 120. ledpi was restored afterwards.
Rebased onto main after the Display tab became an ES-module page (#771); the
hint moved from inline script into display.js, with a jsdom test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(scroll): count only agreeing windows toward the refresh shortfall; ignore a null planned rate

Review fixes on #759.

- A window the period estimate rejects (more than MAX_REFRESH_DROP faster
  than the adopted period) no longer counts toward the shortfall check, and
  it restarts the run. After a loaded start fixed a slow period, later
  windows at the real, faster rate were rejected yet still counted, so the
  warning could name the slow rate against a cap the panel was meeting. It
  now needs REFRESH_CHECK_WINDOWS consecutive windows that agree with the
  period.
- _panel_refresh_hz treats a stats file whose planned_refresh_hz key is
  present but null as no measurement. The emulator and the fallback canvas
  write it that way (DisplayManager never plans a refresh for them), and
  their frame rate says nothing about the cap. A file with no such key (an
  older display) keeps the old behaviour.

Three new tests fail on the previous code and pass now.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 10:40:08 -04:00
ChuckandClaude Opus 5.5 c20c0beac2 feat(web): the Display tab is an ES-module page, with a page-visibility service (stage 4) (#771)
* feat(web): the Display tab is an ES-module page, with a page-visibility service (stage 4)

- core/visibility.js: each page gets ctx.visibility (whileVisible, every,
  isVisible). Work registered there runs only while the page's tab is
  active and the browser tab visible, and ends when the page is swapped
  out. It reads the active tab from window.LEDVisibility, so it agrees
  with the classic partials. The registry gained a mountContext option for
  per-mount services.
- pages/display.js replaces display.html's two inline scripts. The 5 s
  sync status poll runs through ctx.visibility.every; the status and
  scroll-speed hint requests go through ctx.api with ctx.signal, as does
  the Vegas order widget's plugin-list request. The Advanced toggle is a
  delegated data-action; window.updateSyncUI is a deprecated alias.
- New DOM suites test_visibility_service.js and test_display_page.js;
  test_display_partial_ids.js imports the module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(web): no computed keys in the Display page's readout and destroy

Codacy's object-injection rule flagged v[id] and ctx.state[name].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:53:43 -04:00