feat: Add automatic workspace refresh with file watcher

Implement a polling-based file watcher that automatically refreshes
the file list when markdown files are created, deleted, or renamed.

- Add background thread that polls workspace every 2 seconds
- Watcher starts when workspace opens, stops when app exits
- Uses pure Rust std library (no external dependencies)
- Fixes issue where new folders like Act 2 don't appear until manual refresh

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-09-13 18:41:57 -05:00
parent cea944779e
commit f64536ee9e
2 changed files with 106 additions and 0 deletions
+14
View File
@@ -12,6 +12,8 @@ use eframe::egui;
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::sync::mpsc;
use std::thread;
use std::time::{Duration, Instant};
mod autocomplete;
@@ -51,6 +53,10 @@ type LtCheckMsg = (String, LtCheckResult);
/// spelling matches found in it.
type SpellCheckMsg = (String, Vec<crate::langtool::Match>);
/// Message from the file watcher thread indicating the workspace needs a refresh.
#[derive(Debug, Clone)]
struct WatcherMsg;
/// Which check currently owns the editor's underlines and the issues panel.
#[derive(Clone, Copy, PartialEq, Eq)]
enum IssueSource {
@@ -362,6 +368,10 @@ pub struct App {
np_rx: Option<std::sync::mpsc::Receiver<ProjectMsg>>,
/// Whether the new-project settings window is open.
show_project_settings: bool,
/// Receiver for file change notifications from the workspace watcher.
watcher_rx: Option<mpsc::Receiver<WatcherMsg>>,
/// The file watcher thread handle, so it can be stopped when the workspace closes.
watcher_thread: Option<thread::JoinHandle<()>>,
}
impl App {
@@ -460,6 +470,8 @@ impl App {
np_status: String::new(),
np_rx: None,
show_project_settings: false,
watcher_rx: None,
watcher_thread: None,
};
app.load_spell_dict();
app.open_workspace();
@@ -496,6 +508,7 @@ impl eframe::App for App {
self.refresh_find();
}
self.poll_watcher();
self.poll_lt();
self.poll_settings_test();
self.poll_spell();
@@ -610,6 +623,7 @@ impl eframe::App for App {
self.save_current();
self.persist_order();
self.persist_titles();
self.stop_watcher();
self.config.save();
}
}