#![allow(dead_code)] // Phase 2.5: consumed by render_screen dispatch //! Bootstrap screen view. //! //! Shown when the database has no entity data. Guides users to run //! a sync to populate the database. use ftui::core::geometry::Rect; use ftui::render::cell::{Cell, PackedRgba}; use ftui::render::drawing::Draw; use ftui::render::frame::Frame; use crate::state::bootstrap::BootstrapState; // Colors (Flexoki palette). const TEXT: PackedRgba = PackedRgba::rgb(0xCE, 0xCD, 0xC3); // tx const MUTED: PackedRgba = PackedRgba::rgb(0x87, 0x87, 0x80); // tx-2 const ACCENT: PackedRgba = PackedRgba::rgb(0xDA, 0x70, 0x2C); // orange /// Render the bootstrap screen. /// /// Centers a message in the content area, guiding the user to start a sync. /// When a sync is in progress, shows a "syncing" message instead. pub fn render_bootstrap(frame: &mut Frame<'_>, state: &BootstrapState, area: Rect) { if area.width < 10 || area.height < 5 { return; } let center_y = area.y + area.height / 2; let max_x = area.x.saturating_add(area.width); // Title. let title = "No data found"; let title_x = area.x + area.width.saturating_sub(title.len() as u16) / 2; frame.print_text_clipped( title_x, center_y.saturating_sub(2), title, Cell { fg: ACCENT, ..Cell::default() }, max_x, ); if state.sync_started { // Sync in progress. let msg = "Syncing data from GitLab..."; let msg_x = area.x + area.width.saturating_sub(msg.len() as u16) / 2; frame.print_text_clipped( msg_x, center_y, msg, Cell { fg: TEXT, ..Cell::default() }, max_x, ); } else { // Prompt user to start sync. let msg = "Run sync to get started."; let msg_x = area.x + area.width.saturating_sub(msg.len() as u16) / 2; frame.print_text_clipped( msg_x, center_y, msg, Cell { fg: TEXT, ..Cell::default() }, max_x, ); let hint = "Press 'g' then 's' to start sync, or 'q' to quit."; let hint_x = area.x + area.width.saturating_sub(hint.len() as u16) / 2; frame.print_text_clipped( hint_x, center_y + 2, hint, Cell { fg: MUTED, ..Cell::default() }, max_x, ); } } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- #[cfg(test)] mod tests { use super::*; use ftui::render::grapheme_pool::GraphemePool; macro_rules! with_frame { ($width:expr, $height:expr, |$frame:ident| $body:block) => {{ let mut pool = GraphemePool::new(); let mut $frame = Frame::new($width, $height, &mut pool); $body }}; } #[test] fn test_render_bootstrap_no_panic() { with_frame!(80, 24, |frame| { let state = BootstrapState::default(); render_bootstrap(&mut frame, &state, Rect::new(0, 1, 80, 22)); }); } #[test] fn test_render_bootstrap_sync_started() { with_frame!(80, 24, |frame| { let state = BootstrapState { sync_started: true, ..Default::default() }; render_bootstrap(&mut frame, &state, Rect::new(0, 1, 80, 22)); }); } #[test] fn test_render_bootstrap_tiny_area_noop() { with_frame!(8, 3, |frame| { let state = BootstrapState::default(); render_bootstrap(&mut frame, &state, Rect::new(0, 0, 8, 3)); // Should not panic — early return for tiny areas. }); } }