Implements the full lore-tui crate scaffold with 6 Phase 0 modules: - message.rs: Msg (~40 variants), Screen (12), EntityKey, AppError, InputMode - clock.rs: Clock trait with SystemClock + FakeClock for deterministic testing - safety.rs: Terminal sanitizer (ANSI filter), URL policy, PII/secret redaction - db.rs: DbManager with 3 reader pool (round-robin) + dedicated writer (WAL mode) - theme.rs: Flexoki adaptive theme (19 slots), state/event colors, label styling - app.rs: Minimal LoreApp Model trait impl proving FrankenTUI integration 68 tests, clippy clean, fmt clean. Closes bd-3ddw, bd-c9gk, bd-2lg6, bd-3ir1, bd-2kop, bd-5ofk, bd-2emv, bd-1cj0.
102 lines
2.9 KiB
Rust
102 lines
2.9 KiB
Rust
#![allow(dead_code)] // Phase 0: minimal scaffold, fleshed out in bd-6pmy
|
|
|
|
//! Minimal FrankenTUI Model implementation for the lore TUI.
|
|
//!
|
|
//! This is the Phase 0 integration proof — validates that the ftui Model trait
|
|
//! compiles with our Msg type and produces basic output. The full LoreApp with
|
|
//! screen routing, navigation stack, and action dispatch comes in bd-6pmy.
|
|
|
|
use ftui::{Cmd, Frame, Model};
|
|
|
|
use crate::message::Msg;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LoreApp
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Root model for the lore TUI.
|
|
///
|
|
/// Phase 0: minimal scaffold that renders a placeholder and handles Quit.
|
|
/// Phase 1 (bd-6pmy) will add screen routing, DbManager, theme, and subscriptions.
|
|
pub struct LoreApp;
|
|
|
|
impl Model for LoreApp {
|
|
type Message = Msg;
|
|
|
|
fn init(&mut self) -> Cmd<Self::Message> {
|
|
Cmd::none()
|
|
}
|
|
|
|
fn update(&mut self, msg: Self::Message) -> Cmd<Self::Message> {
|
|
match msg {
|
|
Msg::Quit => Cmd::quit(),
|
|
_ => Cmd::none(),
|
|
}
|
|
}
|
|
|
|
fn view(&self, _frame: &mut Frame) {
|
|
// Phase 0: no-op view. Phase 1 will render screens via the frame.
|
|
}
|
|
}
|
|
|
|
/// Verify that `App::fullscreen(LoreApp).run()` compiles.
|
|
///
|
|
/// This is a compile-time check — we don't actually run it because that
|
|
/// would require a real TTY. The function exists solely to prove the wiring.
|
|
#[cfg(test)]
|
|
fn _assert_app_fullscreen_compiles() {
|
|
// This function is never called — it only needs to compile.
|
|
fn _inner() {
|
|
use ftui::App;
|
|
let _app_builder = App::fullscreen(LoreApp);
|
|
// _app_builder.run() would need a TTY, so we don't call it.
|
|
}
|
|
}
|
|
|
|
/// Verify that `App::inline(LoreApp, 12).run()` compiles.
|
|
#[cfg(test)]
|
|
fn _assert_app_inline_compiles() {
|
|
fn _inner() {
|
|
use ftui::App;
|
|
let _app_builder = App::inline(LoreApp, 12);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_lore_app_init_returns_none() {
|
|
let mut app = LoreApp;
|
|
let cmd = app.init();
|
|
assert!(matches!(cmd, Cmd::None));
|
|
}
|
|
|
|
#[test]
|
|
fn test_lore_app_quit_returns_quit_cmd() {
|
|
let mut app = LoreApp;
|
|
let cmd = app.update(Msg::Quit);
|
|
assert!(matches!(cmd, Cmd::Quit));
|
|
}
|
|
|
|
#[test]
|
|
fn test_lore_app_tick_returns_none() {
|
|
let mut app = LoreApp;
|
|
let cmd = app.update(Msg::Tick);
|
|
assert!(matches!(cmd, Cmd::None));
|
|
}
|
|
|
|
#[test]
|
|
fn test_lore_app_navigate_returns_none() {
|
|
use crate::message::Screen;
|
|
let mut app = LoreApp;
|
|
let cmd = app.update(Msg::NavigateTo(Screen::Dashboard));
|
|
assert!(matches!(cmd, Cmd::None));
|
|
}
|
|
}
|