--- phase: 04-bbs-polish-and-live-content plan: 02 subsystem: ui tags: [walkdir, ratatui, rust, directory-listing, virtual-page, tab-cycling, navigation-history] # Dependency graph requires: - phase: 04-01 provides: "walkdir = 2.5 in Cargo.toml, navigation history (navigate_back/forward), link_records + Tab-cycling, splash prepend pattern" provides: - "resolve_wiki_link() returns PathBuf::from('__directory__') for case-insensitive 'Directory' wiki-link" - "DirEntry struct and list_vault_files() in vault.rs using WalkDir::new().sort_by_file_name()" - "navigate_to_directory() method builds tree view via build_directory_lines() and pushes history entry" - "build_directory_lines() emits yellow-bold directory entries and cyan bracketed file links with LinkRecord" - "navigate_back/forward handle __directory__ sentinel — regenerate listing fresh each visit" - "handle_resize() skips re-render for __directory__ pages (fixed-width text, no markdown)" affects: [04-03-live-reload] # Tech tracking tech-stack: added: [] patterns: - "Virtual page pattern: current_path == '__directory__' sentinel distinguishes virtual pages from real vault documents" - "Directory builder pattern: build_directory_lines() returns (Vec>, Vec) — same shape as render_markdown output" - "Always-fresh pattern: navigate_back/forward call list_vault_files() again on __directory__ — never stale cache" - "Resize guard pattern: early return in handle_resize() for virtual pages avoids crash on None raw_content" key-files: created: [] modified: - "src/vault.rs — resolve_wiki_link() Directory sentinel, DirEntry struct, list_vault_files() with WalkDir" - "src/app.rs — navigate_to_directory(), build_directory_lines(), follow_selected_link() sentinel routing, navigate_back/forward __directory__ branches, handle_resize() guard" key-decisions: - "resolve_wiki_link() sentinel check placed BEFORE rfind('/') split — prevents 'directory' being misinterpreted as a filename" - "build_directory_lines() computes line_index as lines.len() before push — matches renderer pattern for Tab-cycling correctness" - "navigate_back/forward use if/else-if chain (not load_document for __directory__) — avoids spurious Missing document state" - "handle_resize() returns early for __directory__ (raw_content is None for virtual pages) — prevents stale-content confusion" - "link_records for directory entries use is_wiki: false with direct vault_path strings — navigate_to() handles them without resolve step" patterns-established: - "Virtual page sentinel: __directory__ string as current_path distinguishes virtual page from real file throughout app" - "Tab-cycling for directory entries: same LinkRecord model as rendered markdown — no special-case in key handling" - "Directory listing always regenerated: list_vault_files() called on every visit (forward/back/direct) — always reflects current vault state" requirements-completed: [LIVE-02] # Metrics duration: 8min completed: 2026-03-01 --- # Phase 4 Plan 02: Directory Listing Summary **Virtual [[Directory]] page with WalkDir tree view — yellow-bold dirs, cyan bracketed file links, Tab-cycling, and history back/forward all wired through existing link model** ## Performance - **Duration:** 8 min - **Started:** 2026-03-01T00:00:00Z - **Completed:** 2026-03-01T00:08:00Z - **Tasks:** 2 - **Files modified:** 2 ## Accomplishments - `resolve_wiki_link()` intercepts case-insensitive "Directory" before filesystem scan and returns `PathBuf::from("__directory__")` sentinel — zero false positives, works with `[[directory]]`, `[[DIRECTORY]]`, `[[Directory]]` - `DirEntry` struct and `list_vault_files()` added to `vault.rs` using `WalkDir::new().sort_by_file_name()` — alphabetically sorted, skips hidden entries and non-.md files, reports depth for tree indentation - `build_directory_lines()` module-level helper produces `(Vec>, Vec)` matching `render_markdown` output shape — directories as yellow-bold `name/`, files as cyan `[name]` with full LinkRecord for Tab-cycling - `navigate_to_directory()` saves/truncates history, builds listing, sets `current_path = "__directory__"`, pushes HistoryEntry — full history participation - `navigate_back()` and `navigate_forward()` detect `target_path == "__directory__"` and regenerate listing fresh — never stale - `handle_resize()` guards with early return for `current_path == "__directory__"` — prevents crash on `None` raw_content ## Task Commits Each task was committed atomically: 1. **Task 1: Add [[Directory]] interception and vault file listing** - `fe69cf5` (feat) 2. **Task 2: Wire directory as virtual page with Tab-cycling navigation** - `600b46a` (feat) ## Files Created/Modified - `src/vault.rs` - `resolve_wiki_link()` Directory sentinel check; `DirEntry` pub struct; `list_vault_files()` using `walkdir::WalkDir` - `src/app.rs` - `navigate_to_directory()`; `build_directory_lines()`; `follow_selected_link()` `__directory__` routing; `navigate_back/forward` `__directory__` branch; `handle_resize()` early return guard ## Decisions Made - Sentinel placed at the top of `resolve_wiki_link()` before `rfind('/')` — ensures "directory" is never split as a subpath prefix - `build_directory_lines()` records `line_index = lines.len()` before the `lines.push()` call — matches the renderer's pattern exactly, Tab-cycling targets the correct line - `navigate_back/forward` use an `if target_path == "__directory__"` branch rather than passing `"__directory__"` to `load_document()` — avoids a `VaultDocument::Missing` error state for a valid virtual page - Directory entries stored with `is_wiki: false` — `navigate_to()` handles them directly as vault-relative paths without needing `resolve_wiki_link()` ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered None. `walkdir 2.5` was already in `Cargo.toml` from Plan 01. The `DirEntry` struct, `list_vault_files()`, and `build_directory_lines()` all compiled on the first attempt. The `navigate_back/forward` branching was straightforward given the clear sentinel convention. ## User Setup Required None - no external service configuration required. The `[[Directory]]` wiki-link works out of the box in any vault. ## Next Phase Readiness - Phase 4 Plan 03 (live reload via `notify`) can now start — `notify = "6.1"` was added to `Cargo.toml` in Plan 01 - Directory listing is always regenerated fresh on each visit — will automatically reflect vault changes even before live reload is wired - All navigation paths handle `__directory__` sentinel consistently — no special cases needed in Plan 03 --- *Phase: 04-bbs-polish-and-live-content* *Completed: 2026-03-01*