diff --git a/design/03-rust-web/process/3-19-rust-web-browser-runtime-module-extraction-v1.md b/design/03-rust-web/process/3-19-rust-web-browser-runtime-module-extraction-v1.md index 8fa94981..91ee4429 100644 --- a/design/03-rust-web/process/3-19-rust-web-browser-runtime-module-extraction-v1.md +++ b/design/03-rust-web/process/3-19-rust-web-browser-runtime-module-extraction-v1.md @@ -497,6 +497,9 @@ node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js - pdf/code -> 对应 lightweight viewer - office -> OnlyOffice path builder - [ ] 从 `SIDEBAR_TREE_JS` 中迁出本地文件夹上传目标计算和 adapter 调用;资源归属语义仍由 Rust API / kernel 合同决定。 + - 2026-05-24 / `reasonix-2026-05-24T15-17-16-430Z-18054487` 只读审计:`leptos-tiptap` 只应发出上传 intent;本地 Markdown 路径、`rootUri`、`/api/local-folder/files/open` URL、`markdownRelativePath` 与真实上传落盘都归本设计的 shell host runtime。 + - 下一刀低风险候选:`resolveEditorUploadContext`、`editorRootFromUploadOptions`、`openEditorUploadFilePicker`,优先保持调用 `uploadFilesWithResolvedTarget`,不同时迁上传主流程。 + - 暂不迁:`uploadFileToMediaAsset`、`uploadFilesWithResolvedTarget`、`insertUploadedAssetIntoEditor`。这些函数同时涉及 API POST、file tree refresh、Tiptap chain 插入、OnlyOffice/resource tab,需在后续批次继续拆边界。 - [ ] 保持现有 smoke: - `node scripts/task472-side-target-secondary-pane-smoke.js` - `node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js` diff --git a/design/05-editor-mainline/process/5-28-leptos-tiptap-editor-runtime-module-extraction-v1.md b/design/05-editor-mainline/process/5-28-leptos-tiptap-editor-runtime-module-extraction-v1.md index d3f0fcf8..2613a645 100644 --- a/design/05-editor-mainline/process/5-28-leptos-tiptap-editor-runtime-module-extraction-v1.md +++ b/design/05-editor-mainline/process/5-28-leptos-tiptap-editor-runtime-module-extraction-v1.md @@ -138,10 +138,15 @@ rust/spikes/leptos-tiptap-spike/src/ ### P3:抽出附件上传与本地 Markdown 路径 -- [ ] 新增 `editor_runtime/attachment_upload.rs`。 +- [x] 新增 `editor_runtime/attachment_upload.rs`。 - [ ] 新增 `editor_runtime/local_markdown_paths.rs`。 -- [ ] 迁出 slash 上传附件、图片、Office/PDF/code 文件的编辑器内插入逻辑。 -- [ ] 明确 editor 只负责插入附件链接/块,资源保存位置和归属仍由 shell/Rust API 合同决定。 + - 2026-05-24 复核:`local_markdown_paths.rs` 不应落在 `leptos-tiptap` 内部;本地 Markdown 路径、`rootUri`、`/api/local-folder/files/open` URL 和上传落盘归 `03-rust-web` 的 shell host runtime / `local-upload-runtime.js`。 +- [x] 迁出 slash 上传附件、图片的编辑器 intent。 + - `dispatch_editor_upload_request(kind, accept)` 已迁入 `attachment_upload.rs`。 + - `SlashActionKind::Image` / `UploadAttachment` 仍留在 slash menu 基础结构中,只调用 editor intent。 +- [ ] 迁出 Office/PDF/code 文件的编辑器内插入逻辑。 + - 当前实际插入链路在 `layout.rs` / `local-upload-runtime.js`,不是 editor runtime;后续由 `03-rust-web` 继续收口。 +- [x] 明确 editor 只负责发起上传 intent;资源保存位置和归属仍由 shell/Rust API 合同决定。 - [ ] 覆盖: - 上传第一个/第二个附件后链接仍可点击。 - 刷新后链接不退化为普通外链。 @@ -347,6 +352,45 @@ Codex 主控复核: - `cargo test --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml standalone_mindmap_object_uses_isolated_draft_identity` - 阻塞原因:lib test 编译阶段命中既有 `slash_menu_css_uses_viewport_overlay_layer` 中 `STYLE` 未定义;HEAD 中该测试已引用 `STYLE`,而实际常量名为 `SPIKE_STYLE`。该问题不属于本批拆分,已记录为后续单独处理。 +### 10.4 Batch 3 第一刀执行记录(2026-05-24) + +Reasonix read-only audit: + +- run id: `reasonix-2026-05-24T15-17-16-430Z-18054487` +- worker worktree: `/mnt/Data1T/mnote-wt-5-28-upload-boundary-b3` +- handoff: `/home/lix/.codex/runtime/reasonix-coding-worker/reasonix-2026-05-24T15-17-16-430Z-18054487/process-handoff.md` +- 类型:只读审计,主控已读取 Hindsight recall、`process-handoff.md`、`process-handoff.json`。 + +采纳结论: + +- 当前上传链路应保持 `editor intent -> CustomEvent -> shell pickup`。 +- `attachment_upload.rs` 只承接 `dispatch_editor_upload_request(kind, accept)` 这一类编辑器 intent,不迁入真实上传、文件选择器、API POST、资源归属或插入链接主流程。 +- `local_markdown_paths.rs` 暂不在 `leptos-tiptap` 内创建;本地 Markdown 路径、`rootUri`、`/api/local-folder/files/open` URL、`markdownRelativePath` 与落盘语义归 `03-rust-web` shell host runtime / `local-upload-runtime.js`。 + +采纳内容: + +- 新增 `editor_runtime/attachment_upload.rs`,迁出 `dispatch_editor_upload_request(kind, accept)`。 +- `src/lib.rs` 的 `run_slash_action` 继续保留 `SlashActionKind::Image` / `UploadAttachment` 的 slash menu 决策,只调用 `attachment_upload` 发出上传 intent。 +- `src/lib.rs` 不再直接构造 `mnote:editor-upload-request`。 + +已通过验证: + +- `cargo fmt --manifest-path rust/Cargo.toml --all --check` +- `cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`(35 个既有 warning) +- `cargo build --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml --target wasm32-unknown-unknown --release`(35 个既有 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 scripts/task488-local-attachment-link-delete-undo-smoke.js` + - 结果:`ok=true` + - 证据:`tmp/task488-local-attachment-link-delete-undo-smoke/result.json` +- `node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js` + - 结果:`ok=true` + - 证据:命令 stdout JSON,覆盖 `editor-upload-to-page-resource-dir`、`filetree-folder-drop-to-target`、`broken-link-after-real-file-delete`、`direct-attach-open-without-active-md`、`editor-delete-one-attachment-preserves-adjacent` 等检查。 + +后续归属: + +- `resolveEditorUploadContext`、`editorRootFromUploadOptions`、`openEditorUploadFilePicker` 等 shell-side DOM/context 探针适合进入 `03-rust-web` 的 `local-upload-runtime.js` 后续批次。 +- `uploadFileToMediaAsset`、`uploadFilesWithResolvedTarget`、`insertUploadedAssetIntoEditor` 仍是高耦合主流程,暂不迁入 editor runtime。 + ## 11. 测试与验收 ### Rust 检查 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 14ea47e7..4aed409a 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 @@ -1284,7 +1284,7 @@ function __wbg_get_imports() { return ret; }, __wbindgen_cast_0000000000000006: function(arg0, arg1) { - // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 758, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("KeyboardEvent")], shim_idx: 745, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hd90af689bc3e71bf); 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 efdf6d05..4a721bf9 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/attachment_upload.rs b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/attachment_upload.rs new file mode 100644 index 00000000..0958228d --- /dev/null +++ b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/attachment_upload.rs @@ -0,0 +1,21 @@ +use serde_json::json; +use web_sys::{window, CustomEvent, CustomEventInit}; + +pub(crate) fn dispatch_editor_upload_request(kind: &str, accept: &str) -> Result<(), String> { + let win = window().ok_or_else(|| "当前浏览器窗口不可用".to_string())?; + let detail = json!({ + "kind": kind, + "accept": accept, + "multiple": true, + "insertIntoEditor": true, + }); + let detail = + serde_wasm_bindgen::to_value(&detail).map_err(|err| format!("构造上传请求失败:{err}"))?; + let init = CustomEventInit::new(); + init.set_detail(&detail); + let event = CustomEvent::new_with_event_init_dict("mnote:editor-upload-request", &init) + .map_err(|_| "构造上传事件失败".to_string())?; + win.dispatch_event(&event) + .map(|_| ()) + .map_err(|_| "派发上传事件失败".to_string()) +} 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 2f2e0c27..4eb1a916 100644 --- a/rust/spikes/leptos-tiptap-spike/src/editor_runtime/mod.rs +++ b/rust/spikes/leptos-tiptap-spike/src/editor_runtime/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod attachment_links; +pub(crate) mod attachment_upload; pub(crate) mod command_sync; pub(crate) mod history_safe_commands; pub(crate) mod persistence; diff --git a/rust/spikes/leptos-tiptap-spike/src/lib.rs b/rust/spikes/leptos-tiptap-spike/src/lib.rs index fba668ea..ed16ce9e 100644 --- a/rust/spikes/leptos-tiptap-spike/src/lib.rs +++ b/rust/spikes/leptos-tiptap-spike/src/lib.rs @@ -20,6 +20,7 @@ use web_sys::{ HtmlInputElement, MouseEvent, Node, RequestInit, RequestMode, Response, WheelEvent, }; +use editor_runtime::attachment_upload::dispatch_editor_upload_request; use editor_runtime::command_sync::{ read_editor_snapshot, sync_editor_outputs, sync_persisted_editor_command, }; @@ -7134,25 +7135,6 @@ fn table_option_checked(document: &Value, action: TableOptionAction) -> bool { } } -fn dispatch_editor_upload_request(kind: &str, accept: &str) -> Result<(), String> { - let win = window().ok_or_else(|| "当前浏览器窗口不可用".to_string())?; - let detail = json!({ - "kind": kind, - "accept": accept, - "multiple": true, - "insertIntoEditor": true, - }); - let detail = - serde_wasm_bindgen::to_value(&detail).map_err(|err| format!("构造上传请求失败:{err}"))?; - let init = CustomEventInit::new(); - init.set_detail(&detail); - let event = CustomEvent::new_with_event_init_dict("mnote:editor-upload-request", &init) - .map_err(|_| "构造上传事件失败".to_string())?; - win.dispatch_event(&event) - .map(|_| ()) - .map_err(|_| "派发上传事件失败".to_string()) -} - fn run_slash_action( editor: TiptapEditorHandle, action: SlashActionKind,