# Project State ## Project Reference See: .planning/PROJECT.md (updated 2026-02-28) **Core value:** Users can connect via SSH and seamlessly browse a vault of linked markdown documents with retro BBS aesthetics **Current focus:** Phase 4 IN PROGRESS — Plans 01-02 done, Plan 03 (live reload) remaining ## Current Position Phase: 4 of 4 (BBS Polish and Live Content) — IN PROGRESS Plan: 2 of 3 in current phase (04-01, 04-02 done, 04-03 next) Status: 04-02 complete — [[Directory]] virtual page with tree view, Tab-cycling, history back/forward Last activity: 2026-03-01 — Plan 02 complete (DirEntry, list_vault_files, navigate_to_directory, build_directory_lines) Progress: [█████████░] 87% ## Performance Metrics **Velocity:** - Total plans completed: 9 - Average duration: 3.1 min - Total execution time: 0.44 hours **By Phase:** | Phase | Plans | Total | Avg/Plan | |-------|-------|-------|----------| | 01-safety-foundation | 3 | 7 min | 2.3 min | | 02-vault-core-and-rendering | 3 | 9 min | 3.0 min | | 03-navigation-and-links | 2 | 7 min | 3.5 min | | 04-bbs-polish-and-live-content | 2 | 12 min | 6.0 min | **Recent Trend:** - Last 5 plans: 2 min, 4 min, 3 min, 4 min, 8 min - Trend: Stable *Updated after each plan completion* ## Accumulated Context ### Decisions Decisions are logged in PROJECT.md Key Decisions table. Recent decisions affecting current work: - Pre-Phase 1: Verify `edition = "2024"` compiles before changing to 2021 — leave it if `cargo build` succeeds - Pre-Phase 1: Use pulldown-cmark event-based approach for markdown parsing (not comrak) - Pre-Phase 1: No tokio/async runtime — synchronous event loop only - Pre-Phase 2: Choose wiki-link parsing strategy before implementation (regex pre-pass vs post-parse interception) - 01-01: Resolve relative vault_path against config file parent dir (not cwd) — bbs.toml lives next to binary - 01-01: detect_login_shell() called before parse_cli() — both read argv[0], parse_cli strips the dash - 01-02: Use ratatui::crossterm re-export path — crossterm is transitive dep, not direct dep in Cargo.toml - 01-02: SIGINT not registered via signal-hook — crossterm key events handle double-press Ctrl+C in Plan 03 - 01-02: restore_terminal() skips ratatui::restore() — that would call LeaveAlternateScreen (never entered) - 01-03: show_goodbye() called after restore_terminal() — terminal must be in cooked mode before println! is safe - 01-03: app_config stored in App struct for Phase 2 access — vault_path accessible inside event loop - 01-03: Quit prompt cleared on any non-Ctrl+C key — pressing any key dismisses double-press window - 02-01: LinesWithNewlines iterator added to preserve trailing \n for syntect grammar correctness - 02-01: mod declarations added to main.rs during Plan 02 (not Plan 03) — compiler needs them to check renderer.rs - 02-01: syntect default-fancy feature uses pure-Rust fancy-regex (avoids Oniguruma C lib) - 02-02: Full emit_code_block and emit_table written in Task 1 (not stubs) — avoids throwaway code - 02-02: Box width for code blocks capped at terminal width — prevents border overflow on long lines - 02-02: Table n_cols derived from max(alignments.len(), max row length) — handles malformed GFM tables - [Phase 02-03]: raw_content stored in App for resize re-render — avoids disk re-read on window resize - [Phase 02-03]: draw_error_screen() unified for Missing and Error via Option<&str> reason parameter - [Phase 02-03]: Scroll keys placed before _ catch-all — j/k during quit prompt does not dismiss it - [Phase 03-navigation-and-links]: vault_path added to render_markdown for render-time broken wiki-link detection - [Phase 03-navigation-and-links]: Broken wiki-links checked at render time; standard links deferred to navigation time - [Phase 03-navigation-and-links]: link_span_start_count field tracks span array position at Tag::Link Start for span_len computation - [Phase 03-02]: navigate_to does NOT push to history on Missing/ReadError — user stays on current page conceptually - [Phase 03-02]: navigate_back/forward re-loads from disk (no render cache) per research recommendation - [Phase 03-02]: Draw-time REVERSED uses lines.clone() per frame — stored lines unchanged for resize re-render - [Phase 03-02]: Span overlap detection extended beyond plan spec — catches spans starting before col_offset that extend into link range - [Phase 04]: resolve_wiki_link() Directory sentinel placed before rfind('/') split — prevents 'directory' being misinterpreted as a subpath prefix - [Phase 04]: navigate_back/forward use if/else-if branch for __directory__ not load_document — avoids spurious Missing document state for virtual page - [Phase 04]: handle_resize() returns early for __directory__ — raw_content is None for virtual pages, prevents stale-content confusion ### Pending Todos None. ### Blockers/Concerns - **REND blocker resolved**: renderer.rs uses no Widget trait at all — `render_markdown()` returns `Vec>` which Plan 03 passes to `Paragraph::new()`. No custom Widget needed. - **NAV path traversal resolved**: is_within_vault() with canonicalize + starts_with guards all link resolution in vault.rs - **LIVE**: notify 8.x API must be verified at integration time; watch only current file (not full vault) to avoid inotify exhaustion ## Session Continuity Last session: 2026-03-01 Stopped at: Completed 04-02-PLAN.md Resume file: .planning/phases/04-bbs-polish-and-live-content/04-03-PLAN.md