diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 2b40b68..c4e457b 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -45,7 +45,11 @@ Plans: 3. User lands on index.md when the app starts; when index.md is missing, a readable error page is shown instead of a crash 4. User can scroll content longer than the terminal with j/k, arrow keys, and PgUp/PgDn 5. User sees keyboard hints in the status bar, box-drawing borders on panels, and a CGA-era retro color theme; the layout reflows correctly when the terminal is resized -**Plans**: TBD +**Plans:** 3 plans +Plans: +- [ ] 02-01-PLAN.md — Dependencies, vault file loading, and syntax highlighter +- [ ] 02-02-PLAN.md — Markdown-to-styled-lines renderer (all constructs) +- [ ] 02-03-PLAN.md — App integration: scrolling, status bar, error screen, startup wiring ### Phase 3: Navigation and Links **Goal**: Users can browse the vault by following links and navigating back and forward through their history @@ -78,6 +82,6 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| | 1. Safety Foundation | 3/3 | Complete | 2026-02-28 | -| 2. Vault Core and Rendering | 0/TBD | Not started | - | +| 2. Vault Core and Rendering | 0/3 | Not started | - | | 3. Navigation and Links | 0/TBD | Not started | - | | 4. BBS Polish and Live Content | 0/TBD | Not started | - | diff --git a/.planning/phases/02-vault-core-and-rendering/02-01-PLAN.md b/.planning/phases/02-vault-core-and-rendering/02-01-PLAN.md new file mode 100644 index 0000000..58b52ff --- /dev/null +++ b/.planning/phases/02-vault-core-and-rendering/02-01-PLAN.md @@ -0,0 +1,156 @@ +--- +phase: 02-vault-core-and-rendering +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - Cargo.toml + - src/vault.rs + - src/highlighter.rs +autonomous: true +requirements: + - REND-03 + - REND-10 + - NAV-06 + - NAV-07 + +must_haves: + truths: + - "pulldown-cmark, syntect, and syntect-tui compile without errors alongside existing ratatui 0.30" + - "vault.rs can load a markdown file from a vault path and return structured success/missing/error states" + - "highlighter.rs initializes syntect once and can highlight a code string into Vec> with CGA 16-color palette" + artifacts: + - path: "Cargo.toml" + provides: "pulldown-cmark, syntect, syntect-tui dependencies" + contains: "pulldown-cmark" + - path: "src/vault.rs" + provides: "VaultDocument enum and load_document function" + contains: "VaultDocument" + - path: "src/highlighter.rs" + provides: "syntect initialization, CGA color mapping, highlight_code function" + contains: "syntect_color_to_cga" + key_links: + - from: "src/highlighter.rs" + to: "syntect" + via: "OnceLock and OnceLock initialized once" + pattern: "OnceLock.*SyntaxSet" + - from: "src/vault.rs" + to: "std::fs" + via: "read_to_string for markdown file loading" + pattern: "read_to_string" +--- + + +Add Phase 2 dependencies (pulldown-cmark, syntect, syntect-tui) to Cargo.toml, create the vault file-loading module, and create the syntax highlighter module with CGA 16-color mapping. + +Purpose: Establish the foundation libraries and I/O layer that the markdown renderer (Plan 02) and app integration (Plan 03) build upon. +Output: Three files modified/created; `cargo build` succeeds with all new dependencies. + + + +@/Users/ruohki/.claude/get-shit-done/workflows/execute-plan.md +@/Users/ruohki/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/02-vault-core-and-rendering/02-RESEARCH.md +@.planning/phases/02-vault-core-and-rendering/02-CONTEXT.md +@src/config.rs +@Cargo.toml + + + + + + Task 1: Add dependencies and create vault file-loading module + Cargo.toml, src/vault.rs + +**Cargo.toml:** Add three new dependencies alongside the existing ones: +```toml +pulldown-cmark = "0.13.1" +syntect = { version = "5.3", default-features = false, features = ["default-fancy"] } +syntect-tui = "3.0" +``` + +Note: `default-features = false, features = ["default-fancy"]` avoids the Oniguruma C library — pure Rust regex engine for clean SSH server builds. + +**src/vault.rs:** Create a new module with: + +1. `VaultDocument` enum with three variants: + - `Loaded { path: PathBuf, content: String }` — file read successfully + - `Missing { path: PathBuf }` — file does not exist (io::ErrorKind::NotFound) + - `ReadError { path: PathBuf, reason: String }` — other I/O error + +2. `load_document(vault_path: &Path, relative: &str) -> VaultDocument`: + - Join `vault_path` with `relative` to get the full path + - Use `std::fs::read_to_string()` with proper match on the Result + - Map `ErrorKind::NotFound` to `VaultDocument::Missing` + - Map other errors to `VaultDocument::ReadError` + - Return `VaultDocument::Loaded` on success + +The function takes `vault_path` (from Config) and a relative filename (e.g., "index.md"). + +Do NOT register the module in main.rs yet — Plan 03 handles wiring. + + `cargo check` passes with no errors (new deps resolve, vault.rs compiles). Run `cargo check 2>&1` and confirm exit code 0. + Cargo.toml has pulldown-cmark 0.13.1, syntect 5.3 with default-fancy, and syntect-tui 3.0. vault.rs exists with VaultDocument enum and load_document function. `cargo check` succeeds. + + + + Task 2: Create syntax highlighter module with CGA color mapping + src/highlighter.rs + +Create `src/highlighter.rs` with these components: + +1. **Module-level OnceLock statics** for one-time initialization: + ```rust + static SYNTAX_SET: OnceLock = OnceLock::new(); + static THEME_SET: OnceLock = OnceLock::new(); + ``` + +2. **`init_highlighter()`** — call once from main.rs before App::new(): + - `SYNTAX_SET.get_or_init(SyntaxSet::load_defaults_newlines);` + - `THEME_SET.get_or_init(ThemeSet::load_defaults);` + +3. **`syntax_set() -> &'static SyntaxSet`** and **`theme_set() -> &'static ThemeSet`** accessor functions that call `.get().expect(...)`. + +4. **`syntect_color_to_cga(c: syntect::highlighting::Color) -> ratatui::style::Color`** — RGB-to-CGA Euclidean distance mapper using the 16 CGA palette entries exactly as documented in the research (Pattern 8). The CGA palette entries: + - Black(0,0,0), Red(170,0,0), Green(0,170,0), Yellow(170,170,0), Blue(0,0,170), Magenta(170,0,170), Cyan(0,170,170), Gray(170,170,170) + - DarkGray(85,85,85), LightRed(255,85,85), LightGreen(85,255,85), LightYellow(255,255,85), LightBlue(85,85,255), LightMagenta(255,85,255), LightCyan(85,255,255), White(255,255,255) + +5. **`highlight_code(code: &str, lang: &str) -> Vec>`** — the public API for code block highlighting: + - Look up syntax by token first, then by name, fall back to plain text + - Use `HighlightLines::new(syntax, &theme_set().themes["base16-ocean.dark"])` + - For each line in `code.lines()`: call `highlight_line()`, map each `(Style, &str)` tuple to a `Span::styled(text.to_string(), Style::default().fg(syntect_color_to_cga(style.foreground)))`, collect into `Line::from(spans)` + - Return `Vec>` (all owned strings, no lifetime leakage) + +Do NOT register the module in main.rs yet — Plan 03 handles wiring. But do ensure the file compiles in isolation (no unresolved imports that depend on other new modules). + + `cargo check` passes. Verify highlighter.rs compiles by temporarily adding `mod highlighter;` to main.rs, running `cargo check`, then removing it. Or simply verify the file has no syntax errors by checking `cargo check` output. + highlighter.rs exists with init_highlighter(), syntax_set(), theme_set(), syntect_color_to_cga(), and highlight_code() functions. All use OnceLock for one-time init. Output is Vec> with CGA 16-color palette. + + + + + +1. `cargo build` succeeds with all new dependencies +2. `src/vault.rs` contains `VaultDocument` enum with `Loaded`, `Missing`, `ReadError` variants +3. `src/vault.rs` contains `load_document()` function +4. `src/highlighter.rs` contains `init_highlighter()`, `highlight_code()`, `syntect_color_to_cga()` +5. No changes to existing Phase 1 files (app.rs, main.rs, config.rs, signals.rs, terminal.rs) other than Cargo.toml + + + +- `cargo build` compiles cleanly with pulldown-cmark 0.13.1, syntect 5.3, and syntect-tui 3.0 +- vault.rs provides file loading with structured error states (Loaded/Missing/ReadError) +- highlighter.rs provides one-shot syntect initialization and CGA-mapped syntax highlighting +- Both new modules produce `'static` lifetime outputs (no borrowed data leaking) + + + +After completion, create `.planning/phases/02-vault-core-and-rendering/02-01-SUMMARY.md` + diff --git a/.planning/phases/02-vault-core-and-rendering/02-02-PLAN.md b/.planning/phases/02-vault-core-and-rendering/02-02-PLAN.md new file mode 100644 index 0000000..1f2dfeb --- /dev/null +++ b/.planning/phases/02-vault-core-and-rendering/02-02-PLAN.md @@ -0,0 +1,278 @@ +--- +phase: 02-vault-core-and-rendering +plan: 02 +type: execute +wave: 2 +depends_on: + - "02-01" +files_modified: + - src/renderer.rs +autonomous: true +requirements: + - REND-01 + - REND-02 + - REND-03 + - REND-04 + - REND-05 + - REND-06 + - REND-07 + - REND-08 + - REND-09 + - REND-10 + +must_haves: + truths: + - "Headings H1-H6 render with distinct CGA colors and decorators (H1=LightCyan+══, H2=LightYellow+──, H3-H6 color-only)" + - "Bold text uses Modifier::BOLD, italic uses Modifier::ITALIC, inline code uses LightCyan styling" + - "Fenced code blocks render with rounded box-drawing borders (╭─╮│╰─╯) and syntax-highlighted content via highlighter.rs" + - "Ordered and unordered lists render with bullets/numbers and proper indentation per nesting depth" + - "Blockquotes render with a yellow │ left border and gray content" + - "Horizontal rules render as full-width ─── lines in DarkGray" + - "Images render as [IMAGE: alt text] placeholders in DarkGray+DIM" + - "GFM tables render with full box-drawing grid (┌┬┐├┼┤└┴┘) and bold LightCyan header row" + artifacts: + - path: "src/renderer.rs" + provides: "Complete markdown-to-styled-lines conversion pipeline" + exports: ["render_markdown"] + min_lines: 200 + key_links: + - from: "src/renderer.rs" + to: "pulldown-cmark" + via: "Parser::new_ext with TextMergeStream consuming all Event variants" + pattern: "TextMergeStream" + - from: "src/renderer.rs" + to: "src/highlighter.rs" + via: "highlight_code() called for fenced code blocks" + pattern: "highlight_code" + - from: "src/renderer.rs" + to: "ratatui::text" + via: "Produces Vec> with Span styling" + pattern: "Vec>" +--- + + +Build the complete markdown-to-styled-lines renderer that converts a markdown string into `Vec>` using pulldown-cmark event processing, CGA-themed styling, syntax-highlighted code blocks, and box-drawing table grids. + +Purpose: This is the core content pipeline — every markdown construct the user sees flows through this module. +Output: `src/renderer.rs` with a public `render_markdown(input: &str, width: u16) -> Vec>` function. + + + +@/Users/ruohki/.claude/get-shit-done/workflows/execute-plan.md +@/Users/ruohki/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/02-vault-core-and-rendering/02-RESEARCH.md +@.planning/phases/02-vault-core-and-rendering/02-CONTEXT.md +@.planning/phases/02-vault-core-and-rendering/02-01-SUMMARY.md +@src/highlighter.rs + + + + + + Task 1: Build core markdown renderer with inline and block elements + src/renderer.rs + +Create `src/renderer.rs` with the public function `render_markdown(input: &str, width: u16) -> Vec>`. + +The `width` parameter is needed for horizontal rules (full-width `─` repeats) and could be used for code block border sizing. + +**Internal state struct (private):** + +```rust +struct RenderState { + lines: Vec>, + current_spans: Vec>, + style_stack: Vec