diff --git a/design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md b/design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md index 3c78b9fa..efd5fded 100644 --- a/design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md +++ b/design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md @@ -6,7 +6,7 @@ ## 1. 目标 -在 `3-19` 已归档的基础上,继续把仍留在 Rust inline / 主壳大 runtime 中的浏览器行为切成可审计模块。Codex 维护本文件作为事实源,Reasonix 只承接短批次审计、窄范围实现或浏览器 smoke;每批最多 4 个 worker。 +在 `3-19` 已归档的基础上,继续把仍留在 Rust inline / 主壳大 runtime 中的浏览器行为切成可审计模块。Codex 维护本文件作为事实源,Reasonix 只承接短批次审计、窄范围实现或浏览器 smoke;每批最多 2 个 worker。 ## 2. 本轮边界 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 c5680c31..36eddbf5 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 @@ -67,7 +67,11 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据 - 已迁函数:`node_is_within_root`、`active_editor_text_selection`、`has_active_text_selection`、`selection_summary`、`selection_has_rich_marks`。 - 未迁:`SelectionPayload`、`selection_payload`、`send_selection_state*`、`selection_event_payload`,原因是仍依赖 bridge event、`HoveredBlockState` 和 runtime block id 边界。 - 已验证:`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`、`task488`、`task489`、CodeGraph pending 0。 -- 2026-05-25:执行口径修正:用户指出只读审计推进过慢,后续 B/C/D 应使用独立 worktree 产出候选 patch,而不是只读审计;每批最多 4 个 worker。 +- 2026-05-25:执行口径修正:继续遵循当前 goal 的“每批最多 2 个 worker”;Reasonix 后续必须使用独立 worktree 或明确只读任务,完成后由 Codex 读取 handoff 并复核。 - 2026-05-25:Reasonix runner 异常:后续实现 run 未写 `result.json`、`process-handoff.md/json`,部分 transcript 停在工具调用阶段;Codex 不采纳其文字结论,只复核实际 diff。 - 2026-05-25:Reasonix overlay / block menu 只读审计尝试未产生 `result.json` / `process-handoff`,且运行期间出现额外 Batch A2 runner,主控已终止进程;该结果不作为正式 handoff 采纳。 - transcript 只显示探索过程,未形成函数归属表;下一次应改为更窄的单问题只读任务,例如只审计 `floating_toolbar_anchor_from_selection` / `sync_text_selection_overlay` 能否迁入 `overlays.rs`。 +- 2026-05-25:Codex 主控直接完成 overlay 第一刀:新增 `editor_runtime/overlays.rs`,迁出 `FloatingToolbarAnchor`、`floating_toolbar_anchor_from_selection`、`sync_text_selection_overlay`。 + - 未迁:`sync_editor_overlay_state` / `try_sync_editor_overlay_state`,原因是仍依赖 `HoveredBlockState`、slash/block menu signal 和 overlay 关闭策略。 + - 已验证:`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`、`node scripts/task488-local-attachment-link-delete-undo-smoke.js`、`node scripts/task489-block-menu-delete-undo-smoke.js`。 + - 运行中再次出现额外 Batch A2 Reasonix runner,主控已终止;这些 runner 未作为本批证据。 diff --git a/rust/spikes/leptos-tiptap-spike/generated/island/mnote-leptos-tiptap-spike-island.js b/rust/spikes/leptos-tiptap-spike/generated/island/mnote-leptos-tiptap-spike-island.js index df393296..08abe00c 100644 --- a/rust/spikes/leptos-tiptap-spike/generated/island/mnote-leptos-tiptap-spike-island.js +++ b/rust/spikes/leptos-tiptap-spike/generated/island/mnote-leptos-tiptap-spike-island.js @@ -1284,7 +1284,7 @@ function __wbg_get_imports() { return ret; }, __wbindgen_cast_0000000000000006: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 312, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 506, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf); return ret; }, diff --git a/rust/spikes/leptos-tiptap-spike/generated/island/mnote-leptos-tiptap-spike-island_bg.wasm b/rust/spikes/leptos-tiptap-spike/generated/island/mnote-leptos-tiptap-spike-island_bg.wasm index 4156e2cc..9c40bd1c 100644 Binary files a/rust/spikes/leptos-tiptap-spike/generated/island/mnote-leptos-tiptap-spike-island_bg.wasm and b/rust/spikes/leptos-tiptap-spike/generated/island/mnote-leptos-tiptap-spike-island_bg.wasm differ diff --git a/rust/spikes/leptos-tiptap-spike/src/editor_runtime/mod.rs b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/mod.rs index 397ef0eb..42b3b61c 100644 --- a/rust/spikes/leptos-tiptap-spike/src/editor_runtime/mod.rs +++ b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/mod.rs @@ -3,4 +3,5 @@ pub(crate) mod attachment_upload; pub(crate) mod command_sync; pub(crate) mod dom_selection; pub(crate) mod history_safe_commands; +pub(crate) mod overlays; pub(crate) mod persistence; diff --git a/rust/spikes/leptos-tiptap-spike/src/editor_runtime/overlays.rs b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/overlays.rs new file mode 100644 index 00000000..b1115fe1 --- /dev/null +++ b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/overlays.rs @@ -0,0 +1,39 @@ +use leptos::prelude::{Set, WriteSignal}; + +use crate::{ + editor_runtime::dom_selection::active_editor_text_selection, editor_stage_element, +}; + +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct FloatingToolbarAnchor { + pub(crate) top: f64, + pub(crate) left: f64, +} + +pub(crate) fn floating_toolbar_anchor_from_selection() -> Option { + let selection = active_editor_text_selection()?; + let range = selection.get_range_at(0).ok()?; + let rect = range.get_bounding_client_rect(); + if rect.width() <= 0.0 && rect.height() <= 0.0 { + return None; + } + + let stage = editor_stage_element()?; + let stage_rect = stage.get_bounding_client_rect(); + + Some(FloatingToolbarAnchor { + top: (rect.top() - stage_rect.top()).max(60.0), + left: rect.left() + (rect.width() / 2.0) - stage_rect.left(), + }) +} + +pub(crate) fn sync_text_selection_overlay( + set_text_selection_active: WriteSignal, + set_floating_toolbar_anchor: WriteSignal>, +) -> bool { + let toolbar_anchor = floating_toolbar_anchor_from_selection(); + let has_text_selection = toolbar_anchor.is_some(); + set_text_selection_active.set(has_text_selection); + set_floating_toolbar_anchor.set(toolbar_anchor); + has_text_selection +} diff --git a/rust/spikes/leptos-tiptap-spike/src/lib.rs b/rust/spikes/leptos-tiptap-spike/src/lib.rs index 53d250f7..e9b37a57 100644 --- a/rust/spikes/leptos-tiptap-spike/src/lib.rs +++ b/rust/spikes/leptos-tiptap-spike/src/lib.rs @@ -25,8 +25,10 @@ use editor_runtime::command_sync::{ read_editor_snapshot, sync_editor_outputs, sync_persisted_editor_command, }; use editor_runtime::dom_selection::{ - active_editor_text_selection, has_active_text_selection, node_is_within_root, - selection_summary, + has_active_text_selection, node_is_within_root, selection_summary, +}; +use editor_runtime::overlays::{ + floating_toolbar_anchor_from_selection, sync_text_selection_overlay, FloatingToolbarAnchor, }; use editor_runtime::persistence::{ load_persisted_document, normalize_identity_value, persist_document_state, @@ -4785,12 +4787,6 @@ struct HoveredBlockState { height: f64, } -#[derive(Clone, Debug, PartialEq)] -struct FloatingToolbarAnchor { - top: f64, - left: f64, -} - #[derive(Clone, Debug, PartialEq)] struct ImageToolbarAnchor { top: f64, @@ -5025,7 +5021,7 @@ pub(crate) fn editor_root_element() -> Option { .and_then(|document| document.query_selector(EDITOR_ROOT_SELECTOR).ok().flatten()) } -fn editor_stage_element() -> Option { +pub(crate) fn editor_stage_element() -> Option { window() .and_then(|win| win.document()) .and_then(|document| { @@ -5338,23 +5334,6 @@ fn top_level_block_range(document: &Value, index: usize) -> Option None } -fn floating_toolbar_anchor_from_selection() -> Option { - let selection = active_editor_text_selection()?; - let range = selection.get_range_at(0).ok()?; - let rect = range.get_bounding_client_rect(); - if rect.width() <= 0.0 && rect.height() <= 0.0 { - return None; - } - - let stage = editor_stage_element()?; - let stage_rect = stage.get_bounding_client_rect(); - - Some(FloatingToolbarAnchor { - top: (rect.top() - stage_rect.top()).max(60.0), - left: rect.left() + (rect.width() / 2.0) - stage_rect.left(), - }) -} - fn slash_menu_anchor_style() -> String { const MENU_WIDTH: f64 = 316.0; const MENU_HEIGHT: f64 = 430.0; @@ -6090,17 +6069,6 @@ fn open_block_menu_overlay( should_open } -fn sync_text_selection_overlay( - set_text_selection_active: WriteSignal, - set_floating_toolbar_anchor: WriteSignal>, -) -> bool { - let toolbar_anchor = floating_toolbar_anchor_from_selection(); - let has_text_selection = toolbar_anchor.is_some(); - set_text_selection_active.set(has_text_selection); - set_floating_toolbar_anchor.set(toolbar_anchor); - has_text_selection -} - fn sync_editor_overlay_state( set_editor_focused: WriteSignal, set_text_selection_active: WriteSignal,