diff --git a/crates/openpencil-shell-native/src/widget_host/input.rs b/crates/openpencil-shell-native/src/widget_host/input.rs index 325aac89c..27bc1e082 100644 --- a/crates/openpencil-shell-native/src/widget_host/input.rs +++ b/crates/openpencil-shell-native/src/widget_host/input.rs @@ -378,6 +378,19 @@ impl WidgetHostNative { // only effect, nothing more to do. return false; } + // Defensive source-validity check — symmetric with the + // cursor_move and paint guards. `reorder_before/after` + // already silently no-ops on a missing source, but bailing + // here keeps the rest of this method (drop_target_at + + // dispatch) from running pointlessly on a dead drag. + if self + .document + .active_page() + .map(|p| p.find(d.source).is_none()) + .unwrap_or(true) + { + return false; + } use openpencil_shell_core::widgets::{DropPosition, LayerPanel, TOP_BAR_HEIGHT}; let layer_rect = openpencil_shell_core::Rect { origin: openpencil_shell_core::Point2D::new(0.0, TOP_BAR_HEIGHT), diff --git a/crates/openpencil-shell-web/src/widget_host.rs b/crates/openpencil-shell-web/src/widget_host.rs index 4184befd8..8bfa44b60 100644 --- a/crates/openpencil-shell-web/src/widget_host.rs +++ b/crates/openpencil-shell-web/src/widget_host.rs @@ -465,6 +465,16 @@ impl WidgetHost { if !d.active { return false; } + // Defensive source-validity check (mirrors native) — bail + // if the dragged node disappeared between move and release. + if self + .document + .active_page() + .map(|p| p.find(d.source).is_none()) + .unwrap_or(true) + { + return false; + } use openpencil_shell_core::widgets::{DropPosition, LayerPanel}; let layer_rect = self.layer_panel_rect(viewport_h); let panel = LayerPanel::from_document(&self.document);