refactor: extract filetree preflight helpers

This commit is contained in:
lix-2026
2026-05-25 08:46:36 +08:00
parent 0245b09301
commit f0c1757c9f
3 changed files with 134 additions and 1 deletions
@@ -216,6 +216,16 @@ UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据
- 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。 - 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。 - 边界:未迁 `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` - 已验证:`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-25Batch AM 只派发 1 个 Reasonix worker 审计 filetree preflight helper 边界,使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-am-worker-a-3-20-filetree-preflight-readonly`run id `reasonix-2026-05-25T00-39-31-251Z-c2c08634`,显式 `--no-memory-recall`
- Codex 已读取 Hindsight recall,并核对 worktree `git status --short` / `git diff --stat` 为空;但该 run 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`,没有 `result.json``process-handoff.md/json``final.md`,因此按合同拒收,不把其文字过程当正式 handoff 采纳。
- Codex 主控基于本地代码证据收口下一刀:`filetree-runtime.js` 新增 `fileTreeRowsForUploadPreflight(deps)``fileTreeDocumentParentsForPreflight(deps)``fileTreeTargetChildrenForPreflight(targetRow, deps)``fileTreeDropPreflightRows(deps)``layout.rs` 同名函数优先委托 runtime,并保留完整 inline fallback。
- 边界:未迁 `ensureFileTreeWritableTarget``/api/tree/filetree/drop-preflight` fetch、`resolveFileTreeUploadTarget``refreshLocalFolderSidebarSnapshot` 或更高层 upload/drop 编排;本批只外置 filetree preflight 的 DOM 行遍历与属性采集层。
- 已验证:`node --check rust/crates/mnote-web/browser/filetree-runtime.js``git diff --check -- design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md rust/crates/mnote-web/browser/filetree-runtime.js rust/crates/mnote-web/src/ssr/pages/layout.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 filetree_runtime_contains_row_accessor_helpers -- --test-threads=1`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3034 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3034 cargo run --manifest-path rust/Cargo.toml -p mnote-web``MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3034 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`
- 2026-05-25:主控复核当前 `3-20` 相关事实源后,确认下一刀应继续保持只读审计,不与主工作区现有 `5-29` 改动混写。
- 当前结论:`filetree-runtime.js` 的 preflight DOM helper 已在主链落地;`resolveFileTreeUploadTarget``refreshLocalFolderSidebarSnapshot`、selection runtime 与 conflict panel 业务编排边界维持不动。
- 2026-05-25Batch BA 计划只派发 1 个只读 Reasonix worker 审计 filetree preflight helper 的进一步收口边界。
- 目标:围绕 `fileTreeRowsForUploadPreflight``fileTreeDocumentParentsForPreflight``fileTreeTargetChildrenForPreflight``fileTreeDropPreflightRows`,确认是否还需要抽共享 row reader / row title helper,还是当前粒度已经足够;同时明确哪些依赖必须留在 `layout.rs` 壳层。
- 约束:只读、独立 worktree、源码零 diff;不得建议整体迁出 `ensureFileTreeWritableTarget`、preflight fetch、`resolveFileTreeUploadTarget` 或 sidebar refresh 编排。
- 2026-05-25Batch 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` - 2026-05-25Batch 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 未修改源码。 - 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` - 实测通过:`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`
@@ -371,6 +371,94 @@ function fileTreeUploadTargetFallback(detail, deps) {
}; };
} }
function fileTreeRowsForUploadPreflight(deps) {
deps = deps || {};
var fileTreeRowKind = typeof deps.fileTreeRowKind === 'function' ? deps.fileTreeRowKind : function(row) {
return row instanceof HTMLElement ? String(row.getAttribute('data-row-kind') || '').trim() : '';
};
var fileTreeRowDocumentId = typeof deps.fileTreeRowDocumentId === 'function' ? deps.fileTreeRowDocumentId : function(row) {
return row instanceof HTMLElement ? String(row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || '').trim() : '';
};
var fileTreeRowAssetId = typeof deps.fileTreeRowAssetId === 'function' ? deps.fileTreeRowAssetId : function(row) {
return row instanceof HTMLElement ? String(row.getAttribute('data-asset-id') || '').trim() : '';
};
return Array.from(document.querySelectorAll('.tree-row[data-shell-mode="filetree"]')).map(function(row) {
var documentId = fileTreeRowDocumentId(row);
var assetId = fileTreeRowAssetId(row) || null;
var badge = row.querySelector('.tree-kind-badge');
return {
rowId: row.getAttribute('data-row-id') || '',
rowKind: fileTreeRowKind(row),
documentId: documentId || null,
assetId: assetId,
assetDocumentId: documentId || null,
assetType: badge ? badge.getAttribute('data-kind') : null,
storagePath: null
};
}).filter(function(row) {
return row.rowId || row.documentId || row.assetId;
});
}
function fileTreeDocumentParentsForPreflight(deps) {
deps = deps || {};
var fileTreeRowDocumentId = typeof deps.fileTreeRowDocumentId === 'function' ? deps.fileTreeRowDocumentId : function(row) {
return row instanceof HTMLElement ? String(row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || '').trim() : '';
};
return Array.from(document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"]')).map(function(row) {
var documentId = fileTreeRowDocumentId(row);
if (!documentId) return null;
var parentRowId = row.getAttribute('data-parent-id') || '';
var parentDocumentId = parentRowId.indexOf('doc:') === 0 ? parentRowId.slice(4) : parentRowId || null;
return { documentId: documentId, parentId: parentDocumentId };
}).filter(Boolean);
}
function fileTreeTargetChildrenForPreflight(targetRow, deps) {
deps = deps || {};
var fileTreeRowKind = typeof deps.fileTreeRowKind === 'function' ? deps.fileTreeRowKind : function(row) {
return row instanceof HTMLElement ? String(row.getAttribute('data-row-kind') || '').trim() : '';
};
var fileTreeRowDocumentId = typeof deps.fileTreeRowDocumentId === 'function' ? deps.fileTreeRowDocumentId : function(row) {
return row instanceof HTMLElement ? String(row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || '').trim() : '';
};
var fileTreeRowAssetId = typeof deps.fileTreeRowAssetId === 'function' ? deps.fileTreeRowAssetId : function(row) {
return row instanceof HTMLElement ? String(row.getAttribute('data-asset-id') || '').trim() : '';
};
var rowTitle = typeof deps.rowTitle === 'function' ? deps.rowTitle : function(row) {
if (!(row instanceof HTMLElement)) return '';
var titleNode = row.querySelector('.tree-link-title');
return String(titleNode && titleNode.textContent || row.getAttribute('title') || '').trim();
};
var node = targetRow instanceof HTMLElement ? targetRow.closest('.tree-node') : null;
if (!node) return [];
return Array.from(node.querySelectorAll(':scope > .tree-children > .tree-node > .tree-row[data-shell-mode="filetree"]')).map(function(row) {
return {
rowKind: fileTreeRowKind(row),
documentId: fileTreeRowDocumentId(row) || null,
assetId: fileTreeRowAssetId(row) || null,
title: rowTitle(row)
};
});
}
function fileTreeDropPreflightRows(deps) {
deps = deps || {};
var cssEscape = typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value).replace(/["\\]/g, '\\$&'); };
var rowTitle = typeof deps.rowTitle === 'function' ? deps.rowTitle : function(row) {
if (!(row instanceof HTMLElement)) return '';
var titleNode = row.querySelector('.tree-link-title');
return String(titleNode && titleNode.textContent || row.getAttribute('title') || '').trim();
};
var rows = typeof deps.fileTreeRowsForUploadPreflight === 'function'
? deps.fileTreeRowsForUploadPreflight()
: fileTreeRowsForUploadPreflight(deps);
return rows.map(function(row) {
var domRow = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(row.rowId) + '"]');
return Object.assign({}, row, { title: rowTitle(domRow) });
});
}
window.__mnoteFileTreeRuntime = { window.__mnoteFileTreeRuntime = {
readProjection: readProjection, readProjection: readProjection,
readSidebarDataset: readSidebarDataset, readSidebarDataset: readSidebarDataset,
@@ -400,5 +488,9 @@ window.__mnoteFileTreeRuntime = {
revealFileTreeRow: revealFileTreeRow, revealFileTreeRow: revealFileTreeRow,
revealFileTreeAssetRow: revealFileTreeAssetRow, revealFileTreeAssetRow: revealFileTreeAssetRow,
appendUploadedAssetRow: appendUploadedAssetRow, appendUploadedAssetRow: appendUploadedAssetRow,
fileTreeUploadTargetFallback: fileTreeUploadTargetFallback fileTreeUploadTargetFallback: fileTreeUploadTargetFallback,
fileTreeRowsForUploadPreflight: fileTreeRowsForUploadPreflight,
fileTreeDocumentParentsForPreflight: fileTreeDocumentParentsForPreflight,
fileTreeTargetChildrenForPreflight: fileTreeTargetChildrenForPreflight,
fileTreeDropPreflightRows: fileTreeDropPreflightRows
}; };
@@ -2966,6 +2966,8 @@ const SIDEBAR_TREE_JS: &str = r##"
}); });
function fileTreeRowsForUploadPreflight() { function fileTreeRowsForUploadPreflight() {
var runtimeFn = fileTreeRuntimeFunction('fileTreeRowsForUploadPreflight');
if (runtimeFn) return runtimeFn(fileTreeRuntimeDeps());
return Array.from(document.querySelectorAll('.tree-row[data-shell-mode="filetree"]')).map(function(row) { return Array.from(document.querySelectorAll('.tree-row[data-shell-mode="filetree"]')).map(function(row) {
return { return {
rowId: row.getAttribute('data-row-id') || '', rowId: row.getAttribute('data-row-id') || '',
@@ -3031,6 +3033,8 @@ const SIDEBAR_TREE_JS: &str = r##"
} }
function fileTreeDocumentParentsForPreflight() { function fileTreeDocumentParentsForPreflight() {
var runtimeFn = fileTreeRuntimeFunction('fileTreeDocumentParentsForPreflight');
if (runtimeFn) return runtimeFn(fileTreeRuntimeDeps());
return Array.from(document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"]')).map(function(row) { return Array.from(document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"]')).map(function(row) {
var documentId = row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || ''; var documentId = row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || '';
if (!documentId) return null; if (!documentId) return null;
@@ -3041,6 +3045,8 @@ const SIDEBAR_TREE_JS: &str = r##"
} }
function fileTreeTargetChildrenForPreflight(targetRow) { function fileTreeTargetChildrenForPreflight(targetRow) {
var runtimeFn = fileTreeRuntimeFunction('fileTreeTargetChildrenForPreflight');
if (runtimeFn) return runtimeFn(targetRow, fileTreeRuntimeDeps());
var node = targetRow instanceof HTMLElement ? targetRow.closest('.tree-node') : null; var node = targetRow instanceof HTMLElement ? targetRow.closest('.tree-node') : null;
if (!node) return []; if (!node) return [];
return Array.from(node.querySelectorAll(':scope > .tree-children > .tree-node > .tree-row[data-shell-mode="filetree"]')).map(function(row) { return Array.from(node.querySelectorAll(':scope > .tree-children > .tree-node > .tree-row[data-shell-mode="filetree"]')).map(function(row) {
@@ -3054,6 +3060,10 @@ const SIDEBAR_TREE_JS: &str = r##"
} }
function fileTreeDropPreflightRows() { function fileTreeDropPreflightRows() {
var runtimeFn = fileTreeRuntimeFunction('fileTreeDropPreflightRows');
if (runtimeFn) return runtimeFn(Object.assign({}, fileTreeRuntimeDeps(), {
fileTreeRowsForUploadPreflight: fileTreeRowsForUploadPreflight
}));
return fileTreeRowsForUploadPreflight().map(function(row) { return fileTreeRowsForUploadPreflight().map(function(row) {
var domRow = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(row.rowId) + '"]'); var domRow = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(row.rowId) + '"]');
row.title = rowTitle(domRow); row.title = rowTitle(domRow);
@@ -11099,6 +11109,14 @@ mod tests {
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('revealFileTreeAssetRow')")); assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('revealFileTreeAssetRow')"));
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('appendUploadedAssetRow')")); assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('appendUploadedAssetRow')"));
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('fileTreeUploadTargetFallback')")); assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('fileTreeUploadTargetFallback')"));
assert!(
SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('fileTreeRowsForUploadPreflight')")
);
assert!(SIDEBAR_TREE_JS
.contains("fileTreeRuntimeFunction('fileTreeDocumentParentsForPreflight')"));
assert!(SIDEBAR_TREE_JS
.contains("fileTreeRuntimeFunction('fileTreeTargetChildrenForPreflight')"));
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('fileTreeDropPreflightRows')"));
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('readProjection')")); assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('readProjection')"));
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('readSidebarDataset')")); assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('readSidebarDataset')"));
assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('readDatasetProjection')")); assert!(SIDEBAR_TREE_JS.contains("fileTreeRuntimeFunction('readDatasetProjection')"));
@@ -11160,6 +11178,10 @@ mod tests {
assert!(FILETREE_RUNTIME_JS.contains("function revealFileTreeAssetRow")); assert!(FILETREE_RUNTIME_JS.contains("function revealFileTreeAssetRow"));
assert!(FILETREE_RUNTIME_JS.contains("function appendUploadedAssetRow")); assert!(FILETREE_RUNTIME_JS.contains("function appendUploadedAssetRow"));
assert!(FILETREE_RUNTIME_JS.contains("function fileTreeUploadTargetFallback")); assert!(FILETREE_RUNTIME_JS.contains("function fileTreeUploadTargetFallback"));
assert!(FILETREE_RUNTIME_JS.contains("function fileTreeRowsForUploadPreflight"));
assert!(FILETREE_RUNTIME_JS.contains("function fileTreeDocumentParentsForPreflight"));
assert!(FILETREE_RUNTIME_JS.contains("function fileTreeTargetChildrenForPreflight"));
assert!(FILETREE_RUNTIME_JS.contains("function fileTreeDropPreflightRows"));
assert!(FILETREE_RUNTIME_JS.contains("function readProjection")); assert!(FILETREE_RUNTIME_JS.contains("function readProjection"));
assert!(FILETREE_RUNTIME_JS.contains("function readSidebarDataset")); assert!(FILETREE_RUNTIME_JS.contains("function readSidebarDataset"));
assert!(FILETREE_RUNTIME_JS.contains("function readDatasetProjection")); assert!(FILETREE_RUNTIME_JS.contains("function readDatasetProjection"));
@@ -11182,6 +11204,15 @@ mod tests {
.contains("applyLocalFolderSidebarSnapshot: applyLocalFolderSidebarSnapshot")); .contains("applyLocalFolderSidebarSnapshot: applyLocalFolderSidebarSnapshot"));
assert!(FILETREE_RUNTIME_JS assert!(FILETREE_RUNTIME_JS
.contains("markLocalFolderWatchApplied: markLocalFolderWatchApplied")); .contains("markLocalFolderWatchApplied: markLocalFolderWatchApplied"));
assert!(FILETREE_RUNTIME_JS
.contains("fileTreeRowsForUploadPreflight: fileTreeRowsForUploadPreflight"));
assert!(FILETREE_RUNTIME_JS
.contains("fileTreeDocumentParentsForPreflight: fileTreeDocumentParentsForPreflight"));
assert!(FILETREE_RUNTIME_JS
.contains("fileTreeTargetChildrenForPreflight: fileTreeTargetChildrenForPreflight"));
assert!(
FILETREE_RUNTIME_JS.contains("fileTreeDropPreflightRows: fileTreeDropPreflightRows")
);
assert!(FILETREE_RUNTIME_JS assert!(FILETREE_RUNTIME_JS
.contains("uploadIntent: String(detail.uploadIntent || 'filetree.folder.drop')")); .contains("uploadIntent: String(detail.uploadIntent || 'filetree.folder.drop')"));
assert!(FILETREE_RUNTIME_JS assert!(FILETREE_RUNTIME_JS