Files
2026-02-28 23:41:11 +01:00

9.8 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
04-bbs-polish-and-live-content 01 execute 1
Cargo.toml
src/splash.rs
src/app.rs
src/main.rs
true
BBS-01
BBS-02
truths artifacts key_links
When splash.txt exists in vault root, index.md page displays ANSI art header above the markdown content
When splash.txt is missing, index.md renders normally with no error or fallback banner
Every loaded page shows 'Last modified: Mon DD, YYYY | X.X KB' in the right side of the status bar
Status bar left side still shows breadcrumb, right side now shows metadata + keyboard hints
path provides min_lines
src/splash.rs load_splash() function parsing ANSI art from splash.txt into Vec<Line<'static>> 10
path provides contains
src/app.rs PageMeta struct, read_page_meta(), status bar metadata display, splash prepend logic PageMeta
path provides contains
Cargo.toml ansi-to-tui, notify, walkdir dependencies ansi-to-tui
from to via pattern
src/splash.rs ansi-to-tui crate IntoText trait on Vec<u8> into_text
from to via pattern
src/app.rs src/splash.rs splash::load_splash() called during navigate_to for index.md load_splash
from to via pattern
src/app.rs std::fs::metadata read_page_meta() reading mtime and size read_page_meta
Add ANSI art splash screen support and file metadata display to complete the BBS aesthetic.

Purpose: The splash screen makes the landing page feel like a real BBS with colorful ANSI art. The file metadata (last-modified timestamp and file size) in the status bar gives users context about content freshness.

Output: src/splash.rs module, updated src/app.rs with PageMeta and splash prepend, updated Cargo.toml with all Phase 4 dependencies (ansi-to-tui, notify, walkdir — added now so later plans don't need to touch Cargo.toml).

<execution_context> @/Users/ruohki/.claude/get-shit-done/workflows/execute-plan.md @/Users/ruohki/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/04-bbs-polish-and-live-content/04-RESEARCH.md @src/app.rs @src/main.rs @Cargo.toml Task 1: Add Phase 4 dependencies and create splash + metadata modules Cargo.toml, src/splash.rs, src/main.rs 1. Add all three Phase 4 dependencies to Cargo.toml: ```toml notify = "6.1" ansi-to-tui = "8.0" walkdir = "2.5" ```
  1. Run cargo tree | grep ratatui-core to verify ansi-to-tui resolves to a single ratatui-core version. If two versions appear, add ratatui-core = "0.1" to force unification.

  2. Create src/splash.rs with:

    use std::path::Path;
    use ansi_to_tui::IntoText;
    use ratatui::text::Line;
    
    /// Load ANSI art from `splash.txt` in the vault root.
    /// Returns None if file doesn't exist or can't be parsed (graceful degradation).
    pub fn load_splash(vault_path: &Path) -> Option<Vec<Line<'static>>> {
        let bytes: Vec<u8> = std::fs::read(vault_path.join("splash.txt")).ok()?;
        let text = bytes.into_text().ok()?;
        Some(text.lines)
    }
    
  3. Add mod splash; to src/main.rs module declarations (after mod signals;).

  4. Run cargo build to verify compilation with new dependencies. cargo build succeeds. cargo tree | grep ratatui-core shows exactly one version. src/splash.rs exists. All three Phase 4 crate dependencies compile. splash.rs provides load_splash(). Module is declared in main.rs.

Task 2: Wire splash prepend on index and add file metadata to status bar src/app.rs, src/main.rs 1. Add file metadata types and helpers to `src/app.rs` (near the top, after use statements):
use std::time::UNIX_EPOCH;

/// File metadata for status bar display.
struct PageMeta {
    modified: String,  // "Feb 25, 2026"
    size: String,      // "2.4 KB"
}

fn read_page_meta(full_path: &Path) -> Option<PageMeta> {
    let meta = std::fs::metadata(full_path).ok()?;
    let secs = meta.modified().ok()?.duration_since(UNIX_EPOCH).ok()?.as_secs();
    let (y, m, d) = unix_secs_to_ymd(secs);
    let months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
    Some(PageMeta {
        modified: format!("{} {}, {}", months[(m-1) as usize], d, y),
        size: format_file_size(meta.len()),
    })
}

