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
@@ -1,7 +1,122 @@
// MNote 文件树上下文菜单运行时外置模块。
// 当前先承接 CommandContext 构造与 when 表达式求值,树语义仍由 Rust/kernel 主导。
function commandContextResourceKindForRow(row) {
if (!(row instanceof HTMLElement)) return 'unknown';
var rowKind = String(row.getAttribute('data-row-kind') || '').trim();
var assetType = String(row.getAttribute('data-asset-type') || '').trim();
var assetId = String(row.getAttribute('data-asset-id') || '').trim();
if (rowKind === 'folder' || rowKind === 'directory' || rowKind === 'asset-folder') return 'folder';
if (rowKind === 'document' || rowKind === 'index' || rowKind === 'markdown') return 'page';
if (assetType) return assetType;
if (assetId) return 'asset';
return rowKind || 'unknown';
}
function commandContextDirtyState(value) {
var state = String(value || '').trim();
if (!state) return 'clean';
var lowered = state.toLowerCase();
if (lowered === 'dirty' || lowered === 'stale' || lowered === 'deleted') return lowered;
if (lowered === 'externalmodified' || lowered === 'external_modified' || lowered === 'external-change-conflict') return 'external_modified';
if (lowered === 'hasexternalconflict' || lowered === 'has_external_conflict') return 'has_external_conflict';
return lowered === 'clean' || lowered === 'saved' ? 'clean' : lowered;
}
function commandContextIsDirtyState(value) {
return commandContextDirtyState(value) !== 'clean';
}
function commandContextEditorMatchesRow(editor, row) {
if (!editor || !(row instanceof HTMLElement)) return false;
var rowDocumentId = String(row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || '').trim();
if (rowDocumentId && String(editor.documentId || '').trim() === rowDocumentId) return true;
var rowAssetId = String(row.getAttribute('data-asset-id') || '').trim();
if (rowAssetId && String(editor.assetId || editor.workspacePath && editor.workspacePath.assetId || '').trim() === rowAssetId) return true;
var rowRelativePath = String(row.getAttribute('data-local-relative-path') || '').trim();
var editorRelativePath = String(editor.workspacePath && (editor.workspacePath.relativePath || editor.workspacePath.localRelativePath) || '').trim();
return Boolean(rowRelativePath && editorRelativePath && rowRelativePath === editorRelativePath);
}
function commandContextDirtyStateForRows(rows, openEditorsSnapshot, bufferState) {
if (bufferState && typeof bufferState === 'object') {
var directState = bufferState.dirtyState || bufferState.state || bufferState.status || '';
if (commandContextIsDirtyState(directState)) return commandContextDirtyState(directState);
}
var editors = [];
if (openEditorsSnapshot && Array.isArray(openEditorsSnapshot.editors)) editors = editors.concat(openEditorsSnapshot.editors);
if (openEditorsSnapshot && Array.isArray(openEditorsSnapshot.resourceEditors)) editors = editors.concat(openEditorsSnapshot.resourceEditors);
for (var r = 0; r < rows.length; r += 1) {
var row = rows[r];
for (var i = 0; i < editors.length; i += 1) {
var editor = editors[i];
if (!commandContextEditorMatchesRow(editor, row)) continue;
var editorState = editor.dirtyState || editor.dirtyGuard || '';
if (commandContextIsDirtyState(editorState)) return commandContextDirtyState(editorState);
}
}
return 'clean';
}
function selectedRowsFromDeps(deps, fallbackRow) {
var selection = deps && deps.selection ? deps.selection : {};
var rowIds = Array.isArray(selection.selectedRowIds)
? selection.selectedRowIds
: Array.from(selection.selectedRowIds || []);
var rows = [];
var queryRowById = deps && typeof deps.queryRowById === 'function'
? deps.queryRowById
: function() { return null; };
for (var i = 0; i < rowIds.length; i += 1) {
var row = queryRowById(rowIds[i]);
if (row instanceof HTMLElement) rows.push(row);
}
if (!rows.length && fallbackRow instanceof HTMLElement) rows.push(fallbackRow);
return rows;
}
function buildFileTreeCommandContext(row, openEditorsSnapshot, bufferState, deps) {
deps = deps || {};
var rows = selectedRowsFromDeps(deps, row);
var resourceKinds = {};
for (var j = 0; j < rows.length; j += 1) {
resourceKinds[commandContextResourceKindForRow(rows[j])] = true;
}
var rkKeys = Object.keys(resourceKinds);
var currentSourceKind = typeof deps.currentSourceKind === 'function'
? deps.currentSourceKind
: function() { return ''; };
var readonly = typeof deps.workspaceReadonly === 'function'
? deps.workspaceReadonly()
: false;
var dirtyState = commandContextDirtyStateForRows(rows, openEditorsSnapshot, bufferState);
var editorDirty = commandContextIsDirtyState(dirtyState);
var targetKind = commandContextResourceKindForRow(row);
return {
'workspace.sourceKind': currentSourceKind() || '',
'workspace.readonly': readonly === true,
'tree.focusKind': deps.focusKind || 'file_tree',
'tree.selectionCount': rows.length,
'tree.selectionResourceKind': rkKeys.length === 1 ? rkKeys[0] : rows.length ? 'mixed' : targetKind,
'tree.targetResourceKind': targetKind,
'tree.targetRowKind': row instanceof HTMLElement ? String(row.getAttribute('data-row-kind') || '').trim() : '',
'tree.targetIsFolder': targetKind === 'folder',
'tree.targetIsAsset': Boolean(row instanceof HTMLElement && String(row.getAttribute('data-asset-id') || '').trim()),
'editor.dirty': editorDirty,
'editor.dirtyState': dirtyState,
'editor.hasSelection': Boolean(deps.editorHasSelection),
'ai.canWrite': readonly !== true && !editorDirty,
};
}
function buildSidebarFileTreeContext(kind, deps) {
var targetRow = deps && deps.targetRow instanceof HTMLElement ? deps.targetRow : null;
return buildFileTreeCommandContext(targetRow, deps && deps.openEditorsSnapshot, deps && deps.bufferState, Object.assign({}, deps || {}, {
focusKind: kind === 'filetree' ? 'file_tree' : kind === 'page' ? 'page_tree' : kind,
}));
}
function buildLegacySidebarFileTreeContext(kind, deps) {
var selection = deps && deps.selection ? deps.selection : {};
var rowIds = Array.isArray(selection.selectedRowIds)
? selection.selectedRowIds
@@ -181,7 +296,9 @@ function tokenizeWhenExpression(expr) {
}
window.__mnoteFileTreeContextMenuRuntime = {
buildFileTreeCommandContext: buildFileTreeCommandContext,
buildSidebarFileTreeContext: buildSidebarFileTreeContext,
buildLegacySidebarFileTreeContext: buildLegacySidebarFileTreeContext,
evaluateSidebarFileTreeWhen: evaluateSidebarFileTreeWhen,
tokenizeWhenExpression: tokenizeWhenExpression
};