From b11f064e792994b4ec344a4040fbbc8ed704ac23 Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Tue, 12 May 2026 02:21:59 +0800 Subject: [PATCH] fix(shell): defensive commit_layer_drag source-validity guard Final codex re-review CONCERN: `commit_layer_drag` was relying on `Document::reorder_before/after`'s own source-existence check to no-op on a deleted source. Per codex: the commit path should bail explicitly for symmetry with the existing cursor_move + paint guards. Added the same `active_page().find(source).is_none()` check on both native (`widget_host/input.rs`) and web (`widget_host.rs`) at the top of `commit_layer_drag`, right after the `!d.active` early-out. No behavior change in the safe path (reorder still happens), but short-circuits the drop_target_at + dispatch when the source is gone. --- .../src/widget_host/input.rs | 13 +++++++++++++ crates/openpencil-shell-web/src/widget_host.rs | 10 ++++++++++ 2 files changed, 23 insertions(+) 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);