From 0fdb4d03e4089acfd0df055d9ca651b30c6f4864 Mon Sep 17 00:00:00 2001 From: prxnvt <53838599+prxnvt@users.noreply.github.com> Date: Sat, 24 Oct 2026 02:27:09 -0400 Subject: [PATCH] UI: a macro question is answered only in the workbook that asked it The Macros dialog's Delete question and the questions a macro run asks (approve it, overwrite or move the data in the way) keep the command and run it again with the answer. When another workbook became active before the answer (an agent can switch workbooks under the prompt), the answer ran there: Delete removed the other workbook's macro of the same name. A question now keeps its workbook, and an answer given while another one is active does nothing and says why. --- crates/ui-egui/src/macro_ui.rs | 13 +++++++++++- crates/ui-egui/tests/it/macros_ui.rs | 30 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/macro_ui.rs b/crates/ui-egui/src/macro_ui.rs index acbc77a2c..78fee3317 100644 --- a/crates/ui-egui/src/macro_ui.rs +++ b/crates/ui-egui/src/macro_ui.rs @@ -50,6 +50,8 @@ impl MacroUi { /// runs the command again with its parameters added; Cancel drops it. #[derive(Clone, Debug)] pub struct Question { + /// The workbook (`DocState::uid`) the question is about: the answer runs only there. + pub doc: u64, pub title: String, pub text: String, pub command: String, @@ -147,7 +149,8 @@ pub fn ask(app: &mut SheetApp, title: &str, id: &str, params: &Json, e: &str) -> } else { return false; }; - app.macros.question = Some(Question { title: title.into(), text, command: id.into(), params: params.clone(), choices }); + let doc = app.session.active().map_or(0, |d| d.uid); + app.macros.question = Some(Question { doc, title: title.into(), text, command: id.into(), params: params.clone(), choices }); true } @@ -202,6 +205,13 @@ pub fn question_box(app: &mut SheetApp, ctx: &egui::Context) { }); }); match answer { + // Another workbook became active meanwhile (an agent): the answer would run there. + Some(Some(_)) if app.session.active().is_none_or(|d| d.uid != q.doc) => { + app.macros.question = None; + let book = app.session.documents().iter().find(|d| d.uid == q.doc).map(|d| d.display_title()).unwrap_or_default(); + app.message = + Some((q.title.clone(), format!("The question was about “{book}”, which is no longer the active workbook, so nothing was done."))); + } Some(Some(extra)) => { app.macros.question = None; let mut params = q.params.clone(); @@ -441,6 +451,7 @@ pub fn macros_dialog(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog, open: &mut Kind::Script => ("Delete Script", "automate.deleteScript", "script"), }; app.macros.question = Some(Question { + doc: app.session.active().map_or(0, |d| d.uid), title: title.into(), text: format!("Do you want to delete {what} “{}”?", e.name), command: command.into(), diff --git a/crates/ui-egui/tests/it/macros_ui.rs b/crates/ui-egui/tests/it/macros_ui.rs index 7c18b08ff..c8e6ba490 100644 --- a/crates/ui-egui/tests/it/macros_ui.rs +++ b/crates/ui-egui/tests/it/macros_ui.rs @@ -314,3 +314,33 @@ fn the_macros_and_record_macro_dialogs_fit_a_narrow_window() { } } } + +#[test] +fn a_question_is_answered_in_the_workbook_that_asked_it() { + let mut h = harness("Automate"); + save(&mut h, json!({"name": "Tidy", "steps": []})); + h.state_mut().open_dialog("macros", json!({"name": "Tidy"})); + h.run_steps(2); + h.get_by_label("Delete").click(); + h.run_steps(2); + assert!(h.state().macros.question.is_some()); + // Another workbook with a macro of that name becomes active before the answer (an agent). + h.state_mut().dialog = None; + h.state_mut().session.new_workbook(); + save(&mut h, json!({"name": "Tidy", "steps": []})); + h.get_by_label("Delete").click(); + h.run_steps(3); + assert_eq!(h.state().session.active().unwrap().wb.macros.len(), 1, "the other workbook's macro is kept"); + assert!(h.state().macros.question.is_none()); + assert!(h.state().message.as_ref().is_some_and(|(_, m)| m.contains("Book1")), "{:?}", h.state().message); + // Asked and answered in the same workbook, it deletes there. + h.state_mut().message = None; + h.run_steps(2); + h.state_mut().open_dialog("macros", json!({"name": "Tidy"})); + h.run_steps(2); + h.get_by_label("Delete").click(); + h.run_steps(2); + h.get_all_by_label("Delete").last().unwrap().click(); + h.run_steps(3); + assert!(h.state().session.active().unwrap().wb.macros.is_empty()); +}