 我手动测试，以下bug仍然存在：
 1.我的空间：向文件树中的md文件中拖动文件进行上传文件，能正常在主编辑 md 中显示，点击也能正常打开，但是见 /mnt/Data1T/mnote/tmp/image copy
  65.png，文件是和主文件夹同级（应该在我的文件夹下级）。
2.上传文件后，弹出文件冲突：/mnt/Data1T/mnote/tmp/image copy 76.png，重新新建页面(冲突信息被带到新页面，
  刷新后消失)，文件冲突依然存在。冲突信息：/mnt/Data1T/mnote/tmp/image copy 77.png;另外一个例子：/mnt/Data1T/mnote/tmp/image copy 85.png，新建的页面直接向文件树拖入文件就提示冲突。

   处理记录（2026-05-24）：
   - 归类：`05-editor-mainline`，本质是本地 Markdown working copy 冲突合同缺失，不是单个上传入口问题。
   - 参考设计：`design/05-editor-mainline/process/5-27-local-markdown-working-copy-conflict-contract-v1.md`。
   - Sidex/VSCode 对照结论：正文冲突只应属于同一个 Markdown resource；同 root 下的附件、拖入资源、目录级 watcher 事件不能升级成当前 working copy 冲突。
   - 根因 1：前端本地文件夹 document event channel 只按 `rootUri` 共享，且 SSE 请求没有携带当前 `documentId`，后端无法按 Markdown 正文路径过滤。
   - 根因 2：上传附件/文件树拖入等非 Markdown 资源事件的 `documentId` 为空，前端仍继续设置 `externalChangePending`，导致误报文件冲突。
   - 根因 3：冲突面板是手动插入 `.document-pane` 的 DOM，页面切换时没有随 editor view unmount 清理，因此旧页面冲突 UI 会短暂残留到新页面。
   - 修复：
     - `rust/crates/mnote-web/src/routes/web_shell.rs`：document event channel key 从 `rootUri` 收窄为 `rootUri#documentId`，订阅 `/api/local-folder/events` 时携带 `documentId`。
     - `rust/crates/mnote-web/src/routes/web_shell.rs`：收到空 `documentId` 的资源事件直接忽略，不进入正文冲突链路。
     - `rust/crates/mnote-web/src/routes/web_shell.rs`：`unmountEditorViewBinding` 清理旧 session 冲突面板，避免跨页面残留。
     - `rust/crates/mnote-web/src/routes/local_folder_events.rs`：补充 document event filter 测试，确保资源路径不会匹配 Markdown 正文路径。
     - `scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`：上传附件和文件树拖入后增加 no-conflict 断言。
   - 验证：
     - `cargo test --manifest-path rust/Cargo.toml -p mnote-web document_event_filter_rejects_resource_only_changes -- --nocapture` 通过。
     - `cargo test --manifest-path rust/Cargo.toml -p mnote-web document_shell_renders_local_markdown_with_same_sidebar_surfaces -- --nocapture` 通过。
     - `cargo fmt --manifest-path rust/Cargo.toml --all --check` 通过。
     - `node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js` 通过，说明真实冲突路径仍可用。
     - `node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js` 中上传附件 no-conflict 与文件树拖入 no-conflict 通过；后续 broken-link 检查 timeout 属于第 2 条以外的资源缺失解析问题，需单独处理。
   - Worker 对比：
     - Reasonix coding 正确定位空 `documentId` 污染，但漏掉 DOM 生命周期与按 `documentId` 隔离 channel。
     - Claude Code coding 额外定位 DOM 生命周期问题，但清理位置偏晚，仍需主控调整到 editor view unmount。
     - Claude Code browser 证据更完整，覆盖 no-conflict 与真实冲突；Reasonix browser 只适合作为部分 UI 证据，因为其 result/handoff 状态不一致且误判 SSE 500 为真实冲突阻塞。
