Add workspace search, synopsis view, snapshot diffs, typewriter mode, and a writing streak

Five productivity features: search/replace across the whole workspace
(with a safety commit before Replace all), a Synopsis window compiling
every chapter's slug, a Diff button per Auto-Snapshot to compare against
an older snapshot instead of only HEAD, typewriter-style cursor centering
and paragraph dimming gated on Distraction-free mode, and a per-day
word-count streak persisted to word_log.json alongside order.json/titles.json.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-21 15:55:15 -05:00
parent 229e2413c4
commit 6ca8681d08
12 changed files with 781 additions and 17 deletions
+34
View File
@@ -136,6 +136,17 @@ impl App {
files are not counted)",
);
// A streak of consecutive days with words added,
// persisted in word_log.json so it survives restarts.
let streak = self.word_log.streak(crate::word_log::today());
if streak > 1 {
ui.separator();
ui.label(egui::RichText::new(format!("🔥 {streak}-day streak")).weak())
.on_hover_text(
"Consecutive days the manuscript's word count has grown",
);
}
// Progress toward the file's Word Count Target, if set.
if let Some((goal, prose)) = goal {
let (frac, color, text) = goal_progress(goal, prose);
@@ -238,6 +249,14 @@ impl App {
ui.close_menu();
self.open_find(true);
}
if ui
.button("🔎 Search workspace… Ctrl+Shift+F")
.on_hover_text("Find (and optionally replace) across every file")
.clicked()
{
ui.close_menu();
self.open_workspace_search();
}
if ui.button("📸 Snapshots…").clicked() {
ui.close_menu();
self.show_snapshots = true;
@@ -260,6 +279,10 @@ impl App {
ui.close_menu();
self.open_story_bible_editor();
}
if ui.button("🧾 Synopsis…").clicked() {
ui.close_menu();
self.show_synopsis = true;
}
ui.separator();
let busy = self.beats_rx.is_some();
if ui
@@ -302,6 +325,17 @@ impl App {
ui.close_menu();
self.toggle_distraction_free();
}
if ui
.checkbox(&mut self.config.typewriter_mode, "Typewriter scrolling & focus")
.on_hover_text(
"While Distraction-free mode is on, keep the caret's line \
centered and dim every paragraph but the current one. \
Has no effect otherwise.",
)
.changed()
{
self.config.save();
}
ui.separator();
if ui
.checkbox(&mut self.config.show_preview, "Preview pane")