mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-29 21:07:24 -06:00
Merge PR #2886: Recovery: keep explicit New/Open visible after startup discovery
crates/ui-egui/src/backstage.rs: #3125's t.nav fill kept, without the recovery black. Docs: status entries prepended to main's lines; the recovery paragraph placed after the status line.
This commit is contained in:
+3
-1
File diff suppressed because one or more lines are too long
@@ -761,7 +761,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn recovery_app() -> (WordApp, std::path::PathBuf) {
|
||||
fn recovery_fixture() -> (WordApp, std::path::PathBuf) {
|
||||
let root = std::env::temp_dir().join(format!(
|
||||
"wordcraft-recovery-ui-{}-{}",
|
||||
std::process::id(),
|
||||
@@ -780,10 +780,42 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
let mut app = WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::new()), Services::default());
|
||||
app.ui.language = "en".into();
|
||||
(app, root)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn recovery_app() -> (WordApp, std::path::PathBuf) {
|
||||
let (mut app, root) = recovery_fixture();
|
||||
app.session.enable_recovery(root.clone(), std::sync::Arc::new(|| {})).unwrap();
|
||||
(app, root)
|
||||
}
|
||||
|
||||
/// Hold discovery polling until the accessible control has dispatched its command.
|
||||
/// Warming up first keeps font installation independent of worker timing.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn pending_recovery_harness(
|
||||
app: WordApp,
|
||||
root: &std::path::Path,
|
||||
) -> (egui_kittest::Harness<'static, WordApp>, std::rc::Rc<std::cell::Cell<bool>>) {
|
||||
let poll = std::rc::Rc::new(std::cell::Cell::new(true));
|
||||
let gate = poll.clone();
|
||||
let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1440.0, 900.0)).build_ui_state(
|
||||
move |ui, app: &mut WordApp| {
|
||||
if gate.get() {
|
||||
app.logic(&ui.ctx().clone());
|
||||
}
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(4);
|
||||
poll.set(false);
|
||||
h.state_mut().session.enable_recovery(root.to_path_buf(), std::sync::Arc::new(|| {})).unwrap();
|
||||
assert!(!h.state().session.recovery_ready());
|
||||
(h, poll)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn recovery_harness(app: WordApp) -> egui_kittest::Harness<'static, WordApp> {
|
||||
egui_kittest::Harness::builder().with_size(egui::vec2(1440.0, 900.0)).build_ui_state(
|
||||
@@ -862,6 +894,72 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn recovery_does_not_replace_an_explicit_new_document() {
|
||||
for template in ["Blank document", "Report"] {
|
||||
let (mut app, root) = recovery_fixture();
|
||||
app.ui.backstage = true;
|
||||
app.ui.backstage_page = "home".into();
|
||||
let (mut h, poll) = pending_recovery_harness(app, &root);
|
||||
let previous = h.state().session.document_id();
|
||||
h.get_by_label("New").click();
|
||||
h.run_steps(4);
|
||||
h.get_by_label(template).click();
|
||||
h.run_steps(4);
|
||||
assert!(!h.state().session.recovery_ready());
|
||||
let document = h.state().session.document_id();
|
||||
let text = h.state().session.doc.plain_text(wordcraft_doc::StoryRef::Body);
|
||||
assert_ne!(document, previous);
|
||||
poll.set(true);
|
||||
settle_recovery(&mut h);
|
||||
assert!(!h.state().ui.backstage, "the requested template must stay visible");
|
||||
assert_eq!(h.state().session.document_id(), document);
|
||||
assert_eq!(h.state().session.doc.plain_text(wordcraft_doc::StoryRef::Body), text);
|
||||
assert_eq!(h.state().session.recovery_documents().len(), 1, "older copies remain available");
|
||||
h.state_mut().ui.backstage = true;
|
||||
h.run_steps(4);
|
||||
h.get_by_label("Recover").click();
|
||||
h.run_steps(4);
|
||||
assert!(h.state().ui.backstage && h.state().ui.backstage_page == "recover");
|
||||
drop(h);
|
||||
let _ = std::fs::remove_dir_all(root);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn recovery_does_not_replace_an_explicitly_opened_file() {
|
||||
let (mut app, root) = recovery_fixture();
|
||||
let path = root.join("Requested.txt");
|
||||
std::fs::write(&path, "Requested synthetic document").unwrap();
|
||||
app.services.pick_open = Some(Box::new(move |_| Some(path.to_string_lossy().into_owned())));
|
||||
app.ui.backstage = true;
|
||||
app.ui.backstage_page = "home".into();
|
||||
let (mut h, poll) = pending_recovery_harness(app, &root);
|
||||
let previous = h.state().session.document_id();
|
||||
h.get_by_label("Open").click();
|
||||
h.run_steps(4);
|
||||
h.get_by_label("Browse…").click();
|
||||
h.run_steps(4);
|
||||
assert!(!h.state().session.recovery_ready());
|
||||
let document = h.state().session.document_id();
|
||||
assert_ne!(document, previous);
|
||||
poll.set(true);
|
||||
settle_recovery(&mut h);
|
||||
assert!(!h.state().ui.backstage);
|
||||
assert_eq!(h.state().session.document_id(), document);
|
||||
assert_eq!(h.state().session.doc.plain_text(wordcraft_doc::StoryRef::Body), "Requested synthetic document");
|
||||
assert_eq!(h.state().session.recovery_documents().len(), 1);
|
||||
h.state_mut().ui.backstage = true;
|
||||
h.run_steps(4);
|
||||
h.get_by_label("Recover").click();
|
||||
h.run_steps(4);
|
||||
assert!(h.state().ui.backstage && h.state().ui.backstage_page == "recover");
|
||||
drop(h);
|
||||
let _ = std::fs::remove_dir_all(root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn recovery_page_keeps_dismissed_copies_and_confirms_discard() {
|
||||
|
||||
@@ -1068,6 +1068,9 @@ impl WordApp {
|
||||
// When the document has been replaced (not when Open only showed its picker), a pending
|
||||
// "Save changes?" and AutoSave's go-ahead were both about the old one.
|
||||
if self.session.document_id() != document {
|
||||
// A requested New/Open wins over the asynchronous startup recovery scan.
|
||||
// Older copies remain available through File > Recover.
|
||||
self.recovery_seen = true;
|
||||
self.autosave_path = None;
|
||||
if matches!(self.dialog, Some(dialogs::Dialog::SaveChanges { .. })) {
|
||||
self.dialog = None;
|
||||
|
||||
+4
-1
@@ -1,6 +1,8 @@
|
||||
# Where WordCraft falls short of Word
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-21 · **Change:** trivial (text mini toolbar available with the right-click menu, #2959; previously trivial: personal templates in File › New and Save as Word Template, #2852; previously trivial: tracked table/row/cell/section property changes record in WordCraft, narrowing gap 5; previously trivial: a document with many content controls saves in the time the controls take, #13; previously trivial: personal templates in File › New and Save as Word Template, #2852; previously trivial: Format Page Numbers dialog carries Word's number formats and Continue from previous section / Start at, #1113; previously trivial: centred list labels after leading page breaks, #2964; previously trivial: fixed font inputs in four layout controls; previously trivial: a right, centre or decimal tab whose text doesn't fit, or a left tab past the right indent, goes to the next line with its text, #1840; previously trivial: Bullets and Numbering toggle off style-inherited lists at their effective level, including basedOn chains and numbered ListParagraph styles; other paragraphs and list definitions stay unchanged (related to #3459); previously trivial: Russian sorting keeps ё with е and recognises decomposed ё/й; previously trivial: personal templates in File › New and Save as Word Template, #2852; previously trivial: #8: English dialects — en-GB, en-CA, en-AU… flag each other's spellings, #1281; previously trivial: #1: shape gradients and transparency, #1500; previously trivial: text typed into a content control takes the control's formatting; localized placeholder styles recognised; previously trivial: forms protection keeps its content controls: no inserting, Properties or Remove Content Control; previously trivial: clearing a no-match font query keeps the recent heading visible, #2068; previously trivial: line breaking: a slash opening a word travels whole to the next line, #2963; previously trivial: recipient list search and merged-only view, #1576; previously trivial: #2 impact restated: only ink rendering and OLE editing remain; previously trivial: #2 narrowed: native ink nested in groups and drawing canvases kept on save; previously trivial: opening a DOCX no longer copies its pictures; previously trivial: RTF export writes sections (gap 19); previously trivial: ODT export writes sections (gap 19); previously trivial: automatic Asian/Latin and Asian/number spacing laid out, #2358; previously trivial: hint-reliant CJK outlines; previously trivial: numbered headings, #1793; previously trivial: legacy outline, shadow, emboss and imprint text effects drawn, #2534; previously trivial: #8 narrowed: Indonesian proofing from the installed Hunspell dictionary, #498; previously trivial: glossary/building blocks closed; previously trivial: Split Cells and Convert Text to Table follow the Word check, #2592; previously trivial: Language dialog keeps inherited proofing language and no-proof choices (#2717); previously trivial: #29: a copied equation also goes in the MathML clipboard formats, #1297; previously trivial: #24: the Spike, Ctrl+F3 and Ctrl+Shift+F3; previously trivial: VML freeforms read and kept on save; previously trivial: consistent preferred cell widths; previously trivial: multi-paragraph field instructions no longer add blank lines; previously trivial: page-break-only list paragraphs hide their labels; previously trivial: floating-table overlap ignores empty line-break lines; previously trivial: copies keep their formatting on the clipboard as HTML, #1523; previously trivial: sample-text formulas on Enter, #1067; previously trivial: #29: copied equations as MathML, OMML open as #1297, #816; previously trivial: the Font list no longer keeps every previewed font in memory, #848; previously trivial: table column and row markers on the rulers, #700; previously trivial: table borders and the resize handle drag on the page, #217; previously trivial: formatting revisions landed, #41; previously trivial: Draw Table and Eraser landed, #303; previously minor: beta distance restated: ~15 points, ~150–230 h; previously: first version; every known shortfall from the 2026-10-10 re-measure, ranked) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-21 · **Change:** trivial (explicit documents keep the editor when recovery discovery finishes; readable recovery controls; previously trivial: text mini toolbar available with the right-click menu, #2959; previously trivial: personal templates in File › New and Save as Word Template, #2852; previously trivial: tracked table/row/cell/section property changes record in WordCraft, narrowing gap 5; previously trivial: a document with many content controls saves in the time the controls take, #13; previously trivial: personal templates in File › New and Save as Word Template, #2852; previously trivial: Format Page Numbers dialog carries Word's number formats and Continue from previous section / Start at, #1113; previously trivial: centred list labels after leading page breaks, #2964; previously trivial: fixed font inputs in four layout controls; previously trivial: a right, centre or decimal tab whose text doesn't fit, or a left tab past the right indent, goes to the next line with its text, #1840; previously trivial: Bullets and Numbering toggle off style-inherited lists at their effective level, including basedOn chains and numbered ListParagraph styles; other paragraphs and list definitions stay unchanged (related to #3459); previously trivial: Russian sorting keeps ё with е and recognises decomposed ё/й; previously trivial: personal templates in File › New and Save as Word Template, #2852; previously trivial: #8: English dialects — en-GB, en-CA, en-AU… flag each other's spellings, #1281; previously trivial: #1: shape gradients and transparency, #1500; previously trivial: text typed into a content control takes the control's formatting; localized placeholder styles recognised; previously trivial: forms protection keeps its content controls: no inserting, Properties or Remove Content Control; previously trivial: clearing a no-match font query keeps the recent heading visible, #2068; previously trivial: line breaking: a slash opening a word travels whole to the next line, #2963; previously trivial: recipient list search and merged-only view, #1576; previously trivial: #2 impact restated: only ink rendering and OLE editing remain; previously trivial: #2 narrowed: native ink nested in groups and drawing canvases kept on save; previously trivial: opening a DOCX no longer copies its pictures; previously trivial: RTF export writes sections (gap 19); previously trivial: ODT export writes sections (gap 19); previously trivial: automatic Asian/Latin and Asian/number spacing laid out, #2358; previously trivial: hint-reliant CJK outlines; previously trivial: numbered headings, #1793; previously trivial: legacy outline, shadow, emboss and imprint text effects drawn, #2534; previously trivial: #8 narrowed: Indonesian proofing from the installed Hunspell dictionary, #498; previously trivial: glossary/building blocks closed; previously trivial: Split Cells and Convert Text to Table follow the Word check, #2592; previously trivial: Language dialog keeps inherited proofing language and no-proof choices (#2717); previously trivial: #29: a copied equation also goes in the MathML clipboard formats, #1297; previously trivial: #24: the Spike, Ctrl+F3 and Ctrl+Shift+F3; previously trivial: VML freeforms read and kept on save; previously trivial: consistent preferred cell widths; previously trivial: multi-paragraph field instructions no longer add blank lines; previously trivial: page-break-only list paragraphs hide their labels; previously trivial: floating-table overlap ignores empty line-break lines; previously trivial: copies keep their formatting on the clipboard as HTML, #1523; previously trivial: sample-text formulas on Enter, #1067; previously trivial: #29: copied equations as MathML, OMML open as #1297, #816; previously trivial: the Font list no longer keeps every previewed font in memory, #848; previously trivial: table column and row markers on the rulers, #700; previously trivial: table borders and the resize handle drag on the page, #217; previously trivial: formatting revisions landed, #41; previously trivial: Draw Table and Eraser landed, #303; previously minor: beta distance restated: ~15 points, ~150–230 h; previously: first version; every known shortfall from the 2026-10-10 re-measure, ranked) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
|
||||
|
||||
Recovery discovery no longer interrupts an explicitly created or opened document. Older copies remain in File › Recover, whose buttons use the interface palette. See [local recovery](recovery.md).
|
||||
|
||||
> **Printing update:** 2026-10-12 · direct desktop printing through craft-print; details below.
|
||||
|
||||
@@ -123,6 +125,7 @@ Footer page-table height and body wrapping are corrected. Other pagination gaps
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-18 | trivial | Explicit New/Open is not interrupted by startup recovery discovery; older copies stay available and Recover follows the interface theme. |
|
||||
| 2026-10-21 | trivial | Bullets and Numbering toggle off style-inherited lists at their effective level, including basedOn chains and numbered ListParagraph styles; other paragraphs and list definitions stay unchanged (related to #3459) |
|
||||
| 2026-10-21 | trivial | #3172: the ribbon's font name and size boxes, and the dropdown arrows beside them, say what they are to a screen reader |
|
||||
| 2026-10-21 | trivial | Gap 19: ODT table column widths are read on import, so they survive a save |
|
||||
|
||||
+5
-1
@@ -1,7 +1,11 @@
|
||||
# Local document recovery
|
||||
|
||||
Desktop WordCraft periodically keeps a local copy of unsaved work, including new drafts.
|
||||
File › Recover lists available copies. The page opens at startup when copies are found.
|
||||
File › Recover lists available copies. The page opens at startup when copies are found,
|
||||
unless a document was explicitly created or opened before the startup scan completed.
|
||||
In particular, a window launched for New/Open keeps the requested document visible;
|
||||
older copies remain available through File › Recover. Recovery uses the regular interface
|
||||
palette so its labels and buttons stay readable in light and dark themes.
|
||||
Restore opens an unsaved document with no file path. Save As chooses where to keep it.
|
||||
Recovery never writes to the original document. AutoSave's permission to write to an earlier
|
||||
file is cleared on restore.
|
||||
|
||||
+4
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user