3.隐藏本地markdown标题，我去除勾选后，新建页面刷新又恢复默认了，应该维持我的选择的状态。另外应该针对我的空间和其它本地文件夹进行区分，我的空间(包括别人分享给我的我的空间,即所有我的空间)中默认显示本地markdown标题，其它的本地文件夹，默认隐藏markdown标题。

   处理记录（2026-05-24）：
   - 归类：`05-editor-mainline`，本质是本地 Markdown page options 默认值与持久化合同不完整。
   - 根因：`resolve_local_markdown_page_aggregate` 在页面没有持久化 `hideTitleHeader` 时无条件设为 `true`，导致所有本地 Markdown 都默认隐藏标题，没有区分托管工作区（我的空间）与普通本地文件夹。用户取消勾选后只对当前页面写入 `.mnote/page-options.json`，新建页面仍因无记录回到错误默认值。
   - 修复：
     - `rust/crates/mnote-web/src/routes/local_folder_source.rs`：无持久化值时检查当前 root 是否存在托管工作区清单；托管工作区默认 `hide_title_header=false`，普通本地文件夹默认 `true`。已有持久化值继续优先，避免覆盖用户选择。
     - `rust/crates/mnote-web/src/routes/local_folder_source.rs`：更新托管工作区标题测试，并新增普通本地文件夹默认隐藏标题测试。
     - `scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`：Check 6 调整为托管工作区默认显示标题；先勾选隐藏并验证持久化，再取消勾选显示并验证刷新后仍显示。
   - 验证：
     - `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_page_title_comes_from_file_name_not_body_heading -- --nocapture` 通过。
     - `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_non_managed_folder_default_hides_title_header -- --nocapture` 通过。
     - `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_folder_documents_save_title_and_options_write_to_disk -- --nocapture` 通过。
     - `cargo fmt --manifest-path rust/Cargo.toml --all --check` 通过。
     - `node --check scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js` 通过。
   - Worker 对比：
     - Reasonix coding 在隔离 worktree `mnote-wt-0524c-reasonix-title` 完成可用修复，范围控制在 `local_folder_source.rs` 与 `task479`，主控复核后采纳。
     - Claude Code coding 本轮 Hindsight 注入成功，但因 `--max-budget-usd 1.20` 超预算退出，只停留在代码探索阶段，未产生 diff；这说明 Claude Code full-mode 对编码任务成本较高，需要更小任务切片或更高预算。
4.CTRL+Z/CTRL+Y对于删除链接不可用，不能恢复删除的链接。(你应该系统看看还有没有其它ctrl+z/CTRL+Y(撤销/恢复)不能用的场景)

   处理记录（2026-05-24）：
   - 归类：`05-editor-mainline`，本质是编辑器命令写入链路绕过 Tiptap/ProseMirror history，不能只按单个链接 UI 修补。
   - 根因：本地 Markdown 附件链接被选中后按 `Backspace/Delete` 时，旧逻辑识别本地附件链接并走 `get_html -> delete_top_level_block_html -> editor.set_content(html)`。`set_content` 是整文替换，不会生成可撤销的用户 transaction，因此 `Ctrl+Z/Ctrl+Y` 无法恢复删除的链接。
   - 修复：
     - `rust/spikes/leptos-tiptap-spike/src/lib.rs`：删除本地附件链接时不再通过 HTML 整文替换；改为在 keydown 捕获阶段识别 Tiptap 当前非空选区是否位于 `/api/local-folder/files/open` 链接 mark 内，并调用 `editor.delete_selection()`，让删除进入 ProseMirror history。
     - `rust/spikes/leptos-tiptap-spike/src/lib.rs`：保留 DOM selection 识别作为鼠标选中文本兜底，但主判断改为读取 Tiptap editor state 的 link attributes，避免内部 selection 与 `window.getSelection()` 不同步。
     - `scripts/task488-local-attachment-link-delete-undo-smoke.js`：新增真实浏览器回归，上传本地 Markdown 附件、选中附件链接文本、Delete 删除、`Ctrl+Z` 恢复、`Ctrl+Y` 再删除。脚本显式等待超过 Tiptap history `newGroupDelay`，避免把“上传+删除”合并为同一个撤销事件而误判。
   - 验证：
     - `cargo fmt --manifest-path rust/Cargo.toml --all --check` 通过。
     - `cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml` 通过，只有既有 warning。
     - `cargo build --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml --target wasm32-unknown-unknown --release` 通过，只有既有 warning。
     - `wasm-bindgen rust/spikes/leptos-tiptap-spike/target/wasm32-unknown-unknown/release/mnote_leptos_tiptap_spike.wasm --target web --out-dir rust/spikes/leptos-tiptap-spike/generated/island --out-name mnote-leptos-tiptap-spike-island` 通过。
     - `node --check scripts/task488-local-attachment-link-delete-undo-smoke.js` 通过。
     - `node scripts/task488-local-attachment-link-delete-undo-smoke.js` 通过。
   - Worker 对比：
     - Claude Code 三轮任务均因预算超限或无可用 `finalText/process-handoff` 失败，隔离 worktree 没有可采纳 diff；本轮说明 Claude Code 轻量 patch mode 仍需要更窄任务和更稳定预算。
     - Reasonix 多轮任务卡住；其中 narrow patch worktree 有 diff，但没有 result/handoff，且包含无关格式和英文注释，未采纳。
     - 本轮对比结论：两者都没有直接解决第 4 条；可用价值主要是暴露 worker 失败模式。最终根因、修复和 smoke 由主控完成。
   - 系统性后续：
     - 当前修复覆盖“选中文本删除本地附件链接”的高频路径。块菜单删除、拖拽重排、折叠标题转换等仍有 `set_content`/HTML 整文替换路径，后续应拆一个 editor command history 设计，把块级删除/重排统一迁移到 Tiptap transaction/command，而不是继续扩散整文替换。
