feat: multi-row braille sparklines with bucket max-pooling resampling (#275)
## Summary Reworks `src/ui/braille.rs` to add a multi-row braille sparkline API and replace nearest-neighbour resampling with bucket max-pooling, per Part 1 of the sparkline readability chain (#272 -> #273 -> #274). No call-site changes: `sparkline_braille` keeps its existing signature and behaviour contract, so the header, GPU Metrics panel, and remote panel sparklines automatically benefit from the improved resampling. ## What changed - Added `sparkline_braille_rows(data, width, rows, range) -> Vec<String>` as the shared rendering core, returning one string per terminal row (top row first), with `rows * 4` vertical dot levels filled bottom-up across the whole stack (btop-style). - Reimplemented `sparkline_braille` as a thin wrapper over `sparkline_braille_rows(..., rows = 1, ...)`, so `rows == 1` output is produced by the same shared code path rather than a separate implementation. - Replaced the nearest-neighbour `resample` closure with bucket max-pooling: each of the `width * 2` braille sub-columns owns a contiguous, non-empty bucket of the input series, and the rendered level uses the bucket's maximum finite value. Buckets stretch (repeat samples) when `data.len() < width * 2`, the rightmost sub-column always covers the most recent sample, and an all-non-finite bucket clamps to the range minimum. - Preserved all existing edge-case behaviour: empty data renders `width` spaces, `width == 0` renders empty output, constant data or a degenerate/non-finite range fills only the bottom dot row, and NaN/non-finite samples clamp to the range minimum. - Extended the module doc comment to describe the multi-row model and the bucket max-pooling resampling scheme. - Added unit tests covering multi-row dimensions, level continuity across row boundaries (a 50%-of-range value with `rows = 2` fills the entire bottom terminal row), spike preservation at width 8, `rows == 1` parity with the wrapper, bucket max-pooling correctness against a known bucket layout, bucket stretching when `data.len() < width * 2` (a sample repeated across sub-columns, with the rightmost sub-column still covering the most recent sample), and the multi-row empty/zero-width/zero-rows edge cases. Note on the "rows == 1 parity" acceptance criterion: it is satisfied by construction, since `sparkline_braille` now calls `sparkline_braille_rows(..., 1, ...)` directly, not by byte-for-byte matching the old nearest-neighbour output. The resampling change intentionally alters rendered output in places where nearest-neighbour previously dropped peaks between sampled indices; all pre-existing unit tests (which assert lengths and edge cases, not nearest-neighbour-specific output strings) pass unmodified. ## Test plan - [x] `cargo test --lib ui::braille` (18 tests, all pass) - [x] `cargo test --lib ui::gpu_sparkline_panel` (20 tests, all pass, no call-site regression) - [x] `cargo check --lib --tests` - [x] `cargo clippy --lib --tests -- -D warnings` - [x] `cargo fmt --check` Closes #272
J
Jeongkyu Shin committed
d3306f62c566d061b9729f266fb440316e01e6a0
Parent: 2476f7e
Committed by GitHub <noreply@github.com>
on 7/20/2026, 12:39:22 AM