收口 MNote P0 P1 P2 审查尾项

- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目
- 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线
- 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径

验证:
- cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1
- cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1
- git diff --check
- git diff --cached --check
- codegraph index . --force && codegraph status .
- codegraph sync . && codegraph status .
This commit is contained in:
lix-2026
2026-06-01 09:29:12 +08:00
parent 49a0545148
commit 1882db7681
143 changed files with 29810 additions and 3228 deletions
@@ -64,6 +64,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
const resourceTabMru = { primary: [], secondary: [] };
const resourceTabMruMax = 20;
const resourceTabCloseGuardAttribute = 'data-resource-tab-close-guarded';
let onlyofficeBridgeReadyListenerBound = false;
const normalizePaneRole = (paneRole) => String(paneRole || '').trim() === 'secondary' ? 'secondary' : 'primary';
@@ -127,6 +128,30 @@ export const createResourceTabRuntime = (dependencies = {}) => {
}
};
const currentFileTreeWorkspacePath = () => {
try {
const row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path]');
const reader = window.__mnoteFileTreeRuntime?.readWorkspacePathFromRow;
if (row instanceof HTMLElement && typeof reader === 'function') {
const workspacePath = reader(row);
if (workspacePath && typeof workspacePath === 'object') {
return {
...workspacePath,
sourceKind: String(workspacePath.sourceKind || row.getAttribute('data-source-kind') || '').trim(),
rootUri: String(workspacePath.rootUri || row.getAttribute('data-root-uri') || '').trim(),
};
}
}
if (row instanceof HTMLElement) {
return {
sourceKind: String(row.getAttribute('data-source-kind') || '').trim(),
rootUri: String(row.getAttribute('data-root-uri') || '').trim(),
};
}
} catch (_) {}
return null;
};
const currentWebShellDocumentId = () => {
const explicit = typeof currentDocumentId === 'function' ? String(currentDocumentId() || '').trim() : '';
if (explicit) return explicit;
@@ -157,17 +182,23 @@ export const createResourceTabRuntime = (dependencies = {}) => {
const currentWebShellSourceKind = () => {
try {
return currentUrl().searchParams.get('sourceKind') || '';
return currentUrl().searchParams.get('sourceKind')
|| document.body?.dataset?.mnoteSourceKind
|| currentFileTreeWorkspacePath()?.sourceKind
|| '';
} catch (_) {
return '';
return document.body?.dataset?.mnoteSourceKind || currentFileTreeWorkspacePath()?.sourceKind || '';
}
};
const currentWebShellRootUri = () => {
try {
return currentUrl().searchParams.get('rootUri') || '';
return currentUrl().searchParams.get('rootUri')
|| document.body?.dataset?.mnoteRootUri
|| currentFileTreeWorkspacePath()?.rootUri
|| '';
} catch (_) {
return '';
return document.body?.dataset?.mnoteRootUri || currentFileTreeWorkspacePath()?.rootUri || '';
}
};
@@ -196,7 +227,9 @@ export const createResourceTabRuntime = (dependencies = {}) => {
resourceKind: String(fromInput.resourceKind || fromInput.objectKind || resourceKind || '').trim(),
};
}
const id = String(objectIdentity || documentId || assetId || relativePath || '').trim();
const structuredObjectIdentity = objectIdentity && typeof objectIdentity === 'object' ? objectIdentity : null;
const objectIdentityText = structuredObjectIdentity ? '' : String(objectIdentity || '').trim();
const id = String(objectIdentityText || documentId || assetId || relativePath || '').trim();
if (!id) return null;
return {
schema: 'mnote.workspace_path.v1',
@@ -205,7 +238,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
rootUri: String(rootUri || '').trim(),
relativePath: String(relativePath || '').trim(),
documentId: String(documentId || '').trim(),
objectIdentity: String(objectIdentity || '').trim(),
objectIdentity: structuredObjectIdentity || objectIdentityText,
assetId: String(assetId || '').trim(),
resourceKind: String(resourceKind || '').trim(),
};
@@ -371,6 +404,18 @@ export const createResourceTabRuntime = (dependencies = {}) => {
});
};
const officeBridgeDebugForEntry = (entry) => {
if (!entry?.panel || !(entry.panel instanceof HTMLElement)) return null;
const frame = entry.panel.querySelector('iframe.mnote-resource-tab-frame');
if (!(frame instanceof HTMLIFrameElement)) return null;
try {
const debug = frame.contentWindow?.__MNOTE_ONLYOFFICE_DEBUG__;
return debug && typeof debug === 'object' ? debug : null;
} catch (_) {
return null;
}
};
const openEditorsSnapshotEntry = (entry, key, generatedAt = Date.now()) => {
const active = entry?.tab instanceof HTMLElement
? entry.tab.getAttribute('aria-selected') === 'true'
@@ -384,6 +429,13 @@ export const createResourceTabRuntime = (dependencies = {}) => {
const assetId = String(entry?.assetId || entry?.session?.assetId || '').trim();
const kind = normalizeResourceTabKind(entry);
const dirtyState = resourceTabCloseGuardReason(entry?.session);
const officeBridgeDebug = kind === 'office' ? officeBridgeDebugForEntry(entry) : null;
const onlyofficeSessionId = String(
officeBridgeDebug?.bridgeSessionId
|| entry?.onlyofficeSessionId
|| entry?.bridgeSessionId
|| '',
).trim();
return {
objectIdentity,
workspacePath: buildWorkspacePath({
@@ -411,6 +463,11 @@ export const createResourceTabRuntime = (dependencies = {}) => {
dirtyGuard: dirtyState,
assetId,
path: relativePath,
onlyofficeSessionId,
bridgeSessionId: onlyofficeSessionId,
bridgeSessionReady: Boolean(onlyofficeSessionId),
bridgeDocumentId: String(officeBridgeDebug?.documentId || '').trim(),
bridgeAssetId: String(officeBridgeDebug?.assetId || '').trim(),
lastActiveAt: Number(entry?.session?.lastActiveAt || entry?.lastActiveAt || 0) || (active ? generatedAt : 0),
preview: false,
pinned: false,
@@ -427,6 +484,12 @@ export const createResourceTabRuntime = (dependencies = {}) => {
const rootUri = currentWebShellRootUri();
const relativePath = sourceKind === 'local_folder' ? localMarkdownRelativePathFromDocumentId(documentId) : '';
const objectIdentity = `page:${paneRole}`;
const workspaceObjectIdentity = {
objectKind: 'page',
documentId,
blockId: null,
assetId: null,
};
const active = nodes.pageTab instanceof HTMLElement
? nodes.pageTab.getAttribute('aria-selected') === 'true'
: false;
@@ -443,7 +506,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
rootUri,
relativePath,
documentId,
objectIdentity,
objectIdentity: workspaceObjectIdentity,
assetId: '',
resourceKind: 'page',
}),
@@ -1035,6 +1098,17 @@ export const createResourceTabRuntime = (dependencies = {}) => {
markIntendedSlashRoot(entry);
};
const ensureOnlyofficeBridgeReadyListener = () => {
if (onlyofficeBridgeReadyListenerBound) return;
onlyofficeBridgeReadyListenerBound = true;
window.addEventListener('message', (event) => {
if (event.origin !== window.location.origin) return;
const detail = event.data && typeof event.data === 'object' ? event.data : null;
if (!detail || detail.type !== 'mnote:onlyoffice-bridge-ready') return;
syncOpenEditorsSnapshot();
});
};
const openPassiveResourceTab = (entry, input) => {
const href = String(input.officeUrl || input.href || '').trim();
if (entry.kind === 'image') {
@@ -1051,6 +1125,12 @@ export const createResourceTabRuntime = (dependencies = {}) => {
const frame = entry.panel.querySelector('iframe');
if (frame instanceof HTMLIFrameElement) {
frame.title = entry.title;
if (entry.kind === 'office') {
ensureOnlyofficeBridgeReadyListener();
frame.addEventListener('load', () => {
syncOpenEditorsSnapshot();
}, { once: true });
}
frame.src = href;
}
installPassiveResourceWatch(entry);
@@ -15,6 +15,18 @@ export const firstNonEmptyText = (...values) => {
return '';
};
export const normalizeMindmapDimension = (value, kind) => {
const raw = typeof value === 'number'
? value
: typeof value === 'string'
? Number(value.trim().replace(/px$/i, ''))
: NaN;
if (!Number.isFinite(raw)) return null;
const rounded = Math.round(raw);
const min = kind === 'height' ? 240 : 900;
return rounded >= min ? rounded : null;
};
// 过渡适配(TODO step-4):legacy→Tiptap inline marks 转换函数组。
// AST/block 迁移 complete 后,前端应直接消费 block document 中的
// tiptap 格式 marks(已由 Rust 侧 local_markdown_parser 输出),
@@ -125,19 +137,26 @@ export const legacyBlockToTiptap = (block, index = 0) => {
}
if (type === 'divider' || type === 'horizontalRule' || type === 'horizontal_rule') return { type: 'horizontalRule', attrs: { blockId } };
if (type === 'mindmap') {
const attrs = block?.attrs && typeof block.attrs === 'object' ? block.attrs : null;
const data = block?.props?.data && typeof block.props.data === 'object' ? block.props.data : null;
const mindmapId = firstNonEmptyText(
block?.props?.mindmapId,
block?.props?.mindmap_id,
block?.props?.sourcePath,
block?.props?.source_path,
attrs?.mindmapId,
attrs?.mindmap_id,
attrs?.sourcePath,
attrs?.source_path,
block?.mindmapId,
block?.mindmap_id,
data?.mindmapId,
data?.mindmap_id,
data?.id
);
const rootNodeId = firstNonEmptyText(block?.props?.rootNodeId, block?.props?.root_node_id, data?.rootNodeId, data?.root_node_id) || 'root';
const rootNodeId = firstNonEmptyText(block?.props?.rootNodeId, block?.props?.root_node_id, attrs?.rootNodeId, attrs?.root_node_id, data?.rootNodeId, data?.root_node_id) || 'root';
const mindmapWidth = normalizeMindmapDimension(block?.props?.mindmapWidth ?? block?.props?.mindmap_width ?? attrs?.mindmapWidth ?? attrs?.mindmap_width ?? data?.mindmapWidth ?? data?.mindmap_width, 'width');
const mindmapHeight = normalizeMindmapDimension(block?.props?.mindmapHeight ?? block?.props?.mindmap_height ?? attrs?.mindmapHeight ?? attrs?.mindmap_height ?? data?.mindmapHeight ?? data?.mindmap_height, 'height');
return {
type: 'paragraph',
attrs: withTextAlign({
@@ -145,6 +164,8 @@ export const legacyBlockToTiptap = (block, index = 0) => {
mnoteBlockType: 'mindmap',
mindmapId,
rootNodeId,
...(mindmapWidth !== null ? { mindmapWidth } : {}),
...(mindmapHeight !== null ? { mindmapHeight } : {}),
}),
};
}
@@ -225,7 +246,9 @@ export const mindmapDomDescriptors = (root) => {
const rootNodeId = typeof node.dataset.mnoteRootNodeId === 'string' && node.dataset.mnoteRootNodeId.trim()
? node.dataset.mnoteRootNodeId.trim()
: 'root';
return [{ mindmapId, rootNodeId }];
const mindmapWidth = normalizeMindmapDimension(node.dataset.mnoteMindmapWidth, 'width');
const mindmapHeight = normalizeMindmapDimension(node.dataset.mnoteMindmapHeight, 'height');
return [{ mindmapId, rootNodeId, mindmapWidth, mindmapHeight }];
});
};
@@ -246,6 +269,12 @@ export const hydrateMindmapAttrsFromDom = (tiptapDocument, root) => {
if (typeof node.attrs.rootNodeId !== 'string' || !node.attrs.rootNodeId.trim()) {
node.attrs.rootNodeId = descriptor.rootNodeId;
}
if (normalizeMindmapDimension(node.attrs.mindmapWidth, 'width') === null && descriptor.mindmapWidth !== null) {
node.attrs.mindmapWidth = descriptor.mindmapWidth;
}
if (normalizeMindmapDimension(node.attrs.mindmapHeight, 'height') === null && descriptor.mindmapHeight !== null) {
node.attrs.mindmapHeight = descriptor.mindmapHeight;
}
}
return tiptapDocument;
};
@@ -445,20 +474,17 @@ export const localizeTiptapAssetUrls = (node, context) => {
};
export const pageBodyTiptapDocumentSource = (body, fallbackText = '') => {
const blockDocument = body?.blockDocument || body?.block_document || body?.editorDocument || body?.editor_document || null;
if (blockDocument) return 'page_aggregate.block_document';
if (String(body?.projectionSource || body?.projection_source || '') === 'local_markdown.content' && body?.content) {
return 'local_markdown.content';
}
const blockDocument = body?.blockDocument || body?.block_document || body?.editorDocument || body?.editor_document || null;
if (blockDocument) return 'page_aggregate.block_document';
if (body?.content) return 'compat.legacy_content';
return fallbackText ? 'degraded.fallback_text' : 'empty';
};
export const pageBodyTiptapDocument = (body, fallbackText = '', context = null) => {
const projectionContext = { ...(context || {}), body };
if (pageBodyTiptapDocumentSource(body, fallbackText) === 'local_markdown.content') {
return localizeTiptapAssetUrls(toTiptapDocument(body.content, fallbackText), projectionContext);
}
const blockDocument = body?.blockDocument || body?.block_document || body?.editorDocument || body?.editor_document || null;
if (blockDocument) return localizeTiptapAssetUrls(toTiptapDocument(blockDocument, fallbackText), projectionContext);
return localizeTiptapAssetUrls(toTiptapDocument(body?.content, fallbackText), projectionContext);
@@ -533,10 +559,14 @@ export const mindmapPropsFromAttrs = (attrs, fallbackMindmapId) => {
);
const rootNodeId = firstNonEmptyText(attrs?.rootNodeId, attrs?.root_node_id, data?.rootNodeId, data?.root_node_id) || 'root';
const projectionVersion = Number(attrs?.projectionVersion ?? attrs?.projection_version);
const mindmapWidth = normalizeMindmapDimension(attrs?.mindmapWidth ?? attrs?.mindmap_width ?? data?.mindmapWidth ?? data?.mindmap_width, 'width');
const mindmapHeight = normalizeMindmapDimension(attrs?.mindmapHeight ?? attrs?.mindmap_height ?? data?.mindmapHeight ?? data?.mindmap_height, 'height');
return {
mindmapId,
rootNodeId,
...(Number.isFinite(projectionVersion) ? { projectionVersion } : {}),
...(mindmapWidth !== null ? { mindmapWidth } : {}),
...(mindmapHeight !== null ? { mindmapHeight } : {}),
};
};
@@ -9,7 +9,12 @@ function fileTreeRowKind(row) {
function fileTreeRowDocumentId(row) {
if (!(row instanceof HTMLElement)) return '';
return String(row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || '').trim();
return String(
row.getAttribute('data-document-id')
|| row.getAttribute('data-doc-id')
|| row.getAttribute('data-owner-document-id')
|| '',
).trim();
}
function fileTreeRowAssetId(row, deps) {
@@ -98,6 +103,11 @@ function readWorkspacePathFromRow(row, deps) {
var documentId = fileTreeRowDocumentId(row);
var rowId = String(row.getAttribute('data-row-id') || '').trim();
var rowKind = fileTreeRowKind(row);
if (!documentId && (rowKind === 'folder' || objectKind === 'index') && relativePath) {
documentId = 'local-dir:' + relativePath.replace(/^\/+/, '').split('/').map(function(segment) {
return encodeURIComponent(segment).replace(/%20/g, '~20');
}).join('~2F');
}
var sourceKind = String(row.getAttribute('data-source-kind') || currentSourceKind() || '').trim();
var rootUri = String(row.getAttribute('data-root-uri') || currentRootUri() || '').trim();
return {
@@ -410,7 +410,7 @@ export const createSidebarFileTreeOpenRuntime = (dependencies = {}) => {
navigateToMindmapObject(String(detail && detail.documentId || currentDocumentId() || '').trim(), assetId, String(detail.workspaceId || '').trim());
return;
}
var requestedOfficeMode = forceNewWindow || forceEditMode ? 'edit' : 'view';
var requestedOfficeMode = forceEditMode ? 'edit' : 'view';
var localOfficeUrl = buildLocalOnlyOfficeOpenUrl(localFilePath, localFileName, String(detail && detail.documentId || currentDocumentId() || '').trim(), assetId, requestedOfficeMode);
if (localOfficeUrl) {
if (!forceNewWindow && await openLocalResourceInActiveTab({
@@ -0,0 +1,107 @@
export function createSidebarPageAiMarkdownRuntime(context) {
const { escapeHtml } = context;
function textFromUnknown(value) {
if (value == null) return '';
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return String(value);
if (Array.isArray(value)) return value.map(textFromUnknown).filter(Boolean).join(' ');
if (typeof value !== 'object') return '';
var parts = [];
['text', 'title', 'content', 'children', 'blocks', 'body'].forEach(function(key) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
var text = textFromUnknown(value[key]);
if (text) parts.push(text);
}
});
return parts.join(' ');
}
function renderPageAiMarkdownInline(text) {
var html = escapeHtml(String(text || ''));
var codeSpans = [];
html = html.replace(/`([^`\n]+)`/g, function(_, code) {
var key = '\u0000CODE' + codeSpans.length + '\u0000';
codeSpans.push('<code>' + code + '</code>');
return key;
});
html = html.replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>');
html = html.replace(/(^|[^*])\*([^*\n]+)\*/g, '$1<em>$2</em>');
codeSpans.forEach(function(value, index) {
html = html.replace('\u0000CODE' + index + '\u0000', value);
});
return html;
}
function renderPageAiMarkdown(content) {
var lines = String(content || '').replace(/\r\n?/g, '\n').split('\n');
var blocks = [];
var index = 0;
function isBlockBoundary(line) {
return !line.trim() ||
/^```/.test(line.trim()) ||
/^#{1,6}\s+/.test(line) ||
/^\s*[-*]\s+/.test(line) ||
/^\s*\d+[.)]\s+/.test(line);
}
while (index < lines.length) {
var line = lines[index];
if (!line.trim()) {
index += 1;
continue;
}
if (/^```/.test(line.trim())) {
index += 1;
var codeLines = [];
while (index < lines.length && !/^```/.test(lines[index].trim())) {
codeLines.push(lines[index]);
index += 1;
}
if (index < lines.length) index += 1;
blocks.push('<pre><code>' + escapeHtml(codeLines.join('\n')) + '</code></pre>');
continue;
}
var heading = line.match(/^(#{1,6})\s+(.+)$/);
if (heading) {
var level = Math.min(6, heading[1].length);
blocks.push('<h' + level + '>' + renderPageAiMarkdownInline(heading[2]) + '</h' + level + '>');
index += 1;
continue;
}
if (/^\s*[-*]\s+/.test(line)) {
var unordered = [];
while (index < lines.length && /^\s*[-*]\s+/.test(lines[index])) {
unordered.push('<li>' + renderPageAiMarkdownInline(lines[index].replace(/^\s*[-*]\s+/, '')) + '</li>');
index += 1;
}
blocks.push('<ul>' + unordered.join('') + '</ul>');
continue;
}
if (/^\s*\d+[.)]\s+/.test(line)) {
var ordered = [];
while (index < lines.length && /^\s*\d+[.)]\s+/.test(lines[index])) {
ordered.push('<li>' + renderPageAiMarkdownInline(lines[index].replace(/^\s*\d+[.)]\s+/, '')) + '</li>');
index += 1;
}
blocks.push('<ol>' + ordered.join('') + '</ol>');
continue;
}
var paragraph = [];
while (index < lines.length && !isBlockBoundary(lines[index])) {
paragraph.push(renderPageAiMarkdownInline(lines[index]));
index += 1;
}
if (paragraph.length) {
blocks.push('<p>' + paragraph.join('<br />') + '</p>');
} else {
index += 1;
}
}
return blocks.join('') || escapeHtml(String(content || ''));
}
return {
textFromUnknown,
renderPageAiMarkdown,
renderPageAiMarkdownInline
};
}
@@ -0,0 +1,132 @@
export function createSidebarPageAiPermissionRuntime(context) {
const {
documentRef,
pageAiPreviewValue,
pageUiState,
renderPageAiConversation,
} = context;
const doc = documentRef || document;
function pageAiPermissionMessage(payload, eventType) {
payload = payload && typeof payload === 'object' ? payload : {};
var permissionId = String(payload.permissionId || payload.permission_id || payload.id || ('perm_' + Date.now())).trim();
var toolName = String(payload.toolName || payload.tool || payload.name || payload.method || 'session/request_permission').trim();
var args = payload.arguments || payload.args || payload.input || payload.params || payload;
var decision = String(payload.decision || payload.result || '').trim();
if (!decision && eventType === 'permission.denied') decision = 'denied';
if (!decision && eventType === 'permission.allowed') decision = 'allowed';
return {
role: 'tool',
kind: 'permission',
permissionId: permissionId,
toolName: toolName,
argsSummary: pageAiPreviewValue(args),
content: decision === 'denied' ? '已自动拒绝权限请求' : (decision === 'allowed' ? '已自动允许权限请求' : '等待权限确认'),
resolved: decision === 'denied' || decision === 'allowed',
decision: decision
};
}
function pageAiApplyPermissionEvent(eventName, payloadText) {
var payload = null;
try {
payload = JSON.parse(payloadText || 'null');
} catch (_) {
payload = {};
}
var message = pageAiPermissionMessage(payload, eventName);
var existing = pageUiState.pageAiMessages.find(function(item) {
return item.kind === 'permission' && item.permissionId === message.permissionId;
});
if (existing) {
Object.assign(existing, message);
} else {
pageUiState.pageAiMessages.push(message);
}
pageUiState.pageAiPermissionRequests = pageUiState.pageAiPermissionRequests.filter(function(item) {
return item.permissionId !== message.permissionId;
}).concat([message]).slice(-20);
if (!message.resolved) {
pageAiShowPermissionDialog(message);
} else {
pageAiHidePermissionDialog();
}
}
function pageAiResolvePermission(permissionId, decision) {
permissionId = String(permissionId || '').trim();
if (!permissionId) return;
var runId = pageUiState.pageAiCurrentRunId;
if (runId) {
fetch('/api/hermes/client/runs/' + encodeURIComponent(runId) + '/resolve-permission', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ permissionId: permissionId, decision: decision })
}).then(function(response) {
if (!response.ok) console.warn('resolve-permission 后端返回异常', response.status);
}).catch(function(err) {
console.warn('resolve-permission 请求失败', err);
});
} else {
console.warn('resolve-permission: 无活跃 runId,只能本地更新');
}
pageUiState.pageAiMessages.forEach(function(item) {
if (item.kind === 'permission' && item.permissionId === permissionId) {
item.resolved = true;
item.decision = decision;
item.content = decision === 'allow' ? '已允许权限请求' : '已拒绝权限请求';
}
});
var dialog = doc.querySelector('[data-page-ai-permission-dialog]');
if (dialog instanceof HTMLElement) dialog.hidden = true;
renderPageAiConversation();
}
function pageAiHidePermissionDialog() {
var dialog = doc.querySelector('[data-page-ai-permission-dialog]');
if (dialog instanceof HTMLElement) dialog.hidden = true;
}
function pageAiShowPermissionDialog(message) {
if (!message || message.kind !== 'permission') return;
if (message.resolved) {
pageAiHidePermissionDialog();
return;
}
var dialog = doc.querySelector('[data-page-ai-permission-dialog]');
if (!(dialog instanceof HTMLElement)) {
dialog = doc.createElement('div');
dialog.className = 'wolai-page-ai-permission-dialog';
dialog.setAttribute('data-page-ai-permission-dialog', 'true');
dialog.innerHTML = '' +
'<div class="wolai-page-ai-permission-panel" role="dialog" aria-modal="false">' +
'<div class="wolai-page-ai-memory-title" data-page-ai-permission-tool></div>' +
'<div class="wolai-page-ai-tool-meta" data-page-ai-permission-args></div>' +
'<div class="wolai-page-ai-message-actions">' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="allow" data-page-ai-permission-dialog-action>允许</button>' +
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="deny" data-page-ai-permission-dialog-action>拒绝</button>' +
'</div>' +
'</div>';
doc.body.appendChild(dialog);
}
var tool = dialog.querySelector('[data-page-ai-permission-tool]');
if (tool instanceof HTMLElement) tool.textContent = message.toolName || 'session/request_permission';
var args = dialog.querySelector('[data-page-ai-permission-args]');
if (args instanceof HTMLElement) args.textContent = message.argsSummary || message.content || '';
dialog.querySelectorAll('[data-page-ai-permission-dialog-action]').forEach(function(button) {
if (button instanceof HTMLButtonElement) {
button.setAttribute('data-page-ai-permission-id', message.permissionId || '');
button.disabled = Boolean(message.resolved);
}
});
dialog.hidden = false;
}
return {
pageAiPermissionMessage,
pageAiApplyPermissionEvent,
pageAiResolvePermission,
pageAiHidePermissionDialog,
pageAiShowPermissionDialog
};
}
@@ -0,0 +1,220 @@
export function createSidebarPageAiProfileRuntime(context) {
const {
chatOnlyProfileRegistry,
documentRef,
pageAiAgentRecord,
pageAiCurrentAgentId,
pageAiNormalizeAgentId,
pageUiState,
} = context;
const PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY = Array.isArray(chatOnlyProfileRegistry) ? chatOnlyProfileRegistry : [];
function pageAiProviderLabel(provider) {
if (provider === 'codex') return 'Codex';
if (provider === 'claudecode') return 'ClaudeCode';
return 'Hermes';
}
function pageAiNormalizeArray(value) {
return Array.isArray(value) ? value : [];
}
function pageAiDefaultAcpRuntimes() {
return [
{
name: 'reasonix',
title: 'ACP · Reasonix',
description: '通过 ACP 协议直连 ReasonixDeepSeek 缓存优先)',
model: 'deepseek-chat',
preset: 'auto'
},
{
name: 'hermes',
title: 'ACP · Hermes',
description: '通过 ACP 协议直连 Hermes agent runtime'
}
];
}
function pageAiNormalizeAcpRuntimes(runtimes) {
var byName = {};
pageAiDefaultAcpRuntimes().forEach(function(runtime) {
byName[runtime.name] = Object.assign({}, runtime);
});
pageAiNormalizeArray(runtimes).forEach(function(runtime) {
var name = String(runtime && runtime.name || '').trim();
if (name !== 'reasonix' && name !== 'hermes') return;
byName[name] = Object.assign({}, byName[name] || {}, runtime, { name: name });
});
return ['reasonix', 'hermes'].map(function(name) { return byName[name]; }).filter(Boolean);
}
function pageAiUnwrapUpstream(payload) {
if (payload && typeof payload === 'object' && payload.upstream) return payload.upstream;
return payload || null;
}
function pageAiProfileValue(profile) {
if (profile && typeof profile === 'object') {
return String(profile.profileId || profile.name || profile.profile || profile.id || '').trim();
}
return String(profile || '').trim();
}
function pageAiCurrentProfile() {
var active = String(pageUiState.pageAiActiveProfileName || '').trim();
if (active) return active;
var selected = pageUiState.pageAiProfiles.find(function(profile) {
return profile && profile.active;
});
return pageAiProfileValue(selected) || 'mnoteai';
}
function pageAiRunProfile() {
if (String(pageUiState.pageAiAcpRuntime || 'reasonix').trim() === 'reasonix') return 'reasonix';
if (pageAiCurrentAgentId() === 'chat_only') return pageAiNormalizeChatOnlyProfileId(pageAiCurrentProfile());
return pageAiCurrentProfile();
}
function pageAiMnoteToolModel() {
var doc = documentRef || document;
return String(doc.documentElement.getAttribute('data-mnote-page-ai-tool-model') || 'deepseek-v4-flash').trim() || 'deepseek-v4-flash';
}
function pageAiCurrentProfileRecord() {
var active = pageAiCurrentProfile();
return pageUiState.pageAiProfiles.find(function(profile) {
return pageAiProfileValue(profile) === active;
}) || null;
}
function pageAiChatOnlyProfileSpec(profile) {
var profileId = pageAiProfileValue(profile);
var baseProfile = String(profile && profile.baseProfile || '').trim();
var isolatedProfile = String(profile && profile.isolatedProfile || '').trim();
return PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY.find(function(spec) {
return spec.profileId === profileId || spec.baseProfile === baseProfile || isolatedProfile.indexOf(spec.baseProfile) >= 0;
}) || null;
}
function pageAiDefaultChatOnlyProfileSpec() {
return PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY[0] || { profileId: 'shared_deepseek_chat', baseProfile: 'deepseek-chat', label: 'DeepSeek' };
}
function pageAiNormalizeChatOnlyProfileId(profileId) {
var profile = pageAiProfileRecordById(profileId) || { profileId: String(profileId || '').trim(), name: String(profileId || '').trim() };
var spec = pageAiChatOnlyProfileSpec(profile);
return (spec || pageAiDefaultChatOnlyProfileSpec()).profileId;
}
function pageAiProfileDisplayLabel(profile, fallback) {
var alias = String(profile && profile.alias || '').trim();
var displayName = String(profile && (profile.displayName || profile.label || '') || '').trim();
var name = pageAiProfileValue(profile);
return alias || displayName || fallback || name || 'default';
}
function pageAiProfileRecordById(profileId) {
var normalized = String(profileId || '').trim();
if (!normalized) return null;
return pageAiNormalizeArray(pageUiState.pageAiProfiles).find(function(profile) {
return pageAiProfileValue(profile) === normalized;
}) || null;
}
function pageAiSessionAgentFilterValue(session) {
var agentId = pageAiNormalizeAgentId(session && session.agentId);
if (agentId === 'reasonix') return 'reasonix';
var profileId = String(session && (session.profileId || session.profile) || '').trim();
if (agentId === 'chat_only') profileId = pageAiNormalizeChatOnlyProfileId(profileId);
return agentId + ':' + profileId;
}
function pageAiSessionAgentLabel(session) {
var agentId = pageAiNormalizeAgentId(session && session.agentId);
if (agentId === 'reasonix') return 'Reasonix';
var profileId = String(session && (session.profileId || session.profile) || '').trim();
var profile = pageAiProfileRecordById(profileId) || { profileId: profileId, name: profileId };
var chatOnlySpec = pageAiChatOnlyProfileSpec(profile);
if (agentId === 'chat_only') {
return 'ChatOnly / ' + (chatOnlySpec || pageAiDefaultChatOnlyProfileSpec()).label;
}
if (agentId === 'hermes') return 'Hermes / ' + pageAiProfileDisplayLabel(profile, profileId);
return pageAiAgentRecord(agentId).label;
}
function pageAiSessionPreviewText(session) {
var preview = Array.isArray(session && session.messages) && session.messages.length
? String(session.messages.slice(-1)[0].content || '')
: String(session && (session.snippet || session.preview || '暂无消息') || '暂无消息');
preview = preview.replace(/\s+/g, ' ').trim();
var limit = 96;
return preview.length > limit ? preview.slice(0, limit) + '…' : preview;
}
function pageAiSessionAgentFilterOptions(rows) {
var byValue = { all: '全部 agent' };
pageAiNormalizeArray(rows).forEach(function(session) {
var value = pageAiSessionAgentFilterValue(session);
byValue[value] = pageAiSessionAgentLabel(session);
});
return Object.keys(byValue).map(function(value) {
return { value: value, label: byValue[value] };
});
}
function pageAiFilteredHistoryRows(rows) {
var filterValue = String(pageUiState.pageAiSessionAgentFilter || 'all').trim() || 'all';
var normalized = pageAiNormalizeArray(rows);
if (filterValue === 'all') return normalized;
return normalized.filter(function(session) {
return pageAiSessionAgentFilterValue(session) === filterValue;
});
}
function pageAiTimestamp(value) {
if (typeof value === 'number' && Number.isFinite(value)) return value;
if (typeof value === 'string' && value.trim()) {
var parsed = Date.parse(value);
if (Number.isFinite(parsed)) return parsed;
}
return Date.now();
}
function pageAiUsageSummary(usage) {
if (!usage || typeof usage !== 'object') return '';
var used = usage.used ?? usage.contextUsed ?? usage.input_tokens ?? usage.inputTokens ?? usage.prompt_tokens ?? usage.promptTokens;
var size = usage.size ?? usage.contextSize ?? usage.total_tokens ?? usage.totalTokens;
var output = usage.output_tokens ?? usage.outputTokens ?? usage.completion_tokens ?? usage.completionTokens;
var parts = [];
if (Number.isFinite(Number(used))) parts.push('ctx ' + Number(used));
if (Number.isFinite(Number(size)) && Number(size) > 0) parts.push('/ ' + Number(size));
if (Number.isFinite(Number(output)) && Number(output) > 0) parts.push('out ' + Number(output));
return parts.join(' ') || '';
}
return {
pageAiProviderLabel,
pageAiNormalizeArray,
pageAiDefaultAcpRuntimes,
pageAiNormalizeAcpRuntimes,
pageAiUnwrapUpstream,
pageAiProfileValue,
pageAiCurrentProfile,
pageAiRunProfile,
pageAiMnoteToolModel,
pageAiCurrentProfileRecord,
pageAiChatOnlyProfileSpec,
pageAiDefaultChatOnlyProfileSpec,
pageAiNormalizeChatOnlyProfileId,
pageAiProfileDisplayLabel,
pageAiProfileRecordById,
pageAiSessionAgentFilterValue,
pageAiSessionAgentLabel,
pageAiSessionPreviewText,
pageAiSessionAgentFilterOptions,
pageAiFilteredHistoryRows,
pageAiTimestamp,
pageAiUsageSummary
};
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,613 @@
export function createSidebarPageAiSessionRuntime(context) {
const {
currentDocumentId,
currentRootUri,
currentSourceKind,
documentRef,
pageAiApplyRuntimeState,
pageAiCurrentAgentId,
pageAiCurrentProfile,
pageAiErrorMessage,
pageAiNormalizeAgentId,
pageAiNormalizeArray,
pageAiNormalizeChatOnlyProfileId,
pageAiPermissionMessage,
pageAiPreviewValue,
pageAiRunProfile,
pageAiSetActiveProfile,
pageAiTimestamp,
pageUiState,
renderPageAiControls,
renderPageAiConversation,
resolveWorkspaceId,
sessionStorageVersion,
windowRef,
} = context;
const doc = documentRef || document;
const win = windowRef || window;
function pageAiStorageKey() {
return 'hermes_page_ai_session:' + currentDocumentId();
}
function pageAiBackendSessionQuery(extra) {
var params = new URLSearchParams();
params.set('source', 'acp');
params.set('workspaceId', resolveWorkspaceId(doc.body));
params.set('documentId', currentDocumentId());
params.set('profile', pageAiRunProfile());
params.set('sourceKind', currentSourceKind());
if (currentRootUri()) params.set('rootUri', currentRootUri());
Object.keys(extra || {}).forEach(function(key) {
var value = extra[key];
if (value !== undefined && value !== null && String(value).trim() !== '') {
params.set(key, String(value));
}
});
return params.toString();
}
function pageAiNewSession(title) {
var now = Date.now();
var agentId = pageAiCurrentAgentId();
var profile = agentId === 'chat_only' ? pageAiNormalizeChatOnlyProfileId(pageAiCurrentProfile()) : pageAiCurrentProfile();
return {
id: 'sess_' + now + '_' + Math.random().toString(16).slice(2, 8),
title: title || '新会话',
agentId: agentId,
profileId: pageUiState.pageAiAcpRuntime === 'hermes' ? profile : '',
profile: profile,
acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
createdAt: now,
updatedAt: now,
source: 'local',
usage: null,
status: 'idle',
messages: []
};
}
function pageAiNormalizeSessions(sessions) {
return (Array.isArray(sessions) ? sessions : [])
.slice(0, 20)
.map(function(session) {
var sessionStorage = String(session && (session.sessionStorage || session.session_storage) || '').trim();
var persistence = String(session && session.persistence || '').trim();
var agentId = pageAiNormalizeAgentId(session && (session.agentId || session.agent_id));
var profileId = String(session && (session.profileId || session.profile_id || '') || '').trim();
var profile = String(session && session.profile || pageAiCurrentProfile()).trim() || 'default';
if (!profileId && agentId !== 'reasonix') profileId = profile;
if (agentId === 'chat_only') {
profileId = pageAiNormalizeChatOnlyProfileId(profileId || profile);
profile = profileId;
}
return {
id: String(session && session.id || '').trim() || pageAiNewSession().id,
title: String(session && session.title || '').trim() || '新会话',
agentId: agentId,
profileId: profileId,
profile: profile,
acpRuntime: String(session && session.acpRuntime || 'reasonix').trim(),
createdAt: pageAiTimestamp(session && session.createdAt),
updatedAt: pageAiTimestamp(session && session.updatedAt),
source: String(session && session.source || 'local').trim() || 'local',
persistence: persistence,
sessionStorage: sessionStorage,
permissionLevel: String(session && (session.permissionLevel || session.permission_level) || '').trim(),
shareId: String(session && (session.shareId || session.share_id) || '').trim(),
acpSessionId: String(session && (session.acpSessionId || session.acp_session_id) || '').trim(),
runId: String(session && (session.runId || session.run_id) || '').trim(),
status: String(session && session.status || '').trim(),
usage: session && session.usage && typeof session.usage === 'object' ? session.usage : null,
preview: String(session && session.preview || '').trim(),
messages: Array.isArray(session && session.messages) ? session.messages.slice(-300) : []
};
})
.sort(function(a, b) {
return Number(b.updatedAt || 0) - Number(a.updatedAt || 0);
});
}
function pageAiNormalizeBackendSessionRow(row) {
if (!row || typeof row !== 'object') return null;
var payload = row.payload && typeof row.payload === 'object' ? row.payload : {};
var sessionId = String(row.sessionId || row.session_id || '').trim();
if (!sessionId) return null;
var title = String(row.title || payload.title || payload.message || '').trim();
if (title.length > 28) title = title.slice(0, 28) + '…';
var persistence = String(row.persistence || payload.persistence || '').trim();
var sessionStorage = String(row.sessionStorage || row.session_storage || payload.sessionStorage || '').trim();
var agentId = pageAiNormalizeAgentId(row.agentId || row.agent_id || payload.agentId || payload.agent_id);
var profileId = String(row.profileId || row.profile_id || payload.profileId || payload.profile_id || '').trim();
var profile = String(row.profile || payload.profile || pageAiRunProfile()).trim() || 'default';
if (!profileId && agentId !== 'reasonix') profileId = profile;
if (agentId === 'chat_only') {
profileId = pageAiNormalizeChatOnlyProfileId(profileId || profile);
profile = profileId;
}
if (!sessionStorage && persistence === 'convex_acp_runtime_store') sessionStorage = 'cloud';
if (!sessionStorage && persistence === 'local_ai_session_jsonl') sessionStorage = String(row.shareId || payload.shareId || '').trim() ? 'local_shared' : 'local_private';
return {
id: sessionId,
title: title || '当前页问答',
agentId: agentId,
profileId: profileId,
profile: profile,
acpRuntime: String(row.acpRuntime || row.acp_runtime || payload.acpRuntime || 'reasonix').trim(),
createdAt: pageAiTimestamp(row.createdAt || row.created_at),
updatedAt: pageAiTimestamp(row.updatedAt || row.updated_at),
source: sessionStorage === 'local_private' || sessionStorage === 'local_shared' ? 'local' : 'acp',
persistence: persistence,
sessionStorage: sessionStorage,
permissionLevel: String(row.permissionLevel || row.permission_level || payload.permissionLevel || '').trim(),
shareId: String(row.shareId || row.share_id || payload.shareId || '').trim(),
acpSessionId: String(row.acpSessionId || row.acp_session_id || payload.acpSessionId || payload.acp_session_id || '').trim(),
runId: String(row.runId || row.run_id || '').trim(),
status: String(row.status || (row.runtime && row.runtime.status) || '').trim(),
usage: row.usage && typeof row.usage === 'object' ? row.usage : null,
preview: String(payload.message || row.snippet || '').trim(),
messages: []
};
}
function pageAiMergeSessions(localSessions, backendSessions) {
var byId = {};
pageAiNormalizeSessions(localSessions).forEach(function(session) {
byId[session.id] = session;
});
pageAiNormalizeSessions(backendSessions).forEach(function(session) {
var existing = byId[session.id];
byId[session.id] = Object.assign({}, existing || {}, session, {
messages: session.messages.length ? session.messages : (existing && existing.messages || [])
});
});
return pageAiNormalizeSessions(Object.keys(byId).map(function(id) { return byId[id]; }));
}
function pageAiSessionStorageLabel(session) {
var storage = String(session && (session.sessionStorage || session.session_storage) || '').trim();
var persistence = String(session && session.persistence || '').trim();
if (storage === 'local_shared') return '共享会话';
if (storage === 'local_private') return '本地私有';
if (storage === 'cloud' || persistence === 'convex_acp_runtime_store') return '云端会话';
if (persistence === 'local_ai_session_jsonl') return '本地私有';
return String(session && session.source || '').trim() === 'acp' ? '云端会话' : '本地私有';
}
function pageAiLoadSessions() {
if (pageUiState.pageAiSessions.length && pageUiState.pageAiActiveSessionId) return;
try {
var raw = win.localStorage.getItem(pageAiStorageKey());
var parsed = raw ? JSON.parse(raw) : null;
var activeId = String(parsed && parsed.activeSessionId || '').trim();
var activeProfile = String(parsed && parsed.activeProfileName || '').trim();
var activeAcpRuntime = String(parsed && parsed.activeAcpRuntime || '').trim();
var sessions = pageAiNormalizeSessions(parsed && parsed.sessions);
var storageVersion = Number(parsed && parsed.version || 0);
if (storageVersion >= sessionStorageVersion && activeAcpRuntime) pageUiState.pageAiAcpRuntime = activeAcpRuntime;
if (activeProfile) pageAiSetActiveProfile(activeProfile);
if (sessions.length) {
pageUiState.pageAiSessions = sessions;
var activeSession = sessions.find(function(session) { return session.id === activeId; }) || sessions[0];
pageUiState.pageAiActiveSessionId = activeSession.id;
if (activeSession.profile) pageAiSetActiveProfile(activeSession.profile);
if (activeSession.agentId) pageUiState.pageAiAgentId = pageAiNormalizeAgentId(activeSession.agentId);
if (!pageUiState.pageAiAcpRuntime && activeSession.acpRuntime) pageUiState.pageAiAcpRuntime = activeSession.acpRuntime;
pageUiState.pageAiMessages = Array.isArray(activeSession.messages) ? activeSession.messages.slice() : [];
return;
}
if (activeId) {
pageUiState.pageAiSessions = [pageAiNewSession('当前页问答')];
pageUiState.pageAiSessions[0].id = activeId;
pageUiState.pageAiActiveSessionId = activeId;
pageUiState.pageAiMessages = [];
return;
}
} catch (_) {}
var fresh = pageAiNewSession();
pageUiState.pageAiSessions = [fresh];
pageUiState.pageAiActiveSessionId = fresh.id;
pageUiState.pageAiMessages = [];
}
async function pageAiLoadBackendSessions() {
var response = await fetch('/api/hermes/client/sessions?' + pageAiBackendSessionQuery({ limit: 50 }), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'session_list_failed_' + response.status));
}
var backendSessions = pageAiNormalizeArray(payload.sessions).map(pageAiNormalizeBackendSessionRow).filter(Boolean);
if (!backendSessions.length) return [];
pageUiState.pageAiSessions = pageAiMergeSessions(pageUiState.pageAiSessions, backendSessions);
if (!pageUiState.pageAiActiveSessionId || !pageUiState.pageAiSessions.find(function(session) { return session.id === pageUiState.pageAiActiveSessionId; })) {
pageUiState.pageAiActiveSessionId = pageUiState.pageAiSessions[0].id;
}
var active = pageAiCurrentSession();
if (active) {
if (active.profile) pageAiSetActiveProfile(active.profile);
if (!pageUiState.pageAiAcpRuntime && active.acpRuntime) pageUiState.pageAiAcpRuntime = active.acpRuntime;
pageUiState.pageAiMessages = Array.isArray(active.messages) ? active.messages.slice() : pageUiState.pageAiMessages;
}
pageUiState.pageAiSessionError = '';
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
return backendSessions;
}
function pageAiMessageFromRuntimeEvent(event) {
if (!event || typeof event !== 'object') return null;
var eventType = String(event.eventType || event.event_type || event.event || '').trim();
var payload = event.payload && typeof event.payload === 'object' ? event.payload : event;
if (eventType === 'message.delta') {
var delta = String(payload.delta || payload.text || payload.output_text || '').trim();
return delta ? { role: 'assistant', content: delta } : null;
}
if (eventType === 'thought.delta') {
var thought = String(payload.delta || payload.text || '').trim();
return thought ? { role: 'assistant', kind: 'thought', content: thought } : null;
}
if (eventType === 'tool.started' || eventType === 'tool.completed' || eventType === 'tool.failed') {
var toolName = String(payload.toolName || payload.tool || payload.name || eventType).trim();
var rawLocations = payload.locations;
return {
role: 'tool',
content: toolName,
toolName: toolName,
toolCallId: String(payload.toolCallId || payload.tool_call_id || payload.id || ''),
toolKind: String(payload.kind || ''),
status: eventType === 'tool.completed' ? 'completed' : (eventType === 'tool.failed' ? 'failed' : 'running'),
argsSummary: pageAiPreviewValue(payload.args || payload.arguments || payload.input),
resultSummary: pageAiPreviewValue(payload.summary || payload.result || payload.output || payload.error),
locations: Array.isArray(rawLocations) ? rawLocations.filter(function(l) { return typeof l === 'string' || (typeof l === 'object' && l && l.path); }).map(function(l) { return typeof l === 'string' ? l : l.path; }) : [],
traceId: String(payload.traceId || payload.trace_id || ''),
auditId: String(payload.auditId || payload.audit_id || '')
};
}
if (eventType === 'permission.requested' || eventType === 'permission.denied' || eventType === 'permission.allowed') {
return pageAiPermissionMessage(payload, eventType);
}
if (eventType === 'run.completed') {
var output = String(payload.output || payload.text || '').trim();
return output ? { role: 'assistant', content: output } : null;
}
return null;
}
function pageAiApplyBackendSessionDetail(payload) {
var sessionPayload = payload && payload.session ? payload.session : {};
var runs = pageAiNormalizeArray(sessionPayload.runs);
var latest = runs.length ? pageAiNormalizeBackendSessionRow(runs[0]) : null;
var events = pageAiNormalizeArray(payload && payload.events);
var storedMessages = pageAiNormalizeArray(sessionPayload.messages).map(function(message) {
return {
role: message.role === 'user' ? 'user' : (message.role === 'tool' ? 'tool' : 'assistant'),
content: String(message.content || '')
};
}).filter(function(message) { return message.content; });
var eventsByRunId = {};
events.forEach(function(event) {
var runId = String(event && (event.runId || event.run_id) || '').trim();
if (!runId) return;
if (!eventsByRunId[runId]) eventsByRunId[runId] = [];
eventsByRunId[runId].push(event);
});
var messages = [];
if (runs.length) {
runs.slice().reverse().forEach(function(run) {
var runPayload = run && run.payload && typeof run.payload === 'object' ? run.payload : {};
var userMessage = String(runPayload.message || runPayload.input || '').trim();
if (userMessage) messages.push({ role: 'user', content: userMessage });
var runId = String(run && (run.runId || run.run_id) || '').trim();
pageAiNormalizeArray(eventsByRunId[runId]).forEach(function(event) {
var message = pageAiMessageFromRuntimeEvent(event);
if (message) messages.push(message);
});
});
}
if (!messages.length) messages = storedMessages;
var acpSessionId = '';
events.forEach(function(event) {
var eventType = String(event && (event.eventType || event.event_type || event.event) || '').trim();
var payload = event && event.payload && typeof event.payload === 'object' ? event.payload : event;
if (eventType === 'session.info.updated') {
var nextAcpSessionId = String(payload && (payload.acpSessionId || payload.acp_session_id) || '').trim();
if (nextAcpSessionId) acpSessionId = nextAcpSessionId;
}
});
var current = pageAiCurrentSession();
if (latest && current) {
Object.assign(current, latest);
}
if (current) {
current.messages = messages.slice(-300);
current.updatedAt = Math.max(Number(current.updatedAt || 0), Date.now());
if (acpSessionId) current.acpSessionId = acpSessionId;
if (latest && latest.usage) current.usage = latest.usage;
}
pageAiApplyRuntimeState(payload && payload.runtime);
pageUiState.pageAiMessages = messages.slice(-300);
pageAiPersistSessions();
}
async function pageAiLoadBackendSessionDetail(sessionId) {
sessionId = String(sessionId || pageUiState.pageAiActiveSessionId || '').trim();
if (!sessionId) return null;
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '?' + pageAiBackendSessionQuery({ limit: 200 }), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'session_detail_failed_' + response.status));
}
pageAiApplyBackendSessionDetail(payload);
renderPageAiConversation();
renderPageAiControls();
return payload;
}
async function pageAiSearchBackendSessions(query) {
var q = String(query || '').trim();
pageUiState.pageAiSessionSearchQuery = q;
if (!q) {
pageUiState.pageAiSessionSearchResults = [];
renderPageAiConversation();
return [];
}
var response = await fetch('/api/hermes/client/sessions/search?' + pageAiBackendSessionQuery({ q: q, limit: 20 }), {
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'session_search_failed_' + response.status));
}
pageUiState.pageAiSessionSearchResults = pageAiNormalizeArray(payload.results).map(function(row) {
var normalized = pageAiNormalizeBackendSessionRow(row) || {};
normalized.snippet = String(row && row.snippet || normalized.preview || '').trim();
return normalized;
}).filter(function(row) { return row.id; });
renderPageAiConversation();
return pageUiState.pageAiSessionSearchResults;
}
function pageAiPersistSessions() {
doc.documentElement.setAttribute('data-mnote-page-ai-session-owner', 'hermes');
doc.documentElement.setAttribute('data-mnote-page-ai-session-key', pageAiStorageKey());
try {
pageAiSyncCurrentSessionMessages();
win.localStorage.setItem(pageAiStorageKey(), JSON.stringify({
version: sessionStorageVersion,
activeSessionId: pageUiState.pageAiActiveSessionId,
activeProfileName: pageAiCurrentProfile(),
activeAcpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
sessions: pageAiNormalizeSessions(pageUiState.pageAiSessions)
}));
} catch (_) {}
}
async function pageAiEnsureHermesSession(forceCreate) {
pageAiLoadSessions();
var current = pageAiCurrentSession();
var runProfile = pageAiRunProfile();
if (!forceCreate && current && String(current.id || '').startsWith('mnote_') && current.profile === runProfile) return current;
var response = await fetch('/api/hermes/client/sessions', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
workspaceId: resolveWorkspaceId(doc.body),
documentId: currentDocumentId(),
sourceKind: currentSourceKind(),
rootUri: currentRootUri(),
traceId: 'page-ai-' + Date.now().toString(36),
profile: runProfile,
agentId: pageAiCurrentAgentId(),
profileId: pageUiState.pageAiAcpRuntime === 'hermes' ? runProfile : '',
title: current && current.title ? current.title : '当前页问答'
})
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'hermes_session_failed_' + response.status));
}
var session = {
id: String(payload.sessionId || '').trim(),
title: String(payload.title || '当前页问答'),
agentId: pageAiCurrentAgentId(),
profileId: pageUiState.pageAiAcpRuntime === 'hermes' ? runProfile : '',
profile: String(payload.profile || runProfile).trim() || 'default',
acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
persistence: String(payload.persistence || '').trim(),
sessionStorage: String(payload.sessionStorage || '').trim(),
permissionLevel: String(payload.permissionLevel || '').trim(),
shareId: String(payload.shareId || '').trim(),
acpSessionId: String(payload.acpSessionId || payload.acp_session_id || '').trim(),
createdAt: Date.now(),
updatedAt: Date.now(),
messages: pageUiState.pageAiMessages.slice()
};
pageUiState.pageAiSessions = pageAiNormalizeSessions([session]);
pageUiState.pageAiActiveSessionId = session.id;
pageAiPersistSessions();
renderPageAiConversation();
return session;
}
async function pageAiRestoreHermesSession() {
var current = pageAiCurrentSession();
if (!current || !String(current.id || '').startsWith('mnote_')) return;
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(current.id) + '/resume?' + pageAiBackendSessionQuery({}), {
method: 'POST',
headers: { 'accept': 'application/json' }
});
if (!response.ok) return;
var payload = await response.json().catch(function(){ return null; });
if (payload && payload.persistence === 'convex_acp_runtime_store') {
pageAiApplyBackendSessionDetail(payload);
renderPageAiConversation();
renderPageAiControls();
return;
}
var session = payload && (payload.session || (payload.upstream && payload.upstream.session) || payload);
var messages = session && Array.isArray(session.messages) ? session.messages : [];
pageAiApplyRuntimeState(payload && payload.runtime);
if (session && (session.profile || session.profileName)) {
current.profile = String(session.profile || session.profileName || current.profile || pageAiCurrentProfile());
}
if (!messages.length) {
renderPageAiControls();
return;
}
pageUiState.pageAiMessages = messages.slice(-300).map(function(message) {
return {
role: message.role === 'user' ? 'user' : (message.role === 'tool' ? 'tool' : 'assistant'),
content: String(message.content || '')
};
});
current.messages = pageUiState.pageAiMessages.slice();
current.updatedAt = Date.now();
renderPageAiConversation();
renderPageAiControls();
}
function pageAiCurrentSession() {
return pageUiState.pageAiSessions.find(function(session) {
return session.id === pageUiState.pageAiActiveSessionId;
}) || null;
}
function pageAiSyncCurrentSessionMessages() {
var session = pageAiCurrentSession();
if (!session) return;
var runProfile = pageAiRunProfile();
session.messages = Array.isArray(pageUiState.pageAiMessages) ? pageUiState.pageAiMessages.slice(-300) : [];
session.agentId = pageAiCurrentAgentId();
session.profileId = pageUiState.pageAiAcpRuntime === 'hermes' ? runProfile : '';
session.profile = runProfile;
session.acpRuntime = pageUiState.pageAiAcpRuntime || 'reasonix';
session.updatedAt = Date.now();
}
function pageAiSetActiveSession(sessionId) {
var session = pageUiState.pageAiSessions.find(function(item) { return item.id === sessionId; });
if (!session) return;
pageUiState.pageAiActiveSessionId = session.id;
if (session.agentId) pageUiState.pageAiAgentId = pageAiNormalizeAgentId(session.agentId);
if (session.acpRuntime) pageUiState.pageAiAcpRuntime = session.acpRuntime;
if (session.profileId || session.profile) pageAiSetActiveProfile(session.profileId || session.profile);
pageUiState.pageAiMessages = Array.isArray(session.messages) ? session.messages.slice() : [];
pageUiState.pageAiPage = 'chat';
pageAiPersistSessions();
renderPageAiControls();
renderPageAiConversation();
if (session.source === 'acp' || String(session.id || '').startsWith('mnote_')) {
void pageAiLoadBackendSessionDetail(session.id).catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
}
}
function pageAiStartNewSession() {
var session = pageAiNewSession();
pageUiState.pageAiSessions = pageAiNormalizeSessions([session].concat(pageUiState.pageAiSessions));
pageUiState.pageAiActiveSessionId = session.id;
pageUiState.pageAiMessages = [];
pageUiState.pageAiPage = 'chat';
pageAiPersistSessions();
renderPageAiControls();
renderPageAiConversation();
}
async function pageAiRenameBackendSession(sessionId) {
var session = pageUiState.pageAiSessions.find(function(item) { return item.id === sessionId; });
if (!session) return;
var title = win.prompt('重命名 AI 会话', session.title || '当前页问答');
if (title === null) return;
title = String(title || '').trim();
if (!title) return;
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '/rename?' + pageAiBackendSessionQuery({}), {
method: 'POST',
headers: { 'content-type': 'application/json', 'accept': 'application/json' },
body: JSON.stringify({ title: title })
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'session_rename_failed_' + response.status));
}
session.title = String((payload.result && payload.result.title) || title);
session.updatedAt = Date.now();
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
}
async function pageAiDeleteBackendSession(sessionId) {
var session = pageUiState.pageAiSessions.find(function(item) { return item.id === sessionId; });
if (!session) return;
if (!win.confirm('确定删除 AI 会话“' + (session.title || sessionId) + '”吗?')) return;
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '?' + pageAiBackendSessionQuery({}), {
method: 'DELETE',
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'session_delete_failed_' + response.status));
}
pageUiState.pageAiSessions = pageUiState.pageAiSessions.filter(function(item) { return item.id !== sessionId; });
if (pageUiState.pageAiActiveSessionId === sessionId) {
var next = pageUiState.pageAiSessions[0] || pageAiNewSession();
if (!pageUiState.pageAiSessions.length) pageUiState.pageAiSessions = [next];
pageUiState.pageAiActiveSessionId = next.id;
pageUiState.pageAiMessages = Array.isArray(next.messages) ? next.messages.slice() : [];
}
pageAiPersistSessions();
renderPageAiConversation();
renderPageAiControls();
}
async function pageAiResumeBackendSession(sessionId) {
sessionId = String(sessionId || pageUiState.pageAiActiveSessionId || '').trim();
if (!sessionId) return;
pageAiSetActiveSession(sessionId);
var response = await fetch('/api/hermes/client/sessions/' + encodeURIComponent(sessionId) + '/resume?' + pageAiBackendSessionQuery({}), {
method: 'POST',
headers: { 'accept': 'application/json' }
});
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) {
throw new Error(pageAiErrorMessage(payload, 'session_resume_failed_' + response.status));
}
pageAiApplyBackendSessionDetail(payload);
pageUiState.pageAiPage = 'chat';
renderPageAiConversation();
renderPageAiControls();
}
return {
pageAiStorageKey,
pageAiBackendSessionQuery,
pageAiNewSession,
pageAiNormalizeSessions,
pageAiNormalizeBackendSessionRow,
pageAiMergeSessions,
pageAiSessionStorageLabel,
pageAiLoadSessions,
pageAiLoadBackendSessions,
pageAiMessageFromRuntimeEvent,
pageAiApplyBackendSessionDetail,
pageAiLoadBackendSessionDetail,
pageAiSearchBackendSessions,
pageAiPersistSessions,
pageAiEnsureHermesSession,
pageAiRestoreHermesSession,
pageAiCurrentSession,
pageAiSyncCurrentSessionMessages,
pageAiSetActiveSession,
pageAiStartNewSession,
pageAiRenameBackendSession,
pageAiDeleteBackendSession,
pageAiResumeBackendSession
};
}
@@ -0,0 +1,285 @@
export function createSidebarPageAiSkillRuntime(context) {
const {
pageAiCurrentAgentId,
pageAiCurrentProfile,
pageAiLoadSkills,
pageAiNormalizeArray,
pageAiPersistAiPreference,
pageAiPersistRawAiPreference,
pageAiProfileValue,
pageUiState,
renderPageAiControls,
} = context;
function pageAiSkillSourceOptions() {
var options = [
{ value: 'mnote', group: 'mnote', label: 'mnote', profile: '' },
{ value: 'reasonix', group: 'reasonix', label: 'reasonix', profile: '' }
];
pageAiNormalizeArray(pageUiState.pageAiProfiles).forEach(function(profile) {
var profileId = pageAiProfileValue(profile);
if (!profileId) return;
var label = profile.kind === 'shared'
? (profile.baseProfile === 'lite' || profileId === 'shared_lite' ? 'hermes_lite' : 'Hermes_shared')
: 'Hermes_user';
var alias = String(profile.alias || '').trim();
options.push({
value: 'hermes:' + profileId,
group: 'hermes',
profile: profileId,
label: alias && alias !== label ? label + ' · ' + alias : label,
readonly: profile.readonly === true
});
});
return options;
}
function pageAiDefaultSkillSource() {
if (pageAiCurrentAgentId() === 'hermes') return 'hermes:' + pageAiCurrentProfile();
if (pageAiCurrentAgentId() === 'reasonix') return 'reasonix';
return 'mnote';
}
function pageAiNormalizeSkillSource(source) {
var value = String(source || '').trim();
var options = pageAiSkillSourceOptions();
if (options.some(function(option) { return option.value === value; })) return value;
if (value === 'hermes') return 'hermes:' + pageAiCurrentProfile();
if (value === 'mnote_builtin') return 'mnote';
var fallback = pageAiDefaultSkillSource();
if (options.some(function(option) { return option.value === fallback; })) return fallback;
return options.length ? options[0].value : 'mnote';
}
function pageAiCurrentSkillSource() {
var normalized = pageAiNormalizeSkillSource(pageUiState.pageAiActiveSkillSource);
pageUiState.pageAiActiveSkillSource = normalized;
return normalized;
}
function pageAiSetSkillSource(source) {
var normalized = pageAiNormalizeSkillSource(source);
pageUiState.pageAiActiveSkillSource = normalized;
pageAiPersistAiPreference('skills.active_source', normalized);
pageUiState.pageAiSkills = { categories: [], archived: [] };
pageUiState.pageAiSkillError = '';
void pageAiLoadSkills();
renderPageAiControls();
}
function pageAiSkillSourceParts(source) {
var normalized = pageAiNormalizeSkillSource(source);
if (normalized.indexOf('hermes:') === 0) {
return { group: 'hermes', profile: normalized.slice('hermes:'.length), source: normalized };
}
if (normalized === 'reasonix') return { group: 'reasonix', profile: '', source: normalized };
return { group: 'mnote', profile: '', source: 'mnote' };
}
function pageAiCurrentSkillSourceLabel() {
var source = pageAiCurrentSkillSource();
var option = pageAiSkillSourceOptions().find(function(item) { return item.value === source; });
return option ? option.label : source;
}
function pageAiSkillOriginLabel(skill) {
var origin = String(skill && skill.origin || '').trim();
if (origin === 'generated' || String(skill && skill.createdBy || '') === 'agent') return '生成';
if (origin === 'installed') return '安装';
if (origin === 'builtin') return '内置';
if (origin === 'copied') return '本地';
var source = String(skill && skill.source || '').trim();
if (source === 'hub') return '安装';
if (source === 'builtin') return '内置';
if (source === 'reasonix') {
if (origin === 'project') return 'Reasonix 项目';
if (origin === 'global') return 'Reasonix 全局';
return 'Reasonix';
}
return '本地';
}
function pageAiSkillPreferenceKey(group, profile) {
var groupName = String(group || '').trim();
if (groupName === 'mnote') return 'ai.agent.mnote_builtin.skills.enabled';
if (groupName === 'reasonix') return 'ai.agent.reasonix.skills.enabled';
if (groupName === 'hermes') {
var nextProfile = String(profile || pageAiCurrentProfile() || 'default').trim() || 'default';
return 'ai.agent.hermes.profile.' + nextProfile + '.skills.enabled';
}
return '';
}
function pageAiSkillPreferenceTable(group, profile) {
var key = pageAiSkillPreferenceKey(group, profile);
var preferences = pageUiState.pageAiSkillPreferences || {};
var value = preferences[key];
if (value && typeof value === 'object' && !Array.isArray(value)) return value;
return {};
}
function pageAiHermesHideBuiltinPreferenceKey(profile) {
return 'ai.agent.hermes.skills.hide_builtin';
}
function pageAiHideHermesBuiltinSkills(profile) {
var preferences = pageUiState.pageAiSkillPreferences || {};
return preferences[pageAiHermesHideBuiltinPreferenceKey(profile)] === true;
}
function pageAiReasonixMemoryEnabled() {
var preferences = pageUiState.pageAiSkillPreferences || {};
return preferences['ai.agent.reasonix.memory_enabled'] === true;
}
function pageAiSetReasonixMemoryEnabled(enabled) {
var preferences = Object.assign({}, pageUiState.pageAiSkillPreferences || {});
preferences['ai.agent.reasonix.memory_enabled'] = Boolean(enabled);
pageUiState.pageAiSkillPreferences = preferences;
pageAiPersistRawAiPreference('ai.agent.reasonix.memory_enabled', Boolean(enabled));
renderPageAiControls();
}
function pageAiSetHideHermesBuiltinSkills(enabled, profile) {
var key = pageAiHermesHideBuiltinPreferenceKey(profile);
var preferences = Object.assign({}, pageUiState.pageAiSkillPreferences || {});
preferences[key] = Boolean(enabled);
pageUiState.pageAiSkillPreferences = preferences;
pageAiPersistRawAiPreference(key, Boolean(enabled));
renderPageAiControls();
}
function pageAiSkillIsBuiltin(skill) {
var origin = String(skill && skill.origin || '').trim();
var source = String(skill && skill.source || '').trim();
return origin === 'builtin' || source === 'builtin';
}
function pageAiToggleableSkillEntries(group, profile) {
var catalogKey = group === 'hermes' && profile ? 'hermes:' + profile : group;
var catalog = pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs[catalogKey]
? pageUiState.pageAiSkillCatalogs[catalogKey]
: pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs[group]
? pageUiState.pageAiSkillCatalogs[group]
: { categories: [], archived: [] };
var overrides = pageAiSkillPreferenceTable(group, profile);
var result = [];
pageAiNormalizeArray(catalog.categories).forEach(function(category) {
pageAiNormalizeArray(category.skills).forEach(function(skill) {
var id = String(skill.id || skill.name || '').trim();
if (!id) return;
result.push({
group: group,
profile: profile || '',
category: category.name,
id: id,
name: skill.name || id,
title: skill.title || skill.name || id,
description: skill.description || '',
enabled: group === 'hermes' ? skill.enabled !== false : (skill.enabled !== false && overrides[id] !== false),
toggleable: skill.toggleable !== false,
source: skill.source || group,
origin: skill.origin || '',
readOnly: skill.readOnly === true || skill.readonly === true || skill.configurable === false,
builtin: pageAiSkillIsBuiltin(skill),
configurable: skill.configurable !== false,
configScope: skill.configScope || '',
skillKind: skill.skillKind || '',
profileId: skill.profileId || '',
toolNames: skill.toolNames || [],
requiresContextRefs: skill.requiresContextRefs || []
});
});
});
pageAiNormalizeArray(catalog.archived).forEach(function(skill) {
var id = String(skill.id || skill.name || '').trim();
if (!id) return;
result.push({
group: group,
profile: profile || '',
category: 'archived',
id: id,
name: skill.name || id,
title: skill.title || skill.name || id,
description: skill.description || '',
enabled: group === 'hermes' ? skill.enabled !== false : (skill.enabled !== false && overrides[id] !== false),
toggleable: skill.toggleable !== false,
source: skill.source || group,
origin: skill.origin || '',
readOnly: skill.readOnly === true || skill.readonly === true || skill.configurable === false,
builtin: pageAiSkillIsBuiltin(skill),
configurable: skill.configurable !== false,
configScope: skill.configScope || '',
skillKind: skill.skillKind || '',
profileId: skill.profileId || '',
toolNames: skill.toolNames || [],
requiresContextRefs: skill.requiresContextRefs || []
});
});
return result;
}
function pageAiAllSkillEntries() {
return []
.concat(pageAiToggleableSkillEntries('mnote', ''))
.concat(pageAiToggleableSkillEntries('reasonix', ''))
.concat(pageAiToggleableSkillEntries('hermes', pageAiCurrentProfile()));
}
function pageAiSkillGroupCollapsed(group) {
var table = pageUiState.pageAiCollapsedSkillGroups || {};
return table[String(group || '').trim()] === true;
}
function pageAiToggleSkillGroup(group) {
var normalized = String(group || '').trim();
if (!normalized) return;
var table = Object.assign({}, pageUiState.pageAiCollapsedSkillGroups || {});
table[normalized] = table[normalized] !== true;
pageUiState.pageAiCollapsedSkillGroups = table;
pageAiPersistRawAiPreference('ai.common.skills.groups.collapsed', table);
renderPageAiControls();
}
function pageAiSetSkillPreference(group, skillId, enabled, profile) {
var key = pageAiSkillPreferenceKey(group, profile);
if (!key || !skillId) return;
var preferences = Object.assign({}, pageUiState.pageAiSkillPreferences || {});
var current = preferences[key] && typeof preferences[key] === 'object' && !Array.isArray(preferences[key])
? Object.assign({}, preferences[key])
: {};
current[skillId] = Boolean(enabled);
preferences[key] = current;
pageUiState.pageAiSkillPreferences = preferences;
pageAiPersistRawAiPreference(key, current);
}
function pageAiSkillEnabled(skill) {
return skill.enabled !== false;
}
return {
pageAiSkillSourceOptions,
pageAiDefaultSkillSource,
pageAiNormalizeSkillSource,
pageAiCurrentSkillSource,
pageAiSetSkillSource,
pageAiSkillSourceParts,
pageAiCurrentSkillSourceLabel,
pageAiSkillOriginLabel,
pageAiSkillPreferenceKey,
pageAiSkillPreferenceTable,
pageAiHermesHideBuiltinPreferenceKey,
pageAiHideHermesBuiltinSkills,
pageAiReasonixMemoryEnabled,
pageAiSetReasonixMemoryEnabled,
pageAiSetHideHermesBuiltinSkills,
pageAiSkillIsBuiltin,
pageAiToggleableSkillEntries,
pageAiAllSkillEntries,
pageAiSkillGroupCollapsed,
pageAiToggleSkillGroup,
pageAiSetSkillPreference,
pageAiSkillEnabled
};
}
@@ -0,0 +1,758 @@
export function createSidebarPageAiTargetRuntime(context) {
const {
currentDocumentId,
currentRootUri,
currentSourceKind,
currentPageOptions,
documentRef,
escapeHtml,
pageAiEnsureContextRefState,
pageUiState,
resolveWorkspaceId,
searchText,
pageAiNormalizeArray,
} = context;
function pageAiCloneJson(value) {
if (value == null) return null;
try {
return JSON.parse(JSON.stringify(value));
} catch (_) {
return null;
}
}
function localMarkdownRelativePathFromPageAiDocumentId(documentId) {
var value = String(documentId || '').trim();
if (!value.startsWith('local-md:')) return '';
return value.slice('local-md:'.length).replace(/~2F/g, '/');
}
function localMarkdownDocumentIdFromPageAiRelativePath(relativePath) {
var normalized = String(relativePath || '').trim().replace(/\\/g, '/').replace(/^\/+/, '');
if (!normalized) return '';
return 'local-md:' + normalized.split('/').map(function(segment) {
return encodeURIComponent(segment).replace(/%20/g, '~20');
}).join('~2F');
}
function pageAiWorkspacePathForDocument(documentId, seed) {
var relativePath = String(seed && seed.relativePath || localMarkdownRelativePathFromPageAiDocumentId(documentId) || '').trim().replace(/\\/g, '/').replace(/^\/+/, '');
var resolvedDocumentId = String(documentId || seed && seed.documentId || '').trim()
|| localMarkdownDocumentIdFromPageAiRelativePath(relativePath);
return {
schema: 'mnote.workspace_path.v1',
workspaceId: String(seed && seed.workspaceId || resolveWorkspaceId(documentRef.body) || '').trim(),
sourceKind: String(seed && seed.sourceKind || currentSourceKind() || '').trim(),
rootUri: String(seed && seed.rootUri || currentRootUri() || '').trim(),
relativePath: relativePath,
documentId: resolvedDocumentId,
objectIdentity: seed && seed.objectIdentity && typeof seed.objectIdentity === 'object'
? seed.objectIdentity
: String(seed && seed.objectIdentity || resolvedDocumentId || '').trim(),
assetId: String(seed && seed.assetId || '').trim(),
resourceKind: String(seed && seed.resourceKind || 'markdown_page').trim()
};
}
function pageAiResourceKindForTarget(entry) {
var kind = String(entry && (entry.editorKind || entry.kind) || '').trim().toLowerCase();
var assetId = String(entry && entry.assetId || '').trim();
var path = String(entry && entry.path || '').trim().toLowerCase();
if (kind === 'page' || kind === 'markdown_page') return 'markdown_page';
if (kind === 'mindmap' || path.endsWith('.mindmap.json')) return 'mindmap';
if (kind === 'office' || kind === 'onlyoffice' || kind === 'only_office' || /\.(doc|docx|ppt|pptx|xls|xlsx)$/.test(path)) return 'only_office';
if (kind === 'resource' && assetId) return 'resource';
return kind || 'markdown_page';
}
function pageAiTargetId(entry) {
if (!entry || typeof entry !== 'object') return '';
var workspacePath = entry.workspacePath && typeof entry.workspacePath === 'object' ? entry.workspacePath : {};
var entryIdentity = typeof entry.objectIdentity === 'string' ? entry.objectIdentity : '';
var workspaceIdentity = typeof workspacePath.objectIdentity === 'string' ? workspacePath.objectIdentity : '';
return String(entryIdentity || workspaceIdentity || entry.documentId || entry.assetId || entry.path || '').trim();
}
function pageAiWorkspacePathForTarget(entry) {
var seed = Object.assign({}, entry && entry.workspacePath && typeof entry.workspacePath === 'object' ? entry.workspacePath : {});
var resourceKind = pageAiResourceKindForTarget(entry);
if (!seed.relativePath && entry && entry.path) seed.relativePath = entry.path;
if (!seed.assetId && entry && entry.assetId) seed.assetId = entry.assetId;
var seedResourceKind = String(seed.resourceKind || '').trim();
if (!seedResourceKind || seedResourceKind === 'page' || seedResourceKind === 'office') seed.resourceKind = resourceKind;
if (!seed.objectIdentity && entry && entry.objectIdentity) seed.objectIdentity = entry.objectIdentity;
if (!seed.workspaceId && entry && entry.workspaceId) seed.workspaceId = entry.workspaceId;
return pageAiWorkspacePathForDocument(entry && entry.documentId || currentDocumentId(), seed);
}
function pageAiTargetFromOpenEditor(entry, source) {
if (!entry || typeof entry !== 'object') return null;
var workspacePath = pageAiWorkspacePathForTarget(entry);
var targetId = pageAiTargetId(entry) || workspacePath.objectIdentity || workspacePath.documentId;
if (!targetId) return null;
var objectIdentity = typeof entry.objectIdentity === 'string'
? entry.objectIdentity
: (typeof workspacePath.objectIdentity === 'string' ? workspacePath.objectIdentity : targetId);
return {
schema: 'mnote.ai_editor_target.v1',
source: source || 'open_editors_snapshot',
targetId: targetId,
objectIdentity: objectIdentity,
workspacePath: workspacePath,
paneRole: entry.paneRole || 'primary',
documentId: entry.documentId || workspacePath.documentId,
workspaceId: entry.workspaceId || workspacePath.workspaceId || resolveWorkspaceId(documentRef.body),
editorKind: entry.editorKind || entry.kind || workspacePath.resourceKind,
resourceKind: workspacePath.resourceKind,
title: entry.title || '',
active: entry.active === true,
dirtyState: entry.dirtyState || '',
preview: entry.preview === true,
pinned: entry.pinned === true,
lastActiveAt: entry.lastActiveAt || 0,
assetId: entry.assetId || workspacePath.assetId || '',
path: entry.path || workspacePath.relativePath || '',
onlyofficeSessionId: entry.onlyofficeSessionId || entry.bridgeSessionId || '',
bridgeSessionId: entry.bridgeSessionId || entry.onlyofficeSessionId || '',
bridgeSessionReady: entry.bridgeSessionReady === true
};
}
function normalizeOpenEditorEntry(entry) {
if (!entry || typeof entry !== 'object') return null;
return {
objectIdentity: String(entry.objectIdentity || '').trim(),
workspacePath: entry.workspacePath && typeof entry.workspacePath === 'object' ? entry.workspacePath : null,
paneRole: String(entry.paneRole || 'primary').trim() === 'secondary' ? 'secondary' : 'primary',
documentId: String(entry.documentId || '').trim(),
workspaceId: String(entry.workspaceId || '').trim(),
title: String(entry.title || '').trim(),
kind: String(entry.kind || entry.editorKind || '').trim(),
editorKind: String(entry.editorKind || entry.kind || '').trim(),
active: entry.active === true,
dirtyState: String(entry.dirtyState || entry.dirtyGuard || '').trim(),
preview: entry.preview === true,
pinned: entry.pinned === true,
lastActiveAt: Number(entry.lastActiveAt || 0) || 0,
assetId: String(entry.assetId || '').trim(),
path: String(entry.path || '').trim(),
onlyofficeSessionId: String(entry.onlyofficeSessionId || entry.bridgeSessionId || '').trim(),
bridgeSessionId: String(entry.bridgeSessionId || entry.onlyofficeSessionId || '').trim(),
bridgeSessionReady: entry.bridgeSessionReady === true
};
}
function currentPageAiOpenEditorsSnapshot() {
var snapshot = null;
try {
if (window.__mnoteDocumentPaneRuntime && typeof window.__mnoteDocumentPaneRuntime.getOpenEditorsSnapshot === 'function') {
snapshot = window.__mnoteDocumentPaneRuntime.getOpenEditorsSnapshot();
}
} catch (_) {}
if (!snapshot || typeof snapshot !== 'object') snapshot = window.__mnoteOpenEditorsSnapshot || null;
if (!snapshot || typeof snapshot !== 'object') return null;
var editors = Array.isArray(snapshot.editors)
? snapshot.editors.map(normalizeOpenEditorEntry).filter(Boolean)
: [];
var resources = Array.isArray(snapshot.resourceEditors)
? snapshot.resourceEditors.map(normalizeOpenEditorEntry).filter(Boolean)
: editors.filter(function(entry) { return entry.kind !== 'page'; });
var normalizeGroup = function(group, paneRole) {
var groupEditors = group && Array.isArray(group.editors)
? group.editors.map(normalizeOpenEditorEntry).filter(Boolean)
: editors.filter(function(entry) { return entry.paneRole === paneRole; });
var groupResources = group && Array.isArray(group.resourceEditors)
? group.resourceEditors.map(normalizeOpenEditorEntry).filter(Boolean)
: resources.filter(function(entry) { return entry.paneRole === paneRole; });
var groupActive = groupEditors.find(function(entry) { return entry.active; }) || null;
return {
paneRole: paneRole,
activeObjectIdentity: String(group && group.activeObjectIdentity || (groupActive ? groupActive.objectIdentity : '') || '').trim(),
editors: groupEditors,
resourceEditors: groupResources
};
};
var groups = {
primary: normalizeGroup(snapshot.groups && snapshot.groups.primary, 'primary'),
secondary: normalizeGroup(snapshot.groups && snapshot.groups.secondary, 'secondary')
};
var activeObjectIdentity = String(snapshot.activeObjectIdentity || '').trim();
var allTargets = editors.concat(resources);
var activeEditor = allTargets.find(function(entry) {
return entry.active && (!activeObjectIdentity || entry.objectIdentity === activeObjectIdentity);
}) || groups.primary.editors.find(function(entry) {
return entry.active;
}) || groups.primary.resourceEditors.find(function(entry) {
return entry.active;
}) || groups.secondary.editors.find(function(entry) {
return entry.active;
}) || groups.secondary.resourceEditors.find(function(entry) {
return entry.active;
}) || null;
return {
schema: String(snapshot.schema || 'mnote.open_editors_snapshot.v1'),
generatedAt: Number(snapshot.generatedAt || 0) || Date.now(),
activeObjectIdentity: activeObjectIdentity || (activeEditor ? activeEditor.objectIdentity : ''),
activeEditor: activeEditor,
editors: editors,
resourceEditors: resources,
groups: groups
};
}
function pageAiFallbackEditorTarget() {
var fallbackDocumentId = currentDocumentId();
var fallbackWorkspacePath = pageAiWorkspacePathForDocument(fallbackDocumentId, null);
var fallbackTargetId = fallbackWorkspacePath.objectIdentity || fallbackDocumentId || '';
return {
schema: 'mnote.ai_editor_target.v1',
source: 'fallback_current_document',
targetId: fallbackTargetId,
objectIdentity: fallbackTargetId,
workspacePath: fallbackWorkspacePath,
paneRole: 'primary',
documentId: fallbackDocumentId,
workspaceId: resolveWorkspaceId(documentRef.body),
editorKind: 'page',
active: true,
dirtyState: '',
preview: false,
pinned: true,
lastActiveAt: Date.now(),
assetId: '',
path: ''
};
}
function pageAiEditorTargetCandidates() {
var snapshot = currentPageAiOpenEditorsSnapshot();
var entries = [];
if (snapshot) {
entries = pageAiNormalizeArray(snapshot.editors).concat(pageAiNormalizeArray(snapshot.resourceEditors));
}
var seen = {};
var targets = entries.map(function(entry) {
return pageAiTargetFromOpenEditor(entry, 'open_editors_snapshot');
}).filter(function(target) {
var id = String(target && target.targetId || '').trim();
if (!id || seen[id]) return false;
seen[id] = true;
return true;
});
if (!targets.length) targets.push(pageAiFallbackEditorTarget());
return targets;
}
function currentPageAiEditorTarget() {
var targets = pageAiEditorTargetCandidates();
var selectedId = String(pageUiState.pageAiSelectedTargetId || '').trim();
var selected = selectedId ? targets.find(function(target) { return target.targetId === selectedId; }) : null;
return selected
|| targets.find(function(target) { return target.active === true; })
|| targets[0]
|| pageAiFallbackEditorTarget();
}
function currentPageAiPageEditorTarget() {
var snapshot = currentPageAiOpenEditorsSnapshot();
var documentId = currentDocumentId();
var workspaceId = resolveWorkspaceId(documentRef.body);
var sourceKind = currentSourceKind();
var rootUri = currentRootUri();
var relativePath = localMarkdownRelativePathFromPageAiDocumentId(documentId);
var fallbackWorkspacePath = pageAiWorkspacePathForDocument(documentId, {
relativePath: relativePath
});
var pageEditor = snapshot && Array.isArray(snapshot.editors)
? snapshot.editors.find(function(entry) {
return entry
&& String(entry.editorKind || entry.kind || '') === 'page'
&& String(entry.documentId || '').trim() === String(documentId || '').trim();
})
: null;
var workspacePath = pageEditor && pageEditor.workspacePath
? Object.assign({}, pageEditor.workspacePath, {
workspaceId: workspaceId,
sourceKind: sourceKind,
rootUri: rootUri,
relativePath: relativePath,
documentId: documentId,
resourceKind: pageEditor.workspacePath.resourceKind || 'markdown_page',
objectIdentity: pageEditor.workspacePath.objectIdentity || fallbackWorkspacePath.objectIdentity
})
: fallbackWorkspacePath;
var objectIdentity = String(pageEditor && pageEditor.objectIdentity || workspacePath.objectIdentity || documentId || '').trim();
return {
schema: 'mnote.ai_editor_target.v1',
source: pageEditor ? 'current_page_from_open_editors_snapshot' : 'current_page_fallback',
targetId: objectIdentity,
objectIdentity: objectIdentity,
workspacePath: Object.assign({}, workspacePath, { objectIdentity: objectIdentity }),
paneRole: String(pageEditor && pageEditor.paneRole || 'primary').trim() === 'secondary' ? 'secondary' : 'primary',
documentId: documentId,
workspaceId: workspaceId,
editorKind: 'page',
active: pageEditor ? pageEditor.active === true : true,
dirtyState: String(pageEditor && pageEditor.dirtyState || '').trim(),
preview: pageEditor ? pageEditor.preview === true : false,
pinned: true,
lastActiveAt: Number(pageEditor && pageEditor.lastActiveAt || 0) || Date.now(),
assetId: '',
path: relativePath
};
}
function currentPageAiScopedEditorTarget() {
var selected = pageAiEnsureContextRefState();
return selected.active_editor ? currentPageAiEditorTarget() : currentPageAiPageEditorTarget();
}
function pageAiSetRunTargetSnapshot(snapshot) {
pageUiState.pageAiCurrentRunTargetSnapshot = snapshot || null;
var target = snapshot && snapshot.editorTarget ? snapshot.editorTarget : null;
var documentId = String(target && target.documentId || snapshot && snapshot.documentId || '').trim();
var workspaceId = String(target && target.workspaceId || snapshot && snapshot.workspaceId || '').trim();
var rootUri = String(target && target.workspacePath && target.workspacePath.rootUri || snapshot && snapshot.rootUri || '').trim();
if (documentId) documentRef.documentElement.setAttribute('data-mnote-page-ai-run-target-document-id', documentId);
if (workspaceId) documentRef.documentElement.setAttribute('data-mnote-page-ai-run-target-workspace-id', workspaceId);
if (rootUri) documentRef.documentElement.setAttribute('data-mnote-page-ai-run-target-root-uri', rootUri);
}
function assertPageAiTargetInCurrentWorkspace(editorTarget) {
var target = editorTarget && typeof editorTarget === 'object' ? editorTarget : {};
var workspacePath = target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {};
var targetSourceKind = String(workspacePath.sourceKind || '').trim();
var currentKind = String(currentSourceKind() || '').trim();
if (targetSourceKind && currentKind && targetSourceKind !== currentKind) {
var sourceError = new Error('AI target 与当前页面 sourceKind 不一致,请重新选择当前工作区内的目标。');
sourceError.code = 'page_ai_target_workspace_mismatch';
throw sourceError;
}
var targetRootUri = String(workspacePath.rootUri || '').trim();
var currentRoot = String(currentRootUri() || '').trim();
if (targetRootUri && currentRoot && targetRootUri !== currentRoot) {
var rootError = new Error('AI target 与当前本地工作区 rootUri 不一致,请重新选择当前工作区内的目标。');
rootError.code = 'page_ai_target_workspace_mismatch';
throw rootError;
}
var targetWorkspaceId = String(target.workspaceId || workspacePath.workspaceId || '').trim();
var currentWorkspaceId = String(resolveWorkspaceId(documentRef.body) || '').trim();
if (targetWorkspaceId && currentWorkspaceId && targetWorkspaceId !== currentWorkspaceId) {
var workspaceError = new Error('AI target 与当前 workspaceId 不一致,请重新选择当前工作区内的目标。');
workspaceError.code = 'page_ai_target_workspace_mismatch';
throw workspaceError;
}
}
function pageAiBuildContextRefs(scopedContext, runTargetSnapshot) {
var selected = pageAiEnsureContextRefState();
var refs = [];
var documentId = currentDocumentId();
var rootUri = currentRootUri();
var workspaceId = resolveWorkspaceId(documentRef.body);
var editorTarget = scopedContext && scopedContext.editorTarget ? scopedContext.editorTarget : currentPageAiEditorTarget();
if (selected.current_page) {
refs.push({
kind: 'current_page',
documentId: documentId,
rootUri: rootUri,
workspaceId: workspaceId
});
}
if (selected.selection && scopedContext && scopedContext.selectedText) {
refs.push({
kind: 'selection',
documentId: documentId,
rootUri: rootUri,
selectedBlockId: scopedContext.selectedBlockId || ''
});
}
if (selected.active_editor && editorTarget) {
var workspacePath = editorTarget.workspacePath && typeof editorTarget.workspacePath === 'object' ? editorTarget.workspacePath : {};
refs.push({
kind: 'active_editor',
documentId: editorTarget.documentId || documentId,
workspaceId: editorTarget.workspaceId || workspacePath.workspaceId || workspaceId,
rootUri: workspacePath.rootUri || rootUri,
relativePath: workspacePath.relativePath || '',
editorKind: editorTarget.editorKind || '',
resourceKind: editorTarget.resourceKind || workspacePath.resourceKind || '',
targetId: editorTarget.targetId || workspacePath.objectIdentity || '',
objectIdentity: editorTarget.objectIdentity || workspacePath.objectIdentity || '',
assetId: editorTarget.assetId || workspacePath.assetId || '',
onlyofficeSessionId: editorTarget.onlyofficeSessionId || editorTarget.bridgeSessionId || '',
bridgeSessionId: editorTarget.bridgeSessionId || editorTarget.onlyofficeSessionId || ''
});
}
if (selected.file) {
refs.push({
kind: 'file',
documentId: documentId,
rootUri: rootUri,
relativePath: localMarkdownRelativePathFromPageAiDocumentId(documentId)
});
}
if (selected.folder) {
refs.push({
kind: 'folder',
rootUri: rootUri,
relativePath: ''
});
}
if (selected.changed_files) {
refs.push({
kind: 'changed_files',
rootUri: rootUri,
sinceRunTargetSnapshot: runTargetSnapshot && runTargetSnapshot.frozenAt || null
});
}
return refs.filter(function(ref) {
return ref && String(ref.kind || '').trim();
});
}
function pageAiBuildAllowedRoots() {
return pageAiNormalizeArray(pageUiState.pageAiAllowedRoots).map(function(root) {
return {
rootUri: root.rootUri,
permission: root.permission === 'write' || root.permission === 'read_write' ? 'write' : 'read',
recursive: root.recursive !== false,
source: root.source === 'auto' || root.source === 'user' || root.source === 'admin'
? 'sqlite_directory_grant'
: (root.source || 'sqlite_directory_grant'),
grantId: root.id || ''
};
});
}
function pageAiBuildRunTargetSnapshot(scopedContext, prompt) {
var aiContext = scopedContext && scopedContext.pageContext && scopedContext.pageContext.aiContext
? scopedContext.pageContext.aiContext
: {};
var editorTarget = scopedContext && scopedContext.editorTarget ? scopedContext.editorTarget : currentPageAiEditorTarget();
return {
schema: 'mnote.page_ai_run_target_snapshot.v1',
source: 'open_editors_snapshot',
frozenAt: Date.now(),
workspaceId: resolveWorkspaceId(documentRef.body),
documentId: currentDocumentId(),
sourceKind: currentSourceKind(),
rootUri: currentRootUri(),
contextScope: pageUiState.pageAiContextScope || 'page',
promptPreview: searchText(prompt || '').slice(0, 160),
editorTarget: pageAiCloneJson(editorTarget),
activeEditorTarget: pageAiCloneJson(aiContext.activeEditorTarget || editorTarget),
openEditorsSnapshot: pageAiCloneJson(aiContext.openEditorsSnapshot || currentPageAiOpenEditorsSnapshot())
};
}
function pageAiContextKindsFromRefs(contextRefs) {
var kinds = {};
pageAiNormalizeArray(contextRefs).forEach(function(ref) {
var kind = String(ref && ref.kind || '').trim();
if (kind) kinds[kind] = true;
});
return kinds;
}
function pageAiPageContextForRefs(pageContext, contextRefs) {
var cloned = pageAiCloneJson(pageContext) || {};
var aiContext = cloned.aiContext && typeof cloned.aiContext === 'object' ? cloned.aiContext : {};
var kinds = pageAiContextKindsFromRefs(contextRefs);
delete cloned.documentBlocks;
delete cloned.evidence;
delete aiContext.contextBlocks;
delete aiContext.pageText;
delete aiContext.pageXml;
delete aiContext.truncated;
delete aiContext.warnings;
if (!kinds.selection) {
delete aiContext.selectedText;
delete aiContext.selectedBlockIds;
delete aiContext.selectedBlocks;
delete aiContext.allowedTargetBlockIds;
}
cloned.aiContext = aiContext;
return cloned;
}
function pageAiBuildAgentTargetPackage(scopedContext, runTargetSnapshot) {
var target = scopedContext && scopedContext.editorTarget ? scopedContext.editorTarget : currentPageAiEditorTarget();
var workspacePath = target && target.workspacePath && typeof target.workspacePath === 'object'
? pageAiWorkspacePathForDocument(target.documentId || currentDocumentId(), target.workspacePath)
: pageAiWorkspacePathForDocument(target && target.documentId || currentDocumentId(), null);
var relativePath = String(workspacePath.relativePath || '').trim();
var allowedFiles = relativePath ? [relativePath] : [];
var writable = pageAiBuildAllowedRoots().some(function(root) {
return String(root && root.rootUri || '').trim() === String(workspacePath.rootUri || '').trim()
&& String(root && root.permission || '').trim() === 'write';
});
var primaryTargetId = String(target && target.targetId || workspacePath.objectIdentity || workspacePath.documentId || '').trim();
var onlyofficeSessionId = String(target && (target.onlyofficeSessionId || target.bridgeSessionId) || '').trim();
var targetEntry = {
targetId: primaryTargetId,
objectIdentity: primaryTargetId,
documentId: workspacePath.documentId,
workspaceId: workspacePath.workspaceId,
sourceKind: workspacePath.sourceKind,
rootUri: workspacePath.rootUri,
relativePath: relativePath,
resourceKind: workspacePath.resourceKind,
assetId: workspacePath.assetId || target && target.assetId || '',
onlyofficeSessionId: onlyofficeSessionId,
bridgeSessionId: onlyofficeSessionId,
paneRole: target && target.paneRole || 'primary',
title: target && target.title || '',
policy: {
permission: allowedFiles.length && writable ? 'read_write' : 'read',
writeRequiresCleanBuffer: true,
conflictPolicy: 'fail_on_dirty_or_stale'
}
};
return {
schema: 'mnote.agent_target_package.v1',
source: 'page_ai_run_target_snapshot',
frozenAt: runTargetSnapshot && runTargetSnapshot.frozenAt || Date.now(),
primaryTargetId: primaryTargetId,
onlyofficeSessionId: onlyofficeSessionId,
bridgeSessionId: onlyofficeSessionId,
workspaceId: workspacePath.workspaceId,
sourceKind: workspacePath.sourceKind,
rootUri: workspacePath.rootUri,
documentId: workspacePath.documentId,
objectIdentity: primaryTargetId,
resourceKind: workspacePath.resourceKind,
workspacePath: workspacePath,
currentFile: relativePath ? {
rootUri: workspacePath.rootUri,
relativePath: relativePath,
documentId: workspacePath.documentId,
objectIdentity: primaryTargetId,
resourceKind: workspacePath.resourceKind
} : null,
allowedFiles: allowedFiles,
targets: [targetEntry],
policy: {
writeRequiresExplicitTarget: true,
allowedFilesSource: 'selected_page_ai_target',
conflictPolicy: 'fail_on_dirty_or_stale'
}
};
}
function pageAiBlockingDirtyState(dirtyState) {
var state = String(dirtyState || '').trim();
var normalized = state.toLowerCase();
if (normalized === 'dirty') return 'Dirty';
if (normalized === 'stale') return 'Stale';
if (normalized === 'deleted') return 'Deleted';
if (normalized === 'externalmodified' || normalized === 'external-change-conflict' || normalized === 'hasexternalconflict') return 'ExternalModified';
return '';
}
async function fetchPageAiTargetBufferState(editorTarget) {
if (currentSourceKind() !== 'local_folder') return null;
var target = editorTarget && typeof editorTarget === 'object' ? editorTarget : {};
var workspacePath = target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {};
var resourceKind = String(target.resourceKind || target.editorKind || workspacePath.resourceKind || '').trim();
if (resourceKind === 'office' || resourceKind === 'only_office' || resourceKind === 'onlyoffice') return null;
var documentId = String(target.documentId || currentDocumentId() || '').trim();
var rootUri = String(workspacePath.rootUri || currentRootUri() || '').trim();
if (!documentId || !rootUri) return null;
var relativePath = String(workspacePath.relativePath || '').trim()
|| localMarkdownRelativePathFromPageAiDocumentId(documentId);
var url = new URL('/api/documents/buffer-state', window.location.origin);
url.searchParams.set('documentId', documentId);
url.searchParams.set('sourceKind', 'local_folder');
url.searchParams.set('rootUri', rootUri);
var workspaceId = String(target.workspaceId || resolveWorkspaceId(documentRef.body) || '').trim();
if (workspaceId) url.searchParams.set('workspaceId', workspaceId);
if (relativePath) url.searchParams.set('relativePath', relativePath);
try {
var response = await fetch(url.toString(), { cache: 'no-store', headers: { accept: 'application/json' } });
var payload = await response.json().catch(function() { return null; });
if (!response.ok || !payload || payload.ok !== true) return null;
return payload.result || null;
} catch (_) {
return null;
}
}
async function assertPageAiTargetWritable(editorTarget) {
var workspacePath = editorTarget && editorTarget.workspacePath && typeof editorTarget.workspacePath === 'object' ? editorTarget.workspacePath : {};
var resourceKind = String(editorTarget && (editorTarget.resourceKind || editorTarget.editorKind) || workspacePath.resourceKind || '').trim();
var onlyofficeSessionId = String(editorTarget && (editorTarget.onlyofficeSessionId || editorTarget.bridgeSessionId) || '').trim();
if ((resourceKind === 'office' || resourceKind === 'only_office' || resourceKind === 'onlyoffice') && !onlyofficeSessionId) {
var sessionError = new Error('ONLYOFFICE 资源仍在连接 MNote bridge,请等待 Office 页面加载完成后再让 AI 操作。');
sessionError.code = 'page_ai_onlyoffice_bridge_not_ready';
throw sessionError;
}
var snapshotState = pageAiBlockingDirtyState(editorTarget && editorTarget.dirtyState);
var bufferState = await fetchPageAiTargetBufferState(editorTarget);
var bufferDirtyState = pageAiBlockingDirtyState(bufferState && bufferState.dirtyState);
var blockedState = bufferDirtyState || snapshotState;
if (!blockedState) return bufferState;
var documentId = String(editorTarget && editorTarget.documentId || currentDocumentId() || '').trim();
var error = new Error('目标文档存在未保存或外部变更状态(' + blockedState + '),请先保存、解决冲突或刷新后再让 AI 写入。');
error.code = 'page_ai_target_buffer_not_writable';
error.documentId = documentId;
error.dirtyState = blockedState;
throw error;
}
function currentPageAiSelectedText() {
try {
var selection = window.getSelection ? window.getSelection() : null;
return selection ? searchText(selection.toString() || '') : '';
} catch (_) {
return '';
}
}
function pageAiProjectionBlocks(aggregate) {
var blocks = aggregate && aggregate.body && aggregate.body.blockDocument && aggregate.body.blockDocument.blocks;
return Array.isArray(blocks) ? blocks : [];
}
function pageAiBlockText(block) {
return searchText(block && (block.text || block.title || block.content) || '');
}
function pageAiSelectedBlockIdsFromSelection() {
try {
var selection = window.getSelection ? window.getSelection() : null;
if (!selection || selection.rangeCount === 0 || searchText(selection.toString() || '') === '') return [];
var range = selection.getRangeAt(0);
var editor = documentRef.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror');
if (!(editor instanceof HTMLElement)) return [];
return Array.from(editor.children).filter(function(node) {
if (!(node instanceof HTMLElement)) return false;
try {
return range.intersectsNode(node);
} catch (_) {
return false;
}
}).map(function(node) {
return searchText(node.getAttribute('data-id') || node.id || '');
}).filter(Boolean);
} catch (_) {
return [];
}
}
function pageAiBlocksToPageXml(blocks, aggregate) {
var revision = aggregate && aggregate.body ? String(aggregate.body.revision || '') : '';
var pageId = currentDocumentId() || 'current-page';
var lines = ['<page id="' + escapeHtml(pageId) + '" revision="' + escapeHtml(revision) + '">'];
blocks.forEach(function(block) {
var blockId = String(block && (block.blockId || block.id) || '');
var type = String(block && block.type || 'paragraph');
var revisionRef = String(block && block.revisionRef || '');
var level = block && block.attrs && block.attrs.level ? ' level="' + escapeHtml(block.attrs.level) + '"' : '';
lines.push(' <block id="' + escapeHtml(blockId) + '" type="' + escapeHtml(type) + '" revisionRef="' + escapeHtml(revisionRef) + '"' + level + '>' + escapeHtml(pageAiBlockText(block)) + '</block>');
});
lines.push('</page>');
return lines.join('\n');
}
function buildPageAiContext(contextSnapshot, scope, selectedText) {
var aggregate = contextSnapshot.aggregate || {};
var body = aggregate.body || {};
var allBlocks = pageAiProjectionBlocks(aggregate);
var selectedBlockIds = scope === 'selection' ? pageAiSelectedBlockIdsFromSelection() : [];
var selectedSet = {};
selectedBlockIds.forEach(function(id) { selectedSet[id] = true; });
var selectedBlocks = selectedBlockIds.length
? allBlocks.filter(function(block) { return selectedSet[String(block.blockId || block.id || '')]; })
: [];
var contextBlocks = selectedBlocks.length ? selectedBlocks : allBlocks.slice(0, 120);
var truncated = !selectedBlocks.length && allBlocks.length > contextBlocks.length;
return {
schema: 'mnote.page_ai_context.v1',
workspaceId: resolveWorkspaceId(documentRef.body),
documentId: currentDocumentId(),
activeEditorTarget: currentPageAiScopedEditorTarget(),
openEditorsSnapshot: currentPageAiOpenEditorsSnapshot(),
scope: scope,
revision: body.revision || null,
conflictDetectionKey: body.conflictDetectionKey || null,
selectedText: selectedText || '',
selectedBlockIds: selectedBlockIds,
allowedTargetBlockIds: selectedBlockIds,
selectedBlocks: selectedBlocks,
contextBlocks: contextBlocks,
pageText: contextBlocks.map(pageAiBlockText).filter(Boolean).join('\n'),
pageXml: pageAiBlocksToPageXml(contextBlocks, aggregate),
truncated: truncated,
warnings: truncated ? [{ code: 'page_ai_context_truncated', message: '页面 AI context 已按前 120 个块裁剪' }] : []
};
}
function pageAiScopedPageContext(contextSnapshot) {
var aggregate = contextSnapshot.aggregate || {};
var body = contextSnapshot.body || {};
var subtree = contextSnapshot.subtree || null;
var outline = subtree && subtree.outline ? subtree.outline : null;
var scope = pageUiState.pageAiContextScope || 'page';
var title = aggregate.head && aggregate.head.title ? aggregate.head.title : '';
var selectedText = scope === 'selection' ? currentPageAiSelectedText() : '';
var aiContext = buildPageAiContext(contextSnapshot, scope, selectedText);
var editorTarget = aiContext.activeEditorTarget || currentPageAiScopedEditorTarget();
return {
pageContext: {
contextScope: scope,
documentBlocks: null,
node: {
documentId: currentDocumentId(),
title: title
},
subtree: null,
outline: null,
pageSubtreeSource: scope === 'page' ? (contextSnapshot.pageSubtreeSource || 'none') : 'scope:' + scope,
evidence: selectedText ? [{ kind: 'selection', text: selectedText }] : null,
pageOptions: scope === 'options' || scope === 'page' ? currentPageOptions() : null,
contentAccess: 'mnote.context.read_current_page',
aiContext: aiContext
},
editorTarget: editorTarget,
selectedText: selectedText,
selectedBlockId: aiContext.selectedBlockIds[0] || null
};
}
return {
currentPageAiSelectedText,
currentPageAiEditorTarget,
currentPageAiPageEditorTarget,
currentPageAiScopedEditorTarget,
currentPageAiOpenEditorsSnapshot,
pageAiBlockText,
pageAiBlockingDirtyState,
pageAiBlocksToPageXml,
pageAiBuildAgentTargetPackage,
pageAiBuildAllowedRoots,
pageAiBuildContextRefs,
pageAiBuildRunTargetSnapshot,
pageAiCloneJson,
pageAiContextKindsFromRefs,
pageAiEditorTargetCandidates,
pageAiFallbackEditorTarget,
pageAiPageContextForRefs,
pageAiProjectionBlocks,
pageAiResourceKindForTarget,
pageAiSelectedBlockIdsFromSelection,
pageAiSetRunTargetSnapshot,
pageAiScopedPageContext,
pageAiTargetFromOpenEditor,
pageAiTargetId,
pageAiWorkspacePathForDocument,
pageAiWorkspacePathForTarget,
assertPageAiTargetInCurrentWorkspace,
assertPageAiTargetWritable,
buildPageAiContext,
fetchPageAiTargetBufferState,
localMarkdownDocumentIdFromPageAiRelativePath,
localMarkdownRelativePathFromPageAiDocumentId,
};
}
@@ -262,6 +262,9 @@ export function createSidebarPageSettingsRuntime(context) {
var shell = document.querySelector('.document-shell');
var editorRoot = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
var editorSurface = editorRoot && editorRoot.querySelector('.editor-surface');
var mindmapWidthPreference = currentPageWidthPreferences().mindmap || DEFAULT_PAGE_WIDTH_PREFERENCES.mindmap;
var mindmapMaxWidth = String(mindmapWidthPreference.cssMaxWidth || pageWidthCssMaxWidth(mindmapWidthPreference.resolvedMode));
var mindmapMaxWidthValue = mindmapMaxWidth === 'none' ? 'none' : mindmapMaxWidth;
if (shell instanceof HTMLElement) {
var widthPreference = activeResourceWidthPreference(options);
var cssMaxWidth = String(widthPreference.cssMaxWidth || pageWidthCssMaxWidth(widthPreference.resolvedMode));
@@ -276,12 +279,14 @@ export function createSidebarPageSettingsRuntime(context) {
shell.setAttribute('data-page-show-heading-numbers', String(showHeadingNumbers));
shell.style.width = '100%';
shell.style.maxWidth = cssMaxWidth === 'none' ? 'none' : cssMaxWidth;
shell.style.setProperty('--mnote-mindmap-block-max-width', mindmapMaxWidthValue);
}
if (editorRoot instanceof HTMLElement) {
editorRoot.setAttribute('data-page-wide-layout', String(Boolean(options.wideLayout)));
editorRoot.setAttribute('data-page-small-text', String(Boolean(options.smallText)));
editorRoot.setAttribute('data-page-show-heading-numbers', String(showHeadingNumbers));
editorRoot.setAttribute('data-page-embed-default-block-id', options.embedDefaultBlockId == null ? '' : String(options.embedDefaultBlockId));
editorRoot.style.setProperty('--mnote-mindmap-block-max-width', mindmapMaxWidthValue);
}
if (editorSurface instanceof HTMLElement) {
editorSurface.setAttribute('data-layout-density', String(options.layoutDensity || 'normal'));
@@ -299,6 +304,7 @@ export function createSidebarPageSettingsRuntime(context) {
document.documentElement.setAttribute('data-global-show-heading-numbers', String(globalShowHeadingNumbers));
document.documentElement.setAttribute('data-page-show-heading-numbers', String(showHeadingNumbers));
document.documentElement.setAttribute('data-page-hide-title-header', String(Boolean(options.hideTitleHeader)));
document.documentElement.style.setProperty('--mnote-mindmap-block-max-width', mindmapMaxWidthValue);
var titleHeader = document.querySelector('.document-pane[data-pane-role="primary"] .document-shell-header') || document.querySelector('.document-shell-header');
if (titleHeader instanceof HTMLElement) {
var hidden = Boolean(options.hideTitleHeader);
@@ -32,46 +32,6 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
pageWidthPreferences: null,
historySnapshots: [],
pageSettingsOpen: false,
pageAiOpen: false,
pageAiBusy: false,
pageAiMessages: [],
pageAiSuggestionIndex: 0,
pageAiProvider: 'hermes',
pageAiPage: 'chat',
pageAiRunStatus: 'idle',
pageAiCurrentRunId: '',
pageAiAcpRuntime: 'reasonix',
pageAiAcpRuntimes: [],
pageAiQueueLength: 0,
pageAiQueuedItems: [],
pageAiStoppedRunIds: {},
pageAiAbortController: null,
pageAiContextScope: 'page',
pageAiTools: [],
pageAiToolsError: '',
pageAiGatewayHealth: null,
pageAiGatewayHealthError: '',
pageAiLastToolCall: null,
pageAiProfiles: [],
pageAiActiveProfileName: 'mnoteai',
pageAiProfileError: '',
pageAiProfileMemory: { memory: '', user: '', soul: '' },
pageAiProfileMemoryDrafts: { memory: '', user: '', soul: '' },
pageAiProfileMemoryError: '',
pageAiSkills: { categories: [], archived: [] },
pageAiSkillCatalogs: { mnote: { categories: [], archived: [] }, reasonix: { categories: [], archived: [] }, hermes: { categories: [], archived: [] } },
pageAiSkillPreferences: {},
pageAiCollapsedSkillGroups: {},
pageAiSkillQuery: '',
pageAiSkillError: '',
pageAiSkillLoadSeq: 0,
pageAiSessions: [],
pageAiActiveSessionId: '',
pageAiSessionSearchQuery: '',
pageAiSessionSearchResults: [],
pageAiSessionSearchTimer: 0,
pageAiSessionError: '',
pageAiPermissionRequests: [],
localIndexSummary: {
scopeKey: '',
loading: false,
@@ -928,6 +888,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
function fileTreeRuntimeDeps() {
return {
currentSourceKind: currentSourceKind,
currentRootUri: currentRootUri,
currentDocumentId: currentDocumentId,
localFilePathFromAssetId: localFilePathFromAssetId,
rowTitle: rowTitle,
@@ -2382,12 +2343,16 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
const pageAiLoadProfileMemory = (...args) => sidebarPageAi.pageAiLoadProfileMemory(...args);
const pageAiLoadSkills = (...args) => sidebarPageAi.pageAiLoadSkills(...args);
const pageAiSwitchProfile = (...args) => sidebarPageAi.pageAiSwitchProfile(...args);
const pageAiSetSkillSource = (...args) => sidebarPageAi.pageAiSetSkillSource(...args);
const pageAiToggleSkillGroup = (...args) => sidebarPageAi.pageAiToggleSkillGroup(...args);
const pageAiSetReasonixMemoryEnabled = (...args) => sidebarPageAi.pageAiSetReasonixMemoryEnabled(...args);
const pageAiSetHideHermesBuiltinSkills = (...args) => sidebarPageAi.pageAiSetHideHermesBuiltinSkills(...args);
const pageAiSetContextScope = (...args) => sidebarPageAi.pageAiSetContextScope(...args);
const pageAiSetAgentId = (...args) => sidebarPageAi.pageAiSetAgentId(...args);
const pageAiToggleContextRef = (...args) => sidebarPageAi.pageAiToggleContextRef(...args);
const pageAiSetAgentPopoverOpen = (...args) => sidebarPageAi.pageAiSetAgentPopoverOpen(...args);
const pageAiSetTargetPopoverOpen = (...args) => sidebarPageAi.pageAiSetTargetPopoverOpen(...args);
const pageAiSelectTarget = (...args) => sidebarPageAi.pageAiSelectTarget(...args);
const updatePageAiTriggerState = (...args) => sidebarPageAi.updatePageAiTriggerState(...args);
const sidebarAttachmentOpen = createSidebarAttachmentOpenRuntime({
@@ -2532,255 +2497,6 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
return;
}
var pageAiClose = closestAction(e.target, '[data-page-ai-action="close"]');
if (pageAiClose) {
e.preventDefault();
closePageAiDrawer();
return;
}
var pageAiSettings = closestAction(e.target, '[data-page-ai-action="open-hermes-settings"]');
if (pageAiSettings) {
e.preventDefault();
pageAiOpenHermesSettings();
return;
}
var pageAiStop = closestAction(e.target, '[data-page-ai-action="stop-run"]');
if (pageAiStop) {
e.preventDefault();
void pageAiStopRun();
return;
}
var pageAiRotate = closestAction(e.target, '[data-page-ai-action="rotate"]');
if (pageAiRotate) {
e.preventDefault();
pageUiState.pageAiSuggestionIndex += 1;
renderPageAiSuggestions();
return;
}
var pageAiIntent = closestAction(e.target, '[data-page-ai-intent]');
if (pageAiIntent) {
e.preventDefault();
var intentName = pageAiIntent.getAttribute('data-page-ai-intent') || '';
if (intentName === 'create-summary') {
void sendPageAiMessage('请通过 Hermes 调用 mnote.artifact.create_summary,为当前页面创建或更新 AI Summary。');
return;
}
if (intentName === 'create-ai-note') {
void sendPageAiMessage('请通过 Hermes 调用 mnote.artifact.create_ai_note,基于当前页面创建一篇新的 AI Note。');
return;
}
}
var pageAiTab = closestAction(e.target, '[data-page-ai-tab]');
if (pageAiTab) {
e.preventDefault();
pageUiState.pageAiPage = pageAiTab.getAttribute('data-page-ai-tab') || 'chat';
if (pageUiState.pageAiPage === 'runtime') void pageAiLoadGatewayHealth();
renderPageAiControls();
renderPageAiConversation();
return;
}
var pageAiProvider = closestAction(e.target, '[data-page-ai-provider]');
if (pageAiProvider) {
e.preventDefault();
pageUiState.pageAiProvider = pageAiProvider.getAttribute('data-page-ai-provider') || 'hermes';
renderPageAiProviderButtons();
return;
}
var pageAiAgent = closestAction(e.target, '[data-page-ai-agent-id]');
if (pageAiAgent) {
e.preventDefault();
pageAiSetAgentId(pageAiAgent.getAttribute('data-page-ai-agent-id') || 'reasonix');
return;
}
var pageAiAgentButton = closestAction(e.target, '[data-page-ai-agent-button]');
if (pageAiAgentButton) {
e.preventDefault();
pageAiSetAgentPopoverOpen(pageAiAgentButton.getAttribute('aria-expanded') !== 'true');
return;
}
var pageAiAgentPopoverClose = closestAction(e.target, '[data-page-ai-action="close-agent-popover"]');
if (pageAiAgentPopoverClose) {
e.preventDefault();
pageAiSetAgentPopoverOpen(false);
return;
}
var pageAiContextButton = closestAction(e.target, '[data-page-ai-context-button]');
if (pageAiContextButton) {
e.preventDefault();
sidebarPageAi.pageAiSetContextPopoverOpen(
pageAiContextButton.getAttribute('aria-expanded') !== 'true'
);
return;
}
var pageAiContextPopoverClose = closestAction(e.target, '[data-page-ai-action="close-context-popover"]');
if (pageAiContextPopoverClose) {
e.preventDefault();
sidebarPageAi.pageAiSetContextPopoverOpen(false);
return;
}
var pageAiContextRef = closestAction(e.target, '[data-page-ai-context-ref]');
if (pageAiContextRef) {
e.preventDefault();
pageAiToggleContextRef(pageAiContextRef.getAttribute('data-page-ai-context-ref') || '');
return;
}
var pageAiMemorySave = closestAction(e.target, '[data-page-ai-memory-save]');
if (pageAiMemorySave) {
e.preventDefault();
void pageAiSaveProfileMemory(pageAiMemorySave.getAttribute('data-page-ai-memory-save') || '');
return;
}
var pageAiSkillToggle = closestAction(e.target, '[data-page-ai-skill-toggle]');
if (pageAiSkillToggle) {
e.preventDefault();
var skillName = pageAiSkillToggle.getAttribute('data-page-ai-skill-toggle') || '';
var skillGroup = pageAiSkillToggle.getAttribute('data-page-ai-skill-group') || '';
var skillProfile = pageAiSkillToggle.getAttribute('data-page-ai-skill-profile') || '';
var nextEnabled = pageAiSkillToggle.getAttribute('aria-pressed') !== 'true';
void pageAiToggleSkill(skillName, nextEnabled, skillGroup, skillProfile);
return;
}
var pageAiSkillGroupToggle = closestAction(e.target, '[data-page-ai-skill-group-toggle]');
if (pageAiSkillGroupToggle) {
e.preventDefault();
pageAiToggleSkillGroup(pageAiSkillGroupToggle.getAttribute('data-page-ai-skill-group-toggle') || '');
return;
}
var pageAiToolToggle = closestAction(e.target, '[data-page-ai-tool-toggle]');
if (pageAiToolToggle) {
e.preventDefault();
var toolName = pageAiToolToggle.getAttribute('data-page-ai-tool-toggle') || '';
var nextToolEnabled = pageAiToolToggle.getAttribute('aria-pressed') !== 'true';
void pageAiToggleTool(toolName, nextToolEnabled);
return;
}
var pageAiSessionResume = closestAction(e.target, '[data-page-ai-session-resume]');
if (pageAiSessionResume) {
e.preventDefault();
void pageAiResumeBackendSession(pageAiSessionResume.getAttribute('data-page-ai-session-resume') || '').catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
return;
}
var pageAiSessionRename = closestAction(e.target, '[data-page-ai-session-rename]');
if (pageAiSessionRename) {
e.preventDefault();
void pageAiRenameBackendSession(pageAiSessionRename.getAttribute('data-page-ai-session-rename') || '').catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
return;
}
var pageAiSessionDelete = closestAction(e.target, '[data-page-ai-session-delete]');
if (pageAiSessionDelete) {
e.preventDefault();
void pageAiDeleteBackendSession(pageAiSessionDelete.getAttribute('data-page-ai-session-delete') || '').catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
return;
}
var pageAiPermissionAction = closestAction(e.target, '[data-page-ai-permission-action]');
if (pageAiPermissionAction) {
e.preventDefault();
pageAiResolvePermission(
pageAiPermissionAction.getAttribute('data-page-ai-permission-id') || '',
pageAiPermissionAction.getAttribute('data-page-ai-permission-action') || 'deny'
);
return;
}
var pageAiOpenLocationAction = closestAction(e.target, '[data-page-ai-open-location]');
if (pageAiOpenLocationAction) {
e.preventDefault();
var loc = String(pageAiOpenLocationAction.getAttribute('data-page-ai-open-location') || '').trim();
if (loc) pageAiOpenLocation(loc);
return;
}
var pageAiSession = closestAction(e.target, '[data-page-ai-session]');
if (pageAiSession) {
e.preventDefault();
pageAiSetActiveSession(pageAiSession.getAttribute('data-page-ai-session') || '');
return;
}
var pageAiSuggestion = closestAction(e.target, '[data-page-ai-suggestion]');
if (pageAiSuggestion) {
e.preventDefault();
var text = pageAiSuggestion.getAttribute('data-page-ai-suggestion') || '';
var inputNode = ensurePageAiDrawer().querySelector('[data-page-ai-input]');
if (inputNode instanceof HTMLTextAreaElement) {
inputNode.value = text;
inputNode.focus();
}
return;
}
var pageAiNewSession = closestAction(e.target, '[data-page-ai-action="new-session"]');
if (pageAiNewSession) {
e.preventDefault();
pageUiState.pageAiPage = 'chat';
pageAiStartNewSession();
return;
}
var pageAiHistory = closestAction(e.target, '[data-page-ai-action="history"]');
if (pageAiHistory) {
e.preventDefault();
pageAiLoadSessions();
pageUiState.pageAiPage = pageUiState.pageAiPage === 'history' ? 'chat' : 'history';
renderPageAiControls();
renderPageAiConversation();
if (pageUiState.pageAiPage === 'history') {
void pageAiLoadBackendSessions().catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
}
return;
}
var pageAiSend = closestAction(e.target, '[data-page-ai-action="send"]');
if (pageAiSend) {
e.preventDefault();
var input = ensurePageAiDrawer().querySelector('[data-page-ai-input]');
if (input instanceof HTMLTextAreaElement) {
var message = input.value;
input.value = '';
void sendPageAiMessage(message);
}
return;
}
var cancelQueuedRun = closestAction(e.target, '[data-page-ai-action="cancel-queued-run"]');
if (cancelQueuedRun) {
e.preventDefault();
void pageAiCancelQueuedRun(cancelQueuedRun.getAttribute('data-page-ai-queue-id'));
return;
}
var searchTrigger = closestAction(e.target, '[data-mnote-action="open-search-modal"]');
if (searchTrigger) {
e.preventDefault();
@@ -3112,79 +2828,9 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
closeSearchModal();
closeTreeContextMenu();
}
if (event.key === 'Enter' && !event.shiftKey) {
var aiInput = closestAction(event.target, '[data-page-ai-input]');
if (aiInput instanceof HTMLTextAreaElement) {
event.preventDefault();
var text = aiInput.value;
aiInput.value = '';
void sendPageAiMessage(text);
}
}
});
document.addEventListener('input', function(event) {
var skillSearch = closestAction(event.target, '[data-page-ai-skill-search]');
if (skillSearch instanceof HTMLInputElement) {
pageUiState.pageAiSkillQuery = skillSearch.value;
renderPageAiControls();
return;
}
var sessionSearch = closestAction(event.target, '[data-page-ai-session-search]');
if (sessionSearch instanceof HTMLInputElement) {
var sessionQuery = sessionSearch.value;
window.clearTimeout(pageUiState.pageAiSessionSearchTimer || 0);
pageUiState.pageAiSessionSearchTimer = window.setTimeout(function() {
void pageAiSearchBackendSessions(sessionQuery).catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
renderPageAiControls();
});
}, 200);
return;
}
var memoryEditor = closestAction(event.target, '[data-page-ai-memory-editor]');
if (memoryEditor instanceof HTMLTextAreaElement) {
var section = memoryEditor.getAttribute('data-page-ai-memory-editor') || '';
if (['memory', 'user', 'soul'].indexOf(section) >= 0) {
pageUiState.pageAiProfileMemoryDrafts[section] = memoryEditor.value;
}
}
});
document.addEventListener('change', function(event) {
var pageAiAcpRuntimeSelect = closestAction(event.target, '[data-page-ai-acp-runtime]');
if (pageAiAcpRuntimeSelect instanceof HTMLSelectElement) {
var next = String(pageAiAcpRuntimeSelect.value || 'reasonix').trim() || 'reasonix';
pageUiState.pageAiAcpRuntime = next;
pageUiState.pageAiSkills = { categories: [], archived: [] };
pageUiState.pageAiSkillError = '';
pageAiPersistSessions();
void pageAiLoadProfiles();
if (next !== 'reasonix') void pageAiLoadProfileMemory();
void pageAiLoadSkills();
void pageAiLoadBackendSessions().catch(function(error) {
pageUiState.pageAiSessionError = error instanceof Error ? error.message : String(error);
});
renderPageAiControls();
renderPageAiProviderButtons();
return;
}
var pageAiProfileSelect = closestAction(event.target, '[data-page-ai-profile-select]');
if (pageAiProfileSelect instanceof HTMLSelectElement) {
void pageAiSwitchProfile(pageAiProfileSelect.value);
return;
}
var pageAiHideHermesBuiltin = closestAction(event.target, '[data-page-ai-hide-hermes-builtin]');
if (pageAiHideHermesBuiltin instanceof HTMLInputElement) {
pageAiSetHideHermesBuiltinSkills(pageAiHideHermesBuiltin.checked);
return;
}
var pageAiContextSelect = closestAction(event.target, '[data-page-ai-context-scope]');
if (pageAiContextSelect instanceof HTMLSelectElement) {
pageAiSetContextScope(pageAiContextSelect.value);
renderPageAiControls();
return;
}
var globalCheckbox = closestAction(event.target, '[data-global-option-checkbox="showHeadingNumbers"]');
if (globalCheckbox instanceof HTMLInputElement) {
writeGlobalShowHeadingNumbers(globalCheckbox.checked);
@@ -3218,6 +2864,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
void persistPageWidthPreference(pageWidthType, pageWidthSelect.value);
}
});
sidebarPageAi.installPageAiDelegates();
function initializePageUiSurfaces() {
pageUiState.pageOptions = null;
@@ -3243,6 +2890,14 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
}
scheduleInitializePageUiSurfaces();
function mnoteDevHotReloadEnabled() {
try {
return new URL(import.meta.url).searchParams.has('devHot');
} catch (_) {
return false;
}
}
function installMnoteDevHotReload() {
var bootId = '';
var failedOnce = false;
@@ -3271,7 +2926,9 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
tick();
timer = window.setInterval(tick, 1000);
}
installMnoteDevHotReload();
if (mnoteDevHotReloadEnabled()) {
installMnoteDevHotReload();
}
document.addEventListener('dragstart', function(event) {
var pageRow = closestAction(event.target, '.tree-row[data-shell-mode="page"][draggable="true"]');