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(); var workspacePath = entry && entry.workspacePath && typeof entry.workspacePath === 'object' ? entry.workspacePath : {}; var workspaceResourceKind = String(workspacePath.resourceKind || '').trim().toLowerCase(); var officeOpenMode = String(entry && entry.officeOpenMode || '').trim().toLowerCase(); var onlyofficeSessionId = String(entry && (entry.onlyofficeSessionId || entry.bridgeSessionId) || '').trim(); 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' || workspaceResourceKind === 'only_office' || /\.(doc|docx|ppt|pptx|xls|xlsx)$/.test(path)) { return officeOpenMode === 'onlyoffice_live' || onlyofficeSessionId ? 'only_office' : 'attachment'; } if (kind === 'resource' && assetId) return 'resource'; return kind || 'markdown_page'; } function pageAiIsOnlyOfficeLiveTarget(editorTarget) { var workspacePath = editorTarget && editorTarget.workspacePath && typeof editorTarget.workspacePath === 'object' ? editorTarget.workspacePath : {}; var resourceKind = String(editorTarget && (editorTarget.resourceKind || editorTarget.editorKind) || workspacePath.resourceKind || '').trim().toLowerCase(); var officeOpenMode = String(editorTarget && editorTarget.officeOpenMode || '').trim().toLowerCase(); return resourceKind === 'only_office' || resourceKind === 'onlyoffice' || officeOpenMode === 'onlyoffice_live'; } 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 || '', officeOpenMode: entry.officeOpenMode || '', 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(), officeOpenMode: String(entry.officeOpenMode || '').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 (pageAiIsOnlyOfficeLiveTarget(editorTarget) && !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 = ['']; 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(' ' + escapeHtml(pageAiBlockText(block)) + ''); }); lines.push(''); 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, }; }