feat(tui): global navigation — sidebar, status bar, layout #52

Merged
charles merged 1 commit from tui/navigation-17 into main 2026-04-11 20:41:40 +00:00
Owner

Summary

Fifth PR in the loom-tui stack. Adds the sidebar + status bar + layout so the TUI finally looks like a TUI.

Stacks on #51. Closes charles/loom#17.

What's in

  • components::Sidebar — lists ScreenKind::ALL with mnemonic keys; active row highlighted; border colour reflects focus
  • components::StatusBar — mode badge (NORMAL/INSERT/COMMAND), backend name slot, queue depth, pending-chord indicator, message slot, right-aligned HH:MM clock
  • components::compute_layout — splits the terminal into sidebar? + main + 1-row status. Clamps sidebar width into [10, 60]
  • App::show_sidebar / focus / sidebar_width_pct / config — new fields. Sidebar width sourced from [tui] sidebar_width via LoomTuiConfig
  • Actions wired: APP_TOGGLE_SIDEBAR (Ctrl+B), APP_FOCUS_NEXT (Tab), APP_FOCUS_PREV (Shift+Tab). Hiding the sidebar while focused on it falls back to Main
  • ChordTracker::is_pending — surfaces for the status bar's …chord indicator

Tests (6 new, 27 total)

  • compute_layout with sidebar reserves the status row and splits horizontally
  • Without sidebar it uses the full body width
  • Sidebar width clamps at both ends
  • Ctrl+B toggles show_sidebar round-trip
  • Tab cycles focus between Sidebar and Main
  • Tab is a no-op when the sidebar is hidden

Out of scope

  • Quit-during-generation confirmation (spec calls for this on q/Ctrl+C when a job is running — lands with #42 confirm dialog)
  • Live status-bar backend / queue depth — populated once loom-core is wired through AppCtx in the Generate ticket (#20) and onwards
  • Keyboard routing from the focused sidebar into selection movement (today the sidebar is purely display; j/k navigation inside it comes with the Gallery folders sub-sidebar pattern in #29)

Notes

  • App::config is #[allow(dead_code)] — read only for the settings screen (#39) and notification bar (#42) in later PRs. Keeping it wired now avoids reshuffling App constructors later
  • The "backend name" slot in the status bar intentionally shows no backend until loom-core is wired through AppCtx
## Summary Fifth PR in the loom-tui stack. Adds the sidebar + status bar + layout so the TUI finally looks like a TUI. Stacks on #51. Closes charles/loom#17. ## What's in - **`components::Sidebar`** — lists `ScreenKind::ALL` with mnemonic keys; active row highlighted; border colour reflects focus - **`components::StatusBar`** — mode badge (NORMAL/INSERT/COMMAND), backend name slot, queue depth, pending-chord indicator, message slot, right-aligned `HH:MM` clock - **`components::compute_layout`** — splits the terminal into `sidebar?` + `main` + 1-row status. Clamps sidebar width into `[10, 60]` - **`App::show_sidebar / focus / sidebar_width_pct / config`** — new fields. Sidebar width sourced from `[tui] sidebar_width` via `LoomTuiConfig` - **Actions wired**: `APP_TOGGLE_SIDEBAR` (Ctrl+B), `APP_FOCUS_NEXT` (Tab), `APP_FOCUS_PREV` (Shift+Tab). Hiding the sidebar while focused on it falls back to `Main` - **`ChordTracker::is_pending`** — surfaces for the status bar's `…chord` indicator ## Tests (6 new, 27 total) - `compute_layout` with sidebar reserves the status row and splits horizontally - Without sidebar it uses the full body width - Sidebar width clamps at both ends - Ctrl+B toggles `show_sidebar` round-trip - Tab cycles focus between `Sidebar` and `Main` - Tab is a no-op when the sidebar is hidden ## Out of scope - Quit-during-generation confirmation (spec calls for this on `q`/`Ctrl+C` when a job is running — lands with #42 confirm dialog) - Live status-bar backend / queue depth — populated once loom-core is wired through `AppCtx` in the Generate ticket (#20) and onwards - Keyboard routing from the focused sidebar into selection movement (today the sidebar is purely display; `j`/`k` navigation inside it comes with the Gallery folders sub-sidebar pattern in #29) ## Notes - `App::config` is `#[allow(dead_code)]` — read only for the settings screen (#39) and notification bar (#42) in later PRs. Keeping it wired now avoids reshuffling `App` constructors later - The "backend name" slot in the status bar intentionally shows `no backend` until loom-core is wired through `AppCtx`
Adds the full chrome around screens:

- Sidebar widget listing all six top-level screens with their mnemonic
  key, highlighting the active one and fading its border when unfocused
- StatusBar widget showing (left→right) the key mode, backend name
  placeholder, queue depth, pending-chord indicator, centered message
  slot, and right-aligned clock
- compute_layout helper that splits the terminal into
  sidebar + main + 1-row status, with graceful behaviour when the
  sidebar is hidden

App gains show_sidebar / focus / sidebar_width_pct fields and wires
APP_TOGGLE_SIDEBAR (Ctrl+B), APP_FOCUS_NEXT (Tab), APP_FOCUS_PREV
(Shift+Tab) into invoke_action. Hiding the sidebar while focused on
it falls back to Main. Sidebar width comes from [tui] sidebar_width
in the config loaded by #43, clamped to [10, 60].

ChordTracker grows an is_pending() accessor so the status bar can
show a …chord indicator when a first-half key is held.

Closes charles/loom#17

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
charles changed target branch from tui/config-43 to main 2026-04-11 20:41:34 +00:00
charles deleted branch tui/navigation-17 2026-04-11 20:41:41 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/loom!52
No description provided.