5，md文件上传后刷新前面的黑色方块会变成灰色：/mnt/Data1T/mnote/tmp/image copy 80.png；/mnt/Data1T/mnote/tmp/image copy 81.png

   处理记录（2026-05-26）：
   - 归类：`05-editor-mainline`，本质是本地 Markdown 上传附件插入后，Tiptap DOM、document session 保存、刷新后附件增强三者之间的时序合同不完整。
   - CodeGraph 定位：拆分后可直接从 `local-upload-runtime.js::insertUploadedAssetIntoEditor`、`sidebar-attachment-open-runtime.js::enhanceEditorAttachmentLink`、`document-editor-adapter-runtime.js::handleSessionChange`、`document-session-runtime.js::persistSession` 定位，不再需要在 `layout.rs` 大型 raw string 中翻找。
   - 根因：
     - 上传插入成功后，链接只进入当前 Tiptap DOM，没有稳定触发 local-folder document save，刷新后会从旧 Markdown 重建。
     - 刷新后 `/api/local-folder/files/open` 本地链接增强存在首屏时序竞态，普通 local open 链接可能暂时只有灰色通用文件 badge。
   - 修复：
     - `rust/crates/mnote-web/browser/local-upload-runtime.js`：上传插入后派发 Tiptap bridge change，并对 local-folder 上传直接按现有 conversion runtime 转为 `editorBlocks` 调 `/api/documents/save`；保存前登记 self-change suppression，避免 watcher 把自写入误报为外部冲突。
     - `rust/crates/mnote-web/browser/sidebar-attachment-open-runtime.js`：本地 open 链接也先补 `data-mnote-attachment-link`、`data-asset-id` 和类型 class，再做存在性刷新；并增加初始附件增强重扫。
     - `rust/crates/mnote-web/browser/document-tiptap-conversion-runtime.js`：刷新时从本地 open URL 的 `path` 重新推导附件类型，在 Markdown → Tiptap link mark 边界直接补 `mnote-uploaded-attachment-code` 等类型 class；CSS 只消费 class，不再用 `.md` href 颜色选择器兜底。
     - `scripts/task491-local-md-attachment-icon-refresh-smoke.js`：新增窄浏览器回归，覆盖上传 md 附件、写入 Markdown、刷新后仍保持黑色 md 附件图标。
     - `scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`：第 1 项补充刷新前后 md 附件图标断言，必须恢复 `mnote-uploaded-attachment-code` class，不再接受单纯 `::before` 黑色背景。
   - 验证：
     - `node scripts/task491-local-md-attachment-icon-refresh-smoke.js` 通过。
     - `node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js` 中 `editor-upload-to-page-resource-dir` 通过；该宽脚本仍有既有 `broken-link-after-real-file-delete` 和 `editor-delete-one-attachment-preserves-adjacent` 失败，非本条阻塞。
6. 当前office文件本地可以打开，我们当前使用sakura frp映射3000端口至：https://www.aichem.dpdns.org 后，发现pdf可以正常打开，但onlyoffice打不开了，报错为：ONLYOFFICE 加载失败{"target":{"frameOrigin":"https://www.aichem.dpdns.org"},"data":{"errorCode":-4,"errorDescription":"下载失败"}}。请检查原因，同时要兼容未来更换域名。

   处理记录（2026-05-26）：
   - 归类：`05-editor-mainline` / OnlyOffice transport，问题是 DocumentServer 下载地址随浏览器公网域名变化。
   - 根因：OnlyOffice 页面在非 localhost 访问时把 `proxyOrigin` 设为 `location.origin`，导致 DocumentServer 去拉 `https://www.aichem.dpdns.org/api/onlyoffice/proxy?...`；公网映射域名对 DocumentServer 不一定可达，触发 `errorCode=-4 下载失败`。
   - 修复：`rust/crates/mnote-web/src/routes/onlyoffice.rs` 中 `proxyOrigin` 和 `callbackOrigin` 固定使用 `documentUrlBase`，即 `ONLYOFFICE_DOCUMENT_URL_BASE` / 内部可达 base，不再依赖浏览器当前域名；未来换 FRP 域名不影响 DocumentServer 回源。
   - 验证：`cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_page_exposes_documentserver_fetch_base_for_local_files -- --test-threads=1` 通过。
