refactor: delegate media asset upload runtime
This commit is contained in:
@@ -106,3 +106,10 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据
|
||||
- Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`,并核对 worktree `git status --short` / `git diff --stat` 为空;runner 已产出 completed handoff 但进程未自行退出,主控已终止残留 runner。
|
||||
- 审计结论:`uploadFileToMediaAsset` 的 API 调用、editor 插入、多文件循环等纯 helper 已外置;剩余主要是 source 判定、sidebar refresh / append、事件分发等 shell 编排职责,不适合继续整体迁入 runtime。
|
||||
- 下一刀建议:不要盲迁整个 `uploadFileToMediaAsset`;若继续 3-20,应转向 filetree DOM runtime 的 `refreshLocalFolderSidebarSnapshot` / `appendUploadedAssetRow` 依赖拆分,或只做 fallback 精简但需重新评估是否仍要保留 inline fallback 策略。
|
||||
- 2026-05-25:Codex 独立复核并采纳 3-20 候选 patch:`local-upload-runtime.js` 新增并导出 `uploadFileToMediaAsset(file, plan, options, deps)`,`layout.rs` 的同名 wrapper 优先委托 runtime,仍保留完整 inline fallback;`resolveFileTreeUploadTarget` 未迁出,资源归属、上传 API、editor 插入状态和事件名保持不变。
|
||||
- Reasonix 协作证据:只读审计 run `reasonix-2026-05-24T18-04-44-852Z-2c2d5ce9` 已有 Hindsight recall 与 `process-handoff.md/json`,指出剩余职责主要是 shell 编排;实现 run `reasonix-2026-05-24T18-07-08-227Z-1358f36b` 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`,无 `final.md`、`result.json`、`process-handoff.md/json`,因此不作为完整 Reasonix handoff 采纳。
|
||||
- 候选 worktree:`/mnt/Data1T/mnote-worktrees/0525-d-worker-b-3-20-upload-file-orchestrator`;Codex 已核对 `git diff --stat`、`git diff --check` 和修改范围,并按“runtime 委托 + shell deps 注入 + 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_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
|
||||
- 过程说明:Worker B 在独立 worktree 中复跑 `task479` 时曾出现 `broken-link-after-real-file-delete` 等待超时且 JSON `ok:false`;Codex 在主工作区移植后复跑同一 smoke,结果 `ok:true`,截图证据位于该 smoke 输出的临时目录。
|
||||
- 未完成:`resolveFileTreeUploadTarget`、filetree DOM runtime 的 `refreshLocalFolderSidebarSnapshot` / `appendUploadedAssetRow` 更细拆分、conflict merge / accept / keep 业务逻辑、secondary pane conflict smoke 仍未迁出。
|
||||
|
||||
@@ -67,7 +67,7 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据
|
||||
- 已迁函数:`node_is_within_root`、`active_editor_text_selection`、`has_active_text_selection`、`selection_summary`、`selection_has_rich_marks`。
|
||||
- 未迁:`SelectionPayload`、`selection_payload`、`send_selection_state*`、`selection_event_payload`,原因是仍依赖 bridge event、`HoveredBlockState` 和 runtime block id 边界。
|
||||
- 已验证:`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`、wasm release build、`wasm-bindgen`、`task488`、`task489`、CodeGraph pending 0。
|
||||
- 2026-05-25:执行口径修正:继续遵循当前 goal 的“每批最多 2 个 worker”;Reasonix 后续必须使用独立 worktree 或明确只读任务,完成后由 Codex 读取 handoff 并复核。
|
||||
- 2026-05-25:执行口径修正:继续遵循当前 goal 的“每批最多 4 个 worker”;Reasonix 后续必须使用独立 worktree 或明确只读任务,完成后由 Codex 读取 handoff 并复核。
|
||||
- 2026-05-25:Batch D 额外 runner 曾自动启动 Worker A / Worker B,未经过本轮主控计划确认;主控已终止相关 runner / ACP 进程,不采纳这些未受控 run。遗留 task 草稿暂不删除,后续如继续使用必须重新生成受控任务书。
|
||||
- 2026-05-25:Reasonix runner 异常:后续实现 run 未写 `result.json`、`process-handoff.md/json`,部分 transcript 停在工具调用阶段;Codex 不采纳其文字结论,只复核实际 diff。
|
||||
- 2026-05-25:Reasonix overlay / block menu 只读审计尝试未产生 `result.json` / `process-handoff`,且运行期间出现额外 Batch A2 runner,主控已终止进程;该结果不作为正式 handoff 采纳。
|
||||
@@ -92,7 +92,7 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据
|
||||
- 未覆盖:`cargo test --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml block_menu_document -- --test-threads=1` 被既有 `STYLE` 测试编译错误阻断,未进入本次新增 helper 测试。
|
||||
- 2026-05-25:根据用户纠正,后续 B/C/D 等实现 worker 不再只读或在主工作区直接修改;Batch C 统一采用独立 worktree 候选 patch,Codex 读取 Hindsight recall、`process-handoff.md/json`、`result.json`、diff 与验证证据后再选择性合入。
|
||||
- 2026-05-25:根据用户再次纠正,后续 B/C/D 等实现 worker 必须统一在独立 worktree 中产出候选 patch,再由 Codex 复核 diff / handoff / 验证证据后选择性合入;不得在主工作区直接推进实现 worker。
|
||||
- 2026-05-25:Batch D Reasonix runner 再次异常:Worker A run `reasonix-2026-05-24T18-07-08-220Z-ccc4102a`、Worker B run `reasonix-2026-05-24T18-07-08-227Z-1358f36b` 未经过本轮主控计划确认且已被终止;该结果不采纳。
|
||||
- 2026-05-25:Batch D Reasonix runner 再次异常:Worker A run `reasonix-2026-05-24T18-07-08-220Z-ccc4102a`、Worker B run `reasonix-2026-05-24T18-07-08-227Z-1358f36b` 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`,无 `final.md`、`result.json`、`process-handoff.md/json`,对应 worktree diff 均为空;该结果拒绝采纳。为保持推进速度,本批改用独立 worktree worker 继续产出候选 patch,Codex 仍按同一采纳规则复核。
|
||||
- 2026-05-25:Batch C Worker B(`reasonix-2026-05-24T17-41-07-634Z-a39f8963`)在独立 worktree `/mnt/Data1T/mnote-worktrees/0525-c-worker-b-block-delete-undo` 未产出 `final.md`、`result.json` 或 `process-handoff.md/json`,worktree diff 为空,拒绝采纳;Codex 随后在同一 worktree 独立定位根因、产出候选 patch、通过 smoke 后移植到主工作区。
|
||||
- 2026-05-25:Reasonix 只读审计 `sync_editor_overlay_state` / `try_sync_editor_overlay_state` 下一刀,使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-d-5-29-overlay-readonly`,run id `reasonix-2026-05-24T17-53-20-962Z-d3077308`。
|
||||
- Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`,并核对 worktree `git status --short` / `git diff --stat` 为空;runner 已产出 completed handoff 但进程未自行退出,主控已终止残留 runner。
|
||||
|
||||
@@ -298,6 +298,66 @@ async function insertUploadedAssetIntoEditor(asset, targetRoot, deps) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async function uploadFileToMediaAsset(file, plan, options, deps) {
|
||||
deps = deps || {};
|
||||
options = options || {};
|
||||
var getCurrentSourceKind = typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; };
|
||||
var getCurrentRootUri = typeof deps.currentRootUri === 'function' ? deps.currentRootUri : currentRootUri;
|
||||
var getCurrentDocumentId = typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId : function() { return ''; };
|
||||
var resolveEditorRoot = typeof deps.editorRootFromUploadOptions === 'function'
|
||||
? deps.editorRootFromUploadOptions
|
||||
: function(nextOptions) { return editorRootFromUploadOptions(nextOptions, deps); };
|
||||
var insertIntoEditor = typeof deps.insertUploadedAssetIntoEditor === 'function'
|
||||
? deps.insertUploadedAssetIntoEditor
|
||||
: function(asset, targetRoot) { return insertUploadedAssetIntoEditor(asset, targetRoot, deps); };
|
||||
var dispatch = typeof deps.dispatchEvent === 'function'
|
||||
? deps.dispatchEvent
|
||||
: function(event) { return window.dispatchEvent(event); };
|
||||
var EventCtor = typeof deps.CustomEvent === 'function' ? deps.CustomEvent : window.CustomEvent;
|
||||
if (getCurrentSourceKind() === 'local_folder') {
|
||||
var rootUri = getCurrentRootUri();
|
||||
var documentId = String(plan && plan.targetDocumentId || getCurrentDocumentId() || '').trim();
|
||||
var localResult = await uploadLocalFolderAsset(file, plan, {
|
||||
rootUri: rootUri,
|
||||
documentId: documentId,
|
||||
timeoutMs: 15000
|
||||
});
|
||||
var localAsset = localResult && localResult.asset ? localResult.asset : null;
|
||||
if (!localAsset) {
|
||||
throw new Error('上传失败');
|
||||
}
|
||||
if (options.insertIntoEditor) {
|
||||
await insertIntoEditor(localAsset, resolveEditorRoot(options));
|
||||
}
|
||||
if (typeof deps.refreshLocalFolderSidebarSnapshot === 'function') {
|
||||
void deps.refreshLocalFolderSidebarSnapshot();
|
||||
}
|
||||
dispatch(new EventCtor('wolai:local-assets-changed', {
|
||||
detail: { docId: documentId, asset: localAsset, assetIds: [localAsset.id] }
|
||||
}));
|
||||
return localAsset;
|
||||
}
|
||||
var mediaResult = await uploadMediaAsset(file, plan, { timeoutMs: 15000 });
|
||||
var mediaAsset = mediaResult && mediaResult.asset ? mediaResult.asset : null;
|
||||
if (!mediaAsset) {
|
||||
throw new Error('上传失败');
|
||||
}
|
||||
if (typeof deps.appendUploadedAssetRow === 'function') {
|
||||
deps.appendUploadedAssetRow(mediaAsset, plan && plan.targetDocumentId);
|
||||
}
|
||||
if (options.insertIntoEditor) {
|
||||
await insertIntoEditor(mediaAsset, resolveEditorRoot(options));
|
||||
}
|
||||
dispatch(new EventCtor('wolai:assets-changed', {
|
||||
detail: {
|
||||
docId: plan && plan.targetDocumentId,
|
||||
asset: mediaAsset,
|
||||
assetIds: [mediaAsset.id]
|
||||
}
|
||||
}));
|
||||
return mediaAsset;
|
||||
}
|
||||
|
||||
async function uploadFilesWithResolvedTarget(files, detail, options, deps) {
|
||||
deps = deps || {};
|
||||
var resolveFileTreeUploadTarget = typeof deps.resolveFileTreeUploadTarget === 'function' ? deps.resolveFileTreeUploadTarget : null;
|
||||
@@ -460,6 +520,7 @@ window.__mnoteLocalUploadRuntime = {
|
||||
uploadLocalFolderAsset: uploadLocalFolderAsset,
|
||||
uploadMediaAsset: uploadMediaAsset,
|
||||
insertUploadedAssetIntoEditor: insertUploadedAssetIntoEditor,
|
||||
uploadFileToMediaAsset: uploadFileToMediaAsset,
|
||||
uploadFilesWithResolvedTarget: uploadFilesWithResolvedTarget,
|
||||
localAssetOpenUrl: localAssetOpenUrl,
|
||||
uploadedAssetType: uploadedAssetType,
|
||||
|
||||
@@ -4014,6 +4014,25 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}
|
||||
|
||||
async function uploadFileToMediaAsset(file, plan, options) {
|
||||
var runtimeFn = localUploadRuntimeFunction('uploadFileToMediaAsset');
|
||||
if (runtimeFn) {
|
||||
return await runtimeFn(file, plan, options || {}, {
|
||||
currentSourceKind: currentSourceKind,
|
||||
currentRootUri: currentRootUri,
|
||||
currentDocumentId: currentDocumentId,
|
||||
editorRootFromUploadOptions: editorRootFromUploadOptions,
|
||||
appendUploadedAssetRow: appendUploadedAssetRow,
|
||||
refreshLocalFolderSidebarSnapshot: refreshLocalFolderSidebarSnapshot,
|
||||
dispatchEvent: function(event) { return window.dispatchEvent(event); },
|
||||
CustomEvent: CustomEvent,
|
||||
buildOnlyOfficeAssetOpenUrl: buildOnlyOfficeAssetOpenUrl,
|
||||
fetchCurrentOnlyOfficeUserId: fetchCurrentOnlyOfficeUserId,
|
||||
buildOnlyOfficeOpenPath: buildOnlyOfficeOpenPath,
|
||||
inferOnlyOfficeFileType: inferOnlyOfficeFileType,
|
||||
enhanceEditorAttachmentLinks: enhanceEditorAttachmentLinks,
|
||||
cssEscape: cssEscape
|
||||
});
|
||||
}
|
||||
if (currentSourceKind() === 'local_folder') {
|
||||
var rootUri = currentRootUri();
|
||||
var documentId = String(plan && plan.targetDocumentId || currentDocumentId() || '').trim();
|
||||
@@ -10972,6 +10991,7 @@ mod tests {
|
||||
assert!(
|
||||
SIDEBAR_TREE_JS.contains("localUploadRuntimeFunction('insertUploadedAssetIntoEditor')")
|
||||
);
|
||||
assert!(SIDEBAR_TREE_JS.contains("localUploadRuntimeFunction('uploadFileToMediaAsset')"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("localUploadRuntimeFunction('uploadMediaAsset')"));
|
||||
assert!(
|
||||
SIDEBAR_TREE_JS.contains("localUploadRuntimeFunction('uploadFilesWithResolvedTarget')")
|
||||
@@ -10993,8 +11013,10 @@ 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 uploadFileToMediaAsset"));
|
||||
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("uploadFileToMediaAsset: uploadFileToMediaAsset"));
|
||||
assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("uploadFilesWithResolvedTarget"));
|
||||
assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("__mnoteLastEditorUploadRoot"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user