From 2c9ad682daec77a4e277dc51306cf9eda22b0546 Mon Sep 17 00:00:00 2001 From: ruohki Date: Sat, 28 Feb 2026 23:12:39 +0100 Subject: [PATCH] docs(03-02): complete navigation, history, and breadcrumb status bar plan - Create 03-02-SUMMARY.md documenting HistoryEntry, navigate_to/back/forward, Tab-cycling, breadcrumb - Update STATE.md: Phase 3 complete, plan 2/2 done, decisions added, session updated - Update ROADMAP.md: Phase 3 marked complete, 2/2 plans checked - Mark requirements NAV-03, NAV-04, NAV-11 complete in REQUIREMENTS.md --- .planning/REQUIREMENTS.md | 12 +- .planning/ROADMAP.md | 10 +- .planning/STATE.md | 28 +-- .../03-navigation-and-links/03-02-SUMMARY.md | 160 ++++++++++++++++++ 4 files changed, 187 insertions(+), 23 deletions(-) create mode 100644 .planning/phases/03-navigation-and-links/03-02-SUMMARY.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index df61dae..6a30808 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -31,15 +31,15 @@ Requirements for initial release. Each maps to roadmap phases. - [x] **NAV-01**: User can follow `[[wiki-links]]` to other vault documents - [x] **NAV-02**: User can follow standard `[text](path.md)` links to other documents -- [ ] **NAV-03**: User can navigate back through history stack -- [ ] **NAV-04**: User can navigate forward after going back +- [x] **NAV-03**: User can navigate back through history stack +- [x] **NAV-04**: User can navigate forward after going back - [x] **NAV-05**: User can scroll content with j/k, arrows, PgUp/PgDn - [x] **NAV-06**: User lands on index.md as the entry point - [x] **NAV-07**: User sees graceful error page when a linked file is not found - [x] **NAV-08**: User sees keyboard hints in status bar - [x] **NAV-09**: App handles terminal resize without breaking layout - [x] **NAV-10**: User sees links highlighted inline and can Tab-cycle between them -- [ ] **NAV-11**: User sees breadcrumb / current location in status bar +- [x] **NAV-11**: User sees breadcrumb / current location in status bar ### BBS Aesthetic @@ -122,10 +122,10 @@ Which phases cover which requirements. Updated during roadmap creation. | NAV-09 | Phase 2 | Complete | | NAV-01 | Phase 3 | Complete | | NAV-02 | Phase 3 | Complete | -| NAV-03 | Phase 3 | Pending | -| NAV-04 | Phase 3 | Pending | +| NAV-03 | Phase 3 | Complete | +| NAV-04 | Phase 3 | Complete | | NAV-10 | Phase 3 | Complete | -| NAV-11 | Phase 3 | Pending | +| NAV-11 | Phase 3 | Complete | | BBS-01 | Phase 4 | Pending | | BBS-02 | Phase 4 | Pending | | LIVE-01 | Phase 4 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 2e0dd0d..8d83ed9 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -14,7 +14,7 @@ Decimal phases appear between their surrounding integers in numeric order. - [x] **Phase 1: Safety Foundation** - Process lifecycle safety, shell integration, and configuration (completed 2026-02-28) - [x] **Phase 2: Vault Core and Rendering** - Markdown parsing pipeline and full content display (completed 2026-02-28) -- [ ] **Phase 3: Navigation and Links** - Link following, back/forward history, link cycling +- [x] **Phase 3: Navigation and Links** - Link following, back/forward history, link cycling (completed 2026-02-28) - [ ] **Phase 4: BBS Polish and Live Content** - Retro aesthetic, filesystem watching, directory listing ## Phase Details @@ -61,10 +61,10 @@ Plans: 3. User can press Backspace or a back key to return to the previous document, and then press a forward key to return to where they were 4. User can Tab-cycle between links on a page to select and follow them without using a mouse 5. User sees their current location (breadcrumb) in the status bar -**Plans:** 1/2 plans executed +**Plans:** 2/2 plans complete Plans: -- [ ] 03-01-PLAN.md — Renderer link extraction, wiki-link styling, and vault link resolution -- [ ] 03-02-PLAN.md — App navigation: history, link cycling, draw-time selection, breadcrumb status bar +- [x] 03-01-PLAN.md — Renderer link extraction, wiki-link styling, and vault link resolution +- [x] 03-02-PLAN.md — App navigation: history, link cycling, draw-time selection, breadcrumb status bar ### Phase 4: BBS Polish and Live Content **Goal**: The app feels like a real BBS — the index page has an ANSI art header, pages show last-updated timestamps, a vault directory is browsable, and content updates live without restarting @@ -86,5 +86,5 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 |-------|----------------|--------|-----------| | 1. Safety Foundation | 3/3 | Complete | 2026-02-28 | | 2. Vault Core and Rendering | 3/3 | Complete | 2026-02-28 | -| 3. Navigation and Links | 1/2 | In Progress| | +| 3. Navigation and Links | 2/2 | Complete | 2026-02-28 | | 4. BBS Polish and Live Content | 0/TBD | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 1be6535..a474cbe 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,23 +5,23 @@ 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 3 IN PROGRESS — Plan 01 done (LinkRecord, wiki-link resolver, path traversal guard) +**Current focus:** Phase 3 COMPLETE — Plans 01 and 02 done (navigation, history, Tab-cycling, breadcrumbs all wired) ## Current Position -Phase: 3 of 4 (Navigation and Links) — IN PROGRESS -Plan: 1 of 2 in current phase (03-01 done, 03-02 next) -Status: 03-01 complete — link metadata extraction and wiki-link resolution implemented -Last activity: 2026-02-28 — Plan 01 complete (LinkRecord, ENABLE_WIKILINKS, resolve_wiki_link, path traversal guard) +Phase: 3 of 4 (Navigation and Links) — COMPLETE +Plan: 2 of 2 in current phase (03-01 done, 03-02 done) +Status: 03-02 complete — navigation history, link cycling, breadcrumb status bar implemented +Last activity: 2026-02-28 — Plan 02 complete (HistoryEntry, navigate_to/back/forward, Tab-cycling, REVERSED highlight, breadcrumb) -Progress: [████████░░] 75% +Progress: [██████████] 100% ## Performance Metrics **Velocity:** -- Total plans completed: 6 -- Average duration: 2.7 min -- Total execution time: 0.27 hours +- Total plans completed: 7 +- Average duration: 2.8 min +- Total execution time: 0.33 hours **By Phase:** @@ -29,7 +29,7 @@ Progress: [████████░░] 75% |-------|-------|-------|----------| | 01-safety-foundation | 3 | 7 min | 2.3 min | | 02-vault-core-and-rendering | 3 | 9 min | 3.0 min | -| 03-navigation-and-links | 1 so far | 3.5 min | 3.5 min | +| 03-navigation-and-links | 2 | 7 min | 3.5 min | **Recent Trend:** - Last 5 plans: 3 min, 2 min, 2 min, 4 min, 3 min @@ -68,6 +68,10 @@ Recent decisions affecting current work: - [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 ### Pending Todos @@ -82,5 +86,5 @@ None. ## Session Continuity Last session: 2026-02-28 -Stopped at: Completed 03-01-PLAN.md -Resume file: .planning/phases/03-navigation-and-links/03-02-PLAN.md +Stopped at: Completed 03-02-PLAN.md (Phase 3 complete) +Resume file: .planning/phases/04-live-reload/04-01-PLAN.md diff --git a/.planning/phases/03-navigation-and-links/03-02-SUMMARY.md b/.planning/phases/03-navigation-and-links/03-02-SUMMARY.md new file mode 100644 index 0000000..0be2867 --- /dev/null +++ b/.planning/phases/03-navigation-and-links/03-02-SUMMARY.md @@ -0,0 +1,160 @@ +--- +phase: 03-navigation-and-links +plan: "02" +subsystem: app +tags: + - navigation + - history + - link-cycling + - breadcrumb + - tab-navigation +dependency_graph: + requires: + - 03-01 (LinkRecord from render_markdown, resolve_wiki_link, resolve_standard_link) + - 02-vault-core-and-rendering (vault.rs load_document, DocumentState) + provides: + - Browser-style navigation history with scroll and link state restoration + - Tab/Shift-Tab link cycling with wrap-around and auto-scroll + - Enter to follow links (wiki-link and standard link resolution) + - Backspace and Alt+Left/Right back/forward navigation + - Draw-time REVERSED modifier for selected link highlighting + - Breadcrumb status bar with back/forward indicators and link counter + affects: + - End-user experience (core interactive vault browsing) +tech_stack: + added: [] + patterns: + - "HistoryEntry snapshot pattern: path + scroll_offset + selected_link saved per navigation" + - "Browser-style forward history truncation on navigate_to after going back" + - "Draw-time mutation: lines.clone() + REVERSED applied only for current frame, stored lines unchanged" + - "Auto-scroll centering: link_line.saturating_sub(half) with max_scroll clamp" + - "Breadcrumb via Path::components() map with .md suffix strip and > join" +key_files: + modified: + - path: src/app.rs + role: "Added HistoryEntry struct, Phase 3 navigation fields on App, navigate_to/back/forward, follow_selected_link, select_next/prev_link, scroll_to_selected_link, draw-time REVERSED modifier, build_breadcrumb, updated draw_status_bar with breadcrumb and nav indicators" + - path: src/main.rs + role: "Updated initial render to capture link_records, pass link_records and current_path to App::new()" +decisions: + - "Task 1 and Task 2 implemented together in single app.rs rewrite — draw-time REVERSED and breadcrumb are part of the same cohesive navigation system" + - "Double-REVERSED fix: spans overlapping link range get modifier via both col range check and overlap check" + - "navigate_to does NOT push history on Missing/ReadError — user stays on current page conceptually" + - "navigate_back/forward re-loads from disk (no cache) per research recommendation" +metrics: + duration_seconds: 198 + completed_date: "2026-02-28" + tasks_completed: 2 + tasks_total: 2 + files_modified: 2 +--- + +# Phase 03 Plan 02: Navigation, History, Tab-Cycling, and Breadcrumb Status Bar Summary + +**One-liner:** Browser-style navigation history with Tab/Shift-Tab link cycling, Enter-to-follow, Backspace/Alt+arrows back/forward, draw-time REVERSED link highlight, and breadcrumb status bar wired into the App event loop. + +## What Was Built + +### Task 1: Navigation state, history, link cycling, and navigate_to (commit d705313) + +Implemented the complete navigation system in `src/app.rs` and updated `src/main.rs`. + +**`HistoryEntry` struct** (new, in `src/app.rs`): +```rust +struct HistoryEntry { + path: String, // vault-relative path + scroll_offset: u16, // scroll position at time of navigation away + selected_link: Option, // selected link index at time of navigation +} +``` + +**Phase 3 fields added to `App`:** +- `history: Vec` — browser-style navigation stack +- `history_index: usize` — current position in history +- `link_records: Vec` — links from current render +- `selected_link: Option` — currently highlighted link +- `current_path: String` — vault-relative path for breadcrumb and link resolution + +**`App::new()` signature updated:** +Now accepts `link_records: Vec` and `current_path: String`. Initializes `history` with one entry for the initial page at scroll 0, no selected link. + +**Navigation methods:** + +- `navigate_to(vault_relative)`: Saves current state to history, truncates forward history (browser fork), loads + renders new document, pushes new history entry. On Missing/ReadError, shows error screen without pushing to history. +- `navigate_back()`: Decrements `history_index`, saves current scroll/link state, re-loads and re-renders target document with restored scroll and link selection. +- `navigate_forward()`: Increments `history_index`, same re-load pattern as navigate_back. +- `follow_selected_link()`: Reads `link_records[selected_link]`, dispatches to `resolve_wiki_link` (for wiki-links) or `resolve_standard_link` (for standard links), then calls `navigate_to`. +- `select_next_link()` / `select_prev_link()`: Cycle with wrap-around (`% len`), then call `scroll_to_selected_link()`. +- `scroll_to_selected_link()`: If selected link is outside viewport, centers it: `link_line.saturating_sub(half)`, clamped to `max_scroll()`. + +**Key bindings added** (in `handle_key`, after 'q', before scroll keys): +``` +Tab → select_next_link() +BackTab → select_prev_link() +Enter → follow_selected_link() +Backspace → navigate_back() +Alt+Left → navigate_back() +Alt+Right → navigate_forward() +``` + +**`handle_resize()` updated**: Now captures `link_records` from re-render, validates `selected_link` still in range, uses `Some(&self.config.vault_path)` for render-time broken wiki-link detection. + +**`main.rs` updated**: +- `render_markdown` call now passes `Some(&app_config.vault_path)` for broken wiki-link detection at startup +- Destructures `(lines, link_records)` from render result +- `App::new()` call includes `initial_link_records` and `"index.md".to_string()` + +### Task 2: Draw-time link selection and breadcrumb status bar (included in d705313) + +Both draw-time features were implemented together with Task 1 in the single coherent rewrite. + +**Draw-time REVERSED modifier** (in `draw()` → `DocumentState::Loaded` branch): +- Calls `lines.clone()` to get a mutable copy for the current frame only (stored lines unchanged) +- Walks `line.spans.iter_mut()`, tracking cumulative `col` (chars-count) +- Applies `Modifier::REVERSED` to any span whose column range overlaps `[record.col_offset, record.col_offset + record.span_len)` +- Uses both exact-range and overlap checks to handle multi-span link brackets + +**`build_breadcrumb(vault_relative)` helper** (private fn, bottom of app.rs): +- Iterates `Path::components()`, strips `.md` suffix from each component +- Joins with ` > ` separator +- Examples: `"index.md"` → `"index"`, `"guides/getting-started.md"` → `"guides > getting-started"` + +**`draw_status_bar()` rewritten**: +- Left side: ` {breadcrumb} ` from `build_breadcrumb(&self.current_path)` +- Quit prompt mode: breadcrumb left + yellow bold REVERSED warning right (existing behavior preserved) +- Normal mode right side (space-separated): + - `< Back` if `history_index > 0` + - `Link N/M` if `selected_link` is Some + - `Forward >` if `history_index < history.len() - 1` + - Keyboard hints: `Tab:Links Enter:Go Bksp:Back q:Quit` (or Ctrl+C×2 for login shell) +- `#[allow(dead_code)]` removed from `config` field (now actively used) + +## Verification Results + +1. `cargo build` succeeds — zero errors, 2 pre-existing warnings (init_logging and is_within_vault — unrelated to this plan) +2. All required functions exist in src/app.rs: HistoryEntry, navigate_to, navigate_back, navigate_forward, follow_selected_link, select_next_link, select_prev_link, scroll_to_selected_link, build_breadcrumb +3. main.rs passes link_records and current_path to App::new() +4. Key bindings in correct match order: Ctrl+C > q > Tab > BackTab > Enter > Backspace > Alt+Left > Alt+Right > j/k/Down/Up > PgDn/PgUp > _ catch-all + +## Deviations from Plan + +### Implementation Notes + +**1. Tasks 1 and 2 implemented together:** +- **Found during:** Implementation +- **Note:** The draw-time REVERSED modifier and breadcrumb status bar are tightly coupled to the same `App` struct fields (link_records, selected_link, history, history_index, current_path) added in Task 1. Implementing them in a single coherent rewrite produced cleaner code than two separate partial passes. +- **Impact:** Single commit covers both tasks. All required functionality is present. + +**2. Span overlap detection extended:** +- **Found during:** Task 2 implementation +- **Issue:** The plan's span-walking code only checked `col >= col_offset && col < col_offset + span_len`, which misses spans that start before `col_offset` but extend into the link range. +- **Fix:** Added a second condition `col < col_offset + span_len && col + span_chars > col_offset` to catch overlapping spans. +- **Files modified:** src/app.rs (draw method) + +## Self-Check: PASSED + +Files verified: +- `src/app.rs`: contains `struct HistoryEntry`, `navigate_to`, `navigate_back`, `navigate_forward`, `follow_selected_link`, `select_next_link`, `select_prev_link`, `scroll_to_selected_link`, `build_breadcrumb`, REVERSED modifier in draw(), breadcrumb in draw_status_bar() +- `src/main.rs`: passes `Some(&app_config.vault_path)` to render_markdown, destructures `link_records`, calls `App::new()` with 6 arguments + +Commits verified: +- `d705313`: feat(03-02): add navigation history, link cycling, and navigate_to to App