Files
mnote/docs/superpowers/plans/2026-05-20-main-editor-resource-tabs.md
T

4.5 KiB

Main Editor Resource Tabs Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 在 MNote 主编辑区增加 VSCode 风格 resource tab,使本地 .md / text / code 资源可用 tiptap 编辑,Office/PDF/图片可在主编辑区 tab 打开,并保留右侧 pane 与新窗口能力。

Architecture:resource input + open target 为边界,前端新增主编辑区 tab host,local-first 资源读写走新的 /api/local-folder/resource/* API。页面正文 session 与资源 session 使用不同 object identity 和保存 endpoint,避免资源编辑污染 Page Aggregate body。

Tech Stack: Rust Axum (mnote-web routes), Leptos SSR shell, existing leptos-tiptap WASM runtime, Playwright smoke scripts.


Task 1: 本地资源读写 API

Files:

  • Modify: rust/crates/mnote-web/src/routes/mod.rs

  • Modify: rust/crates/mnote-web/src/routes/local_folder_source.rs

  • Step 1: 增加 API 路由

routes/mod.rs 的 local-folder route 区域添加:

.route(
    "/api/local-folder/resource/read",
    get(local_folder_source::read_local_resource),
)
.route(
    "/api/local-folder/resource/write",
    post(local_folder_source::write_local_resource),
)
  • Step 2: 实现读写 handler

local_folder_source.rs 复用 resolve_local_file_open_path,读取 raw text 或写入 UTF-8 内容。写入请求字段:

{
  "rootUri": "file:///tmp/workspace",
  "path": "Page.assets/note.md",
  "contentFormat": "tiptapDocument",
  "tiptapDocument": { "type": "doc", "content": [] },
  "expectedFileVersion": "sha256:..."
}

保存 .md 使用现有 editor-blocks -> markdown 能力;保存 .txt / code 使用 plain text。

  • Step 3: 增加单测

新增测试覆盖:

  • root escape 被拒绝。
  • .md 读写返回 text/markdownfileVersion
  • .md 后不修改页面正文 Markdown。

Task 2: 主编辑区 Resource Tab Host

Files:

  • Modify: rust/crates/mnote-web/src/ssr/pages/document.rs

  • Modify: rust/crates/mnote-web/src/ssr/styles.rs

  • Modify: rust/crates/mnote-web/src/routes/web_shell.rs

  • Step 1: SSR 增加 tab strip 与 resource host

DocumentPage.document-workspace 内添加主编辑区 tab strip 和 resource pane 容器。默认 page tab 对应 primary document pane。

  • Step 2: web shell 新增 openResourceInActiveTab

新增运行时函数:

window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({
  objectIdentity,
  title,
  kind,
  rootUri,
  path,
  href,
  officeUrl
});

同一 objectIdentity 已存在时只激活 tab。

  • Step 3: 挂载 tiptap resource editor

.md / .txt / code resource tab 创建独立 root,读取 /api/local-folder/resource/read,转为 tiptap document 后挂载。change 事件写回 /api/local-folder/resource/write

Task 3: 打开路径收口

Files:

  • Modify: rust/crates/mnote-web/src/ssr/pages/layout.rs

  • Modify: rust/crates/mnote-web/src/routes/web_shell.rs

  • Step 1: 文件树资源普通点击默认 active-tab

修改 openConvexAssetFromFileTree(detail):本地资源优先调用 openResourceInActiveTab,失败时 fallback 到旧 window.open

  • Step 2: 附件点击默认 active-tab

修改 openEditorAttachmentDetail(detail):本地附件优先打开 resource tab,显式下载/新窗口保持旧行为。

  • Step 3: 菜单补齐 new-window

为文件树资源和附件菜单增加“在新窗口打开”,动作显式调用旧 window.open 路径。

Task 4: Smoke 与回归

Files:

  • Modify: scripts/task174-rust-onlyoffice-attachment-open-smoke.js

  • Modify: scripts/task456-resource-object-shell-sync-smoke.js

  • Create: scripts/task457-main-editor-resource-tab-smoke.js

  • Step 1: 新增 resource tab smoke

覆盖上传 .md、点击打开主编辑区 tab、编辑保存回资源文件、页面正文不变。

  • Step 2: 更新 Office smoke

默认点击断言主编辑区 Office resource tab;显式新窗口动作继续断言 /onlyoffice

  • Step 3: 运行验证

运行:

cargo test -p mnote-web -- --test-threads=1
node scripts/task457-main-editor-resource-tab-smoke.js
node scripts/task174-rust-onlyoffice-attachment-open-smoke.js
node scripts/task456-resource-object-shell-sync-smoke.js
codegraph sync .

预期:全部通过。