feat: consolidate local-first mnote web runtime
This commit is contained in:
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user