refactor: move selection payload helpers
This commit is contained in:
@@ -190,3 +190,11 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据
|
||||
- 修改:`rust/spikes/leptos-tiptap-spike/src/editor_runtime/overlays.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`。
|
||||
- 未迁:`send_selection_state_to_target` / `selection_payload` bridge 链路、`runtime_block_id_from_index`、selection bridge event、`drop_indicator_from_target`、拖拽 listener 和 block reorder 行为仍留在 `lib.rs`。
|
||||
- 2026-05-25:Batch V 只派发 1 个只读 Reasonix worker 审计 selection bridge 迁出边界,使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-v-worker-a-5-29-selection-bridge-readonly`,run id `reasonix-2026-05-24T21-22-36-118Z-1e489f85`,并显式使用 `--no-memory-recall`。
|
||||
- Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`、`final.md`,并核对 worktree `git status --short` / `git diff --stat` 为空;Reasonix 未修改源码。
|
||||
- 审计结论经 Codex 复核后采纳:selection bridge 的事件分发层必须留在 `lib.rs`,但 `SelectionPayload`、`selection_payload`、`runtime_block_id_from_index` 及其 block id 读取 helper 属于可迁出的 selection 数据层。
|
||||
- Codex 主控基于只读审计结论在主工作区完成最小实现;本批没有实现 worker 候选 patch,因此不记录虚构 worktree 采纳链。
|
||||
- 已合入最小切片:`SelectionPayload`、`selection_payload`、`runtime_block_id_from_index`、`block_id_from_element`、`block_id_from_json_node` 迁入 `editor_runtime/dom_selection.rs`;`send_selection_state_to_target`、`dispatch_selection_event_to_target`、`CustomEvent` bridge 和 `selection_event_target` 壳层调用继续留在 `lib.rs`。
|
||||
- 修改:`rust/spikes/leptos-tiptap-spike/src/editor_runtime/dom_selection.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`。
|
||||
- 未迁/未删:`send_selection_state_to_target`、selection event dispatch、`send_selection_state`、`selection_event_payload`、`runtime_state_payload`、`runtime_status_payload` 等未使用 bridge/helper 函数;死代码清理后续单独切片处理。
|
||||
|
||||
+9
-9
@@ -1259,47 +1259,47 @@ function __wbg_get_imports() {
|
||||
}
|
||||
}, arguments); },
|
||||
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1144, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1145, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h396914cf76a9e7a5);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1196, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1197, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h3a3182d847094e12);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 972, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 973, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`.
|
||||
const ret = makeClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__had771ddc65647798);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000004: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1094, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1095, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0abd2b2fe4652e2a);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000005: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1144, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1145, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h396914cf76a9e7a5_4);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000006: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 789, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 856, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000007: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1096, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1097, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h794babeffd4f821f);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000008: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1111, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1112, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`.
|
||||
const ret = makeClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h2b648e7ac8ec1fb5);
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_cast_0000000000000009: function(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1147, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1148, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0b7fb40a8610550c);
|
||||
return ret;
|
||||
},
|
||||
|
||||
BIN
Binary file not shown.
@@ -1,8 +1,21 @@
|
||||
use leptos_tiptap::TiptapSelectionState;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{window, Element, Node};
|
||||
|
||||
use crate::editor_root_element;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct SelectionPayload {
|
||||
pub(crate) selection: TiptapSelectionState,
|
||||
pub(crate) summary: String,
|
||||
pub(crate) editor_focused: bool,
|
||||
pub(crate) current_block_index: Option<usize>,
|
||||
pub(crate) current_block_id: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) fn node_is_within_root(node: Node, root: &Element) -> bool {
|
||||
let mut current = Some(node);
|
||||
while let Some(node) = current {
|
||||
@@ -103,3 +116,59 @@ pub(crate) fn selection_has_rich_marks(selection: &TiptapSelectionState) -> bool
|
||||
|| selection.text_style
|
||||
|| selection.link
|
||||
}
|
||||
|
||||
pub(crate) fn block_id_from_element(block: &Element) -> Option<String> {
|
||||
block
|
||||
.get_attribute("data-block-id")
|
||||
.map(|value| value.trim().to_string())
|
||||
.filter(|value| !value.is_empty())
|
||||
}
|
||||
|
||||
pub(crate) fn block_id_from_json_node(node: &Value) -> Option<String> {
|
||||
node.get("attrs")
|
||||
.and_then(|attrs| attrs.get("blockId"))
|
||||
.and_then(Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
}
|
||||
|
||||
pub(crate) fn runtime_block_id_from_index(index: usize) -> Option<String> {
|
||||
editor_root_element()
|
||||
.and_then(|root| root.children().item(index as u32))
|
||||
.and_then(|block| block_id_from_element(&block))
|
||||
.or_else(|| {
|
||||
editor_root_element()
|
||||
.and_then(|root| {
|
||||
js_sys::Reflect::get(root.as_ref(), &JsValue::from_str("editor")).ok()
|
||||
})
|
||||
.and_then(|editor_value| {
|
||||
js_sys::Reflect::get(&editor_value, &JsValue::from_str("getJSON"))
|
||||
.ok()
|
||||
.and_then(|callback| callback.dyn_into::<js_sys::Function>().ok())
|
||||
.and_then(|callback| callback.call0(&editor_value).ok())
|
||||
})
|
||||
.and_then(|value| serde_wasm_bindgen::from_value::<Value>(value).ok())
|
||||
.and_then(|document| {
|
||||
document
|
||||
.get("content")
|
||||
.and_then(Value::as_array)
|
||||
.and_then(|content| content.get(index))
|
||||
.and_then(block_id_from_json_node)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn selection_payload(
|
||||
selection: &TiptapSelectionState,
|
||||
editor_focused: bool,
|
||||
current_block_index: Option<usize>,
|
||||
) -> SelectionPayload {
|
||||
SelectionPayload {
|
||||
selection: selection.clone(),
|
||||
summary: selection_summary(selection),
|
||||
editor_focused,
|
||||
current_block_index,
|
||||
current_block_id: current_block_index.and_then(runtime_block_id_from_index),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ use editor_runtime::command_sync::{
|
||||
read_editor_snapshot, sync_editor_outputs, sync_persisted_editor_command,
|
||||
};
|
||||
use editor_runtime::dom_selection::{
|
||||
selection_summary,
|
||||
runtime_block_id_from_index, selection_payload, selection_summary, SelectionPayload,
|
||||
};
|
||||
use editor_runtime::editor_focus::{active_editor_stage, body_has_focus, schedule_editor_focus};
|
||||
use editor_runtime::overlays::{
|
||||
@@ -3088,16 +3088,6 @@ struct StatePayload {
|
||||
read_only: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SelectionPayload {
|
||||
selection: TiptapSelectionState,
|
||||
summary: String,
|
||||
editor_focused: bool,
|
||||
current_block_index: Option<usize>,
|
||||
current_block_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ChangeMetaPayload {
|
||||
@@ -3539,48 +3529,6 @@ fn runtime_event_target() -> Option<EventTarget> {
|
||||
.map(|body| body.into())
|
||||
}
|
||||
|
||||
fn block_id_from_element(block: &Element) -> Option<String> {
|
||||
block
|
||||
.get_attribute("data-block-id")
|
||||
.map(|value| value.trim().to_string())
|
||||
.filter(|value| !value.is_empty())
|
||||
}
|
||||
|
||||
fn block_id_from_json_node(node: &Value) -> Option<String> {
|
||||
node.get("attrs")
|
||||
.and_then(|attrs| attrs.get("blockId"))
|
||||
.and_then(Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
}
|
||||
|
||||
fn runtime_block_id_from_index(index: usize) -> Option<String> {
|
||||
editor_root_element()
|
||||
.and_then(|root| root.children().item(index as u32))
|
||||
.and_then(|block| block_id_from_element(&block))
|
||||
.or_else(|| {
|
||||
editor_root_element()
|
||||
.and_then(|root| {
|
||||
js_sys::Reflect::get(root.as_ref(), &JsValue::from_str("editor")).ok()
|
||||
})
|
||||
.and_then(|editor_value| {
|
||||
js_sys::Reflect::get(&editor_value, &JsValue::from_str("getJSON"))
|
||||
.ok()
|
||||
.and_then(|callback| callback.dyn_into::<js_sys::Function>().ok())
|
||||
.and_then(|callback| callback.call0(&editor_value).ok())
|
||||
})
|
||||
.and_then(|value| serde_wasm_bindgen::from_value::<Value>(value).ok())
|
||||
.and_then(|document| {
|
||||
document
|
||||
.get("content")
|
||||
.and_then(Value::as_array)
|
||||
.and_then(|content| content.get(index))
|
||||
.and_then(block_id_from_json_node)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn current_runtime_block_info() -> CurrentBlockInfo {
|
||||
hovered_block_from_selection()
|
||||
.and_then(|block| {
|
||||
@@ -5860,20 +5808,6 @@ fn current_block_info_from_index(index: Option<usize>) -> CurrentBlockInfo {
|
||||
})
|
||||
}
|
||||
|
||||
fn selection_payload(
|
||||
selection: &TiptapSelectionState,
|
||||
editor_focused: bool,
|
||||
current_block_index: Option<usize>,
|
||||
) -> SelectionPayload {
|
||||
SelectionPayload {
|
||||
selection: selection.clone(),
|
||||
summary: selection_summary(selection),
|
||||
editor_focused,
|
||||
current_block_index,
|
||||
current_block_id: current_block_index.and_then(runtime_block_id_from_index),
|
||||
}
|
||||
}
|
||||
|
||||
fn state_payload(
|
||||
document_id: Option<String>,
|
||||
workspace_id: Option<String>,
|
||||
|
||||
Reference in New Issue
Block a user