| 04-bbs-polish-and-live-content |
03 |
app |
| notify |
| filesystem-watcher |
| live-reload |
| debounce |
| mpsc |
| rust |
| event-loop |
|
| phase |
provides |
| 04-01 |
notify = 6.1 in Cargo.toml |
|
| phase |
provides |
| 04-02 |
__directory__ sentinel, navigate_to_directory(), list_vault_files(), rewatch target for virtual page |
|
|
| FileWatcher pub struct with RecommendedWatcher, mpsc Receiver, rewatch() method |
| file_watcher and pending_reload_at fields on App struct |
| reload_current_document() preserving scroll position and link selection |
| rewatch_for_current_page() re-pointing watcher on every navigation event |
| try_recv() drain loop in event loop for non-blocking filesystem event polling |
| 300ms debounce timer firing reload after last relevant event |
| FileWatcher initialized in main.rs with non-fatal failure handling |
|
|
| added |
patterns |
| notify 6.1 RecommendedWatcher with mpsc channel — already in Cargo.toml from Plan 01 |
|
| FileWatcher wraps RecommendedWatcher + Receiver in a struct with a rewatch() helper |
| Drain loop pattern: loop { match rx.try_recv() { ... Err(Empty) => break } } drains all queued events per poll iteration |
| Debounce pattern: pending_reload_at = Some(Instant::now()) on each event, reload fires 300ms after last event |
| Directory watch pattern: watch parent dir, not the file itself — survives atomic saves (vim/neovim rename-over) |
| Non-fatal watcher creation: watcher failure prints warning, app runs without live reload (None passed to App) |
|
|
| created |
modified |
|
|
| src/app.rs — FileWatcher struct, file_watcher/pending_reload_at fields, App::new() new param, reload_current_document(), rewatch_for_current_page(), try_recv drain loop + debounce in run_event_loop(), rewatch calls in all navigate_* methods |
| src/main.rs — FileWatcher creation before App::new(), non-fatal error handling, updated App::new() call with file_watcher arg |
|
|
| Watch parent directory of current file (not the file itself) — survives atomic saves where editors write to temp file then rename |
| Drain loop pattern: loop { try_recv() ... break on Empty/Disconnected } — processes all queued events per poll so rapid saves do not queue lag |
| 300ms debounce: pending_reload_at reset on each event — reload fires only after 300ms of silence from last event |
| File watcher initialized in main.rs before App::new() — avoids self-referential borrowing, keeps App::new() pure |
| Non-fatal watcher failure: eprintln warning only, None passed to App — app fully functional without live reload |
|
| notify mpsc pattern: RecommendedWatcher::new(tx, Config::default()) + Receiver<notify::Result<notify::Event>> stored together |
| Debounce in event loop: check pending_reload_at after polling phase, not in key handler |
|
|
7min |
2026-03-01 |