refactor: extract conflict panel host mount helper
This commit is contained in:
@@ -207,3 +207,17 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据
|
||||
- 审计结论经 Codex 复核后采纳:`syncSidebarFileTreeSelection` 已通过 `filetree-selection-runtime.js` 完成 runtime 外置,不应合并进 `filetree-runtime.js`;selection runtime、filetree DOM/data runtime、context menu runtime 继续保持分离。
|
||||
- Codex 主控完成后续小切片:`selectSidebarFileTreeDocument` 继续负责按 documentId 找行、active 标记和滚动,但选中状态改为复用 `selectSidebarFileTreeRow(row, { ctrlKey:false, metaKey:false, shiftKey:false })`,避免继续手写第二份 filetree selection 状态同步路径。
|
||||
- 已验证:`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 sidebar_filetree_selection_runtime_helpers_are_externalized_with_inline_fallback -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_selection_runtime_contains_selection_helpers -- --test-threads=1`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3014 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3014 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3014 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
|
||||
- 2026-05-25:Batch AL 只派发 1 个只读 Reasonix worker 审计 document pane host / conflict panel runtime 剩余边界,使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-al-worker-a-3-20-document-pane-host-readonly`,run id `reasonix-2026-05-25T00-32-07-827Z-295cc2b2`,并显式使用 `--no-memory-recall`。
|
||||
- Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`,并核对 worktree `git status --short` / `git diff --stat` 为空;随后终止残留 runner / ACP 父进程,不采纳任何未验证源码写入。
|
||||
- 审计结论经 Codex 复核后采纳:`renderSessionConflictSurface` / `openConflictDiffPanel` 剩余部分主要是 session 编排、error handling、fetch、persist 与 host adapter 责任,不应继续整体迁入 `document-conflict-panel-runtime.js`;若硬要再切,只剩 `resolveDocumentPaneHost(view)` 与 `mountConflictPanelToDocShell(host, panel)` 这类极小的纯 DOM helper,但收益有限。
|
||||
- 当前建议:3-20 的下一刀应优先回到 filetree DOM/runtime 边界或上传后 sidebar refresh 相关 helper,而不是继续收 conflict panel 尾部。
|
||||
- 2026-05-25:Batch AL 派发 1 个只读 Reasonix worker 审计 document pane host / conflict panel runtime 边界,使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-al-worker-a-3-20-document-pane-host-readonly`,run id `reasonix-2026-05-25T00-32-07-827Z-295cc2b2`,显式 `--no-memory-recall`。
|
||||
- Codex 已读取 Hindsight recall、`process-handoff.md/json`,并核对 worktree `git status --short` / `git diff --stat` 为空;只读结论经主控复核后采纳:host 定位/挂载是 document conflict 线当前剩余最合适的纯 DOM helper,`acceptDiskVersion`、`keepCurrentEditorVersion`、`writeMergedConflictResult` 及 fetch/persist/session status 编排继续留在 `web_shell.rs`。
|
||||
- Codex 主控同步推进 conflict panel host 挂载小切片:`document-conflict-panel-runtime.js` 新增 `conflictPanelHost(root)` 与 `mountSessionConflictPanel(root, panel)`,承接 `.document-pane` host 定位和 header 后插入逻辑;`web_shell.rs::renderSessionConflictSurface` 优先委托 runtime 挂载,保留完整 inline fallback。
|
||||
- 边界:未迁 `acceptDiskVersion`、`keepCurrentEditorVersion`、`writeMergedConflictResult`、fetch/persist/session status 业务逻辑,也未改 diff 面板数据加载路径;本批只外置 conflict panel host mount DOM helper。
|
||||
- 已验证:`node --check rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`git diff --check -- design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md rust/crates/mnote-web/browser/document-conflict-panel-runtime.js rust/crates/mnote-web/src/routes/web_shell.rs`、`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`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3033 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3033 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3033 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`。
|
||||
- 2026-05-25:Batch AM 只派发 1 个 browser smoke Reasonix worker,使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression`,run id `reasonix-2026-05-25T00-26-34-914Z-c639fcef`,并显式使用 `--no-memory-recall`。
|
||||
- Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`,并核对 worktree `git status --short` 为空;worker 未修改源码。
|
||||
- 实测通过:`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task487-local-folder-tree-live-consumer-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task489-block-menu-delete-undo-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task488-local-attachment-link-delete-undo-smoke.js`。
|
||||
- 关键证据:`task487` result 显示 `data-mnote-tree-live-transport=\"local-folder-events\"`、PageTree/FileTree 外部创建/删除后 `treeLiveApplied=\"resync\"`、`navigationEvents=0`;`task451` 覆盖 accept-disk / keep-current / merge-save / agent-conflict-source;`task479` / `task489` / `task488` 均 `ok:true`。
|
||||
- 产物:`/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression/tmp/task487-local-folder-tree-live-consumer-smoke/result.json`、`/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression/tmp/task451-local-markdown-conflict-resolution-ui-smoke/result.json`、`/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression/tmp/task489-block-menu-delete-undo-smoke/result.json`、`/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression/tmp/task488-local-attachment-link-delete-undo-smoke/result.json`,以及 `task479` 的系统临时目录截图。
|
||||
|
||||
@@ -17,12 +17,29 @@ function clearSessionConflictSurface(session, deps) {
|
||||
? deps.sessionViews(session)
|
||||
: [];
|
||||
views.forEach(function(view) {
|
||||
var host = view.runtimeDescriptor.root.closest('.document-pane') || view.runtimeDescriptor.root;
|
||||
var host = conflictPanelHost(view && view.runtimeDescriptor && view.runtimeDescriptor.root);
|
||||
if (!(host instanceof HTMLElement)) return;
|
||||
host.querySelectorAll('[data-testid="mnote-editor-conflict-panel"]').forEach(function(node) { node.remove(); });
|
||||
});
|
||||
}
|
||||
|
||||
function conflictPanelHost(root) {
|
||||
if (!(root instanceof Element)) return null;
|
||||
return root.closest('.document-pane') || root;
|
||||
}
|
||||
|
||||
function mountSessionConflictPanel(root, panel) {
|
||||
var host = conflictPanelHost(root);
|
||||
if (!(host instanceof HTMLElement) || !(panel instanceof HTMLElement)) return false;
|
||||
var header = host.querySelector('.document-shell-header');
|
||||
if (header && header.parentNode) {
|
||||
header.parentNode.insertBefore(panel, header.nextSibling);
|
||||
} else {
|
||||
host.prepend(panel);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function createSessionConflictPanel(session, message, deps) {
|
||||
deps = deps || {};
|
||||
var externalConflictMessage = String(deps.externalConflictMessage || '').trim() || '文件已在外部修改';
|
||||
@@ -185,6 +202,7 @@ function runSessionConflictAction(action, deps) {
|
||||
window.__mnoteDocumentConflictPanelRuntime = {
|
||||
clearSessionConflictSurface: clearSessionConflictSurface,
|
||||
createSessionConflictPanel: createSessionConflictPanel,
|
||||
mountSessionConflictPanel: mountSessionConflictPanel,
|
||||
populateSessionConflictDiffPanel: populateSessionConflictDiffPanel,
|
||||
runSessionConflictAction: runSessionConflictAction
|
||||
};
|
||||
|
||||
@@ -2139,8 +2139,6 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
const renderSessionConflictSurface = (session, message) => {
|
||||
clearSessionConflictSurface(session);
|
||||
sessionViews(session).forEach((view) => {
|
||||
const host = view.runtimeDescriptor.root.closest('.document-pane') || view.runtimeDescriptor.root;
|
||||
if (!(host instanceof HTMLElement)) return;
|
||||
const runtime = window.__mnoteDocumentConflictPanelRuntime;
|
||||
const handleConflictActionError = (error) => {
|
||||
setSessionStatus(session, 'external-change-conflict', error instanceof Error ? error.message : String(error));
|
||||
@@ -2211,6 +2209,13 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
});
|
||||
}
|
||||
|
||||
if (runtime && typeof runtime.mountSessionConflictPanel === 'function') {
|
||||
if (runtime.mountSessionConflictPanel(view.runtimeDescriptor.root, panel)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const host = view.runtimeDescriptor.root.closest('.document-pane') || view.runtimeDescriptor.root;
|
||||
if (!(host instanceof HTMLElement)) return;
|
||||
const header = host.querySelector('.document-shell-header');
|
||||
if (header && header.parentNode) {
|
||||
header.parentNode.insertBefore(panel, header.nextSibling);
|
||||
@@ -5628,6 +5633,9 @@ mod tests {
|
||||
assert!(html.contains("mnote-conflict-open-diff"));
|
||||
assert!(html.contains("mnote-conflict-merge-text"));
|
||||
assert!(html.contains("mnote-conflict-merge-save"));
|
||||
assert!(
|
||||
html.contains("runtime.mountSessionConflictPanel(view.runtimeDescriptor.root, panel)")
|
||||
);
|
||||
assert!(html.contains("agent run"));
|
||||
assert!(!html.contains(
|
||||
"setInterval(() => {\n void pollLocalMarkdownExternalChange();\n }, 1200);"
|
||||
@@ -5665,9 +5673,10 @@ mod tests {
|
||||
assert!(DOCUMENT_CONFLICT_PANEL_RUNTIME_JS.contains("onUseCurrent"));
|
||||
assert!(DOCUMENT_CONFLICT_PANEL_RUNTIME_JS.contains("onUseDisk"));
|
||||
assert!(DOCUMENT_CONFLICT_PANEL_RUNTIME_JS.contains("onSaveMerge"));
|
||||
assert!(DOCUMENT_CONFLICT_PANEL_RUNTIME_JS.contains("function runSessionConflictAction"));
|
||||
assert!(DOCUMENT_CONFLICT_PANEL_RUNTIME_JS.contains("function mountSessionConflictPanel"));
|
||||
assert!(DOCUMENT_CONFLICT_PANEL_RUNTIME_JS
|
||||
.contains("runSessionConflictAction: runSessionConflictAction"));
|
||||
assert!(DOCUMENT_CONFLICT_PANEL_RUNTIME_JS.contains("function runSessionConflictAction"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user