TUI walking skeleton (Phase 4)
First implementation milestone: Cargo project, dependencies,
and a minimal but functional TUI shell built on Ratatui +
Crossterm + Tokio in the Elm-style update/view pattern
(Candidate A from Phase 2/3 selection).
Includes:
- Three-region layout: items list (left), output + input + hint
(right), bottom status bar with mode-aware shortcuts.
- Two themes (light, dark) plus COLORFGBG auto-detect, per
NFR-7. CLI: --theme {light,dark}, --log-file <path>.
- Input modes per ADR-0003: simple (default), advanced, with
the `:` one-shot escape including immediate prompt reaction
("Advanced:" label, advanced border) and auto-inserted space
after a leading `:` in simple mode.
- App-level commands: `quit`/`q`, `mode simple`/`mode advanced`
(canonical list per ADR-0003 — remaining commands land in
later iterations).
- File logging via tracing, defaulting to ~/.rdbms-playground/
playground.log so the TUI is not corrupted by stdio.
Testing per ADR-0008:
- Tier 1: 29 unit tests covering input handling, mode switch,
one-shot escape, auto-space, output buffering, CLI parsing.
- Tier 2: 4 insta snapshots (default simple/advanced/light,
one-shot active) of TestBackend frames.
- Tier 3: 7 integration tests driving synthetic events through
App::update + render path.
All green: 36 tests, 0 failures, 0 skips. Clippy clean with
nursery lints enabled.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
//! Theme and colour palette.
|
||||
//!
|
||||
//! Two themes are provided — one for light terminal backgrounds
|
||||
//! and one for dark — per NFR-7. The palette is intentionally
|
||||
//! small for the walking skeleton; it grows as more views are
|
||||
//! added. Contrast is chosen against the target background so
|
||||
//! that foreground text meets WCAG-AA (NFR-5) on both variants.
|
||||
|
||||
use ratatui::style::Color;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Background {
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Theme {
|
||||
pub background: Background,
|
||||
pub bg: Color,
|
||||
pub fg: Color,
|
||||
pub muted: Color,
|
||||
pub border: Color,
|
||||
pub border_advanced: Color,
|
||||
pub mode_simple: Color,
|
||||
pub mode_advanced: Color,
|
||||
pub system: Color,
|
||||
pub error: Color,
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
#[must_use]
|
||||
pub const fn dark() -> Self {
|
||||
Self {
|
||||
background: Background::Dark,
|
||||
bg: Color::Rgb(0x18, 0x1B, 0x22),
|
||||
fg: Color::Rgb(0xE6, 0xE6, 0xE6),
|
||||
muted: Color::Rgb(0x8B, 0x90, 0x9A),
|
||||
border: Color::Rgb(0x4A, 0x52, 0x65),
|
||||
border_advanced: Color::Rgb(0xE0, 0x60, 0x60),
|
||||
mode_simple: Color::Rgb(0x6E, 0xC4, 0xFF),
|
||||
mode_advanced: Color::Rgb(0xFF, 0x9E, 0x6B),
|
||||
system: Color::Rgb(0x9F, 0xD8, 0x91),
|
||||
error: Color::Rgb(0xFF, 0x6B, 0x6B),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn light() -> Self {
|
||||
Self {
|
||||
background: Background::Light,
|
||||
bg: Color::Rgb(0xFA, 0xFA, 0xF7),
|
||||
fg: Color::Rgb(0x1A, 0x1F, 0x2C),
|
||||
muted: Color::Rgb(0x60, 0x66, 0x73),
|
||||
border: Color::Rgb(0xB6, 0xBC, 0xC8),
|
||||
border_advanced: Color::Rgb(0xC2, 0x3A, 0x3A),
|
||||
mode_simple: Color::Rgb(0x21, 0x69, 0xC7),
|
||||
mode_advanced: Color::Rgb(0xB0, 0x4A, 0x12),
|
||||
system: Color::Rgb(0x2E, 0x7C, 0x3C),
|
||||
error: Color::Rgb(0xC0, 0x39, 0x2B),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Theme {
|
||||
fn default() -> Self {
|
||||
Self::dark()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user