Auto-generates structured narratives for issues and MRs from local DB: - EntitySummary with title, state, author, labels, status - Key decisions heuristic (correlates state/label changes with nearby notes) - Activity summary with event counts and time span - Open threads detection (unresolved discussions) - Related entities (closing MRs, related issues) - Timeline of all events in chronological order 7 unit tests, robot-docs entry, autocorrect registry, CLI dispatch wired.
147 lines
5.6 KiB
Rust
147 lines
5.6 KiB
Rust
#![forbid(unsafe_code)]
|
|
|
|
//! Gitlore TUI — terminal interface for exploring GitLab data locally.
|
|
//!
|
|
//! Built on FrankenTUI (Elm architecture): Model, update, view.
|
|
//! The `lore` CLI spawns `lore-tui` via PATH lookup at runtime.
|
|
|
|
use anyhow::{Context, Result};
|
|
|
|
// Phase 0 modules.
|
|
pub mod clock; // Clock trait: SystemClock + FakeClock (bd-2lg6)
|
|
pub mod message; // Msg, Screen, EntityKey, AppError, InputMode (bd-c9gk)
|
|
|
|
pub mod safety; // Terminal safety: sanitize + URL policy + redact (bd-3ir1)
|
|
|
|
pub mod db; // DbManager: read pool + dedicated writer (bd-2kop)
|
|
pub mod theme; // Flexoki theme: build_theme, state_color, label_style (bd-5ofk)
|
|
|
|
pub mod app; // LoreApp Model trait impl (Phase 0 proof: bd-2emv, full: bd-6pmy)
|
|
|
|
// Phase 1 modules.
|
|
pub mod commands; // CommandRegistry: keybindings, help, palette (bd-38lb)
|
|
pub mod crash_context; // CrashContext ring buffer + panic hook (bd-2fr7)
|
|
pub mod layout; // Responsive layout: breakpoints, columns, preview pane (bd-1pzj)
|
|
pub mod navigation; // NavigationStack: back/forward/jump list (bd-1qpp)
|
|
pub mod state; // AppState, LoadState, ScreenIntent, per-screen states (bd-1v9m)
|
|
pub mod task_supervisor; // TaskSupervisor: dedup + cancel + generation IDs (bd-3le2)
|
|
pub mod view; // View layer: render_screen + common widgets (bd-26f2)
|
|
|
|
// Phase 2 modules.
|
|
pub mod action; // Data-fetching actions for TUI screens (bd-35g5+)
|
|
pub mod filter_dsl; // Filter DSL tokenizer for list screen filter bars (bd-18qs)
|
|
|
|
// Phase 4 modules.
|
|
pub mod entity_cache; // Bounded LRU entity cache for detail view reopens (bd-2og9)
|
|
pub mod render_cache; // Bounded render cache for expensive per-frame computations (bd-2og9)
|
|
pub mod scope; // Global scope context: SQL helpers + project listing (bd-1ser)
|
|
|
|
// Phase 5 modules.
|
|
pub mod instance_lock; // Single-instance advisory lock for TUI (bd-3h00)
|
|
pub mod session; // Session state persistence: save/load/quarantine (bd-3h00)
|
|
pub mod text_width; // Unicode-aware text width measurement + truncation (bd-3h00)
|
|
|
|
/// Options controlling how the TUI launches.
|
|
#[derive(Debug, Clone)]
|
|
pub struct LaunchOptions {
|
|
/// Path to lore config file.
|
|
pub config_path: Option<String>,
|
|
/// Run a background sync before displaying data.
|
|
pub sync_on_start: bool,
|
|
/// Clear cached TUI state and start fresh.
|
|
pub fresh: bool,
|
|
/// Render backend: "crossterm" or "native".
|
|
pub render_mode: String,
|
|
/// Use ASCII-only box drawing characters.
|
|
pub ascii: bool,
|
|
/// Disable alternate screen (render inline).
|
|
pub no_alt_screen: bool,
|
|
}
|
|
|
|
/// Launch the TUI in browse mode (no sync).
|
|
///
|
|
/// Loads config from `options.config_path` (or default location),
|
|
/// opens the database read-only, and enters the FrankenTUI event loop.
|
|
///
|
|
/// ## Preflight sequence
|
|
///
|
|
/// 1. **Schema preflight** — validate the database schema version before
|
|
/// creating the app. If incompatible, print an actionable error and exit
|
|
/// with a non-zero code.
|
|
/// 2. **Data readiness** — check whether the database has any entity data.
|
|
/// If empty, start on the Bootstrap screen; otherwise start on Dashboard.
|
|
pub fn launch_tui(options: LaunchOptions) -> Result<()> {
|
|
let _options = options; // remaining fields (fresh, ascii, etc.) consumed in later phases
|
|
|
|
// 1. Resolve database path.
|
|
let db_path = lore::core::paths::get_db_path(None);
|
|
if !db_path.exists() {
|
|
anyhow::bail!(
|
|
"No lore database found at {}.\n\
|
|
Run 'lore init' to create a config, then 'lore sync' to fetch data.",
|
|
db_path.display()
|
|
);
|
|
}
|
|
|
|
// 2. Open DB and run schema preflight.
|
|
let db = db::DbManager::open(&db_path)
|
|
.with_context(|| format!("opening database at {}", db_path.display()))?;
|
|
db.with_reader(schema_preflight)?;
|
|
|
|
// 3. Check data readiness — bootstrap screen if empty.
|
|
let start_on_bootstrap = db.with_reader(|conn| {
|
|
let readiness = action::check_data_readiness(conn)?;
|
|
Ok(!readiness.has_any_data())
|
|
})?;
|
|
|
|
// 4. Build the app model.
|
|
let mut app = app::LoreApp::new();
|
|
app.db = Some(db);
|
|
if start_on_bootstrap {
|
|
app.navigation.reset_to(message::Screen::Bootstrap);
|
|
}
|
|
|
|
// 5. Enter the FrankenTUI event loop.
|
|
ftui::App::fullscreen(app)
|
|
.with_mouse()
|
|
.run()
|
|
.context("running TUI event loop")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Run the schema preflight check.
|
|
///
|
|
/// Returns `Ok(())` if the schema is compatible, or an error with an
|
|
/// actionable message if it's not. The caller should exit non-zero on error.
|
|
pub fn schema_preflight(conn: &rusqlite::Connection) -> Result<()> {
|
|
use state::bootstrap::SchemaCheck;
|
|
|
|
match action::check_schema_version(conn, action::MINIMUM_SCHEMA_VERSION) {
|
|
SchemaCheck::Compatible { .. } => Ok(()),
|
|
SchemaCheck::NoDB => {
|
|
anyhow::bail!(
|
|
"No lore database found.\n\
|
|
Run 'lore init' to create a config, then 'lore sync' to fetch data."
|
|
);
|
|
}
|
|
SchemaCheck::Incompatible { found, minimum } => {
|
|
anyhow::bail!(
|
|
"Database schema version {found} is too old (minimum: {minimum}).\n\
|
|
Run 'lore migrate' to upgrade, or 'lore sync' to rebuild."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Launch the TUI with an initial sync pass.
|
|
///
|
|
/// Runs `lore sync` in the background while displaying a progress screen,
|
|
/// then transitions to browse mode once sync completes.
|
|
pub fn launch_sync_tui(options: LaunchOptions) -> Result<()> {
|
|
let _options = options;
|
|
// Phase 2 will implement the sync progress screen
|
|
eprintln!("lore-tui: sync mode not yet implemented (Phase 2)");
|
|
Ok(())
|
|
}
|