export function createSidebarPageAiRenderRuntime(context) {
const {
contextRefRegistry,
cssEscape,
currentDocumentId,
documentRef,
escapeHtml,
localMarkdownRelativePathFromPageAiDocumentId,
pageAiAgentRecord,
pageAiAllSkillEntries,
pageAiBuildAllowedRoots,
pageAiChatOnlyProfileEntries,
pageAiChatOnlyProfileSpec,
pageAiCurrentAgentId,
pageAiCurrentProfile,
pageAiCurrentProfileRecord,
pageAiCurrentSession,
pageAiCurrentSkillSource,
pageAiCurrentSkillSourceLabel,
pageAiDefaultChatOnlyProfileSpec,
pageAiEditorTargetCandidates,
pageAiEnsureContextRefState,
pageAiFilteredHistoryRows,
pageAiHideHermesBuiltinSkills,
pageAiHermesProfileEntries,
pageAiHermesSettingsUrl,
pageAiMnoteToolModel,
pageAiNormalizeAcpRuntimes,
pageAiNormalizeArray,
pageAiNormalizeSkillSource,
pageAiProfileDisplayLabel,
pageAiProfileValue,
pageAiProviderLabel,
pageAiReasonixMemoryEnabled,
pageAiRunProfile,
pageAiSessionAgentFilterOptions,
pageAiSessionAgentLabel,
pageAiSessionAgentFilterValue,
pageAiSessionPreviewText,
pageAiSessionStorageLabel,
pageAiSkillEnabled,
pageAiSkillGroupCollapsed,
pageAiSkillListEntries,
pageAiSkillOriginLabel,
pageAiSkillSourceOptions,
pageAiSkillSourceParts,
pageAiToggleableSkillEntries,
pageAiUsageSummary,
pageUiState,
renderPageAiMarkdown,
searchText,
currentPageAiEditorTarget,
currentPageAiSelectedText,
} = context;
const PAGE_AI_CONTEXT_REF_REGISTRY = Array.isArray(contextRefRegistry) ? contextRefRegistry : [];
function pageAiTargetLabel(target) {
var workspacePath = target && target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {};
var kind = String(target && (target.resourceKind || target.editorKind) || workspacePath.resourceKind || '').trim();
var title = String(target && target.title || '').trim();
var relativePath = String(workspacePath.relativePath || target && target.path || '').trim();
if (title) return title;
if (relativePath) return relativePath.split('/').filter(Boolean).pop() || relativePath;
if (kind === 'mindmap') return '思维导图';
if (kind === 'only_office') return 'Office 资源';
return '当前页';
}
function pageAiTargetDetail(target) {
var workspacePath = target && target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {};
var kind = String(target && (target.resourceKind || target.editorKind) || workspacePath.resourceKind || '').trim();
var pane = String(target && target.paneRole || '').trim();
var relativePath = String(workspacePath.relativePath || target && target.path || '').trim();
return [kind || 'page', pane, relativePath].filter(Boolean).join(' · ');
}
function pageAiContextRefLabel(kind) {
var record = PAGE_AI_CONTEXT_REF_REGISTRY.find(function(ref) { return ref.id === kind; });
return record ? record.label : kind;
}
function pageAiContextButtonSummary() {
var selected = pageAiEnsureContextRefState();
var labels = PAGE_AI_CONTEXT_REF_REGISTRY
.filter(function(ref) { return selected[ref.id] === true; })
.map(function(ref) { return ref.label; });
if (!labels.length) return '选择上下文';
var head = labels.slice(0, 2).join(' + ');
return labels.length > 2 ? head + ' +' + String(labels.length - 2) : head;
}
function pageAiContextRefDetail(kind) {
var documentId = currentDocumentId();
var relativePath = localMarkdownRelativePathFromPageAiDocumentId(documentId);
if (kind === 'current_page') return relativePath || documentId || '当前页面';
if (kind === 'selection') return currentPageAiSelectedText() ? '当前选区可用' : '当前没有选区';
if (kind === 'active_editor') {
var target = currentPageAiEditorTarget();
return String(target && (target.title || target.documentId) || relativePath || '当前打开资源');
}
if (kind === 'file') return relativePath || '当前文件';
if (kind === 'folder') {
var roots = pageAiBuildAllowedRoots();
if (!roots.length) return '需要授权后可用';
return roots.map(function(root) { return root.rootUri.replace(/^file:\/\//, ''); }).filter(Boolean)[0] || '已授权文件夹';
}
if (kind === 'changed_files') return '本次 run 后可用于追问';
return '';
}
function pageAiContextRefDisabled(kind) {
if (kind === 'selection') return !currentPageAiSelectedText();
return false;
}
function pageAiSuggestions() {
var title = searchText(documentRef.querySelector('[data-page-title-current="true"]')?.textContent) || '当前页面';
return [
'帮我总结《' + title + '》当前内容',
'把当前页面改写得更简洁一些',
'提炼当前页的关键待办和行动项',
'基于当前页内容生成一个三段式摘要'
];
}
function pageAiCurrentAgentSelectionLabel() {
var agentId = pageAiCurrentAgentId();
if (agentId === 'reasonix') return 'Reasonix';
var profile = pageAiCurrentProfileRecord();
var chatOnlySpec = pageAiChatOnlyProfileSpec(profile || pageAiCurrentProfile());
if (agentId === 'chat_only') {
return 'ChatOnly / ' + (chatOnlySpec || pageAiDefaultChatOnlyProfileSpec()).label;
}
if (chatOnlySpec) {
return 'ChatOnly / ' + chatOnlySpec.label;
}
if (agentId === 'hermes') return 'Hermes / ' + pageAiProfileDisplayLabel(profile, pageAiCurrentProfile());
return pageAiAgentRecord(agentId).label;
}
function pageAiRenderAgentProfileOption(agentId, profile, label, detail) {
var profileId = pageAiProfileValue(profile);
var active = pageAiCurrentAgentId() === agentId && profileId === pageAiCurrentProfile();
return '' +
'';
}
function pageAiCurrentModelLabel() {
var profile = pageAiCurrentProfileRecord();
var toolModel = pageAiMnoteToolModel();
if (!profile) return 'tool: ' + toolModel;
var model = String(profile.model || '').trim();
var gateway = String(profile.gateway || '').trim();
var profileLabel = [model, gateway].filter(Boolean).join(' · ') || '由 Hermes 决定';
return 'tool: ' + toolModel + ' · profile: ' + profileLabel;
}
function pageAiFormatChangedFiles(files) {
return pageAiNormalizeArray(files).map(function(file) {
var path = String(file && file.path || '').trim();
var changeType = String(file && file.changeType || file.change_type || 'modified').trim();
var summary = String(file && file.summary || '').trim();
var version = String(file && (file.version || file.revision || '') || '').trim();
var hashBefore = String(file && file.hashBefore || '').trim();
var hashAfter = String(file && file.hashAfter || '').trim();
if (!version && (hashBefore || hashAfter)) version = 'hash ' + [hashBefore || '0', hashAfter || '0'].join('→');
var modifiedBefore = Number(file && file.modifiedBeforeMs || 0) || 0;
var modifiedAfter = Number(file && file.modifiedAfterMs || 0) || 0;
if (!version && (modifiedBefore || modifiedAfter)) version = 'mtime ' + [String(modifiedBefore), String(modifiedAfter)].join('→');
var actor = [file && file.agentKind, file && file.actorType, file && file.actorId].map(function(value) {
return String(value || '').trim();
}).filter(Boolean).join('/');
return [changeType, path, version, summary, actor].filter(Boolean).join(' · ');
}).filter(Boolean).join('\n');
}
function pageAiRunStatusLabel(status) {
if (status === 'queued') return '排队中';
if (status === 'running') return '运行中';
if (status === 'tool_calling') return '调用工具';
if (status === 'completed') return '已完成';
if (status === 'failed') return '失败';
if (status === 'aborted') return '已停止';
return '空闲';
}
function pageAiMemoryFileLabel(section) {
if (section === 'soul') return 'SOUL.md';
if (section === 'user') return 'USER.md';
return 'MEMORY.md';
}
function pageAiContextScopeLabel(scope) {
if (scope === 'selection') return '当前选区';
if (scope === 'block') return '当前块';
if (scope === 'options') return '页面设置';
return '当前页';
}
function pageAiFilteredSkillEntries() {
var query = String(pageUiState.pageAiSkillQuery || '').trim().toLowerCase();
var parts = pageAiSkillSourceParts(pageAiCurrentSkillSource());
var entries = pageAiToggleableSkillEntries(parts.group, parts.profile);
if (!entries.length) {
entries = pageAiSkillListEntries().map(function(skill) {
return Object.assign({}, skill, {
group: parts.group,
profile: parts.profile || ''
});
});
}
return entries.filter(function(skill) {
if (parts.group === 'hermes' && pageAiHideHermesBuiltinSkills(parts.profile) && skill.builtin === true) return false;
if (!query) return true;
return String(skill.name || skill.id || '').toLowerCase().indexOf(query) >= 0
|| String(skill.title || '').toLowerCase().indexOf(query) >= 0
|| String(skill.description || '').toLowerCase().indexOf(query) >= 0
|| String(skill.category || '').toLowerCase().indexOf(query) >= 0
|| pageAiSkillOriginLabel(skill).toLowerCase().indexOf(query) >= 0;
});
}
function renderPageAiProviderButtons() {
var drawer = ensurePageAiDrawer();
var isAcp = pageUiState.pageAiAcpRuntime !== '';
drawer.querySelectorAll('[data-page-ai-provider]').forEach(function(button) {
var provider = button.getAttribute('data-page-ai-provider') || 'hermes';
var active = provider === pageUiState.pageAiProvider;
button.classList.toggle('is-active', active);
button.setAttribute('aria-pressed', active ? 'true' : 'false');
});
var providerNode = drawer.querySelector('.wolai-page-ai-subtitle span:first-child');
if (providerNode instanceof HTMLElement) {
providerNode.textContent = pageAiAgentRecord(pageAiCurrentAgentId()).label;
}
}
function renderPageAiControls() {
var drawer = ensurePageAiDrawer();
var isAcp = pageUiState.pageAiAcpRuntime !== '';
var activeAgentId = pageAiCurrentAgentId();
var activeProfile = pageAiCurrentProfile();
drawer.setAttribute('data-page-ai-page', pageUiState.pageAiPage || 'chat');
drawer.setAttribute('data-page-ai-active-agent-id', activeAgentId);
drawer.setAttribute('data-mnote-acp-runtime', pageUiState.pageAiAcpRuntime || 'reasonix');
documentRef.documentElement.setAttribute('data-mnote-page-ai-agent-id', activeAgentId);
var agentButton = drawer.querySelector('[data-page-ai-agent-button]');
var agentPopoverId = 'mnote-page-ai-agent-popover';
var activeAgentLabel = pageAiCurrentAgentSelectionLabel();
if (agentButton instanceof HTMLElement) {
agentButton.textContent = 'AI';
agentButton.setAttribute('aria-expanded', pageUiState.pageAiAgentPopoverOpen ? 'true' : 'false');
agentButton.setAttribute('aria-controls', agentPopoverId);
agentButton.setAttribute('data-page-ai-agent-summary', activeAgentLabel);
agentButton.setAttribute('aria-label', 'Agent:' + activeAgentLabel);
agentButton.setAttribute('title', 'Agent:' + activeAgentLabel);
}
var agentPopover = drawer.querySelector('[data-page-ai-agent-popover]');
if (agentPopover instanceof HTMLElement) {
agentPopover.id = agentPopoverId;
agentPopover.hidden = !pageUiState.pageAiAgentPopoverOpen;
var chatOnlyOptions = pageAiChatOnlyProfileEntries().map(function(profile) {
var spec = pageAiChatOnlyProfileSpec(profile);
var label = profile.menuLabel || (spec && spec.label) || pageAiProfileDisplayLabel(profile, '');
return pageAiRenderAgentProfileOption('chat_only', profile, label, '网页问答 · 不申请文件写权限');
}).join('');
var hermesOptions = pageAiHermesProfileEntries().map(function(profile) {
var profileId = pageAiProfileValue(profile);
var scope = profile.kind === 'shared'
? (profile.readonly ? '共享 · 只读配置' : '共享')
: (profile.kind === 'personal' ? '个人 profile' : 'Hermes profile');
return pageAiRenderAgentProfileOption('hermes', profile, pageAiProfileDisplayLabel(profile, profileId), scope);
}).join('');
var reasonixActive = activeAgentId === 'reasonix';
agentPopover.innerHTML = '' +
'
' +
'选择 Agent' +
'' +
'
' +
'' +
'
ChatOnly
' +
'
' + chatOnlyOptions + '
' +
'
' +
'' +
'
Hermes
' +
'
' + hermesOptions + '
' +
'
' +
'' +
'
Reasonix
' +
'
' +
'' +
'
' +
'
';
}
var agentChip = drawer.querySelector('[data-page-ai-agent-chip]');
if (agentChip instanceof HTMLElement) {
agentChip.textContent = activeAgentLabel;
agentChip.setAttribute('title', '当前 Agent:' + activeAgentLabel);
}
var contextButton = drawer.querySelector('[data-page-ai-context-button]');
var contextPopoverId = 'mnote-page-ai-context-popover';
if (contextButton instanceof HTMLElement) {
var summary = pageAiContextButtonSummary();
contextButton.textContent = '⇅';
contextButton.setAttribute('aria-expanded', pageUiState.pageAiContextPopoverOpen ? 'true' : 'false');
contextButton.setAttribute('aria-controls', contextPopoverId);
contextButton.setAttribute('data-page-ai-context-summary', summary);
contextButton.setAttribute('aria-label', '上下文:' + summary);
contextButton.setAttribute('title', '上下文:' + summary);
}
var contextPopover = drawer.querySelector('[data-page-ai-context-popover]');
if (contextPopover instanceof HTMLElement) {
var selectedRefs = pageAiEnsureContextRefState();
contextPopover.id = contextPopoverId;
contextPopover.hidden = !pageUiState.pageAiContextPopoverOpen;
contextPopover.innerHTML = '' +
'' +
'发送给 AI 的上下文' +
'' +
'
' +
'' +
PAGE_AI_CONTEXT_REF_REGISTRY.map(function(ref) {
var checked = selectedRefs[ref.id] === true;
var disabled = pageAiContextRefDisabled(ref.id);
return '' +
'';
}).join('') +
'
' +
'';
}
var targetButton = drawer.querySelector('[data-page-ai-target-button]');
var targetPopover = drawer.querySelector('[data-page-ai-target-popover]');
var targetChip = drawer.querySelector('[data-page-ai-target-chip]');
var targetPopoverId = 'mnote-page-ai-target-popover';
var targetCandidates = pageAiEditorTargetCandidates();
var activeTarget = currentPageAiEditorTarget();
var activeTargetLabel = pageAiTargetLabel(activeTarget);
if (targetButton instanceof HTMLElement) {
targetButton.textContent = '◎';
targetButton.setAttribute('aria-expanded', pageUiState.pageAiTargetPopoverOpen ? 'true' : 'false');
targetButton.setAttribute('aria-controls', targetPopoverId);
targetButton.setAttribute('aria-label', '目标:' + activeTargetLabel);
targetButton.setAttribute('title', '目标:' + activeTargetLabel);
}
if (targetChip instanceof HTMLElement) {
targetChip.textContent = activeTargetLabel;
targetChip.setAttribute('title', '当前目标:' + activeTargetLabel + ' · ' + pageAiTargetDetail(activeTarget));
}
if (targetPopover instanceof HTMLElement) {
targetPopover.id = targetPopoverId;
targetPopover.hidden = !pageUiState.pageAiTargetPopoverOpen;
targetPopover.innerHTML = '' +
'' +
'选择写入目标' +
'' +
'
' +
'' +
targetCandidates.map(function(target) {
var targetId = String(target && target.targetId || '').trim();
var active = targetId && targetId === String(activeTarget && activeTarget.targetId || '').trim();
return '' +
'';
}).join('') +
'
';
}
var allowedRootsNode = drawer.querySelector('[data-page-ai-allowed-roots]');
if (allowedRootsNode instanceof HTMLElement) {
var allowedRoots = pageAiBuildAllowedRoots();
if (!allowedRoots.length) {
var errorText = String(pageUiState.pageAiAllowedRootsError || '').trim();
allowedRootsNode.innerHTML = '' + escapeHtml(errorText || '未选择授权区域') + '';
} else {
allowedRootsNode.innerHTML = allowedRoots.map(function(root) {
var label = root.rootUri.replace(/^file:\/\//, '') || root.rootUri;
return '' + escapeHtml(root.permission + ' · ' + label) + '';
}).join('');
}
}
// Populate ACP runtime dropdown
var acpSelect = drawer.querySelector('[data-page-ai-acp-runtime]');
if (acpSelect instanceof HTMLSelectElement) {
var runtimes = pageUiState.pageAiAcpRuntimes.length ? pageUiState.pageAiAcpRuntimes : pageAiNormalizeAcpRuntimes([]);
acpSelect.innerHTML = runtimes.map(function(rt) {
return '';
}).join('');
acpSelect.value = pageUiState.pageAiAcpRuntime || 'reasonix';
}
// Show/hide Hermes-specific profile select
var profileLabel = drawer.querySelector('[data-page-ai-hermes-profile]');
if (profileLabel instanceof HTMLElement) {
profileLabel.style.display = pageUiState.pageAiAcpRuntime === 'reasonix' ? 'none' : '';
}
// When ACP is selected, populate agent panel with ACP runtime info
var agentPanel = drawer.querySelector('[data-page-ai-agent-panel]');
if (agentPanel instanceof HTMLElement) {
agentPanel.innerHTML = '' +
'' +
'授权区域
' + escapeHtml(pageAiBuildAllowedRoots().length ? 'SQLite directory_grants' : '需要授权') + '
' +
'' +
'' +
'';
}
var profileSummary = drawer.querySelector('[data-page-ai-profile-summary]');
if (profileSummary instanceof HTMLElement) profileSummary.textContent = '上下文可选';
var profileSummary = drawer.querySelector('[data-page-ai-profile-summary]');
if (profileSummary instanceof HTMLElement) profileSummary.textContent = '上下文可选';
drawer.querySelectorAll('[data-page-ai-profile-select]').forEach(function(profileSelect) {
if (!(profileSelect instanceof HTMLSelectElement)) return;
var profiles = pageUiState.pageAiProfiles.length ? pageUiState.pageAiProfiles : [{ name: activeProfile, active: true }];
profileSelect.innerHTML = profiles.map(function(profile) {
var name = pageAiProfileValue(profile) || 'default';
var model = [profile.model, profile.gateway].filter(Boolean).join(' / ');
var scope = profile.kind === 'shared'
? (profile.readonly ? 'shared · readonly' : 'shared · admin')
: (profile.kind === 'personal' ? 'personal · 可配置' : '');
var label = [profile.alias || name, scope, model].filter(Boolean).join(' · ');
return '';
}).join('');
profileSelect.value = activeProfile;
});
var runStatus = drawer.querySelector('[data-page-ai-run-status]');
if (runStatus instanceof HTMLElement) {
var queueSuffix = pageUiState.pageAiQueueLength > 0 ? ' · 队列 ' + pageUiState.pageAiQueueLength : '';
runStatus.textContent = pageAiRunStatusLabel(pageUiState.pageAiRunStatus) + queueSuffix;
}
var stopButton = drawer.querySelector('[data-page-ai-action="stop-run"]');
if (stopButton instanceof HTMLButtonElement) {
var canStop = ['queued', 'running', 'tool_calling'].indexOf(pageUiState.pageAiRunStatus) >= 0 && pageUiState.pageAiCurrentRunId;
stopButton.disabled = !canStop;
stopButton.setAttribute('aria-disabled', canStop ? 'false' : 'true');
}
var settingsLink = drawer.querySelector('[data-page-ai-action="open-hermes-settings"]');
if (settingsLink instanceof HTMLButtonElement) {
settingsLink.disabled = !pageAiHermesSettingsUrl();
settingsLink.title = pageAiHermesSettingsUrl() ? '打开 Hermes 设置' : '未配置 MNOTE_WEB_HERMES_UPSTREAM_URL';
}
var queueList = drawer.querySelector('[data-page-ai-queue-list]');
if (queueList instanceof HTMLElement) {
if (!pageUiState.pageAiQueuedItems.length) {
queueList.innerHTML = '暂无排队项。
';
} else {
queueList.innerHTML = pageUiState.pageAiQueuedItems.map(function(item, index) {
var label = '队列 ' + (index + 1);
return '' +
'' +
'
' + escapeHtml(label) + '
' + escapeHtml(item.queueId) + '
' +
'
' +
'
';
}).join('');
}
}
var sessionNode = drawer.querySelector('[data-page-ai-session-status]');
if (sessionNode instanceof HTMLElement) {
var session = pageAiCurrentSession();
var usageText = session && session.usage ? pageAiUsageSummary(session.usage) : '';
sessionNode.textContent = session && session.id
? [String(session.title || '当前页问答'), pageAiSessionStorageLabel(session), usageText].filter(Boolean).join(' · ')
: '等待 AI session';
}
var modelNode = drawer.querySelector('[data-page-ai-model-status]');
if (modelNode instanceof HTMLElement) modelNode.textContent = pageAiBuildAllowedRoots().length ? '已授权' : '需授权';
var scopeSelect = drawer.querySelector('[data-page-ai-context-scope]');
if (scopeSelect instanceof HTMLSelectElement) scopeSelect.value = pageUiState.pageAiContextScope;
var scopeLabel = drawer.querySelector('[data-page-ai-context-scope-label]');
if (scopeLabel instanceof HTMLElement) scopeLabel.textContent = pageAiContextScopeLabel(pageUiState.pageAiContextScope);
drawer.querySelectorAll('[data-page-ai-tab]').forEach(function(button) {
var target = button.getAttribute('data-page-ai-tab') || 'chat';
var active = target === pageUiState.pageAiPage;
button.classList.toggle('is-active', active);
button.setAttribute('aria-selected', active ? 'true' : 'false');
});
drawer.querySelectorAll('[data-page-ai-panel]').forEach(function(panel) {
if (panel instanceof HTMLElement) {
panel.hidden = panel.getAttribute('data-page-ai-panel') !== pageUiState.pageAiPage;
}
});
var profileError = drawer.querySelector('[data-page-ai-profile-error]');
if (profileError instanceof HTMLElement) {
profileError.textContent = pageUiState.pageAiProfileError || '';
profileError.hidden = !pageUiState.pageAiProfileError;
}
var memoryError = drawer.querySelector('[data-page-ai-memory-error]');
if (memoryError instanceof HTMLElement) {
var memoryErrorText = pageUiState.pageAiProfileMemoryError || '';
if (memoryErrorText && memoryErrorText === pageUiState.pageAiProfileError) memoryErrorText = '';
memoryError.textContent = memoryErrorText;
memoryError.hidden = !memoryErrorText;
}
var hermesMemoryPanel = drawer.querySelector('[data-page-ai-hermes-panel]');
if (hermesMemoryPanel instanceof HTMLElement) {
hermesMemoryPanel.innerHTML = ['soul', 'user', 'memory'].map(function(section) {
var label = pageAiMemoryFileLabel(section);
var value = pageUiState.pageAiProfileMemoryDrafts[section] || '';
return '' +
'' +
'' +
'
' +
'
' + escapeHtml(label) + '
' +
'
保存到 Hermes profile: ' + escapeHtml(activeProfile) + '
' +
'
' +
'
' +
'
' +
'' +
'';
}).join('');
}
var skillError = drawer.querySelector('[data-page-ai-skill-error]');
if (skillError instanceof HTMLElement) {
skillError.textContent = pageUiState.pageAiSkillError || '';
skillError.hidden = !pageUiState.pageAiSkillError;
}
var sessionError = drawer.querySelector('[data-page-ai-session-error]');
if (sessionError instanceof HTMLElement) {
sessionError.textContent = pageUiState.pageAiSessionError || '';
sessionError.hidden = !pageUiState.pageAiSessionError;
}
var sessionSearch = drawer.querySelector('[data-page-ai-session-search]');
if (sessionSearch instanceof HTMLInputElement && documentRef.activeElement !== sessionSearch) {
sessionSearch.value = pageUiState.pageAiSessionSearchQuery || '';
}
var sessionAgentFilter = drawer.querySelector('[data-page-ai-session-agent-filter]');
if (sessionAgentFilter instanceof HTMLSelectElement) {
var historyRowsForFilter = pageUiState.pageAiSessionSearchResults.length
? pageUiState.pageAiSessionSearchResults
: pageUiState.pageAiSessions;
var options = pageAiSessionAgentFilterOptions(historyRowsForFilter);
var activeFilter = String(pageUiState.pageAiSessionAgentFilter || 'all').trim() || 'all';
if (!options.some(function(option) { return option.value === activeFilter; })) activeFilter = 'all';
sessionAgentFilter.innerHTML = options.map(function(option) {
return '';
}).join('');
sessionAgentFilter.value = activeFilter;
pageUiState.pageAiSessionAgentFilter = activeFilter;
}
var skillSearch = drawer.querySelector('[data-page-ai-skill-search]');
if (skillSearch instanceof HTMLInputElement && documentRef.activeElement !== skillSearch) {
skillSearch.value = pageUiState.pageAiSkillQuery;
}
var skillSourceSelect = drawer.querySelector('[data-page-ai-skill-source-select]');
if (skillSourceSelect instanceof HTMLSelectElement) {
var activeSkillSource = pageAiCurrentSkillSource();
skillSourceSelect.innerHTML = pageAiSkillSourceOptions().map(function(option) {
return '';
}).join('');
skillSourceSelect.value = activeSkillSource;
}
var hermesBuiltinToggle = drawer.querySelector('[data-page-ai-hide-hermes-builtin]');
if (hermesBuiltinToggle instanceof HTMLInputElement) {
var skillSourceParts = pageAiSkillSourceParts(pageAiCurrentSkillSource());
var showHermesFilter = skillSourceParts.group === 'hermes';
var hermesFilterLabel = hermesBuiltinToggle.closest('.wolai-page-ai-skill-filter-toggle');
if (hermesFilterLabel instanceof HTMLElement) hermesFilterLabel.hidden = !showHermesFilter;
hermesBuiltinToggle.checked = showHermesFilter && pageAiHideHermesBuiltinSkills(skillSourceParts.profile);
}
var skillList = drawer.querySelector('[data-page-ai-skill-list]');
if (skillList instanceof HTMLElement) {
var skills = pageAiFilteredSkillEntries();
if (!skills.length) {
skillList.innerHTML = '没有匹配的技能。
';
} else {
var sourceParts = pageAiSkillSourceParts(pageAiCurrentSkillSource());
var group = sourceParts.group;
var headingExtra = group === 'hermes' ? ' · ' + sourceParts.profile : '';
var collapsed = pageAiSkillGroupCollapsed(pageAiCurrentSkillSource());
skillList.innerHTML = '' +
'' +
'' +
(collapsed ? '' : skills.map(function(skill) {
var sourceText = pageAiSkillOriginLabel(skill)
+ (skill.configScope ? ' · ' + skill.configScope : '')
+ (skill.readOnly ? ' · 只读' : '')
+ (skill.modified ? ' · modified' : '');
var description = String(skill.description || '').trim();
var hasDescription = description && description !== '---' && description !== '无描述';
var displayName = String(skill.title || skill.name || skill.id || '').trim();
var disabled = skill.toggleable === false || skill.readOnly === true;
return '' +
'' +
'
' +
'
' +
'
' + escapeHtml(displayName) + '
' +
'
' + escapeHtml(sourceText) + '
' +
'
' +
(hasDescription ? '
' + escapeHtml(description) + '
' : '') +
'
' +
'
' +
'
';
}).join('')) +
'';
}
}
var toolsList = drawer.querySelector('[data-page-ai-tool-list]');
if (toolsList instanceof HTMLElement) {
var tools = pageAiNormalizeArray(pageUiState.pageAiTools);
if (!tools.length) {
toolsList.innerHTML = '尚未读取到 mnote tool manifest。
';
} else {
toolsList.innerHTML = tools.map(function(tool) {
return '' +
'' +
'
' +
'
' + escapeHtml(tool.name) + '
' +
'
' + escapeHtml(tool.scope || tool.kind || 'mnote') + ' · ' + escapeHtml(tool.status || 'available') + '
' +
(tool.unavailableReason ? '
' + escapeHtml(tool.unavailableReason) + '
' : '') +
'
' +
'
' +
'
';
}).join('');
}
}
var gatewayError = drawer.querySelector('[data-page-ai-gateway-error]');
if (gatewayError instanceof HTMLElement) {
gatewayError.textContent = pageUiState.pageAiGatewayHealthError || '';
gatewayError.hidden = !pageUiState.pageAiGatewayHealthError;
}
var gatewayStatusNode = drawer.querySelector('[data-page-ai-gateway-status]');
var gatewayDetail = drawer.querySelector('[data-page-ai-gateway-detail]');
var gatewayHealth = pageUiState.pageAiGatewayHealth;
if (gatewayStatusNode instanceof HTMLElement) {
if (!gatewayHealth) {
gatewayStatusNode.textContent = '未检查';
} else {
var gateway = gatewayHealth.gateway || {};
var profile = gatewayHealth.profile || {};
gatewayStatusNode.textContent = gatewayHealth.ok ? '可用' : '需要设置';
if (gateway.status) gatewayStatusNode.textContent += ' · ' + gateway.status;
if (profile.name) gatewayStatusNode.textContent += ' · ' + profile.name;
}
}
if (gatewayDetail instanceof HTMLElement) {
if (!gatewayHealth) {
gatewayDetail.innerHTML = '打开高级后会检查 agent runtime 与当前 profile。
';
} else {
var gatewayInfo = gatewayHealth.gateway || {};
var profileInfo = gatewayHealth.profile || {};
var suggestionText = pageAiNormalizeArray(gatewayHealth.suggestions).join(';');
gatewayDetail.innerHTML = '' +
'' +
'
' + escapeHtml(profileInfo.name || activeProfile) + '
' +
'
model.default: ' + escapeHtml(profileInfo.modelDefault || '未设置') + '
' +
'
provider: ' + escapeHtml(profileInfo.provider || '未设置') + ' · API key: ' + escapeHtml(profileInfo.apiKeyConfigured ? '已配置' : '未检测到') + '
' +
'
' +
'' +
'
' + escapeHtml(gatewayInfo.upstream || '未配置 upstream') + '
' +
'
gateway: ' + escapeHtml(gatewayInfo.status || 'unknown') + (gatewayInfo.httpStatus ? ' · HTTP ' + escapeHtml(gatewayInfo.httpStatus) : '') + '
' +
(suggestionText ? '
' + escapeHtml(suggestionText) + '
' : '') +
'
';
}
}
var toolError = drawer.querySelector('[data-page-ai-tool-error]');
if (toolError instanceof HTMLElement) {
toolError.textContent = pageUiState.pageAiToolsError || '';
toolError.hidden = !pageUiState.pageAiToolsError;
}
var lastTool = drawer.querySelector('[data-page-ai-last-tool]');
if (lastTool instanceof HTMLElement) {
var call = pageUiState.pageAiLastToolCall;
lastTool.textContent = call
? [call.event, call.name, call.runId, call.traceId || call.auditId].filter(Boolean).join(' · ')
: '暂无 tool call';
}
var reasonixPanel = drawer.querySelector('[data-page-ai-reasonix-panel]');
if (reasonixPanel instanceof HTMLElement) {
reasonixPanel.innerHTML = '' +
'' +
'' +
'
Reasonix 专属设置
ACP runtime / memory
' +
'
' +
'
' +
'' + escapeHtml(pageAiReasonixMemoryEnabled() ? '当前用户已开启 Reasonix global/project memory。' : '默认关闭 Reasonix memory,避免跨用户偏好混入。') + '
' +
'';
}
var chatOnlyPanel = drawer.querySelector('[data-page-ai-chat-only-panel]');
if (chatOnlyPanel instanceof HTMLElement) {
chatOnlyPanel.innerHTML = '';
}
}
function humanizePageAiResponse(rawText, promptText) {
var providerLabel = pageAiProviderLabel(pageUiState.pageAiProvider);
var text = String(rawText || '').trim();
if (!text) {
return providerLabel + ' 已收到你的问题“' + searchText(promptText) + '”,但这次没有返回可读内容。';
}
if (text.startsWith('{')) {
try {
var payload = JSON.parse(text);
var operation = payload && payload.operation ? payload.operation : {};
var normalized = operation && operation.normalized_input ? operation.normalized_input : {};
var args = normalized && normalized.args ? normalized.args : {};
var documentId = searchText(args.documentId || args.pageId || currentDocumentId());
var toolName = searchText(operation.tool_name || normalized.toolName || 'doc_get');
return providerLabel + ' 已收到你的问题“' + searchText(promptText) + '”。当前已通过 Hermes 调用 mnote plugin 工具,目标页面是 ' + (documentId || '当前页面') + ',本次选择的工具是 ' + toolName + '。如果你继续追问,我会沿当前页面上下文继续回复。';
} catch (_) {
return providerLabel + ' 已返回结果,但当前结果是结构化文本,已为你保留原始内容。';
}
}
return text;
}
function ensurePageAiDrawer() {
var existing = documentRef.querySelector('[data-testid="wolai-page-ai-drawer"]');
if (existing instanceof HTMLElement) return existing;
var drawer = documentRef.createElement('aside');
drawer.className = 'wolai-page-ai-drawer';
drawer.setAttribute('data-testid', 'wolai-page-ai-drawer');
drawer.setAttribute('data-mnote-surface', 'page-ai');
drawer.hidden = true;
drawer.innerHTML = '' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'等待 AI session' +
'' +
'当前页' +
'空闲' +
'
' +
'' +
'' +
'' +
'
' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'' +
'
' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'' +
'' +
'' +
'
' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'' +
'' +
'
' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'' +
'
' +
'' +
'' +
'' +
'
' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'' +
'
' +
'gateway' +
'未检查' +
'
' +
'
' +
'
' +
'' +
'
' +
'queue' +
'由 mnote-web 管理' +
'
' +
'
' +
'
' +
'' +
'
' +
'mnote tools' +
'暂无 tool call' +
'
' +
'
' +
'
' +
'
' +
'' +
'
' +
'' +
'' +
'
' +
'' +
'' +
'' +
'' +
'' +
'
' +
'' +
'
';
documentRef.body.appendChild(drawer);
return drawer;
}
function renderPageAiSuggestions() {
var drawer = ensurePageAiDrawer();
var list = drawer.querySelector('[data-page-ai-panel="chat"] [data-page-ai-suggestion-list]');
if (!(list instanceof HTMLElement)) return;
var container = list.closest('.wolai-page-ai-suggestions');
if (container instanceof HTMLElement) {
container.hidden = pageUiState.pageAiPage !== 'chat' || pageUiState.pageAiMessages.length > 0;
}
var suggestions = pageAiSuggestions();
var offset = pageUiState.pageAiSuggestionIndex % suggestions.length;
var ordered = suggestions.slice(offset).concat(suggestions.slice(0, offset)).slice(0, 3);
list.innerHTML = ordered.map(function(text) {
return '';
}).join('');
}
function renderPageAiConversation() {
var drawer = ensurePageAiDrawer();
renderPageAiSuggestions();
var conversation = drawer.querySelector('[data-page-ai-panel="' + cssEscape(pageUiState.pageAiPage || 'chat') + '"] [data-page-ai-conversation]');
if (!(conversation instanceof HTMLElement)) return;
if (pageUiState.pageAiPage === 'history') {
var sourceRows = pageUiState.pageAiSessionSearchResults.length
? pageUiState.pageAiSessionSearchResults
: pageUiState.pageAiSessions;
var historyRows = pageAiFilteredHistoryRows(sourceRows);
if (!historyRows.length) {
conversation.innerHTML = '没有匹配的历史会话。
';
return;
}
conversation.innerHTML = historyRows.map(function(session) {
var preview = pageAiSessionPreviewText(session);
var active = session.id === pageUiState.pageAiActiveSessionId;
var usage = pageAiUsageSummary(session.usage);
var agentLabel = pageAiSessionAgentLabel(session);
var meta = [agentLabel, pageAiSessionStorageLabel(session), session.permissionLevel, session.shareId, session.status, usage].filter(Boolean).join(' · ');
return '' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'
' +
'
';
}).join('');
return;
}
if (!pageUiState.pageAiMessages.length) {
conversation.innerHTML = '围绕当前页面提问,我会优先使用当前页内容、页面结构和已保存设置。
';
return;
}
conversation.innerHTML = pageUiState.pageAiMessages.map(function(item) {
var roleLabel = item.role === 'user' ? '你' : (item.role === 'tool' ? '工具' : 'AI');
if (item.role === 'tool') {
var statusLabel = item.status === 'completed' ? '完成' : (item.status === 'failed' ? '失败' : '运行中');
var locationRows = Array.isArray(item.locations) && item.locations.length
? '位置:' + item.locations.map(function(loc, idx) {
return '' +
'' + escapeHtml(loc) + '' +
'' +
'';
}).join('') + '
'
: '';
var detailRows = [
locationRows,
item.argsSummary ? '参数 ' + escapeHtml(item.argsSummary) + '
' : '',
item.resultSummary ? '结果 ' + escapeHtml(item.resultSummary) + '
' : '',
item.changedFiles && item.changedFiles.length ? '' + escapeHtml(pageAiFormatChangedFiles(item.changedFiles)) + '
' : '',
(item.traceId || item.auditId) ? '' + escapeHtml([item.traceId, item.auditId].filter(Boolean).join(' · ')) + '
' : ''
].filter(Boolean).join('');
return '' +
'' +
'
' + escapeHtml(roleLabel) + '
' +
'
' +
'
' +
'' +
'' + escapeHtml(item.toolName || item.content || 'tool') + '' +
'' + escapeHtml([statusLabel, item.toolKind, item.toolCallId].filter(Boolean).join(' · ')) + '' +
'
' +
(detailRows || '暂无参数或结果详情。
') +
' ' +
'
' +
'
';
}
if (item.kind === 'thought') {
return '' +
'' +
'思考过程
' +
'' + escapeHtml(item.content || '') + '
' +
' ';
}
if (item.kind === 'permission') {
var permissionActions = item.resolved ? '' : (
'' +
'' +
'' +
'
'
);
return '' +
'' +
'
权限
' +
'
' +
'' + escapeHtml(item.toolName || 'session/request_permission') + '
' +
'' + escapeHtml(item.argsSummary || item.content || '') + '' +
permissionActions +
'
' +
'
';
}
if (item.kind === 'plan') {
var planEntries = Array.isArray(item.entries) ? item.entries : [];
var listHtml = planEntries.map(function(entry, idx) {
return '' + escapeHtml(String(entry || '')) + '';
}).join('');
return '' +
'' +
'
' +
'执行计划 · ' + planEntries.length + ' 步
' +
'' +
'
' + listHtml + '
' +
'
' +
' ' +
'
';
}
var streamingAttr = item.streaming ? ' data-page-ai-streaming="true"' : '';
return '' +
'' +
'
' + escapeHtml(roleLabel) + '
' +
'
' + (item.role === 'assistant' ? renderPageAiMarkdown(item.content || '') : escapeHtml(item.content || '')) + '
' +
'
';
}).join('');
conversation.scrollTop = conversation.scrollHeight;
}
return {
ensurePageAiDrawer,
humanizePageAiResponse,
pageAiContextButtonSummary,
pageAiContextRefDetail,
pageAiContextRefDisabled,
pageAiContextRefLabel,
pageAiCurrentAgentSelectionLabel,
pageAiCurrentModelLabel,
pageAiFilteredSkillEntries,
pageAiFormatChangedFiles,
pageAiMemoryFileLabel,
pageAiRenderAgentProfileOption,
pageAiRunStatusLabel,
pageAiSuggestions,
pageAiTargetDetail,
pageAiTargetLabel,
pageAiContextScopeLabel,
renderPageAiControls,
renderPageAiConversation,
renderPageAiProviderButtons,
renderPageAiSuggestions,
};
}