From fda2224b6f9c03f86cb66fd5a002790be1115b42 Mon Sep 17 00:00:00 2001 From: lix-2026 Date: Mon, 25 May 2026 02:00:34 +0800 Subject: [PATCH] refactor: delegate upload files orchestration --- ...0-browser-runtime-followup-checklist-v1.md | 9 ++++-- .../mnote-web/browser/local-upload-runtime.js | 31 +++++++++++++++++++ rust/crates/mnote-web/src/ssr/pages/layout.rs | 12 +++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md b/design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md index a8afdcbd..61b73230 100644 --- a/design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md +++ b/design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md @@ -63,7 +63,7 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据 ## 6. 本轮执行记录 - 2026-05-25:Codex 创建本续跑事实源。 -- 2026-05-25:修正执行约束:本文件遵循当前 goal 的“每批最多 2 个 worker”;实现 worker 必须在独立 worktree 产出候选 patch,Codex 复核后再选择性合入。 +- 2026-05-25:修正执行约束:本文件遵循当前 goal 的“每批最多 4 个 worker”;实现 worker 必须在独立 worktree 产出候选 patch,Codex 复核后再选择性合入。 - 2026-05-25:下一批只派发 Worker B(local upload / filetree 下一刀只读审计);Worker A/C/D 暂缓。 - 2026-05-25:Reasonix local upload / filetree 只读审计尝试未产生 `result.json` / `process-handoff`,且运行期间出现额外 Batch A2 runner,主控已终止进程;该结果不作为正式 handoff 采纳。 - 可参考的 transcript 结论:`fetchWithTimeout` 是低风险纯工具函数,可先迁入 `local-upload-runtime.js`;`uploadFileToMediaAsset` / `uploadFilesWithResolvedTarget` / `insertUploadedAssetIntoEditor` 仍与 API、editor 插入和 snapshot refresh 耦合,暂不迁。 @@ -92,8 +92,13 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据 - 修改:`rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`rust/crates/mnote-web/src/routes/web_shell.rs`。 - 已验证:`node --check rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web document_conflict_panel_runtime_contains_dom_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`。 - 未完成:conflict merge / accept / keep 业务逻辑、document session lifecycle、secondary pane conflict smoke 仍未迁出;本轮未声称真实冲突 UI 全链路完成。 -- 2026-05-25:根据用户纠正,后续实现 worker 不能在主工作区直接修改;每批最多 2 个 worker,且必须采用独立 worktree 候选 patch或明确只读任务。Codex 读取 Hindsight recall、`process-handoff.md/json`、`result.json`、diff 与验证证据后再选择性合入。 +- 2026-05-25:根据用户纠正,后续实现 worker 不能在主工作区直接修改;每批最多 4 个 worker,且必须采用独立 worktree 候选 patch 或明确只读任务。Codex 读取 Hindsight recall、`process-handoff.md/json`、`result.json`、diff 与验证证据后再选择性合入。 - 2026-05-25:Codex 主控完成非本地 media upload API 小切片:`local-upload-runtime.js` 新增并导出 `uploadMediaAsset(file, plan, context)`,承接 `/api/media/upload` 的 FormData 构造、fetch 和响应解析;`layout.rs` 的 `uploadFileToMediaAsset` 仍负责 appendUploadedAssetRow、editor 插入和 `wolai:assets-changed` 事件分发,并保留完整 inline fallback。 - 修改:`rust/crates/mnote-web/browser/local-upload-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。 - 已验证:`node --check rust/crates/mnote-web/browser/local-upload-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_upload_runtime_contains_editor_upload_context_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder -- --test-threads=1`、`node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。 - 未完成:`uploadFileToMediaAsset` / `uploadFilesWithResolvedTarget` 编排壳、conflict merge / accept / keep 业务逻辑、secondary pane conflict smoke 仍未迁出;本轮未改资源归属语义。 +- 2026-05-25:Batch C 按用户要求同时派发 3 个独立 worktree 实现 worker:B 修复 5-29 block delete undo,C 迁 `uploadFileToMediaAsset`,D 收口 conflict action boundary;3 个 Reasonix runner 均未产出 `final.md`、`result.json` 或 `process-handoff.md/json`,对应 worktree diff 为空,按采纳规则全部拒绝。Codex 后续只采纳独立复核后的候选 patch。 +- 2026-05-25:Codex 主控完成 upload files orchestration 小切片:`local-upload-runtime.js` 新增并导出 `uploadFilesWithResolvedTarget(files, detail, options, deps)`,承接文件列表归一化、逐个上传、错误聚合和 alert 提示;`layout.rs` 仍注入 `resolveFileTreeUploadTarget` / `uploadFileToMediaAsset`,保留目标解析、单文件上传和完整 inline fallback。 + - 修改:`rust/crates/mnote-web/browser/local-upload-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。 + - 已验证:`node --check rust/crates/mnote-web/browser/local-upload-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_upload_runtime_contains_editor_upload_context_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder -- --test-threads=1`、`node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。 + - 未完成:`uploadFileToMediaAsset` 编排壳、conflict merge / accept / keep 业务逻辑、secondary pane conflict smoke 仍未迁出;本轮未改资源归属语义。 diff --git a/rust/crates/mnote-web/browser/local-upload-runtime.js b/rust/crates/mnote-web/browser/local-upload-runtime.js index f5983f3d..fe663948 100644 --- a/rust/crates/mnote-web/browser/local-upload-runtime.js +++ b/rust/crates/mnote-web/browser/local-upload-runtime.js @@ -298,6 +298,36 @@ async function insertUploadedAssetIntoEditor(asset, targetRoot, deps) { return false; } +async function uploadFilesWithResolvedTarget(files, detail, options, deps) { + deps = deps || {}; + var resolveFileTreeUploadTarget = typeof deps.resolveFileTreeUploadTarget === 'function' ? deps.resolveFileTreeUploadTarget : null; + var uploadFileToMediaAsset = typeof deps.uploadFileToMediaAsset === 'function' ? deps.uploadFileToMediaAsset : null; + if (!resolveFileTreeUploadTarget || !uploadFileToMediaAsset) { + throw new Error('上传运行时缺少目标解析或上传函数'); + } + var list = Array.from(files || []).filter(Boolean); + if (!list.length) return []; + var plan = await resolveFileTreeUploadTarget(detail || {}); + var uploaded = []; + var errors = []; + for (var i = 0; i < list.length; i += 1) { + try { + uploaded.push(await uploadFileToMediaAsset(list[i], plan, options || {})); + } catch (error) { + errors.push(list[i].name + ': ' + (error && error.message ? error.message : '上传失败')); + } + } + if (errors.length) { + var message = '部分文件上传失败:\n' + errors.slice(0, 6).join('\n') + (errors.length > 6 ? '\n...' : ''); + if (typeof deps.alert === 'function') { + deps.alert(message); + } else { + window.alert(message); + } + } + return uploaded; +} + function localAssetOpenUrl(asset, download, context) { if (!isLocalUploadedAsset(asset)) return ''; var rootUri = String(context && context.rootUri || asset && (asset.rootUri || asset.root_uri) || '').trim() || currentRootUri(); @@ -430,6 +460,7 @@ window.__mnoteLocalUploadRuntime = { uploadLocalFolderAsset: uploadLocalFolderAsset, uploadMediaAsset: uploadMediaAsset, insertUploadedAssetIntoEditor: insertUploadedAssetIntoEditor, + uploadFilesWithResolvedTarget: uploadFilesWithResolvedTarget, localAssetOpenUrl: localAssetOpenUrl, uploadedAssetType: uploadedAssetType, fileTreeIconKindForFileName: fileTreeIconKindForFileName, diff --git a/rust/crates/mnote-web/src/ssr/pages/layout.rs b/rust/crates/mnote-web/src/ssr/pages/layout.rs index 8bc63345..f3badb37 100644 --- a/rust/crates/mnote-web/src/ssr/pages/layout.rs +++ b/rust/crates/mnote-web/src/ssr/pages/layout.rs @@ -4098,6 +4098,14 @@ const SIDEBAR_TREE_JS: &str = r##" installMindmapAssetFetchObserver(); async function uploadFilesWithResolvedTarget(files, detail, options) { + var runtimeFn = localUploadRuntimeFunction('uploadFilesWithResolvedTarget'); + if (runtimeFn) { + return await runtimeFn(files, detail || {}, options || {}, { + resolveFileTreeUploadTarget: resolveFileTreeUploadTarget, + uploadFileToMediaAsset: uploadFileToMediaAsset, + alert: function(message) { window.alert(message); } + }); + } var list = Array.from(files || []).filter(Boolean); if (!list.length) return []; var plan = await resolveFileTreeUploadTarget(detail || {}); @@ -10965,6 +10973,9 @@ mod tests { SIDEBAR_TREE_JS.contains("localUploadRuntimeFunction('insertUploadedAssetIntoEditor')") ); assert!(SIDEBAR_TREE_JS.contains("localUploadRuntimeFunction('uploadMediaAsset')")); + assert!( + SIDEBAR_TREE_JS.contains("localUploadRuntimeFunction('uploadFilesWithResolvedTarget')") + ); assert!(SIDEBAR_TREE_JS.contains("__mnoteLastEditorUploadRoot")); assert!(SIDEBAR_TREE_JS.contains("data-pane-role=\"primary\"")); assert!(SIDEBAR_TREE_JS.contains("insertUploadedAssetIntoEditor(localPayload.asset, editorRootFromUploadOptions(options))")); @@ -10982,6 +10993,7 @@ mod tests { assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function uploadLocalFolderAsset")); assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function uploadMediaAsset")); assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function insertUploadedAssetIntoEditor")); + assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function uploadFilesWithResolvedTarget")); assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("data-mnote-last-upload-inserted")); assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("uploadFilesWithResolvedTarget")); assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("__mnoteLastEditorUploadRoot"));