157 lines
7.2 KiB
Markdown
157 lines
7.2 KiB
Markdown
---
|
|
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<Line<'static>> 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<SyntaxSet> and OnceLock<ThemeSet> initialized once"
|
|
pattern: "OnceLock.*SyntaxSet"
|
|
- from: "src/vault.rs"
|
|
to: "std::fs"
|
|
via: "read_to_string for markdown file loading"
|
|
pattern: "read_to_string"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@/Users/ruohki/.claude/get-shit-done/workflows/execute-plan.md
|
|
@/Users/ruohki/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.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
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Add dependencies and create vault file-loading module</name>
|
|
<files>Cargo.toml, src/vault.rs</files>
|
|
<action>
|
|
**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.
|
|
</action>
|
|
<verify>`cargo check` passes with no errors (new deps resolve, vault.rs compiles). Run `cargo check 2>&1` and confirm exit code 0.</verify>
|
|
<done>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.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Create syntax highlighter module with CGA color mapping</name>
|
|
<files>src/highlighter.rs</files>
|
|
<action>
|
|
Create `src/highlighter.rs` with these components:
|
|
|
|
1. **Module-level OnceLock statics** for one-time initialization:
|
|
```rust
|
|
static SYNTAX_SET: OnceLock<SyntaxSet> = OnceLock::new();
|
|
static THEME_SET: OnceLock<ThemeSet> = 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<Line<'static>>`** — 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<Line<'static>>` (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).
|
|
</action>
|
|
<verify>`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.</verify>
|
|
<done>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<Line<'static>> with CGA 16-color palette.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
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
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- `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)
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/02-vault-core-and-rendering/02-01-SUMMARY.md`
|
|
</output>
|