refactor: split tree shell filetree helpers

This commit is contained in:
lix-2026
2026-05-26 02:53:12 +08:00
parent b8a067ce9f
commit 19642cdd7b
6 changed files with 206 additions and 172 deletions
@@ -10,6 +10,16 @@ import {
createTreeShellActionButton,
createTreeShellKindBadge as createKindBadge,
} from "./tree-shell-icons-runtime.js";
import {
buildFileTreeRuntimeEnvironment,
compareTreeShellItems,
getFileTreeRowAssetId,
getFileTreeRowDocumentId,
getFileTreeRowIconKind,
getFileTreeRowMetaLabel,
getFileTreeRowOwnerDocumentId,
normalizeTreeShellTreeItems,
} from "./tree-shell-filetree-runtime.js";
import {
normalizeTreeShellNumber as normalizeNumber,
normalizeTreeShellStringArray as normalizeStringArray,
@@ -20,22 +30,6 @@ import {
resolveTreeShellTargetOrigin,
} from "./tree-shell-state-runtime.js";
function buildFileTreeRuntimeEnvironment(runtimeContext) {
const fileTreeRowById = runtimeContext.fileTreeRowById || new Map();
return {
visibleRowIds: Array.isArray(runtimeContext.visibleFileTreeRowIds)
? runtimeContext.visibleFileTreeRowIds
: [],
rows: Array.from(fileTreeRowById.values()).map((entry) => ({
rowId: entry.rowId,
rowKind: entry.rowKind,
documentId: entry.documentId || null,
assetId: entry.assetId || null,
})),
rootUri: runtimeContext.sourceKind === "local_folder" ? runtimeContext.rootUri || null : null,
};
}
function startTreeShellRuntime() {
const stateElement = document.getElementById("tree-shell-state");
const appElement = document.getElementById("tree-shell-app");
@@ -173,98 +167,8 @@ function startTreeShellRuntime() {
"/api/tree/runtime/reduce",
);
const normalizeParent = (value) => {
const normalized = normalizeText(value);
return normalized || null;
};
const normalizeRowKind = (value) => {
const normalized = normalizeText(value).toLowerCase();
if (normalized === "index") return "index";
if (normalized === "asset") return "asset";
if (normalized === "folder") return "folder";
if (normalized === "markdown") return "markdown";
if (normalized === "asset_folder") return "asset_folder";
return "document";
};
const normalizeCapabilities = (value) =>
Array.isArray(value)
? value
.map((item) => normalizeText(item))
.filter(Boolean)
: [];
const normalizeResourceMeta = (value) => {
if (!value || typeof value !== "object") {
return {
resourceKind: "",
documentId: "",
assetId: "",
assetKind: "",
objectIdentity: null,
blockAssetRelation: null,
};
}
const objectIdentity =
value?.objectIdentity && typeof value.objectIdentity === "object"
? value.objectIdentity
: null;
const blockAssetRelation =
value?.blockAssetRelation && typeof value.blockAssetRelation === "object"
? value.blockAssetRelation
: null;
return {
resourceKind: normalizeText(value?.resourceKind),
documentId: normalizeText(value?.documentId),
assetId: normalizeText(value?.assetId),
assetKind: normalizeText(value?.assetKind),
objectIdentity,
blockAssetRelation,
};
};
const compareItems = (left, right) => {
const byPosition = left.position - right.position;
if (byPosition !== 0) return byPosition;
return left.title.localeCompare(right.title, "zh-CN");
};
const normalizeTreeItems = (items) =>
Array.isArray(items)
? items
.map((item) => {
const nodeId = normalizeText(item?.nodeId);
const resourceMeta = normalizeResourceMeta(item?.resourceMeta);
const rowKind = normalizeRowKind(item?.rowKind);
const fallbackRowId =
rowKind === "index"
? `index:${resourceMeta.documentId || nodeId.replace(/^index:/, "")}`
: rowKind === "asset"
? `asset:${resourceMeta.assetId || nodeId.replace(/^asset:/, "")}`
: rowKind === "asset_folder"
? `asset-folder:${resourceMeta.assetId || nodeId.replace(/^asset-folder:/, "")}`
: `doc:${resourceMeta.documentId || nodeId}`;
return {
rowId: normalizeText(item?.rowId, fallbackRowId),
rowKind,
nodeId,
parentNodeId: normalizeParent(item?.parentNodeId),
title: normalizeText(item?.title, rowKind === "index" ? "index.md" : "无标题"),
depth: normalizeNumber(item?.depth, 0),
childCount: normalizeNumber(item?.childCount, 0),
position: normalizeNumber(item?.position),
expandedByDefault: item?.expandedByDefault !== false,
iconHint: normalizeText(item?.iconHint),
capabilities: normalizeCapabilities(item?.capabilities),
resourceMeta,
};
})
.filter((item) => item.nodeId && !excludedIds.has(item.nodeId))
: [];
let rawItems = Array.isArray(hostOverride.items) ? hostOverride.items : state.items;
let normalizedItems = normalizeTreeItems(rawItems);
let normalizedItems = normalizeTreeShellTreeItems(rawItems, { excludedIds });
let itemById = new Map();
let fileTreeRowById = new Map();
let childrenByParentId = new Map();
@@ -301,8 +205,8 @@ function startTreeShellRuntime() {
bucket.push(item);
childrenByParentId.set(parentId, bucket);
});
roots.sort(compareItems);
childrenByParentId.forEach((bucket) => bucket.sort(compareItems));
roots.sort(compareTreeShellItems);
childrenByParentId.forEach((bucket) => bucket.sort(compareTreeShellItems));
};
rebuildTreeIndexes();
@@ -448,64 +352,6 @@ function startTreeShellRuntime() {
const FILETREE_DRAG_MIME = "application/x-mnote-filetree-row-ids";
const getFileTreeRowDocumentId = (item) => {
if (!item) return "";
if (item.rowKind === "document" || item.rowKind === "markdown") return item.nodeId;
if (item.rowKind === "index") return item.nodeId.replace(/^index:/, "");
return "";
};
const getFileTreeRowOwnerDocumentId = (item) => {
if (!item) return "";
if (item.resourceMeta?.documentId) return item.resourceMeta.documentId;
return getFileTreeRowDocumentId(item);
};
const getFileTreeRowAssetId = (item) => {
if (!item) return "";
if (item.resourceMeta?.assetId) return item.resourceMeta.assetId;
if (item.rowKind === "asset") return item.nodeId.replace(/^asset:/, "");
if (item.rowKind === "asset_folder") return item.nodeId.replace(/^asset-folder:/, "");
return "";
};
const getFileTreeRowIconKind = (item) => {
if (!item) return "file";
const iconHint = normalizeText(item.iconHint).toLowerCase();
if (iconHint === "mindmap") return "mindmap";
if (iconHint === "table") return "table";
if (iconHint === "index") return "index";
if (iconHint === "page") return "page";
if (item.rowKind === "document") return "page";
if (item.rowKind === "folder") return "folder";
if (item.rowKind === "markdown") return "file";
if (item.rowKind === "index") return "index";
if (item.rowKind === "asset_folder") return "mindmap";
if (item.resourceMeta?.resourceKind === "table") return "table";
if (item.resourceMeta?.resourceKind === "mindmap") return "mindmap";
return "file";
};
const getFileTreeRowMetaLabel = (item) => {
if (!item) return "";
if (item.rowKind === "document") {
return `${getFileTreeRowDocumentId(item)} · 页面`;
}
if (item.rowKind === "folder") {
return "本地文件夹";
}
if (item.rowKind === "markdown") {
return `${getFileTreeRowDocumentId(item)} · Markdown`;
}
if (item.rowKind === "index") {
return "页面正文";
}
if (item.rowKind === "asset_folder") {
return `${item.resourceMeta?.resourceKind || "mindmap"} · 资源目录`;
}
return `${item.resourceMeta?.resourceKind || item.resourceMeta?.assetKind || "asset"} · ${getFileTreeRowAssetId(item)}`;
};
const canExpandFileTreeRow = (item) => {
if (!item) return false;
return item.childCount > 0 || item.capabilities.includes("expand");
@@ -1522,7 +1368,7 @@ function startTreeShellRuntime() {
mindmapAssets = Array.isArray(nextState.mindmapAssets) ? nextState.mindmapAssets : [];
tableAssets = Array.isArray(nextState.tableAssets) ? nextState.tableAssets : [];
rawItems = Array.isArray(nextState.items) ? nextState.items : [];
normalizedItems = normalizeTreeItems(rawItems);
normalizedItems = normalizeTreeShellTreeItems(rawItems, { excludedIds });
rebuildTreeIndexes();
if (currentActiveDocumentId && !itemById.has(currentActiveDocumentId)) {
currentActiveDocumentId = roots[0]?.nodeId || "";
@@ -1573,14 +1419,14 @@ function startTreeShellRuntime() {
if (parentId) {
const bucket = childrenByParentId.get(parentId) || [];
bucket.push(item);
bucket.sort(compareItems);
bucket.sort(compareTreeShellItems);
childrenByParentId.set(parentId, bucket);
const parentItem = itemById.get(parentId);
if (parentItem) parentItem.childCount = Math.max(parentItem.childCount || 0, bucket.length);
expanded.add(parentId);
} else {
roots.push(item);
roots.sort(compareItems);
roots.sort(compareTreeShellItems);
}
return true;
};