fn unix_secs_to_ymd(secs: u64) -> (u32, u32, u32) {
    let mut days = (secs / 86400) as u32;
    let mut year = 1970u32;
    loop {
        let days_in_year = if is_leap(year) { 366 } else { 365 };
        if days < days_in_year { break; }
        days -= days_in_year;
        year += 1;
    }
    let leap = is_leap(year);
    let month_days: [u32; 12] = [31, if leap { 29 } else { 28 }, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    let mut month = 0u32;
    for (i, &d) in month_days.iter().enumerate() {
        if days < d { month = i as u32 + 1; break; }
        days -= d;
    }
    (year, month, days + 1)
}

fn is_leap(year: u32) -> bool {
    (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
}

fn format_file_size(bytes: u64) -> String {
    match bytes {
        0..=1023 => format!("{} B", bytes),
        1024..=1_048_575 => format!("{:.1} KB", bytes as f64 / 1024.0),
        _ => format!("{:.1} MB", bytes as f64 / 1_048_576.0),
    }
}
  1. Add page_meta: Option<PageMeta> field to the App struct (Phase 4 additions section). Initialize to None in App::new().

  2. Update navigate_to() — after loading a document successfully (inside the VaultDocument::Loaded match arm):

    • Compute full path: let full_path = vault_path.join(vault_relative);
    • Read metadata: self.page_meta = read_page_meta(&full_path);
    • Check if this is the index page and prepend splash if so:
      if vault_relative == "index.md" {
          if let Some(mut splash_lines) = crate::splash::load_splash(&vault_path) {
              splash_lines.push(Line::default()); // blank separator
              splash_lines.extend(lines);
              lines = splash_lines;
              // Adjust link records: offset all line_index values by the splash line count
              // splash_count = splash_lines_original_len + 1 (for blank separator)
          }
      }
      
    • IMPORTANT: When splash lines are prepended, all link_records line_index values must be offset by the number of splash lines added. Store the splash line count and add it to each record's line_index.
    • For Missing/ReadError arms: set self.page_meta = None;
  3. Apply the same splash prepend logic in navigate_back() and navigate_forward() — when the target path is "index.md", prepend splash lines and adjust link_record line indices. Also set page_meta via read_page_meta().

  4. Apply splash + metadata in main.rs initial load too — after rendering index.md, prepend splash lines if available and adjust link_records, then compute initial page_meta.

  5. Update handle_resize(): when re-rendering, if current_path == "index.md", prepend splash lines again and adjust link_records. Also note: splash lines are NOT re-rendered through render_markdown — they come from load_splash() directly and are prepended after the markdown render.

  6. Update draw_status_bar() to include metadata on the right side:

    • In the normal (non-quit-prompt) branch, before the keyboard hints section:
    • If self.page_meta is Some(meta):
      • Insert format!("Last modified: {} | {}", meta.modified, meta.size) into right_parts (before the hints entry).
    • Implement graceful truncation: if left.len() + right.len() >= width, progressively drop metadata fields:
      • First drop file size (show only "Last modified: ...").
      • Then drop the entire metadata line.
      • Then abbreviate keyboard hints if still too wide.
      • Use width.saturating_sub() to prevent underflow.
  7. Run cargo build to verify. cargo build succeeds. Status bar shows metadata when a document is loaded (visible on next run with a vault). Splash prepend logic handles both presence and absence of splash.txt. index.md shows splash art header when splash.txt exists. All loaded pages show "Last modified: Mon DD, YYYY | X.X KB" in status bar. Status bar gracefully truncates on narrow terminals.

1. `cargo build` — compiles without errors or warnings 2. Run with a vault containing `splash.txt` (ANSI art) — index.md shows art header above content 3. Run with a vault without `splash.txt` — index.md renders normally, no error 4. Check status bar — right side shows "Last modified: ... | ... KB" followed by keyboard hints 5. Resize terminal to <60 cols — status bar does not panic or wrap, metadata fields drop gracefully 6. Navigate to a different page and back to index — splash reappears, metadata updates

<success_criteria>

  • splash.txt ANSI art renders above index.md content with correct colors via ansi-to-tui
  • Missing splash.txt causes no error — graceful degradation to normal index.md
  • Every loaded document page shows BBS-format timestamp and file size in status bar
  • Status bar layout: left=breadcrumb, right=metadata+hints, with graceful truncation
  • Link Tab-cycling still works correctly on index.md when splash lines are prepended (line_index offsets are correct) </success_criteria>
After completion, create `.planning/phases/04-bbs-polish-and-live-content/04-01-SUMMARY.md`