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 94e339c8..534dccd1 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 @@ -406,14 +406,15 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据 | 文件 | 原大小 | 现大小 | 变化 | |------|--------|--------|------| -| `lib.rs` | 9,873 行 | 8,189 行 | **-17%** | +| `lib.rs` | 9,873 行 | 7,951 行 | **-19%** | | `editor_runtime/mindmap_node_view.rs` | — | 906 行 | 新建 | | `editor_runtime/bridge_events.rs` | — | 154 行 | 新建 | | `editor_runtime/slash_actions.rs` | — | 237 行 | 新建 | | `editor_runtime/table_commands.rs` | — | 180 行 | 新建 | +| `editor_runtime/table_toolbar_view.rs` | — | 371 行 | 新建 | | `editor_runtime/bridge_dispatch.rs` | — | 86 行 | 新建 | -### editor_runtime/ 最终模块清单(19 模块,另有 `mod.rs`) +### editor_runtime/ 最终模块清单(20 模块,另有 `mod.rs`) | 模块 | 说明 | 来源 | |------|------|------| @@ -436,6 +437,7 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据 | `persistence.rs` | 持久化 | 5-28 P2 | | `slash_actions.rs` | slash 菜单 action 数据 | **本批新建** | | `table_commands.rs` | table toolbar / option 命令 | **本批新建** | +| `table_toolbar_view.rs` | table controls / toolbar / options view | **本批新建** | ### 未完成(后续独立任务) @@ -464,3 +466,8 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据 - 将 `should_auto_close_toolbar_overlays` 接回两个实际 auto-close 判断点,避免 helper 外置后仍有重复手写判定。 - 对必须持有生命周期但 Rust 编译器无法直接观察用途的 mount handle、host envelope 反序列化字段加局部 `#[allow(dead_code)]`,不扩大到模块级。 - 已验证:`cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml` 无 warning,`cargo test --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml -- --test-threads=1` 13/13 通过。 +- 2026-05-25:Codex 完成 `table_toolbar_view.rs` 第一刀。 + - 新增 `editor_runtime/table_toolbar_view.rs`,迁出 table controls、table toolbar 和 table options view;`lib.rs` 保留 signal 创建与单一调用点。 + - 边界:表格命令仍由 `table_commands.rs` 提供,持久化同步仍委托 `command_sync::sync_persisted_editor_command`,未修改 Tiptap table bridge 或保存语义。 + - 已验证:`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`、`cargo test --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml -- --test-threads=1` 13/13、wasm release build、`wasm-bindgen`。 + - 浏览器验证:`task158-e30-menu-state-smoke.js` 受旧 Convex mutation `127.0.0.1:3210` 503 阻塞;已用 Playwright 在 `http://127.0.0.1:3000` 登录测试账号后直接设置表格 fixture,确认 table toolbar、options menu、row aux、col aux 均可见。 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 65cc8fbd..181f391d 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 @@ -1244,47 +1244,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: 1143, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1148, 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: 1195, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1200, 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: 971, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 976, 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: 1093, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1098, 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: 1143, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 1148, 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: 879, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 747, 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: 1095, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1100, 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: 1110, ret: Unit, inner_ret: Some(Unit) }, mutable: false }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1115, 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: 1146, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 1151, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0b7fb40a8610550c); 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 73c5e1b4..28edb2f1 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 491547c4..b692825d 100644 --- a/rust/spikes/leptos-tiptap-spike/src/editor_runtime/mod.rs +++ b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/mod.rs @@ -17,3 +17,4 @@ pub(crate) mod overlays; pub(crate) mod persistence; pub(crate) mod slash_actions; pub(crate) mod table_commands; +pub(crate) mod table_toolbar_view; diff --git a/rust/spikes/leptos-tiptap-spike/src/editor_runtime/table_toolbar_view.rs b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/table_toolbar_view.rs new file mode 100644 index 00000000..4ba169e2 --- /dev/null +++ b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/table_toolbar_view.rs @@ -0,0 +1,371 @@ +use crate::editor_runtime::{ + command_sync::sync_persisted_editor_command, + overlays::{ + current_table_overlay_anchor, table_col_aux_style, table_row_aux_style, + table_selection_overlay_style, TableOverlayAnchor, TableSelectionKind, + TableSelectionOverlayState, + }, + persistence::{persisted_document_identity, PersistedDocumentIdentity}, + table_commands::{ + run_table_option_action, run_table_toolbar_action, table_option_checked, + TableToolbarAction, TABLE_OPTION_ACTIONS, TABLE_TOOLBAR_ACTIONS, + }, +}; +use leptos::prelude::*; +use leptos_tiptap::TiptapEditorHandle; +use serde_json::Value; +use web_sys::MouseEvent; + +fn runtime_persisted_identity( + document_id: ReadSignal>, + workspace_id: ReadSignal>, +) -> PersistedDocumentIdentity { + persisted_document_identity(document_id.get_untracked(), workspace_id.get_untracked()) +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn table_toolbar_view( + editor: TiptapEditorHandle, + table_toolbar_open: ReadSignal, + set_table_toolbar_open: WriteSignal, + table_options_open: ReadSignal, + set_table_options_open: WriteSignal, + table_overlay_anchor: ReadSignal>, + set_table_overlay_anchor: WriteSignal>, + table_selection_overlay: ReadSignal>, + set_table_selection_overlay: WriteSignal>, + slash_open: ReadSignal, + block_menu_open: ReadSignal, + dragging_block_index: ReadSignal>, + document_json: ReadSignal, + set_document_json: WriteSignal, + set_dirty_count: WriteSignal, + set_html_output: WriteSignal, + set_json_output: WriteSignal, + title: ReadSignal, + document_id: ReadSignal>, + workspace_id: ReadSignal>, + set_command_feedback: WriteSignal, +) -> impl IntoView { + view! { + <> + {move || { + if !table_toolbar_open.get() + || slash_open.get() + || block_menu_open.get() + || dragging_block_index.get().is_some() + { + return ().into_any(); + } + let Some(anchor) = table_overlay_anchor.get() else { + return ().into_any(); + }; + let controls_style = format!( + "left:{:.1}px;top:{:.1}px;width:{:.1}px;height:{:.1}px;", + anchor.left, anchor.top, anchor.width, anchor.height + ); + view! { +
+ {move || { + table_selection_overlay + .get() + .map(|selection| { + view! { +
+ } + }) + .into_any() + }} + {(0..anchor.rows).map(|index| { + let active = table_selection_overlay + .get() + .map(|selection| { + selection.kind == TableSelectionKind::Row && selection.index == index + }) + .unwrap_or(false); + view! { + + } + }).collect_view()} + {(0..anchor.cols).map(|index| { + let active = table_selection_overlay + .get() + .map(|selection| { + selection.kind == TableSelectionKind::Column + && selection.index == index + }) + .unwrap_or(false); + view! { + + } + }).collect_view()} +
+ } + .into_any() + }} + {move || { + if !table_toolbar_open.get() + || slash_open.get() + || block_menu_open.get() + || dragging_block_index.get().is_some() + { + return ().into_any(); + } + view! { +
+ {TABLE_TOOLBAR_ACTIONS.iter().enumerate().map(|(index, action)| { + let action = *action; + let testid = format!("table-toolbar-{}", action.id()); + view! { + <> + {if index == 3 { + view! { + + } + .into_any() + } else { + ().into_any() + }} + + + } + }).collect_view()} + + + {move || { + if !table_options_open.get() { + return ().into_any(); + } + view! { +
+ {TABLE_OPTION_ACTIONS.iter().map(|action| { + let action = *action; + let testid = format!("table-option-{}", action.id()); + view! { + + } + }).collect_view()} +
+ } + .into_any() + }} +
+ } + .into_any() + }} + + } +} diff --git a/rust/spikes/leptos-tiptap-spike/src/lib.rs b/rust/spikes/leptos-tiptap-spike/src/lib.rs index c3bfed07..0d716fd5 100644 --- a/rust/spikes/leptos-tiptap-spike/src/lib.rs +++ b/rust/spikes/leptos-tiptap-spike/src/lib.rs @@ -58,23 +58,19 @@ use editor_runtime::mindmap_node_view::{ }; use editor_runtime::overlays::{ clamp_overlay_anchor, - close_editor_floating_overlays_if_escape, current_table_overlay_anchor, + close_editor_floating_overlays_if_escape, image_element_from_target, image_toolbar_anchor_from_image, open_block_menu_overlay, open_image_toolbar_overlay, open_slash_menu_overlay, selection_bounding_rect, - should_auto_close_toolbar_overlays, sync_overlays_on_selection_change, table_col_aux_style, - table_overlay_anchor_from_table, table_row_aux_style, table_selection_overlay_style, - toolbar_overlay_locked, try_sync_editor_overlay_state, FloatingToolbarAnchor, ImageToolbarAnchor, - TableOverlayAnchor, TableSelectionKind, TableSelectionOverlayState, + should_auto_close_toolbar_overlays, sync_overlays_on_selection_change, + table_overlay_anchor_from_table, toolbar_overlay_locked, try_sync_editor_overlay_state, + FloatingToolbarAnchor, ImageToolbarAnchor, TableOverlayAnchor, TableSelectionOverlayState, }; use editor_runtime::persistence::{ load_persisted_document, normalize_identity_value, persist_document_state, persisted_document_identity, PersistedDocumentIdentity, }; use editor_runtime::slash_actions::{SlashActionKind, SLASH_ACTIONS}; -use editor_runtime::table_commands::{ - run_table_option_action, run_table_toolbar_action, table_option_checked, TableToolbarAction, - TABLE_OPTION_ACTIONS, TABLE_TOOLBAR_ACTIONS, -}; +use editor_runtime::table_toolbar_view::table_toolbar_view; #[cfg(test)] use editor_runtime::persistence::persisted_document_storage_key; @@ -6078,263 +6074,29 @@ fn App(mount_options: MountOptions) -> impl IntoView { set_hovered_block.set(None); } > - {move || { - if !table_toolbar_open.get() || slash_open.get() || block_menu_open.get() || dragging_block_index.get().is_some() { - return ().into_any(); - } - let Some(anchor) = table_overlay_anchor.get() else { - return ().into_any(); - }; - let controls_style = format!( - "left:{:.1}px;top:{:.1}px;width:{:.1}px;height:{:.1}px;", - anchor.left, - anchor.top, - anchor.width, - anchor.height - ); - view! { -
- {move || { - table_selection_overlay.get().map(|selection| { - view! { -
- } - }).into_any() - }} - {(0..anchor.rows).map(|index| { - let active = table_selection_overlay.get() - .map(|selection| selection.kind == TableSelectionKind::Row && selection.index == index) - .unwrap_or(false); - view! { - - } - }).collect_view()} - {(0..anchor.cols).map(|index| { - let active = table_selection_overlay.get() - .map(|selection| selection.kind == TableSelectionKind::Column && selection.index == index) - .unwrap_or(false); - view! { - - } - }).collect_view()} -
- }.into_any() - }} - {move || { - if !table_toolbar_open.get() || slash_open.get() || block_menu_open.get() || dragging_block_index.get().is_some() { - return ().into_any(); - } - view! { -
- {TABLE_TOOLBAR_ACTIONS.iter().enumerate().map(|(index, action)| { - let action = *action; - let testid = format!("table-toolbar-{}", action.id()); - view! { - <> - {if index == 3 { - view! { }.into_any() - } else { - ().into_any() - }} - - - } - }).collect_view()} - - - {move || { - if !table_options_open.get() { - return ().into_any(); - } - view! { -
- {TABLE_OPTION_ACTIONS.iter().map(|action| { - let action = *action; - let testid = format!("table-option-{}", action.id()); - view! { - - } - }).collect_view()} -
- }.into_any() - }} -
- }.into_any() - }} + {table_toolbar_view( + editor, + table_toolbar_open, + set_table_toolbar_open, + table_options_open, + set_table_options_open, + table_overlay_anchor, + set_table_overlay_anchor, + table_selection_overlay, + set_table_selection_overlay, + slash_open, + block_menu_open, + dragging_block_index, + document_json, + set_document_json, + set_dirty_count, + set_html_output, + set_json_output, + title, + document_id, + workspace_id, + set_command_feedback, + )} {move || { let Some(anchor) = image_toolbar_anchor.get() else { return ().into_any();