feat(ui): two-depth controls instrument + ambient surface + L2 widget/motion kit - #71
Merged
Conversation
Introduce a thin adapter that uses ratatui widgets and tui-input purely as libraries, with no terminal backend: widgets render into a detached Buffer and the result is blitted into the existing RenderedOverlay contract, so the hand-rolled compositor and ANSI emission path are untouched. Both crates are added with default-features = false, which keeps a single crossterm in the tree and adds ~65KB to the release binary. - buffer_to_overlay: detached ratatui Buffer -> RenderedOverlay - build_config_browser: PanelBuilder chrome + ratatui List/ListState scroll - build_config_save: PanelBuilder chrome + tui-input field with block caret
Wire the two interactive overlays through the adapter: - Config browser scroll window is now driven by ratatui's ListState (auto keep-selection-visible) instead of the hand-rolled scroll math. - Config-save name buffer changes from a String (append + backspace only, cursor pinned to the end) to a tui_input::Input driven as a pure state machine via InputRequest, adding mid-string insert/delete, Delete, Left/Right and Home/End, plus a rendered block caret. Panel chrome is unchanged (still PanelBuilder), so the overlays render identically; only the interactive behaviour is upgraded.
Extract two reusable adapter helpers and apply them across the config and palette overlays: - list_scroll_offset: ratatui ListState computes the visible window so the selection stays on-screen. Replaces hand-rolled scroll math in the config browser and fixes the palette load dialog's take(8) truncation, which hid selections past the 8th entry. - stamp_caret: places a block caret on a built dialog's editable field. The palette editor save-name buffer moves from String (append + backspace) to tui_input::Input with mid-string insert/delete, Delete, Left/Right and Home/End, plus a rendered caret — matching the config-save dialog. Panel chrome stays on PanelBuilder, so the overlays render unchanged.
Replace the column-index colouriser (generate_controls_rich_lines) with span-driven styling. Each control row is built as a ratatui Line of styled Spans, rendered into a detached Buffer via the new render_styled_rows adapter helper, and the resulting per-cell colours are blitted into the PanelBuilder chrome (border/title-box/separators stay pixel-identical). Text output is byte-identical across all six categories. Colours track the text exactly instead of guessing fixed columns (5-7 keys, 27-38 bar band), which fixes two latent bugs the old magic-column logic carried: - the ESC footer fell through to param-row detection (is_esc_footer compared the bordered line against "ESC", never matching) and coloured blank cells; - the Auto Normalize row lost its key/bar accent when modified, because the param-row detector only accepted ' ' or '✱' at col 3, not the '*' marker that row uses. Binary size unchanged (reuses already-linked ratatui). 1004 lib tests, 56 visual goldens, clippy -D warnings all green.
Align the UI deps on the ratatui-core 0.1 generation so tachyonfx (effects applied post-render to a detached Buffer, no backend) can share buffer types: - ratatui 0.29 -> 0.30 (backend now in the separate ratatui-crossterm crate, so default-features=false keeps the widgets-only / no-second-crossterm setup) - tui-input 0.10 -> 0.15 (ratatui 0.30 compatible) - tachyonfx 0.25 added (default-features=false; crossterm is a dev-dep only) Single crossterm copy (0.27, ours) confirmed via cargo tree. Adapter API (Buffer, set_line, List/ListState, StatefulWidget) unchanged across the bump. 1004 lib tests + 56 visual goldens green; clippy -D warnings clean. Release binary 2,468,816 B (+49,584; 531 KB under the 3 MB ceiling).
…igration cleanup) Drop build_notification_panel, build_notification_rich_lines, RuntimeState.notification field + initializer, update_notifications/current_notification/current_notification_full methods, the OverlayType::Notification variant and OverlayCollection.notification field, and the dead runner.rs update_notifications() call. NotificationLevel enum and all ambient MSG code preserved intact.
… fixes Post-SDD live-review round on the ambient surface and Controls tuner. Ambient surface (prior round): default clean sim frame; TUNE/MSG events render as a centered, opaque, accent-bordered modal; optional always-on compact base row docked bottom-inside-frame, gated by a Controls APP "Status Line" toggle. Fix F10 chrome-cycle crash (resize render buffers before blur_field). draw_rich_overlay_dim gains a solid flag for opaque modals. Tuner-always-visible: param changes must reveal their tuner, but param-adjust handlers also push an Info value-echo toast that outranks the TUNE state (resolve: MSG > TUNE) and hid it. New ambient::surface_tune drops the redundant Info toast then debounces/pushes the Tune; Warning and Error messages survive and momentarily outrank it. Auto-normalize brightness message promoted Info -> Warning so it still surfaces. Orphan params as tuners: IntensityMapping, WindowFrame, and Chrome had no ParamId, so their hotkeys only flashed a toast. Promote all three to first-class registry params (rows + action_for / param_id_for_action round-trips + value formatting); they now appear in Controls and surface a tuner like Theme/Palette. Console overflow fix: the new APP rows pushed that category to 10 > the former MAX_VISIBLE_ROWS cap of 8, panicking build_console on F10 and category navigation. Raise the cap to 10 (it is sized to the largest category; both panes pad). Add no_category_exceeds_max_visible_rows guard. Cross-theme color: replace two hardcoded Gruvbox grays in the expanded-chrome footer with theme tokens (secondary row -> text_secondary; keybinds row -> threaded muted), so the footer stays on-palette across all five themes. Modal polish: enum/toggle tuners omitted the empty value gauge (a lone tick read as "minimum"); add TuneView.show_gauge, true for numeric params only. Verified: 1160 lib + 56 visual goldens + clippy --all-targets -D warnings + fmt; live tmux QA across param/enum/frame/chrome/theme/controls/pause clean.
The centered TUNE/MSG/pause modal tinted its frame with the active palette accent over a status_bar_bg interior, so it read as a separate chrome layer from the Controls/Dashboard panels (border_color frame over bg_color). Switch build_ambient_modal/modal_border_row to border_color + bg_color so the modal's outline and background are identical to the console. Drop the now-dead accent parameter from build_ambient_modal and its call sites. Add a regression test asserting frame=border_color and interior=bg_color.
Collapse the three copy-pasted overlay drawers (single-species draw_overlay, multi-species draw_ms_overlay, and their title-box halves) into a single TerminalRenderer::compose_overlay that both render paths forward to. Removes the dead _border_col/_w tuple from the old get_overlay_colors closure. Behavior-preserving: the themed panel_bg arg was already a no-op in both paths (draw_text_overlay_with_panel only applies it when an indicator color is supplied, which no caller does), so single- and multi-species output was already identical. This removes the hand-sync drift hazard between the copies. Add compose_overlay_applies_theme_pass covering the live per-char theme pass.
The console pads both panes to MAX_VISIBLE_ROWS and the body loop reads exactly that many rows, so a category yielding more rows is silently truncated in release (the debug_assert! in build_console is stripped). Strengthen the guard test from two forced flags to all RegistryCtx bool combinations x all categories, so a row added on any flag polarity is caught in CI.
Drop dim_overlay/crossfade/clip_rows/ease_out_cubic/ease_in_out and their unit tests — they were speculative scaffolding for the overlay-fade feature that was attempted and reverted. Keep breath + lerp_rgb (title-box accent breathing + ambient TUNE pulse + widely-used color lerp).
The reverted overlay-fade feature left a `dim: f32` factor on draw_rich_overlay_dim that every production caller passed as 1.0, plus a `use crate::render::motion;` import feeding the now-unreachable lerp branches. Collapse to draw_rich_overlay_solid(solid) — the load-bearing flag controlling space-cell opacity — inline the fg/bg writes, drop the motion import, and replace the dim-specific test with solid-flag coverage.
Resolve conflicts between the UI-system unification and dev's launch-quick-keys / custom-keybinds feature (#58): - KeyboardHintsOverlay::build_overlay now takes both `st: &PanelStyle` (UI-system theming) and `user_binds` (dev's live key-label lookup); all call sites and tests updated to the 3-arg signature. - runner.rs: keep both the UI-system controls-dispatch/ambient helpers and dev's resolve_bind quick-key resolver; thread panel_style + user_binds into the keyboard-hints build. - overlay.rs comparison header keeps dev's dynamic column label with the UI-system spacing token (add_empty_n(spacing::ROW)). - input.rs / state.rs: keep both sides' added tests. - README: keep both the Controls-panel-navigation and Custom-Keybinds docs.
origin/dev advanced past the first merge with the constellations preset. Sole conflict was a both-sides test addition in runner.rs: keep the UI-system Task-15 ambient-mode tests alongside dev's constellation_reset_is_now_stable test.
This was referenced Jun 28, 2026
Closed
Closed
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
UI-system unification. Two stacked workstreams land together:
[/]category). Replaces the legacy options overlay + prototype (deleted).RowBuf; motion module (ease/lerp/dim/crossfade + persistent phase clock); ambient instrument BASE↔TUNE↔MSG state machine surfaced as a centered modal; Dashboard / config-browser / help rebuilt on the shared kit; identity polish (title-box breath, accent discipline, spacing rhythm).Dependencies trend DOWN
RowBuf/RichCellfit the single-FrameBuffer overlay doctrine better).tui-inputkept (editable save field).Latest fix
16dc9d7— ambient modal frame outline + background now match the Controls/Dashboard console (border_colorframe overbg_colorinterior) instead of palette-accent overstatus_bar_bg.Scope
53 commits · 44 files · +8545 / −3674.
Verification
cargo test --features terminal --lib— 1161 passed.cargo clippy --features terminal --all-targets -- -D warningsclean ·cargo fmt --checkclean.Known-open (tracked, not blocking)
Manual verification still recommended
Overlays/modals only render in the interactive TUI (print-mode goldens draw no overlays). Live-TUI eyeball of colors/layout across the 5 themes is the remaining human check.