From dc1506f2e8fd2fb4e92efaba5b1d9425a6129e47 Mon Sep 17 00:00:00 2001 From: "sorokin.oleg.edu" Date: Sat, 12 Sep 2026 05:04:09 -0700 Subject: [PATCH] Resolve diagram Delete target after placement properties selection --- crates/oa-desktop/src/app/diagram_removal.rs | 18 ++++- .../src/app/diagram_removal_tests.rs | 80 +++++++++++++++++++ harness/handoff-usage-pause-20260912.md | 4 +- 3 files changed, 96 insertions(+), 6 deletions(-) diff --git a/crates/oa-desktop/src/app/diagram_removal.rs b/crates/oa-desktop/src/app/diagram_removal.rs index 63ac9772..66272dc8 100644 --- a/crates/oa-desktop/src/app/diagram_removal.rs +++ b/crates/oa-desktop/src/app/diagram_removal.rs @@ -43,7 +43,13 @@ impl App { } _ => None, }; - if current != Some(intent.context) || self.selected != intent.object { + let selected_object = self + .project() + .diagrams + .get(&intent.context.diagram) + .and_then(|diagram| diagram.nodes.get(&self.selected)) + .map_or(self.selected, |node| node.element); + if current != Some(intent.context) || selected_object != intent.object { return; } let view = projection::project_view(self.project(), intent.context.diagram); @@ -81,15 +87,19 @@ impl App { self.status = "Hidden from this diagram · Model element preserved · Undo restores the placement".into(); } } else { - self.begin_model_delete(Some(intent.context)); + self.begin_model_delete_target(intent.object, Some(intent.context)); } } pub(super) fn begin_model_delete(&mut self, canvas: Option) { - if self.selected == self.project().id || self.behavior.has_pending() { + self.begin_model_delete_target(self.selected, canvas); + } + + fn begin_model_delete_target(&mut self, object: Id, canvas: Option) { + if object == self.project().id || self.behavior.has_pending() { return; } - let mut request = model_menu::Request::object(self.selected, iced::Point::ORIGIN); + let mut request = model_menu::Request::object(object, iced::Point::ORIGIN); request.canvas = canvas; request.placement = if self.page == Page::Behavior { self.behavior.diagram.placement diff --git a/crates/oa-desktop/src/app/diagram_removal_tests.rs b/crates/oa-desktop/src/app/diagram_removal_tests.rs index 58ef9280..be422fe2 100644 --- a/crates/oa-desktop/src/app/diagram_removal_tests.rs +++ b/crates/oa-desktop/src/app/diagram_removal_tests.rs @@ -205,6 +205,86 @@ async fn mounted_shift_delete_captures_target_cancel_escape_and_confirmation_rep } struct FocusField(Rectangle); + +#[tokio::test] +async fn mounted_placement_properties_selection_supports_hide_and_model_delete() { + for layer in [Layer::Oa, Layer::Sa] { + for model in [false, true] { + let (mut f, mut mounted) = mount(layer).await; + let before = f.app.document.snapshot(); + let mut request = crate::model_menu::Request::object(f.object, Point::ORIGIN); + request.placement = Some(f.node); + request.canvas = f.app.route_context(); + let task = f + .app + .update(Message::Menu(crate::model_menu::Event::Open(request))); + mounted.perform(&mut f.app, task).await; + let action = crate::model_menu::popup(&f.app) + .unwrap() + .entries + .into_iter() + .find_map(|entry| match entry { + crate::context_menu::Entry::Action { label, message, .. } + if label == "Placement properties" => + { + message + } + _ => None, + }) + .unwrap(); + let task = f.app.update(action); + mounted.perform(&mut f.app, task).await; + assert_eq!(f.app.selected, f.node); + assert_eq!(f.app.selected_node, Some(f.node)); + let bounds = mounted + .geometry(&f.app) + .bounds(crate::allocation_input::CANVAS_ID); + mounted + .click(&mut f.app, Point::new(bounds.x + 20., bounds.y + 20.)) + .await; + assert_eq!( + f.app.selected, f.node, + "blank canvas preserves placement selection" + ); + delete(&mut mounted, &mut f.app, model).await; + if model { + let target = f + .app + .delete_target + .as_ref() + .expect("model confirmation opens"); + assert_eq!(target.snapshot.request.object, f.object); + assert_eq!(target.selection, f.node); + let token = target.snapshot.token; + let task = f.app.update(Message::ConfirmDelete(token)); + mounted.perform(&mut f.app, task).await; + assert!(!f.app.project().contains(f.object)); + assert!( + !f.app.project().diagrams[&f.copy] + .nodes + .values() + .any(|n| n.element == f.object) + ); + } else { + assert!(f.app.project().contains(f.object)); + assert!(!f.app.project().contains(f.node)); + assert!( + f.app.project().diagrams[&f.copy] + .nodes + .values() + .any(|n| n.element == f.object) + ); + } + consistent(&f.app); + let task = f.app.update(Message::Undo); + mounted.perform(&mut f.app, task).await; + assert_eq!(f.app.document.bundle(), &before.bundle); + assert_eq!(f.app.project(), &before.decoded.project); + assert!(!f.app.document.can_undo()); + } + } +} + impl Operation for FocusField { fn traverse(&mut self, visit: &mut dyn FnMut(&mut dyn Operation)) { visit(self); diff --git a/harness/handoff-usage-pause-20260912.md b/harness/handoff-usage-pause-20260912.md index 64c7108f..19c0d03c 100644 --- a/harness/handoff-usage-pause-20260912.md +++ b/harness/handoff-usage-pause-20260912.md @@ -1,8 +1,8 @@ -# Usage-limit pause — 2026-09-12 +# Historical source checkpoint — 2026-09-12 Superseded historical record: its stop interpretation does not apply. The user's current instruction is to continue. See [saved verification checkpoint](handoff-usage-pause-20260912-final.md) for preserved evidence and Gitea for current coordination. Do not infer a live pause from the historical text below. -The user requested saving all work and stopping. Root session Codex-956atf stopped the allocation worker and started no further builds or checks. Resume only when requested. Linux/WSL setup, networking, builds and checks remain paused pending the user's explicit instruction; Computer Use is also paused. +This record describes the earlier saved allocation source before its subsequent toolbar correction and combined verification. It does not impose a stop or resume condition. The separate Linux/WSL networking deferral remains applicable. This is a saved handoff, not a replacement for live coordination in Gitea issue #3. Branch: `codex/behavior-authoring-20260910`; publication branch: `codex/release1-956atf`.