refactor: share editor content layout helpers
This commit is contained in:
@@ -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. 本轮边界
|
||||
|
||||
@@ -335,3 +335,20 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据
|
||||
- 但主控根据其 `5-29` 线索做了本地代码复核,确认当前粒度判断基本成立:`slash_menu_anchor_style()` 已复用 `selection_bounding_rect()` 与 `clamp_overlay_anchor()`;剩余 fallback 只包含 `hovered_block_from_selection()`、`editor_stage_element()`、`content_text_left(stage_rect.width())` 和 3 行 anchor 算术,不值得再抽新的微型 helper。
|
||||
- 主控本地代码证据:`hovered_block_from_selection()` 已退化为 `block_index_from_selection().and_then(block_state_from_index)` 的薄包装;`block_menu_overlay.rs` 已存在一份独立的 `content_text_left()` / 布局常量体系,若为 `slash_menu_anchor_style()` 再外置同类 helper,反而会引入第三处布局常量复制。
|
||||
- 当前决定:`5-29` 暂不再围绕 `slash_menu_anchor_style()` 继续拆刀;若后续确实需要推进,应另起设计收口 `content_text_left()` / 内容列布局常量,而不是从当前 fallback 中抽 3-5 行几何包装。
|
||||
- 2026-05-25:Batch BC 派发 1 个 browser smoke Reasonix worker 在当前 head 做只读回归,使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-bc-worker-b-smoke-head-readonly`,预期 run id `reasonix-2026-05-25T01-03-07-032Z-a487a063`,显式 `--no-memory-recall`。
|
||||
- 结果拒收:该 run 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`,无 `result.json`、`process-handoff.md/json`、`final.md`;worktree 源码零 diff。
|
||||
- 处置:不采纳其 smoke 结论;后续如需浏览器验收,仍需由主控亲自复跑,或重新派发受控 smoke worker。
|
||||
- 2026-05-25:Batch BE 由 Codex 主控为 `5-29` 侧追加 1 个独立 worktree 只读审计 worker `/mnt/Data1T/mnote-worktrees/0525-be-worker-d-5-29-layout-constant-readonly`。
|
||||
- 目标:不再复述 `slash_menu_anchor_style()` fallback,而是只回答 `content_text_left()` / 内容列布局常量 是否值得作为下一刀独立 design 或只读收口主题。
|
||||
- 约束:任务书写入该 worktree `tmp/`;禁止修改源码、禁止 `.codex/reasonix-tasks/*`、禁止子 agent。Codex 仅在拿到 Hindsight recall、`process-handoff.md/json`、`result.json` 与源码零 diff 证据后才会裁决是否采纳。
|
||||
- 2026-05-25:当前用户已手动覆盖 goal,后续批次统一收紧为“每批最多 2 个 worker”;本文件中早先 3/4-worker 记录仅作为历史执行事实保留,不再作为后续派发口径。
|
||||
- 2026-05-25:Batch BF 只派发 1 个只读 Reasonix worker 审计 `selection_payload / send_selection_state_to_target` 的下一刀边界,使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-bf-worker-a-5-29-selection-payload-readonly`,run id `reasonix-2026-05-25T01-11-58-275Z-2b2e8a66`,显式 `--no-memory-recall`。
|
||||
- 结果拒收:该 run 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`,没有 `result.json`、`process-handoff.md/json`、`final.md`;worktree 源码零 diff。
|
||||
- 过程异常:主控在等待 BF run 期间发现未批准 residual runner 自动启动了 Batch BE 的 4 个 worker(`0525-be-*`),已统一终止并拒收;在当前 2-worker 上限下,后续不再采纳任何未明确派发的 run。
|
||||
- 主控本地代码结论:`SelectionPayload`、`block_id_from_element`、`block_id_from_json_node`、`runtime_block_id_from_index`、`selection_payload` 已经落在 `editor_runtime/dom_selection.rs` 的合理边界内,继续迁出收益有限;`dispatch_selection_event_to_target`、`send_selection_state_to_target`、`on_selection_change` 仍绑定 `EventTarget`、Leptos signal、overlay 同步与 editor lifecycle,必须留在 `lib.rs` 壳层。
|
||||
- 最小下一刀建议:若继续推进,只做 `current_block_info_from_index()` 的单刀收口,把它与 `runtime_block_id_from_index()` 组合成 `dom_selection` 中的轻量 block info helper,供 runtime state / host status / selection bridge 共用;不要继续触碰 `send_selection_state_to_target` 或把 bridge dispatch 下沉进 runtime 模块。
|
||||
- 2026-05-25:Codex 主控继续收口共享内容列布局 helper,去掉 `lib.rs` 与 `block_menu_overlay.rs` 的重复常量与几何函数。
|
||||
- 修改:新增 `rust/spikes/leptos-tiptap-spike/src/editor_runtime/content_layout.rs`,迁入 `content_column_width()`、`content_column_left()`、`content_text_left()` 及对应常量;`editor_runtime/block_menu_overlay.rs` 与 `lib.rs` 改为复用该共享模块,`lib.rs` 仅保留 `handle_lane_left()` 这一层 handle 专属偏移计算。
|
||||
- 边界:未迁 `handle_lane_left()`、`HANDLE_*` 常量、`slash_menu_anchor_style()`、drop indicator 壳层、block menu overlay 自身布局策略或 DOM 查询;本批只消除内容列布局 helper 的重复定义。
|
||||
- 已验证:`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:3023 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3023 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3023 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:3023 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:3023 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
|
||||
- 结果:`task489`、`task488`、`task479` 均通过,说明共享布局 helper 外置未破坏块菜单删除/撤销、附件删除/撤销和本地 Markdown 附件生命周期基线。
|
||||
|
||||
BIN
Binary file not shown.
@@ -8,7 +8,10 @@
|
||||
//! - `hover_anchor_transform`
|
||||
//! - `block_menu_layout`(参数化版)
|
||||
|
||||
use crate::editor_runtime::block_hover_state::{BlockMenuLayout, HoveredBlockState};
|
||||
use crate::editor_runtime::{
|
||||
block_hover_state::{BlockMenuLayout, HoveredBlockState},
|
||||
content_layout::content_text_left,
|
||||
};
|
||||
|
||||
/// 多行块的最小高度阈值,超过此值视作多行。
|
||||
const HANDLE_MULTILINE_HEIGHT: f64 = 40.0;
|
||||
@@ -16,29 +19,6 @@ const HANDLE_MULTILINE_HEIGHT: f64 = 40.0;
|
||||
/// handle shell 菜单左侧最小限制。
|
||||
const HANDLE_MENU_LEFT_LIMIT: f64 = -320.0;
|
||||
|
||||
/// 内容列水平内边距(与 lib.rs 中同名常量同值)。
|
||||
const CONTENT_COLUMN_HORIZONTAL_PADDING: f64 = 48.0;
|
||||
|
||||
/// 内容列最大宽度(与 lib.rs 中同名常量同值)。
|
||||
const CONTENT_COLUMN_MAX_WIDTH: f64 = 708.0;
|
||||
|
||||
/// 根据 stage 宽度计算实际内容列宽。
|
||||
fn content_column_width(stage_rect_width: f64) -> f64 {
|
||||
let available = (stage_rect_width - (CONTENT_COLUMN_HORIZONTAL_PADDING * 2.0)).max(0.0);
|
||||
available.min(CONTENT_COLUMN_MAX_WIDTH)
|
||||
}
|
||||
|
||||
/// 内容列左侧相对于 stage 的偏移。
|
||||
fn content_column_left(stage_rect_width: f64) -> f64 {
|
||||
let column_width = content_column_width(stage_rect_width);
|
||||
((stage_rect_width - column_width) / 2.0).max(0.0)
|
||||
}
|
||||
|
||||
/// 内容文本区左侧相对于 stage 的偏移(列左内边距后)。
|
||||
fn content_text_left(stage_rect_width: f64) -> f64 {
|
||||
content_column_left(stage_rect_width) + CONTENT_COLUMN_HORIZONTAL_PADDING
|
||||
}
|
||||
|
||||
/// 计算 handle shell 锚点在 stage 内的垂直位置。
|
||||
pub(crate) fn hover_anchor_top(block: &HoveredBlockState) -> f64 {
|
||||
if block.height > HANDLE_MULTILINE_HEIGHT {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/// 内容列最大宽度。
|
||||
pub(crate) const CONTENT_COLUMN_MAX_WIDTH: f64 = 708.0;
|
||||
|
||||
/// 内容列水平内边距。
|
||||
pub(crate) const CONTENT_COLUMN_HORIZONTAL_PADDING: f64 = 48.0;
|
||||
|
||||
/// 根据 stage 宽度计算实际内容列宽。
|
||||
pub(crate) fn content_column_width(stage_rect_width: f64) -> f64 {
|
||||
let available = (stage_rect_width - (CONTENT_COLUMN_HORIZONTAL_PADDING * 2.0)).max(0.0);
|
||||
available.min(CONTENT_COLUMN_MAX_WIDTH)
|
||||
}
|
||||
|
||||
/// 内容列左侧相对于 stage 的偏移。
|
||||
pub(crate) fn content_column_left(stage_rect_width: f64) -> f64 {
|
||||
let column_width = content_column_width(stage_rect_width);
|
||||
((stage_rect_width - column_width) / 2.0).max(0.0)
|
||||
}
|
||||
|
||||
/// 内容文本区左侧相对于 stage 的偏移。
|
||||
pub(crate) fn content_text_left(stage_rect_width: f64) -> f64 {
|
||||
content_column_left(stage_rect_width) + CONTENT_COLUMN_HORIZONTAL_PADDING
|
||||
}
|
||||
@@ -6,6 +6,7 @@ pub(crate) mod block_menu_document;
|
||||
pub(crate) mod block_menu_overlay;
|
||||
pub(crate) mod block_menu_legacy_html;
|
||||
pub(crate) mod command_sync;
|
||||
pub(crate) mod content_layout;
|
||||
pub(crate) mod dom_events;
|
||||
pub(crate) mod dom_selection;
|
||||
pub(crate) mod editor_focus;
|
||||
|
||||
@@ -40,6 +40,7 @@ use editor_runtime::block_hover_state::{
|
||||
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_selection::{
|
||||
block_index_from_selection, runtime_block_id_from_index, selection_payload,
|
||||
@@ -66,8 +67,6 @@ use editor_runtime::persistence::persisted_document_storage_key;
|
||||
const EDITOR_STAGE_SELECTOR: &str = "[data-testid=\"mnote-leptos-tiptap-editor-stage\"]";
|
||||
const EDITOR_ROOT_SELECTOR: &str = ".editor-surface .ProseMirror";
|
||||
const HANDLE_SHELL_SELECTOR: &str = ".block-handle-shell";
|
||||
const CONTENT_COLUMN_MAX_WIDTH: f64 = 708.0;
|
||||
const CONTENT_COLUMN_HORIZONTAL_PADDING: f64 = 48.0;
|
||||
const HANDLE_STAGE_PADDING_LEFT: f64 = 18.0;
|
||||
const HANDLE_TRIGGER_WIDTH: f64 = 22.0;
|
||||
const HANDLE_TRIGGER_GAP: f64 = 4.0;
|
||||
@@ -4809,20 +4808,6 @@ pub(crate) fn editor_stage_element() -> Option<Element> {
|
||||
})
|
||||
}
|
||||
|
||||
fn content_column_width(stage_rect_width: f64) -> f64 {
|
||||
let available = (stage_rect_width - (CONTENT_COLUMN_HORIZONTAL_PADDING * 2.0)).max(0.0);
|
||||
available.min(CONTENT_COLUMN_MAX_WIDTH)
|
||||
}
|
||||
|
||||
fn content_column_left(stage_rect_width: f64) -> f64 {
|
||||
let column_width = content_column_width(stage_rect_width);
|
||||
((stage_rect_width - column_width) / 2.0).max(0.0)
|
||||
}
|
||||
|
||||
fn content_text_left(stage_rect_width: f64) -> f64 {
|
||||
content_column_left(stage_rect_width) + CONTENT_COLUMN_HORIZONTAL_PADDING
|
||||
}
|
||||
|
||||
fn handle_lane_left(stage_rect_width: f64) -> f64 {
|
||||
(content_text_left(stage_rect_width) + HANDLE_TEXT_ALIGN_OFFSET
|
||||
- HANDLE_TRIGGER_WIDTH
|
||||
|
||||
Reference in New Issue
Block a user