feat: consolidate local-first mnote web runtime

This commit is contained in:
lix-2026
2026-05-28 22:01:44 +08:00
parent 7354807ee9
commit 39b9a0183a
154 changed files with 13591 additions and 12728 deletions
@@ -70,6 +70,57 @@ function fileTreeRowLocalUploadTargetRelativePath(row, deps) {
return lastSlash >= 0 ? relativePath.slice(0, lastSlash) : '';
}
function parseObjectIdentityAttr(value) {
var raw = String(value || '').trim();
if (!raw) return null;
try {
var parsed = JSON.parse(raw);
return parsed && typeof parsed === 'object' ? parsed : { raw: raw };
} catch (_) {
return { raw: raw };
}
}
function readWorkspacePathFromRow(row, deps) {
if (!(row instanceof HTMLElement)) return null;
deps = deps || {};
var currentSourceKind = typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; };
var currentRootUri = typeof deps.currentRootUri === 'function' ? deps.currentRootUri : function() { return ''; };
var resolveWorkspaceId = typeof deps.resolveWorkspaceId === 'function' ? deps.resolveWorkspaceId : function() { return ''; };
var rowTitle = typeof deps.rowTitle === 'function' ? deps.rowTitle : function(target) {
return String(target && target.textContent || '').trim();
};
var objectIdentityRaw = String(row.getAttribute('data-object-identity') || '').trim();
var objectIdentity = parseObjectIdentityAttr(objectIdentityRaw);
var objectKind = String(row.getAttribute('data-object-kind') || (objectIdentity && objectIdentity.objectKind) || '').trim();
var relativePath = fileTreeRowLocalRelativePath(row, deps);
var assetId = fileTreeRowAssetId(row, deps);
var documentId = fileTreeRowDocumentId(row);
var rowId = String(row.getAttribute('data-row-id') || '').trim();
var rowKind = fileTreeRowKind(row);
var sourceKind = String(row.getAttribute('data-source-kind') || currentSourceKind() || '').trim();
var rootUri = String(row.getAttribute('data-root-uri') || currentRootUri() || '').trim();
return {
schema: 'mnote.workspace_path.v1',
workspaceId: String(row.getAttribute('data-workspace-id') || resolveWorkspaceId(row) || '').trim(),
sourceKind: sourceKind,
rootUri: rootUri,
relativePath: relativePath,
localRelativePath: relativePath,
objectIdentity: objectIdentity,
objectIdentityRaw: objectIdentityRaw,
objectKind: objectKind,
resourceKind: objectKind || rowKind,
rowId: rowId,
rowKind: rowKind,
documentId: documentId,
assetId: assetId,
title: rowTitle(row),
href: String(row.querySelector?.('.tree-link')?.getAttribute?.('href') || '').trim(),
isLocalFolder: sourceKind === 'local_folder' || (!sourceKind && (rowId.indexOf('local:') === 0 || documentId.indexOf('local-md:') === 0 || documentId.indexOf('local-dir:') === 0))
};
}
function isFileTreeDownloadableAssetRow(row, deps) {
if (!(row instanceof HTMLElement)) return false;
var kind = fileTreeRowKind(row);
@@ -98,6 +149,7 @@ function fileTreeAssetDownloadDetail(row, deps) {
rowKind: fileTreeRowKind(row),
assetId: assetId,
localRelativePath: relativePath,
workspacePath: readWorkspacePathFromRow(row, deps),
title: title,
fileName: title,
workspaceId: resolveWorkspaceId(row)
@@ -274,6 +326,49 @@ function revealFileTreeAssetRow(assetId, deps) {
return true;
}
function localUploadedAssetRelativePath(asset, deps) {
deps = deps || {};
var direct = String(asset && (asset.rootRelativePath || asset.root_relative_path || asset.relativePath || asset.relative_path) || '').trim();
if (direct) return direct;
var localFilePathFromAssetId = typeof deps.localFilePathFromAssetId === 'function'
? deps.localFilePathFromAssetId
: function(assetId) {
var value = String(assetId || '').trim();
return value.indexOf('local-file:') === 0
? value.slice('local-file:'.length)
: value.indexOf('local:asset:') === 0
? value.slice('local:asset:'.length)
: '';
};
return localFilePathFromAssetId(String(asset && asset.id || '').trim());
}
function parentRelativePathForUploadedAsset(asset, relativePath) {
var target = String(asset && (asset.targetRelativePath || asset.target_relative_path) || '').trim();
if (target || Object.prototype.hasOwnProperty.call(asset || {}, 'targetRelativePath')) return target;
var normalized = String(relativePath || '').trim().replace(/^\/+|\/+$/g, '');
var lastSlash = normalized.lastIndexOf('/');
return lastSlash >= 0 ? normalized.slice(0, lastSlash) : '';
}
function findLocalFileTreeParentRowForAsset(asset, relativePath, deps) {
deps = deps || {};
var cssEscape = typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value).replace(/["\\]/g, '\\$&'); };
var currentSourceKind = typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; };
if (currentSourceKind() !== 'local_folder') return null;
var parentRelativePath = parentRelativePathForUploadedAsset(asset, relativePath);
var selector = '#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="' + cssEscape(parentRelativePath) + '"]';
if (parentRelativePath) {
var parentRow = document.querySelector(selector);
if (parentRow instanceof HTMLElement) return parentRow;
}
if (!parentRelativePath) {
var root = document.querySelector('#sidebar-file-tree-root .tree-root');
return root instanceof HTMLElement ? null : null;
}
return null;
}
function appendUploadedAssetRow(asset, documentId, deps) {
deps = deps || {};
var cssEscape = typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value).replace(/["\\]/g, '\\$&'); };
@@ -295,8 +390,10 @@ function appendUploadedAssetRow(asset, documentId, deps) {
if (!objectKind && (String(asset && (asset.asset_type || asset.assetType) || '').trim() === 'mindmap' || /\.mindmap\.json$/i.test(assetId))) {
objectKind = 'mindmap';
}
var targetDocumentId = String(documentId || asset.document_id || asset.documentId || currentDocumentId() || '').trim();
var parentRow = targetDocumentId
var localRelativePath = localUploadedAssetRelativePath(asset, deps);
var targetDocumentId = String(documentId || asset.document_id || asset.documentId || asset.ownerDocumentId || currentDocumentId() || '').trim();
var parentRow = findLocalFileTreeParentRowForAsset(asset, localRelativePath, deps);
if (!parentRow) parentRow = targetDocumentId
? document.querySelector('.tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + targetDocumentId) + '"]')
: null;
if (!parentRow && currentSourceKind() === 'local_folder' && objectKind === 'mindmap') return false;
@@ -325,16 +422,20 @@ function appendUploadedAssetRow(asset, documentId, deps) {
objectKind: objectKind || 'attachment',
documentId: targetDocumentId || null,
blockId: null,
assetId: assetId
assetId: assetId,
rootUri: String(asset && (asset.rootUri || asset.root_uri) || '').trim() || null,
relativePath: localRelativePath || null
};
li.setAttribute('data-node-id', 'asset:' + assetId);
var title = uploadedAssetTitle(asset);
var iconKind = fileTreeIconKindForFileName(title) || uploadedAssetType(asset) || 'file';
if (objectKind === 'mindmap') iconKind = 'mindmap';
var ownerDocumentId = String(asset && (asset.ownerDocumentId || asset.owner_document_id) || targetDocumentId || '').trim();
var rootUri = String(asset && (asset.rootUri || asset.root_uri) || '').trim();
li.innerHTML =
'<div class="tree-row" role="treeitem" aria-level="' + (parentRow ? '2' : '1') + '" aria-expanded="false" data-rust-rendered-row="filetree" data-testid="filetree-asset-row" data-row-id="' + escapeHtml('asset:' + assetId) + '" data-row-kind="asset" data-node-id="' + escapeHtml('asset:' + assetId) + '" data-document-id="' + escapeHtml(targetDocumentId) + '" data-doc-id="' + escapeHtml(targetDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '" data-object-identity="' + escapeHtml(objectIdentityAttr(objectIdentity)) + '" data-object-kind="' + escapeHtml(objectKind || 'attachment') + '" data-shell-mode="filetree" data-selected="false" data-active="false" draggable="true">' +
'<div class="tree-row" role="treeitem" aria-level="' + (parentRow ? '2' : '1') + '" aria-expanded="false" data-rust-rendered-row="filetree" data-testid="filetree-asset-row" data-row-id="' + escapeHtml('asset:' + assetId) + '" data-row-kind="asset" data-node-id="' + escapeHtml('asset:' + assetId) + '" data-document-id="' + escapeHtml(targetDocumentId) + '" data-doc-id="' + escapeHtml(targetDocumentId) + '" data-owner-document-id="' + escapeHtml(ownerDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '" data-local-relative-path="' + escapeHtml(localRelativePath) + '" data-root-uri="' + escapeHtml(rootUri) + '" data-source-kind="' + escapeHtml(rootUri ? 'local_folder' : '') + '" data-object-identity="' + escapeHtml(objectIdentityAttr(objectIdentity)) + '" data-object-kind="' + escapeHtml(objectKind || 'attachment') + '" data-shell-mode="filetree" data-selected="false" data-active="false" draggable="true">' +
'<span class="tree-spacer" aria-hidden="true"></span><span class="tree-kind-badge" data-kind="' + escapeHtml(iconKind) + '" aria-hidden="true"></span>' +
'<button type="button" class="tree-link" data-rust-action="open" data-row-id="' + escapeHtml('asset:' + assetId) + '" data-document-id="' + escapeHtml(targetDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '"><span class="tree-link-title">' + escapeHtml(title) + '</span></button>' +
'<button type="button" class="tree-link" data-rust-action="open" data-row-id="' + escapeHtml('asset:' + assetId) + '" data-document-id="' + escapeHtml(targetDocumentId) + '" data-owner-document-id="' + escapeHtml(ownerDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '" data-local-relative-path="' + escapeHtml(localRelativePath) + '"><span class="tree-link-title">' + escapeHtml(title) + '</span></button>' +
'<div class="tree-actions"><button type="button" class="tree-action" data-testid="filetree-action-menu" data-rust-action="menu" data-row-id="' + escapeHtml('asset:' + assetId) + '" aria-label="更多操作">…</button></div></div>';
container.appendChild(li);
revealFileTreeRow(li.querySelector('.tree-row'));
@@ -584,6 +685,7 @@ window.__mnoteFileTreeRuntime = {
decodeLocalEncodedPath: decodeLocalEncodedPath,
fileTreeRowLocalRelativePath: fileTreeRowLocalRelativePath,
fileTreeRowLocalUploadTargetRelativePath: fileTreeRowLocalUploadTargetRelativePath,
readWorkspacePathFromRow: readWorkspacePathFromRow,
isFileTreeDownloadableAssetRow: isFileTreeDownloadableAssetRow,
isFileTreeDownloadableRow: isFileTreeDownloadableRow,
fileTreeAssetDownloadDetail: fileTreeAssetDownloadDetail,