From baae38786de3359b43edc9ce55a2b8f4af533eec Mon Sep 17 00:00:00 2001 From: lix-2026 Date: Mon, 25 May 2026 09:44:28 +0800 Subject: [PATCH] refactor: move menu wheel trap helper --- ...29-editor-runtime-followup-checklist-v1.md | 10 ++++++++- .../src/editor_runtime/dom_events.rs | 20 ++++++++++++++++- rust/spikes/leptos-tiptap-spike/src/lib.rs | 22 +++---------------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/design/05-editor-mainline/process/5-29-editor-runtime-followup-checklist-v1.md b/design/05-editor-mainline/process/5-29-editor-runtime-followup-checklist-v1.md index d8549baa..dd75d78f 100644 --- a/design/05-editor-mainline/process/5-29-editor-runtime-followup-checklist-v1.md +++ b/design/05-editor-mainline/process/5-29-editor-runtime-followup-checklist-v1.md @@ -6,7 +6,7 @@ ## 1. 目标 -在 `5-28` 已完成附件链接、history-safe delete、command sync、persistence、attachment upload intent 首批拆分后,继续把 editor runtime 的 selection / overlay / block menu / mindmap node view 切成可索引模块。Codex 维护本文件作为事实源,Reasonix 只做短批次审计、窄范围实现或浏览器 smoke;当前 goal 下每批最多 4 个 worker。 +在 `5-28` 已完成附件链接、history-safe delete、command sync、persistence、attachment upload intent 首批拆分后,继续把 editor runtime 的 selection / overlay / block menu / mindmap node view 切成可索引模块。Codex 维护本文件作为事实源,Reasonix 只做短批次审计、窄范围实现或浏览器 smoke;当前 goal 下每批最多 2 个 worker。 ## 2. 本轮边界 @@ -373,3 +373,11 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据 - 影响点:`HostCommandKind::RequestCurrentBlockId` 分支继续在 `lib.rs` 壳层里取 hovered block index,但由 `dom_selection::current_block_info_from_index()` 返回 `block_id`;其余 selection event / runtime state 路径不变。 - 已验证:`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`、`cargo build --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml --target wasm32-unknown-unknown --release`、`wasm-bindgen --target web --out-dir rust/spikes/leptos-tiptap-spike/generated/island --out-name mnote-leptos-tiptap-spike-island rust/spikes/leptos-tiptap-spike/target/wasm32-unknown-unknown/release/mnote_leptos_tiptap_spike.wasm`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3025 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3025 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3025 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:3025 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:3025 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。 - 结果:`task489`、`task488`、`task479` 均通过,说明当前块信息 helper 下沉未破坏块菜单删除/撤销、附件删除/撤销和本地 Markdown 附件生命周期基线。 +- 2026-05-25:Codex 主控复核 `image_toolbar_anchor_from_image` / `image_element_from_target` 候选,确认当前代码已在 `editor_runtime/overlays.rs` 中完成,不再重复拆分。 + - 代码证据:`editor_runtime/overlays.rs` 已定义 `ImageToolbarAnchor`、`image_toolbar_anchor_from_image(image)` 与 `image_element_from_target(target)`;`lib.rs` 只保留 click handler 壳层,负责选择图片节点、设置 editor focused 和打开 image toolbar overlay。 + - 决定:该候选从后续待办移除;继续改找仍留在 `lib.rs` 的小型 DOM event helper。 +- 2026-05-25:Codex 主控完成菜单滚轮拦截 helper 下沉。 + - 修改:`editor_runtime/dom_events.rs` 新增 `current_target_html_element(event)` 与 `trap_scroll_inside_menu(event)`;`lib.rs` 删除本地同名实现,菜单 `on:wheel` 调用改为复用 `dom_events` helper。 + - 边界:未迁 block menu / slash menu / turn-into menu 的 Leptos view、signal、命令执行、focus 或 overlay 状态;本批只迁滚轮事件的 prevent/stop 与局部 scrollTop clamp。 + - 已验证:`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`、`cargo build --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml --target wasm32-unknown-unknown --release`。 + - 结果:spike check/build 均通过;仍有既有 unused/dead_code warning,本批未触碰这些历史 warning。 diff --git a/rust/spikes/leptos-tiptap-spike/src/editor_runtime/dom_events.rs b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/dom_events.rs index 82e9b2f3..f4d3d8e7 100644 --- a/rust/spikes/leptos-tiptap-spike/src/editor_runtime/dom_events.rs +++ b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/dom_events.rs @@ -4,7 +4,7 @@ //! 不处理编辑器状态、overlay 或命令分发。 use wasm_bindgen::JsCast; -use web_sys::{Element, EventTarget, Node}; +use web_sys::{Element, EventTarget, HtmlElement, Node, WheelEvent}; pub(crate) fn target_element(target: EventTarget) -> Option { target.clone().dyn_into::().ok().or_else(|| { @@ -21,3 +21,21 @@ pub(crate) fn event_target_matches_selector(target: Option, selecto .and_then(|element| element.closest(selector).ok().flatten()) .is_some() } + +pub(crate) fn current_target_html_element(event: &WheelEvent) -> Option { + event.current_target()?.dyn_into::().ok() +} + +pub(crate) fn trap_scroll_inside_menu(event: &WheelEvent) { + event.prevent_default(); + event.stop_propagation(); + + let Some(menu) = current_target_html_element(event) else { + return; + }; + + let current = f64::from(menu.scroll_top()); + let max_scroll = f64::from((menu.scroll_height() - menu.client_height()).max(0)); + let next = (current + event.delta_y()).clamp(0.0, max_scroll); + menu.set_scroll_top(next.round() as i32); +} diff --git a/rust/spikes/leptos-tiptap-spike/src/lib.rs b/rust/spikes/leptos-tiptap-spike/src/lib.rs index f7a8280b..d2cd9cbe 100644 --- a/rust/spikes/leptos-tiptap-spike/src/lib.rs +++ b/rust/spikes/leptos-tiptap-spike/src/lib.rs @@ -41,7 +41,9 @@ use editor_runtime::command_sync::{ read_editor_snapshot, sync_editor_outputs, sync_persisted_editor_command, }; use editor_runtime::content_layout::{content_column_left, content_text_left}; -use editor_runtime::dom_events::{event_target_matches_selector, target_element}; +use editor_runtime::dom_events::{ + event_target_matches_selector, target_element, trap_scroll_inside_menu, +}; use editor_runtime::dom_selection::{ block_index_from_selection, current_block_info_from_index, runtime_block_id_from_index, selection_payload, selection_summary, SelectionPayload, @@ -4810,24 +4812,6 @@ fn handle_lane_left(stage_rect_width: f64) -> f64 { .max(HANDLE_STAGE_PADDING_LEFT) } -fn current_target_html_element(event: &WheelEvent) -> Option { - event.current_target()?.dyn_into::().ok() -} - -fn trap_scroll_inside_menu(event: &WheelEvent) { - event.prevent_default(); - event.stop_propagation(); - - let Some(menu) = current_target_html_element(event) else { - return; - }; - - let current = f64::from(menu.scroll_top()); - let max_scroll = f64::from((menu.scroll_height() - menu.client_height()).max(0)); - let next = (current + event.delta_y()).clamp(0.0, max_scroll); - menu.set_scroll_top(next.round() as i32); -} - fn hovered_block_from_target(target: web_sys::EventTarget) -> Option { let root = editor_root_element()?; hovered_block_from_target_runtime(target, &root, HANDLE_SHELL_SELECTOR)