refactor: extract block dnd handle predicate

This commit is contained in:
lix-2026
2026-05-25 06:09:42 +08:00
parent 4d08b69d2e
commit 6017a0ad7c
6 changed files with 70 additions and 13 deletions
@@ -0,0 +1,52 @@
# Batch X Worker A: 5-29 block DnD DOM boundary readonly audit
## Role
You are a leaf Reasonix coding worker. Do not spawn any other agent, runner, ACP process, Claude Code, Codex, Hermes, or Reasonix. Do not create, delete, or modify worktrees. Do not create new `.codex/reasonix-tasks/*.md` files.
## Project
Project root for this run:
```text
/mnt/Data1T/mnote-worktrees/0525-x-worker-a-5-29-dnd-dom-readonly
```
## Task
Perform a read-only audit for the next small 5-29 editor runtime extraction around block drag-and-drop DOM helpers.
Focus only on these current symbols:
- `drop_indicator_from_target` in `rust/spikes/leptos-tiptap-spike/src/lib.rs`
- `drop_indicator_from_point` in `rust/spikes/leptos-tiptap-spike/src/lib.rs`
- `hovered_block_from_target` in `rust/spikes/leptos-tiptap-spike/src/lib.rs`
- helper dependencies used by those functions, such as `target_element`, `direct_block_from_element`, `top_level_block_index`, `block_state_from_index`, `editor_root_element`, `editor_stage_element`, and `HANDLE_SHELL_SELECTOR`
- existing `editor_runtime/block_dnd.rs`
## Required output
Write a handoff that answers:
1. Which exact helper, if any, can be moved next into `editor_runtime/block_dnd.rs` without moving Leptos signals, event listeners, or block reorder behavior?
2. Which functions must stay in `lib.rs` for now, and why?
3. What is the smallest safe implementation diff Codex should make next?
4. What commands should Codex run after that diff?
5. What browser smoke, if any, is needed to prove behavior did not regress?
## Constraints
- Read-only only. Do not edit source files, design docs, task files, generated wasm/js, or tests.
- Do not run `cargo build`, `cargo check`, `wasm-bindgen`, browser tests, or dev servers.
- You may run read-only commands such as `rg`, `sed`, `git status`, `git diff --stat`, and CodeGraph if available.
- If you find the task needs implementation, report the proposed patch in prose only.
- Keep the result narrow. Do not propose a broad drag-and-drop rewrite.
## Acceptance contract
The run is acceptable only if:
- Worktree diff is empty.
- `process-handoff.md` and `process-handoff.json` exist.
- The handoff includes the five required answers above.
- The handoff does not claim code was changed or tests were run.
@@ -206,3 +206,9 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据
- 修改:`rust/spikes/leptos-tiptap-spike/src/lib.rs`、generated island wasm。 - 修改:`rust/spikes/leptos-tiptap-spike/src/lib.rs`、generated island wasm。
- 当前执行约束同步修正:`3-20` / `5-29` checklist 均保持“当前 goal 下每批最多 2 个 worker”。 - 当前执行约束同步修正:`3-20` / `5-29` checklist 均保持“当前 goal 下每批最多 2 个 worker”。
- 已验证:`git diff --check``cargo fmt --manifest-path rust/Cargo.toml --all --check``cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`、wasm release build、`wasm-bindgen``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task489-block-menu-delete-undo-smoke.js``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task488-local-attachment-link-delete-undo-smoke.js``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js``codegraph sync .``codegraph status . --json` pending 0。 - 已验证:`git diff --check``cargo fmt --manifest-path rust/Cargo.toml --all --check``cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`、wasm release build、`wasm-bindgen``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task489-block-menu-delete-undo-smoke.js``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task488-local-attachment-link-delete-undo-smoke.js``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js``codegraph sync .``codegraph status . --json` pending 0。
- 2026-05-25Batch X 计划只派发 1 个只读 Reasonix worker 审计 block DnD DOM 边界,任务书 `.codex/reasonix-tasks/2026-05-25-batch-x-worker-a-5-29-dnd-dom-readonly.md`worktree `/mnt/Data1T/mnote-worktrees/0525-x-worker-a-5-29-dnd-dom-readonly`
- 派发异常:启动本只读 worker 后,又出现未由主控批准的 Worker A/B/C/D runner 与 ACP 进程,超过当前 goal 的“每批最多 2 个 worker”;主控已终止全部 `reasonix-coding-runner` / `reasonix acp` / `reasonix/dist/cli/index.js acp` 进程,本批不采纳任何 Reasonix 输出。
- Codex 主控改为本地最小机械切片:`editor_runtime/block_dnd.rs` 新增 `element_within_handle_shell(element, handle_selector)`,只封装 handle-shell DOM predicate`lib.rs``hovered_block_from_target`、editor stage click / mousemove / dragover 调用点改为委托该 helper。
- 未迁:`hovered_block_from_target``drop_indicator_from_target``drop_indicator_from_point`、拖拽 listener、block reorder 行为和 DOM -> block 状态解析链仍留在 `lib.rs`
- 修改:`rust/spikes/leptos-tiptap-spike/src/editor_runtime/block_dnd.rs``rust/spikes/leptos-tiptap-spike/src/lib.rs`
- 已验证:`cargo fmt --manifest-path rust/Cargo.toml --all --check``cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`、wasm release build、`wasm-bindgen``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task489-block-menu-delete-undo-smoke.js``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task488-local-attachment-link-delete-undo-smoke.js``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js``git diff --check``codegraph sync .``codegraph status . --json` pending 0。
@@ -1284,7 +1284,7 @@ function __wbg_get_imports() {
return ret; return ret;
}, },
__wbindgen_cast_0000000000000006: function(arg0, arg1) { __wbindgen_cast_0000000000000006: function(arg0, arg1) {
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 856, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 886, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf); const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf);
return ret; return ret;
}, },
@@ -4,7 +4,7 @@
//! 也不分发 editor command;调用方负责保留编辑器状态编排。 //! 也不分发 editor command;调用方负责保留编辑器状态编排。
use crate::editor_runtime::block_hover_state::{DropIndicatorState, DropPlacement}; use crate::editor_runtime::block_hover_state::{DropIndicatorState, DropPlacement};
use web_sys::{window, EventTarget}; use web_sys::{window, Element, EventTarget};
pub(crate) struct HandleCorridorGeometry { pub(crate) struct HandleCorridorGeometry {
pub(crate) stage_left: f64, pub(crate) stage_left: f64,
@@ -81,6 +81,10 @@ pub(crate) fn event_target_from_point(client_x: i32, client_y: i32) -> Option<Ev
.map(Into::into) .map(Into::into)
} }
pub(crate) fn element_within_handle_shell(element: &Element, handle_selector: &str) -> bool {
element.closest(handle_selector).ok().flatten().is_some()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
+6 -11
View File
@@ -22,8 +22,8 @@ use web_sys::{
use editor_runtime::attachment_upload::dispatch_editor_upload_request; use editor_runtime::attachment_upload::dispatch_editor_upload_request;
use editor_runtime::block_dnd::{ use editor_runtime::block_dnd::{
drop_indicator_from_geometry, event_target_from_point, pointer_in_handle_corridor_geometry, drop_indicator_from_geometry, element_within_handle_shell, event_target_from_point,
HandleCorridorGeometry, pointer_in_handle_corridor_geometry, HandleCorridorGeometry,
}; };
use editor_runtime::block_menu_overlay; use editor_runtime::block_menu_overlay;
use editor_runtime::block_menu_legacy_html::{ use editor_runtime::block_menu_legacy_html::{
@@ -4914,12 +4914,7 @@ fn hovered_block_from_target(target: web_sys::EventTarget) -> Option<HoveredBloc
let root = editor_root_element()?; let root = editor_root_element()?;
let element = target_element(target)?; let element = target_element(target)?;
if element if element_within_handle_shell(&element, HANDLE_SHELL_SELECTOR) {
.closest(HANDLE_SHELL_SELECTOR)
.ok()
.flatten()
.is_some()
{
return None; return None;
} }
@@ -8103,7 +8098,7 @@ fn App(mount_options: MountOptions) -> impl IntoView {
}; };
if let Some(element) = target_element(target.clone()) { if let Some(element) = target_element(target.clone()) {
if element.closest(HANDLE_SHELL_SELECTOR).ok().flatten().is_some() { if element_within_handle_shell(&element, HANDLE_SHELL_SELECTOR) {
return; return;
} }
} }
@@ -8155,7 +8150,7 @@ fn App(mount_options: MountOptions) -> impl IntoView {
}; };
if let Some(element) = target_element(target.clone()) { if let Some(element) = target_element(target.clone()) {
if element.closest(HANDLE_SHELL_SELECTOR).ok().flatten().is_some() { if element_within_handle_shell(&element, HANDLE_SHELL_SELECTOR) {
return; return;
} }
} }
@@ -8208,7 +8203,7 @@ fn App(mount_options: MountOptions) -> impl IntoView {
}; };
if let Some(element) = target_element(target.clone()) { if let Some(element) = target_element(target.clone()) {
if element.closest(HANDLE_SHELL_SELECTOR).ok().flatten().is_some() { if element_within_handle_shell(&element, HANDLE_SHELL_SELECTOR) {
return; return;
} }
} }