Improve local evidence search and AI capabilities
This commit is contained in:
@@ -267,7 +267,15 @@ import {
|
||||
root.setAttribute('data-mnote-evidence-open', 'true');
|
||||
if (blockId) root.setAttribute('data-mnote-evidence-block-id', blockId);
|
||||
if (lineRange) root.setAttribute('data-mnote-evidence-line-range', lineRange);
|
||||
const target = blockId ? root.querySelector(`[data-block-id="${cssSafe(blockId)}"]`) : null;
|
||||
let target = blockId ? root.querySelector(`[data-block-id="${cssSafe(blockId)}"]`) : null;
|
||||
if (!(target instanceof HTMLElement) && lineRange) {
|
||||
const start = Number(String(lineRange).split('-')[0] || 0);
|
||||
if (Number.isFinite(start) && start > 0) {
|
||||
const blocks = Array.from(root.querySelectorAll('.ProseMirror [data-block-id]'))
|
||||
.filter((node) => node instanceof HTMLElement);
|
||||
target = blocks[Math.max(0, Math.min(blocks.length - 1, start - 1))] || null;
|
||||
}
|
||||
}
|
||||
if (target instanceof HTMLElement) {
|
||||
root.querySelectorAll('[data-mnote-evidence-text-highlight="true"]').forEach((node) => {
|
||||
if (node instanceof HTMLElement) node.removeAttribute('data-mnote-evidence-text-highlight');
|
||||
@@ -302,6 +310,12 @@ import {
|
||||
title,
|
||||
fileName: title,
|
||||
openTarget: 'active-tab',
|
||||
page: url.searchParams.get('page') || undefined,
|
||||
bbox: url.searchParams.get('bbox') || undefined,
|
||||
sourceMapPath: String(url.searchParams.get('sourceMapPath') || '').trim(),
|
||||
blockId: String(url.searchParams.get('blockId') || '').trim(),
|
||||
lineRange: url.searchParams.get('lineRange') || null,
|
||||
charRange: url.searchParams.get('charRange') || null,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -418,6 +418,21 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
}
|
||||
};
|
||||
|
||||
const officeOpenModeForEntry = (entry) => {
|
||||
if (!entry || normalizeResourceTabKind(entry) !== 'office') return '';
|
||||
const href = String(entry.passiveFrameSrc || entry.officeUrl || entry.href || '').trim()
|
||||
|| String(entry.panel?.querySelector?.('iframe.mnote-resource-tab-frame')?.getAttribute?.('src') || '').trim();
|
||||
if (href) {
|
||||
try {
|
||||
const url = new URL(href, window.location.origin);
|
||||
if (url.pathname === '/office-preview' || url.pathname === '/office-n') return 'preview';
|
||||
if (url.pathname === '/onlyoffice' || url.pathname.startsWith('/office/')) return 'onlyoffice_live';
|
||||
} catch (_) {}
|
||||
}
|
||||
if (entry.onlyofficeSessionId || entry.bridgeSessionId) return 'onlyoffice_live';
|
||||
return 'preview';
|
||||
};
|
||||
|
||||
const openEditorsSnapshotEntry = (entry, key, generatedAt = Date.now()) => {
|
||||
const active = entry?.tab instanceof HTMLElement
|
||||
? entry.tab.getAttribute('aria-selected') === 'true'
|
||||
@@ -432,15 +447,23 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const kind = normalizeResourceTabKind(entry);
|
||||
const dirtyState = resourceTabCloseGuardReason(entry?.session);
|
||||
const officeBridgeDebug = kind === 'office' ? officeBridgeDebugForEntry(entry) : null;
|
||||
const officeOpenMode = kind === 'office' ? officeOpenModeForEntry(entry) : '';
|
||||
const onlyofficeSessionId = String(
|
||||
officeBridgeDebug?.bridgeSessionId
|
||||
|| entry?.onlyofficeSessionId
|
||||
|| entry?.bridgeSessionId
|
||||
|| '',
|
||||
).trim();
|
||||
const snapshotResourceKind = kind === 'office'
|
||||
? (officeOpenMode === 'onlyoffice_live' || onlyofficeSessionId ? 'only_office' : 'attachment')
|
||||
: kind;
|
||||
const workspacePathSeed = entry?.workspacePath && typeof entry.workspacePath === 'object'
|
||||
? { ...entry.workspacePath, resourceKind: snapshotResourceKind }
|
||||
: entry?.workspacePath;
|
||||
return {
|
||||
objectIdentity,
|
||||
workspacePath: buildWorkspacePath({
|
||||
workspacePath: workspacePathSeed,
|
||||
workspaceId,
|
||||
sourceKind,
|
||||
rootUri,
|
||||
@@ -448,8 +471,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
documentId,
|
||||
objectIdentity,
|
||||
assetId,
|
||||
resourceKind: kind,
|
||||
workspacePath: entry?.workspacePath,
|
||||
resourceKind: snapshotResourceKind,
|
||||
}),
|
||||
paneRole: normalizePaneRole(entry?.paneRole),
|
||||
documentId,
|
||||
@@ -465,13 +487,14 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
dirtyGuard: dirtyState,
|
||||
assetId,
|
||||
path: relativePath,
|
||||
officeOpenMode,
|
||||
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,
|
||||
preview: officeOpenMode === 'preview',
|
||||
pinned: false,
|
||||
};
|
||||
};
|
||||
@@ -848,6 +871,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
panel.setAttribute('data-mnote-object-identity', objectIdentity);
|
||||
panel.setAttribute('data-pane-role', paneRole);
|
||||
panel.setAttribute('data-resource-kind', kind);
|
||||
panel.setAttribute('data-resource-path', String(input.path || '').trim());
|
||||
panel.hidden = true;
|
||||
nodes.strip.append(tab);
|
||||
nodes.panelRoot.append(panel);
|
||||
@@ -989,6 +1013,16 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
if (bbox) url.searchParams.set('bbox', bbox);
|
||||
if (locator.sourceMapPath) url.searchParams.set('sourceMapPath', String(locator.sourceMapPath));
|
||||
if (locator.blockId) url.searchParams.set('blockId', String(locator.blockId));
|
||||
if (url.pathname === '/office-preview' && frame.contentWindow) {
|
||||
frame.contentWindow.postMessage({
|
||||
type: 'mnote:office-evidence-locator',
|
||||
page: locator.page,
|
||||
bbox,
|
||||
sourceMapPath: locator.sourceMapPath || '',
|
||||
blockId: locator.blockId || '',
|
||||
}, window.location.origin);
|
||||
return;
|
||||
}
|
||||
frame.src = url.toString();
|
||||
} catch (_) {}
|
||||
};
|
||||
@@ -1015,11 +1049,22 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
};
|
||||
|
||||
const applyEvidenceLocatorToTextPanel = (entry, locator) => {
|
||||
if (!(entry?.panel instanceof HTMLElement) || !locator?.blockId) return;
|
||||
if (!(entry?.panel instanceof HTMLElement) || !locator) return;
|
||||
const root = entry.panel.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
|
||||
if (!(root instanceof HTMLElement)) return;
|
||||
const selector = `[data-block-id="${cssSafe(locator.blockId)}"]`;
|
||||
const target = root.querySelector(selector);
|
||||
const selector = locator.blockId ? `[data-block-id="${cssSafe(locator.blockId)}"]` : '';
|
||||
let target = selector ? root.querySelector(selector) : null;
|
||||
const query = String(locator?.openAction?.params?.query || locator?.query || '').trim();
|
||||
if (!(target instanceof HTMLElement) && query) {
|
||||
target = Array.from(root.querySelectorAll('.ProseMirror [data-block-id]'))
|
||||
.find((node) => node instanceof HTMLElement && String(node.textContent || '').includes(query)) || null;
|
||||
}
|
||||
const lineStart = Number(locator?.lineRange?.start ?? locator?.line_range?.start ?? 0);
|
||||
if (!(target instanceof HTMLElement) && Number.isFinite(lineStart) && lineStart > 0) {
|
||||
const blocks = Array.from(root.querySelectorAll('.ProseMirror [data-block-id]'))
|
||||
.filter((node) => node instanceof HTMLElement);
|
||||
target = blocks[Math.max(0, Math.min(blocks.length - 1, lineStart - 1))] || null;
|
||||
}
|
||||
if (!(target instanceof HTMLElement)) return;
|
||||
root.querySelectorAll('[data-mnote-evidence-text-highlight="true"]').forEach((node) => {
|
||||
if (node instanceof HTMLElement) node.removeAttribute('data-mnote-evidence-text-highlight');
|
||||
@@ -1364,6 +1409,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
rootUri: '',
|
||||
jobsBySource: new Map(),
|
||||
drawerOpen: false,
|
||||
taskFilter: 'active',
|
||||
eventSource: null,
|
||||
fileTreeRefreshKeys: new Set(),
|
||||
};
|
||||
@@ -1467,13 +1513,36 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
: status || '未知';
|
||||
};
|
||||
|
||||
const localOcrTaskCategory = (job) => {
|
||||
const status = String(job?.status || '').trim();
|
||||
if (['failed', 'stale', 'retry_scheduled'].includes(status)) return 'attention';
|
||||
if (['done', 'succeeded', 'success'].includes(status)) return 'completed';
|
||||
return 'active';
|
||||
};
|
||||
|
||||
const localOcrTaskProgress = (job) => {
|
||||
const current = Number(job?.progressCurrent ?? job?.currentProgress);
|
||||
const total = Number(job?.progressTotal ?? job?.maxProgress);
|
||||
if (Number.isFinite(current) && Number.isFinite(total) && total > 0) {
|
||||
return Math.max(0, Math.min(100, Math.round((current / total) * 100)));
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const localOcrTaskFilterLabel = (filter) => {
|
||||
return filter === 'active' ? '进行中'
|
||||
: filter === 'completed' ? '已完成'
|
||||
: filter === 'attention' ? '需处理'
|
||||
: '全部';
|
||||
};
|
||||
|
||||
const ensureLocalOcrTaskDock = () => {
|
||||
let dock = document.querySelector('[data-testid="mnote-local-ocr-task-dock"]');
|
||||
if (!(dock instanceof HTMLElement)) {
|
||||
dock = document.createElement('section');
|
||||
dock.className = 'mnote-local-ocr-task-dock';
|
||||
dock.setAttribute('data-testid', 'mnote-local-ocr-task-dock');
|
||||
dock.innerHTML = '<div class="mnote-local-ocr-task-drawer" data-testid="mnote-local-ocr-task-drawer" hidden><div class="mnote-local-ocr-task-head"><strong>OCR 任务</strong><button type="button" class="mnote-local-ocr-task-close" data-mnote-local-ocr-task-close aria-label="收起 OCR 任务">×</button></div><div class="mnote-local-ocr-task-list" data-testid="mnote-local-ocr-task-list"></div></div>';
|
||||
dock.innerHTML = '<div class="mnote-local-ocr-task-drawer" data-testid="mnote-local-ocr-task-drawer" hidden><div class="mnote-local-ocr-task-panel"><div class="mnote-local-ocr-task-head"><div><strong>后台任务</strong><span data-mnote-local-ocr-task-summary>暂无任务</span></div><button type="button" class="mnote-local-ocr-task-close" data-mnote-local-ocr-task-close aria-label="收起后台任务">×</button></div><div class="mnote-local-ocr-task-tabs" data-mnote-local-ocr-task-tabs></div><div class="mnote-local-ocr-task-toolbar"><span data-mnote-local-ocr-task-filter-label>进行中</span><button type="button" data-mnote-local-ocr-task-clear-completed>清除已完成</button></div><div class="mnote-local-ocr-task-list" data-testid="mnote-local-ocr-task-list"></div></div></div>';
|
||||
document.body.appendChild(dock);
|
||||
}
|
||||
let toggle = document.querySelector('[data-testid="mnote-local-ocr-task-toggle"]');
|
||||
@@ -1509,6 +1578,20 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
}
|
||||
return;
|
||||
}
|
||||
const tabButton = event.target instanceof Element ? event.target.closest('[data-mnote-local-ocr-task-tab]') : null;
|
||||
if (tabButton instanceof HTMLElement) {
|
||||
localOcrTaskState.taskFilter = tabButton.getAttribute('data-mnote-local-ocr-task-tab') || 'active';
|
||||
renderLocalOcrTaskDock();
|
||||
return;
|
||||
}
|
||||
const clearCompleted = event.target instanceof Element ? event.target.closest('[data-mnote-local-ocr-task-clear-completed]') : null;
|
||||
if (clearCompleted instanceof HTMLElement) {
|
||||
Array.from(localOcrTaskState.jobsBySource.entries()).forEach(([key, job]) => {
|
||||
if (localOcrTaskCategory(job) === 'completed') localOcrTaskState.jobsBySource.delete(key);
|
||||
});
|
||||
renderLocalOcrTaskDock();
|
||||
return;
|
||||
}
|
||||
const deleteButton = event.target instanceof Element ? event.target.closest('[data-mnote-local-ocr-task-delete]') : null;
|
||||
if (deleteButton instanceof HTMLElement) {
|
||||
const sourcePath = deleteButton.getAttribute('data-mnote-local-ocr-task-delete') || '';
|
||||
@@ -1568,12 +1651,18 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const dock = ensureLocalOcrTaskDock();
|
||||
const jobs = Array.from(localOcrTaskState.jobsBySource.values())
|
||||
.sort((left, right) => Number(right.updatedAtMs || 0) - Number(left.updatedAtMs || 0));
|
||||
const runningCount = jobs.filter((job) => !['done', 'failed', 'stale'].includes(String(job?.status || ''))).length;
|
||||
const toggle = document.querySelector('[data-testid="mnote-local-ocr-task-toggle"]');
|
||||
if (toggle instanceof HTMLButtonElement) {
|
||||
const activeJobs = jobs.filter((job) => localOcrTaskCategory(job) === 'active');
|
||||
const attentionJobs = jobs.filter((job) => localOcrTaskCategory(job) === 'attention');
|
||||
const completedJobs = jobs.filter((job) => localOcrTaskCategory(job) === 'completed');
|
||||
const runningCount = activeJobs.length;
|
||||
const taskToggles = Array.from(document.querySelectorAll('[data-testid="mnote-local-ocr-task-toggle"], [data-testid="mnote-floating-task-toggle"]'))
|
||||
.filter((node) => node instanceof HTMLButtonElement);
|
||||
taskToggles.forEach((toggle) => {
|
||||
const opensSettings = toggle.getAttribute('data-mnote-action') === 'open-ocr-settings';
|
||||
const label = runningCount > 0 ? `${runningCount} 个 OCR 处理中,打开 OCR 设置` : (jobs.length > 0 ? `${jobs.length} 个 OCR 任务,打开 OCR 设置` : 'OCR 设置');
|
||||
toggle.setAttribute('title', label);
|
||||
toggle.setAttribute('aria-label', label);
|
||||
const taskLabel = runningCount > 0 ? `${runningCount} 个后台任务正在运行` : (jobs.length > 0 ? `${jobs.length} 个后台任务` : '后台任务');
|
||||
toggle.setAttribute('title', opensSettings ? label : taskLabel);
|
||||
toggle.setAttribute('aria-label', opensSettings ? label : taskLabel);
|
||||
toggle.setAttribute('aria-expanded', localOcrTaskState.drawerOpen ? 'true' : 'false');
|
||||
toggle.classList.toggle('has-mnote-local-ocr-tasks', jobs.length > 0);
|
||||
const badge = toggle.querySelector('[data-mnote-local-ocr-task-count]');
|
||||
@@ -1583,28 +1672,85 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
} else {
|
||||
toggle.textContent = runningCount > 0 ? `OCR ${runningCount} 处理中` : `OCR ${jobs.length}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
const drawer = dock.querySelector('[data-testid="mnote-local-ocr-task-drawer"]');
|
||||
if (drawer instanceof HTMLElement) drawer.hidden = !localOcrTaskState.drawerOpen;
|
||||
const summary = dock.querySelector('[data-mnote-local-ocr-task-summary]');
|
||||
if (summary instanceof HTMLElement) {
|
||||
summary.textContent = `${runningCount} 进行中 · ${attentionJobs.length} 需处理 · ${completedJobs.length} 已完成`;
|
||||
}
|
||||
const tabs = dock.querySelector('[data-mnote-local-ocr-task-tabs]');
|
||||
if (tabs instanceof HTMLElement) {
|
||||
const tabItems = [
|
||||
['active', '进行中', activeJobs.length],
|
||||
['attention', '需处理', attentionJobs.length],
|
||||
['completed', '已完成', completedJobs.length],
|
||||
['all', '全部', jobs.length],
|
||||
];
|
||||
tabs.replaceChildren();
|
||||
tabItems.forEach(([key, label, count]) => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.setAttribute('data-mnote-local-ocr-task-tab', key);
|
||||
button.setAttribute('aria-selected', localOcrTaskState.taskFilter === key ? 'true' : 'false');
|
||||
button.textContent = `${label} ${count}`;
|
||||
tabs.appendChild(button);
|
||||
});
|
||||
}
|
||||
const filterLabel = dock.querySelector('[data-mnote-local-ocr-task-filter-label]');
|
||||
if (filterLabel instanceof HTMLElement) filterLabel.textContent = localOcrTaskFilterLabel(localOcrTaskState.taskFilter);
|
||||
const clearCompleted = dock.querySelector('[data-mnote-local-ocr-task-clear-completed]');
|
||||
if (clearCompleted instanceof HTMLButtonElement) clearCompleted.disabled = completedJobs.length === 0;
|
||||
const list = dock.querySelector('[data-testid="mnote-local-ocr-task-list"]');
|
||||
if (!(list instanceof HTMLElement)) return;
|
||||
list.replaceChildren();
|
||||
if (!jobs.length) {
|
||||
const visibleJobs = jobs.filter((job) => {
|
||||
return localOcrTaskState.taskFilter === 'all' || localOcrTaskCategory(job) === localOcrTaskState.taskFilter;
|
||||
});
|
||||
if (!visibleJobs.length) {
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'mnote-local-ocr-task-empty';
|
||||
empty.textContent = '暂无 OCR 任务';
|
||||
empty.textContent = jobs.length ? `暂无${localOcrTaskFilterLabel(localOcrTaskState.taskFilter)}任务` : '暂无后台任务';
|
||||
list.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
jobs.forEach((job) => {
|
||||
visibleJobs.forEach((job) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'mnote-local-ocr-task-row';
|
||||
row.setAttribute('data-mnote-local-ocr-task-row', String(job.sourceRootRelativePath || ''));
|
||||
row.setAttribute('data-mnote-local-ocr-task-status', String(job.status || ''));
|
||||
row.setAttribute('data-mnote-local-ocr-task-category', localOcrTaskCategory(job));
|
||||
const title = String(job.sourceRootRelativePath || '').split('/').filter(Boolean).pop() || 'OCR';
|
||||
row.innerHTML = '<div class="mnote-local-ocr-task-main"><strong></strong><span></span></div><div class="mnote-local-ocr-task-actions"><button type="button" data-mnote-local-ocr-task-open>打开</button><button type="button" data-mnote-local-ocr-task-retry>重试</button><button type="button" data-mnote-local-ocr-task-clear>清除</button><button type="button" data-mnote-local-ocr-task-delete>删除</button></div>';
|
||||
row.querySelector('strong').textContent = title;
|
||||
row.querySelector('span').textContent = statusTextForLocalOcrJob(job);
|
||||
const category = localOcrTaskCategory(job);
|
||||
const progress = localOcrTaskProgress(job);
|
||||
row.innerHTML = '<div class="mnote-local-ocr-task-main"><div class="mnote-local-ocr-task-title-line"><strong></strong><em></em></div><span></span><div class="mnote-local-ocr-task-progress" role="progressbar"><i></i></div></div><div class="mnote-local-ocr-task-actions"><button type="button" data-mnote-local-ocr-task-open>打开</button><button type="button" data-mnote-local-ocr-task-retry>重试</button><button type="button" data-mnote-local-ocr-task-clear>清除</button><button type="button" data-mnote-local-ocr-task-delete>删除</button></div>';
|
||||
const titleNode = row.querySelector('strong');
|
||||
if (titleNode instanceof HTMLElement) {
|
||||
titleNode.textContent = title;
|
||||
titleNode.setAttribute('title', String(job.sourceRootRelativePath || title));
|
||||
}
|
||||
const categoryNode = row.querySelector('em');
|
||||
if (categoryNode instanceof HTMLElement) {
|
||||
categoryNode.textContent = category === 'active' ? '进行中' : category === 'attention' ? '需处理' : '已完成';
|
||||
}
|
||||
const statusNode = row.querySelector('span');
|
||||
if (statusNode instanceof HTMLElement) statusNode.textContent = statusTextForLocalOcrJob(job);
|
||||
const progressBar = row.querySelector('.mnote-local-ocr-task-progress');
|
||||
const progressValue = row.querySelector('.mnote-local-ocr-task-progress i');
|
||||
if (progressBar instanceof HTMLElement && progressValue instanceof HTMLElement) {
|
||||
progressBar.hidden = category !== 'active' && progress === null;
|
||||
progressBar.setAttribute('aria-valuemin', '0');
|
||||
progressBar.setAttribute('aria-valuemax', '100');
|
||||
if (progress === null) {
|
||||
progressBar.setAttribute('data-progress-mode', 'indeterminate');
|
||||
progressBar.removeAttribute('aria-valuenow');
|
||||
progressValue.style.width = '';
|
||||
} else {
|
||||
progressBar.setAttribute('data-progress-mode', 'determinate');
|
||||
progressBar.setAttribute('aria-valuenow', String(progress));
|
||||
progressValue.style.width = `${progress}%`;
|
||||
}
|
||||
}
|
||||
const open = row.querySelector('[data-mnote-local-ocr-task-open]');
|
||||
if (open instanceof HTMLButtonElement) {
|
||||
open.setAttribute('data-mnote-local-ocr-task-open', String(job.sourceRootRelativePath || ''));
|
||||
@@ -2178,6 +2324,20 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const officePreviewBaseHref = (href) => {
|
||||
const raw = String(href || '').trim();
|
||||
if (!raw) return '';
|
||||
try {
|
||||
const url = new URL(raw, window.location.origin);
|
||||
['page', 'bbox', 'sourceMapPath', 'blockId', 'lineRange', 'charRange', 'mnoteResourceReload'].forEach((key) => {
|
||||
url.searchParams.delete(key);
|
||||
});
|
||||
return url.pathname + '?' + url.searchParams.toString();
|
||||
} catch (_) {
|
||||
return raw;
|
||||
}
|
||||
};
|
||||
|
||||
const refreshExistingOfficeResourceTab = (entry, input) => {
|
||||
if (!entry || entry.kind !== 'office') return false;
|
||||
const nextHref = String(input.officeUrl || input.href || '').trim();
|
||||
@@ -2186,7 +2346,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const currentHref = frame instanceof HTMLIFrameElement
|
||||
? String(frame.getAttribute('src') || frame.src || '').trim()
|
||||
: '';
|
||||
if (currentHref !== nextHref) void openPassiveResourceTab(entry, input);
|
||||
if (officePreviewBaseHref(currentHref) !== officePreviewBaseHref(nextHref)) void openPassiveResourceTab(entry, input);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,12 @@ export function createSidebarPageAiMarkdownRuntime(context) {
|
||||
codeSpans.push('<code>' + code + '</code>');
|
||||
return key;
|
||||
});
|
||||
html = html.replace(/\[([^\]\n]+)\]\(([^)\s]+)\)/g, function(match, label, href) {
|
||||
var normalizedHref = normalizePageAiMarkdownHref(href);
|
||||
if (!normalizedHref) return match;
|
||||
var citationAttr = isMnoteCitationHref(normalizedHref) ? ' data-page-ai-citation-link="true"' : '';
|
||||
return '<a href="' + escapeHtml(normalizedHref) + '" target="_blank" rel="noopener noreferrer"' + citationAttr + '>' + label + '</a>';
|
||||
});
|
||||
html = html.replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>');
|
||||
html = html.replace(/(^|[^*])\*([^*\n]+)\*/g, '$1<em>$2</em>');
|
||||
codeSpans.forEach(function(value, index) {
|
||||
@@ -32,6 +38,28 @@ export function createSidebarPageAiMarkdownRuntime(context) {
|
||||
return html;
|
||||
}
|
||||
|
||||
function normalizePageAiMarkdownHref(value) {
|
||||
var href = String(value || '').trim()
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'");
|
||||
if (!href || /[\u0000-\u001f<>"']/.test(href)) return '';
|
||||
var lower = href.toLowerCase();
|
||||
if (lower.startsWith('javascript:') || lower.startsWith('data:') || lower.startsWith('vbscript:')) return '';
|
||||
if (href.startsWith('/') || href.startsWith('#')) return href;
|
||||
if (/^https?:\/\//i.test(href) || /^mailto:/i.test(href)) return href;
|
||||
return '';
|
||||
}
|
||||
|
||||
function isMnoteCitationHref(href) {
|
||||
try {
|
||||
var url = new URL(href, window.location.origin);
|
||||
return url.origin === window.location.origin && url.pathname.startsWith('/documents/');
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function renderPageAiMarkdown(content) {
|
||||
var lines = String(content || '').replace(/\r\n?/g, '\n').split('\n');
|
||||
var blocks = [];
|
||||
|
||||
@@ -154,7 +154,7 @@ export function createSidebarPageAiProfileRuntime(context) {
|
||||
|
||||
function pageAiSessionAgentFilterOptions(rows) {
|
||||
var byValue = { all: '全部 agent' };
|
||||
pageAiNormalizeArray(rows).forEach(function(session) {
|
||||
pageAiNormalizeArray(rows).filter(pageAiVisibleHistorySession).forEach(function(session) {
|
||||
var value = pageAiSessionAgentFilterValue(session);
|
||||
byValue[value] = pageAiSessionAgentLabel(session);
|
||||
});
|
||||
@@ -163,9 +163,16 @@ export function createSidebarPageAiProfileRuntime(context) {
|
||||
});
|
||||
}
|
||||
|
||||
function pageAiVisibleHistorySession(session) {
|
||||
var source = String(session && session.source || '').trim();
|
||||
var status = String(session && session.status || '').trim();
|
||||
var messages = pageAiNormalizeArray(session && session.messages);
|
||||
return !(source === 'draft' && status === 'draft' && messages.length === 0);
|
||||
}
|
||||
|
||||
function pageAiFilteredHistoryRows(rows) {
|
||||
var filterValue = String(pageUiState.pageAiSessionAgentFilter || 'all').trim() || 'all';
|
||||
var normalized = pageAiNormalizeArray(rows);
|
||||
var normalized = pageAiNormalizeArray(rows).filter(pageAiVisibleHistorySession);
|
||||
if (filterValue === 'all') return normalized;
|
||||
return normalized.filter(function(session) {
|
||||
return pageAiSessionAgentFilterValue(session) === filterValue;
|
||||
|
||||
@@ -563,10 +563,13 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
var skillSourceSelect = drawer.querySelector('[data-page-ai-skill-source-select]');
|
||||
if (skillSourceSelect instanceof HTMLSelectElement) {
|
||||
var activeSkillSource = pageAiCurrentSkillSource();
|
||||
skillSourceSelect.innerHTML = pageAiSkillSourceOptions().map(function(option) {
|
||||
var sourceOptions = pageAiSkillSourceOptions();
|
||||
skillSourceSelect.innerHTML = sourceOptions.map(function(option) {
|
||||
return '<option value="' + escapeHtml(option.value) + '"' + (option.value === activeSkillSource ? ' selected' : '') + '>' + escapeHtml(option.label) + '</option>';
|
||||
}).join('');
|
||||
skillSourceSelect.value = activeSkillSource;
|
||||
var sourceControl = skillSourceSelect.closest('[data-page-ai-skill-source-control]');
|
||||
if (sourceControl instanceof HTMLElement) sourceControl.hidden = sourceOptions.length <= 1;
|
||||
}
|
||||
var hermesBuiltinToggle = drawer.querySelector('[data-page-ai-hide-hermes-builtin]');
|
||||
if (hermesBuiltinToggle instanceof HTMLInputElement) {
|
||||
@@ -580,7 +583,7 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
if (skillList instanceof HTMLElement) {
|
||||
var skills = pageAiFilteredSkillEntries();
|
||||
if (!skills.length) {
|
||||
skillList.innerHTML = '<div class="wolai-page-ai-empty">没有匹配的技能。</div>';
|
||||
skillList.innerHTML = '<div class="wolai-page-ai-empty">没有匹配的能力。</div>';
|
||||
} else {
|
||||
var sourceParts = pageAiSkillSourceParts(pageAiCurrentSkillSource());
|
||||
var group = sourceParts.group;
|
||||
@@ -593,14 +596,33 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'<span>' + escapeHtml(String(skills.length) + headingExtra) + '</span>' +
|
||||
'</button>' +
|
||||
(collapsed ? '' : skills.map(function(skill) {
|
||||
var sourceText = pageAiSkillOriginLabel(skill)
|
||||
+ (skill.configScope ? ' · ' + skill.configScope : '')
|
||||
+ (skill.readOnly ? ' · 只读' : '')
|
||||
+ (skill.modified ? ' · modified' : '');
|
||||
var toolCount = Number(skill.toolCount || pageAiNormalizeArray(skill.tools).length || 0);
|
||||
var disabledToolCount = Number(skill.disabledToolCount || 0);
|
||||
var capabilityMeta = [];
|
||||
if (toolCount > 0) capabilityMeta.push(String(toolCount) + ' 工具');
|
||||
if (disabledToolCount > 0) capabilityMeta.push(String(disabledToolCount) + ' 已关闭');
|
||||
if (pageAiNormalizeArray(skill.requiresContextRefs).length > 0) capabilityMeta.push('需要上下文');
|
||||
var categoryText = group === 'mnote' ? String(skill.categoryTitle || skill.category || '').trim() : '';
|
||||
var sourceMetaParts = [];
|
||||
if (group === 'mnote') {
|
||||
sourceMetaParts.push(categoryText || 'MNote');
|
||||
} else {
|
||||
sourceMetaParts.push(pageAiSkillOriginLabel(skill));
|
||||
sourceMetaParts.push('只读查看');
|
||||
}
|
||||
if (capabilityMeta.length) sourceMetaParts = sourceMetaParts.concat(capabilityMeta);
|
||||
if (skill.readOnly && group === 'mnote') sourceMetaParts.push('只读');
|
||||
if (skill.modified) sourceMetaParts.push('modified');
|
||||
var sourceText = sourceMetaParts.filter(Boolean).join(' · ');
|
||||
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;
|
||||
var switchControl = group === 'mnote'
|
||||
? '<button type="button" class="wolai-page-ai-skill-switch' + (pageAiSkillEnabled(skill) ? ' is-on' : '') + '" data-page-ai-skill-toggle="' + escapeHtml(skill.id || skill.name) + '" data-page-ai-skill-group="' + escapeHtml(group) + '" data-page-ai-skill-profile="' + escapeHtml(skill.profile || sourceParts.profile || '') + '" data-page-ai-skill-kind="' + escapeHtml(skill.skillKind || '') + '" aria-pressed="' + (pageAiSkillEnabled(skill) ? 'true' : 'false') + '"' + (disabled ? ' disabled' : '') + '>' +
|
||||
'<span></span>' +
|
||||
'</button>'
|
||||
: '<span class="wolai-page-ai-skill-readonly-badge">查看</span>';
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-skill-row">' +
|
||||
'<div class="wolai-page-ai-skill-copy">' +
|
||||
@@ -610,9 +632,7 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'</div>' +
|
||||
(hasDescription ? '<div class="wolai-page-ai-skill-desc">' + escapeHtml(description) + '</div>' : '') +
|
||||
'</div>' +
|
||||
'<button type="button" class="wolai-page-ai-skill-switch' + (pageAiSkillEnabled(skill) ? ' is-on' : '') + '" data-page-ai-skill-toggle="' + escapeHtml(skill.id || skill.name) + '" data-page-ai-skill-group="' + escapeHtml(group) + '" data-page-ai-skill-profile="' + escapeHtml(skill.profile || sourceParts.profile || '') + '" data-page-ai-skill-kind="' + escapeHtml(skill.skillKind || '') + '" aria-pressed="' + (pageAiSkillEnabled(skill) ? 'true' : 'false') + '"' + (disabled ? ' disabled' : '') + '>' +
|
||||
'<span></span>' +
|
||||
'</button>' +
|
||||
switchControl +
|
||||
'</div>';
|
||||
}).join('')) +
|
||||
'</section>';
|
||||
@@ -751,7 +771,7 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'<div class="wolai-page-ai-header-actions">' +
|
||||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="new-session" aria-label="新建 AI 会话" title="新建 AI 会话">+</button>' +
|
||||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="history" aria-label="历史会话" title="历史会话">⌕</button>' +
|
||||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-tab="skills" aria-label="技能" title="技能">' +
|
||||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-tab="skills" aria-label="能力" title="能力">' +
|
||||
'<svg class="wolai-page-ai-icon-svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false">' +
|
||||
'<path d="M12 3l1.35 4.15L17.5 8.5l-4.15 1.35L12 14l-1.35-4.15L6.5 8.5l4.15-1.35L12 3z" />' +
|
||||
'<path d="M18.5 13l.8 2.4 2.2.8-2.2.8-.8 2.4-.8-2.4-2.2-.8 2.2-.8.8-2.4z" />' +
|
||||
@@ -856,17 +876,17 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
|
||||
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">Agent</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="skills" role="tab" aria-selected="true">Skills</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="skills" role="tab" aria-selected="true">能力</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="runtime" role="tab" aria-selected="false">Runtime</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-ai-skills-toolbar">' +
|
||||
'<label class="wolai-page-ai-skill-search">' +
|
||||
'<span>搜索技能</span>' +
|
||||
'<input type="search" data-page-ai-skill-search placeholder="搜索技能…" />' +
|
||||
'<span>搜索能力</span>' +
|
||||
'<input type="search" data-page-ai-skill-search placeholder="搜索能力…" />' +
|
||||
'</label>' +
|
||||
'<label class="wolai-page-ai-profile-select" data-page-ai-skill-source-control>' +
|
||||
'<span>技能来源</span>' +
|
||||
'<span>能力来源</span>' +
|
||||
'<select data-page-ai-skill-source-select></select>' +
|
||||
'</label>' +
|
||||
'<label class="wolai-page-ai-skill-filter-toggle">' +
|
||||
@@ -882,7 +902,7 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
|
||||
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">Agent</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="skills" role="tab" aria-selected="false">Skills</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="skills" role="tab" aria-selected="false">能力</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="runtime" role="tab" aria-selected="true">Runtime</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
@@ -911,9 +931,9 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'</div>' +
|
||||
'<div class="wolai-page-ai-tool-list" data-page-ai-queue-list></div>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-ai-tools-panel">' +
|
||||
'<div class="wolai-page-ai-tools-panel" hidden>' +
|
||||
'<div class="wolai-page-ai-tools-head">' +
|
||||
'<span>mnote tools</span>' +
|
||||
'<span>MNote 工具调试</span>' +
|
||||
'<span data-page-ai-last-tool>暂无 tool call</span>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-ai-inline-error" data-page-ai-tool-error hidden></div>' +
|
||||
@@ -982,6 +1002,231 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function pageAiConversationShouldStickToBottom(conversation) {
|
||||
var distance = conversation.scrollHeight - conversation.scrollTop - conversation.clientHeight;
|
||||
return distance <= 48;
|
||||
}
|
||||
|
||||
function pageAiCaptureOpenToolDetails(conversation) {
|
||||
var openToolDetails = {};
|
||||
conversation.querySelectorAll('[data-page-ai-collapse-card], [data-page-ai-tool-card]').forEach(function(card) {
|
||||
if (!(card instanceof HTMLElement)) return;
|
||||
var key = String(card.getAttribute('data-page-ai-collapse-id') || card.getAttribute('data-page-ai-tool-group-id') || card.getAttribute('data-page-ai-tool-call-id') || '').trim();
|
||||
var details = card.querySelector('[data-page-ai-collapse-details], .wolai-page-ai-tool-details');
|
||||
if (key && details instanceof HTMLDetailsElement && details.open) {
|
||||
openToolDetails[key] = true;
|
||||
}
|
||||
});
|
||||
return openToolDetails;
|
||||
}
|
||||
|
||||
function pageAiRestoreOpenToolDetails(conversation, openToolDetails) {
|
||||
if (!openToolDetails || typeof openToolDetails !== 'object') return;
|
||||
conversation.querySelectorAll('[data-page-ai-collapse-card], [data-page-ai-tool-card]').forEach(function(card) {
|
||||
if (!(card instanceof HTMLElement)) return;
|
||||
var key = String(card.getAttribute('data-page-ai-collapse-id') || card.getAttribute('data-page-ai-tool-group-id') || card.getAttribute('data-page-ai-tool-call-id') || '').trim();
|
||||
if (!key || openToolDetails[key] !== true) return;
|
||||
var details = card.querySelector('[data-page-ai-collapse-details], .wolai-page-ai-tool-details');
|
||||
if (details instanceof HTMLDetailsElement) details.open = true;
|
||||
});
|
||||
}
|
||||
|
||||
function pageAiRenderToolDetailRows(item) {
|
||||
var locationRows = Array.isArray(item.locations) && item.locations.length
|
||||
? '<div class="wolai-page-ai-tool-meta">位置:' + item.locations.map(function(loc) {
|
||||
return '<span style="display:inline-flex;align-items:center;gap:4px;margin-right:8px">' +
|
||||
'<span>' + escapeHtml(loc) + '</span>' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" style="font-size:0.85em;padding:0 4px;min-width:unset" data-page-ai-open-location="' + escapeHtml(loc) + '" data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" title="打开文件">打开</button>' +
|
||||
'</span>';
|
||||
}).join('') + '</div>'
|
||||
: '';
|
||||
return [
|
||||
locationRows,
|
||||
item.argsSummary ? '<div class="wolai-page-ai-tool-meta">参数 ' + escapeHtml(item.argsSummary) + '</div>' : '',
|
||||
item.resultSummary ? '<div class="wolai-page-ai-tool-meta">结果 ' + escapeHtml(item.resultSummary) + '</div>' : '',
|
||||
item.changedFiles && item.changedFiles.length ? '<pre class="wolai-page-ai-tool-meta wolai-page-ai-changed-files">' + escapeHtml(pageAiFormatChangedFiles(item.changedFiles)) + '</pre>' : '',
|
||||
(item.traceId || item.auditId) ? '<div class="wolai-page-ai-tool-meta">' + escapeHtml([item.traceId, item.auditId].filter(Boolean).join(' · ')) + '</div>' : ''
|
||||
].filter(Boolean).join('');
|
||||
}
|
||||
|
||||
function pageAiToolStatusLabel(status) {
|
||||
if (status === 'completed') return '完成';
|
||||
if (status === 'failed') return '失败';
|
||||
return '运行中';
|
||||
}
|
||||
|
||||
function pageAiToolGroupStatus(items) {
|
||||
if (items.some(function(item) { return item.status === 'failed'; })) return 'failed';
|
||||
if (items.some(function(item) { return item.status !== 'completed'; })) return 'running';
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
function pageAiRenderToolGroup(items, groupIndex) {
|
||||
var tools = pageAiNormalizeArray(items);
|
||||
if (!tools.length) return '';
|
||||
var status = pageAiToolGroupStatus(tools);
|
||||
var completedCount = tools.filter(function(item) { return item.status === 'completed'; }).length;
|
||||
var failedCount = tools.filter(function(item) { return item.status === 'failed'; }).length;
|
||||
var runningCount = tools.length - completedCount - failedCount;
|
||||
var countParts = [String(tools.length) + ' 个'];
|
||||
if (completedCount) countParts.push('完成 ' + String(completedCount));
|
||||
if (failedCount) countParts.push('失败 ' + String(failedCount));
|
||||
if (runningCount) countParts.push('运行中 ' + String(runningCount));
|
||||
var firstToolId = String(tools[0] && tools[0].toolCallId || '').trim();
|
||||
var groupId = 'tool-group-' + String(groupIndex) + '-' + (firstToolId || String(tools.length));
|
||||
var rows = tools.map(function(item) {
|
||||
var statusLabel = pageAiToolStatusLabel(item.status);
|
||||
var meta = [statusLabel, item.toolKind, item.toolCallId].filter(Boolean).join(' · ');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-tool-item" data-page-ai-tool-item data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" data-page-ai-tool-status="' + escapeHtml(item.status || '') + '">' +
|
||||
'<div class="wolai-page-ai-tool-item-head">' +
|
||||
'<strong>' + escapeHtml(item.toolName || item.content || 'tool') + '</strong>' +
|
||||
'<span>' + escapeHtml(meta) + '</span>' +
|
||||
'</div>' +
|
||||
(pageAiRenderToolDetailRows(item) || '<div class="wolai-page-ai-tool-meta">暂无参数或结果详情。</div>') +
|
||||
'</div>';
|
||||
}).join('');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-collapse-card data-page-ai-collapse-id="' + escapeHtml(groupId) + '" data-page-ai-tool-card data-page-ai-tool-group="true" data-page-ai-tool-group-id="' + escapeHtml(groupId) + '" data-page-ai-tool-status="' + escapeHtml(status) + '">' +
|
||||
'<div class="wolai-page-ai-message-role">工具</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<details class="wolai-page-ai-tool-details wolai-page-ai-tool-group-details" data-page-ai-collapse-details>' +
|
||||
'<summary>' +
|
||||
'<strong>调用工具</strong>' +
|
||||
'<span>' + escapeHtml(countParts.join(' · ')) + '</span>' +
|
||||
'</summary>' +
|
||||
'<div class="wolai-page-ai-tool-group-list">' + rows + '</div>' +
|
||||
'</details>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function pageAiRenderThoughtGroup(items, groupIndex) {
|
||||
var thoughts = pageAiNormalizeArray(items).filter(function(item) {
|
||||
return item && item.kind === 'thought' && String(item.content || '').trim();
|
||||
});
|
||||
if (!thoughts.length) return '';
|
||||
var firstText = String(thoughts[0] && thoughts[0].content || '').slice(0, 24).replace(/\s+/g, ' ').trim();
|
||||
var groupId = 'thought-group-' + String(groupIndex) + '-' + String(thoughts.length) + '-' + (firstText || 'delta');
|
||||
var content = thoughts.map(function(item) {
|
||||
return String(item.content || '').trim();
|
||||
}).filter(Boolean).join('\n\n');
|
||||
var countLabel = thoughts.length > 1 ? String(thoughts.length) + ' 段' : '1 段';
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-collapse-card data-page-ai-collapse-id="' + escapeHtml(groupId) + '" data-page-ai-thought-card="true">' +
|
||||
'<div class="wolai-page-ai-message-role">AI</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<details class="wolai-page-ai-tool-details wolai-page-ai-thought-group-details" data-page-ai-collapse-details data-page-ai-thought-delta="true">' +
|
||||
'<summary>' +
|
||||
'<strong>思考过程</strong>' +
|
||||
'<span>' + escapeHtml(countLabel) + '</span>' +
|
||||
'</summary>' +
|
||||
'<div class="wolai-page-ai-thought-group-list">' + escapeHtml(content) + '</div>' +
|
||||
'</details>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function pageAiRenderNonToolMessage(item) {
|
||||
var roleLabel = item.role === 'user' ? '你' : (item.role === 'tool' ? '工具' : 'AI');
|
||||
if (item.kind === 'thought') {
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-collapse-card data-page-ai-collapse-id="thought-single">' +
|
||||
'<div class="wolai-page-ai-message-role">AI</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<details class="wolai-page-ai-tool-details wolai-page-ai-thought-group-details" data-page-ai-collapse-details data-page-ai-thought-delta="true">' +
|
||||
'<summary><strong>思考过程</strong><span>1 段</span></summary>' +
|
||||
'<div class="wolai-page-ai-thought-group-list">' + escapeHtml(item.content || '') + '</div>' +
|
||||
'</details>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
if (item.kind === 'permission') {
|
||||
var permissionActions = item.resolved ? '' : (
|
||||
'<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-id="' + escapeHtml(item.permissionId || '') + '">允许</button>' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="deny" data-page-ai-permission-id="' + escapeHtml(item.permissionId || '') + '">拒绝</button>' +
|
||||
'</div>'
|
||||
);
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-permission-request="' + escapeHtml(item.permissionId || '') + '">' +
|
||||
'<div class="wolai-page-ai-message-role">权限</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<strong>' + escapeHtml(item.toolName || 'session/request_permission') + '</strong><br />' +
|
||||
'<span>' + escapeHtml(item.argsSummary || item.content || '') + '</span>' +
|
||||
permissionActions +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
if (item.kind === 'plan') {
|
||||
var planEntries = Array.isArray(item.entries) ? item.entries : [];
|
||||
var listHtml = planEntries.map(function(entry) {
|
||||
return '<li style="margin:2px 0">' + escapeHtml(String(entry || '')) + '</li>';
|
||||
}).join('');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-plan="true">' +
|
||||
'<details class="wolai-page-ai-plan-details" open>' +
|
||||
'<summary class="wolai-page-ai-message-role" style="cursor:pointer;user-select:none;opacity:0.8;font-size:0.85em">执行计划 · ' + planEntries.length + ' 步</summary>' +
|
||||
'<div class="wolai-page-ai-message-text" style="margin-top:4px;opacity:0.75;font-size:0.9em;border-left:2px solid var(--wolai-border-color, #ddd);padding-left:8px">' +
|
||||
'<ol style="margin:4px 0;padding-left:20px">' + listHtml + '</ol>' +
|
||||
'</div>' +
|
||||
'</details>' +
|
||||
'</div>';
|
||||
}
|
||||
var streamingAttr = item.streaming ? ' data-page-ai-streaming="true"' : '';
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--' + escapeHtml(item.role || 'assistant') + '"' + streamingAttr + '>' +
|
||||
'<div class="wolai-page-ai-message-role">' + escapeHtml(roleLabel) + '</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' + (item.role === 'assistant' ? renderPageAiMarkdown(item.content || '') : escapeHtml(item.content || '')) + '</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function pageAiConversationRenderHtml(messages) {
|
||||
var turns = [];
|
||||
var current = null;
|
||||
function ensureTurn() {
|
||||
if (!current) current = { user: null, tools: [], status: [], assistants: [], others: [] };
|
||||
return current;
|
||||
}
|
||||
function flushTurn() {
|
||||
if (!current) return;
|
||||
turns.push(current);
|
||||
current = null;
|
||||
}
|
||||
pageAiNormalizeArray(messages).forEach(function(item) {
|
||||
if (item && item.role === 'user') {
|
||||
flushTurn();
|
||||
current = { user: item, tools: [], status: [], assistants: [], others: [] };
|
||||
return;
|
||||
}
|
||||
var turn = ensureTurn();
|
||||
if (item && item.role === 'tool' && item.kind !== 'permission') {
|
||||
turn.tools.push(item);
|
||||
} else if (item && (item.kind === 'thought' || item.kind === 'plan' || item.kind === 'permission')) {
|
||||
turn.status.push(item);
|
||||
} else if (item && item.role === 'assistant') {
|
||||
turn.assistants.push(item);
|
||||
} else {
|
||||
turn.others.push(item);
|
||||
}
|
||||
});
|
||||
flushTurn();
|
||||
var toolGroupIndex = 0;
|
||||
var thoughtGroupIndex = 0;
|
||||
return turns.map(function(turn) {
|
||||
var html = [];
|
||||
if (turn.user) html.push(pageAiRenderNonToolMessage(turn.user));
|
||||
if (turn.tools.length) html.push(pageAiRenderToolGroup(turn.tools, toolGroupIndex++));
|
||||
var thoughts = turn.status.filter(function(item) { return item && item.kind === 'thought'; });
|
||||
var otherStatus = turn.status.filter(function(item) { return !(item && item.kind === 'thought'); });
|
||||
if (thoughts.length) html.push(pageAiRenderThoughtGroup(thoughts, thoughtGroupIndex++));
|
||||
html = html.concat(otherStatus.map(pageAiRenderNonToolMessage));
|
||||
html = html.concat(turn.assistants.map(pageAiRenderNonToolMessage));
|
||||
html = html.concat(turn.others.map(pageAiRenderNonToolMessage));
|
||||
return html.join('');
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderPageAiConversation() {
|
||||
var drawer = ensurePageAiDrawer();
|
||||
renderPageAiSuggestions();
|
||||
@@ -1022,86 +1267,17 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
conversation.innerHTML = '<div class="wolai-page-ai-empty">围绕当前页面提问,我会优先使用当前页内容、页面结构和已保存设置。</div>';
|
||||
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
|
||||
? '<div class="wolai-page-ai-tool-meta">位置:' + item.locations.map(function(loc, idx) {
|
||||
return '<span style="display:inline-flex;align-items:center;gap:4px;margin-right:8px">' +
|
||||
'<span>' + escapeHtml(loc) + '</span>' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" style="font-size:0.85em;padding:0 4px;min-width:unset" data-page-ai-open-location="' + escapeHtml(loc) + '" data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" title="打开文件">打开</button>' +
|
||||
'</span>';
|
||||
}).join('') + '</div>'
|
||||
: '';
|
||||
var detailRows = [
|
||||
locationRows,
|
||||
item.argsSummary ? '<div class="wolai-page-ai-tool-meta">参数 ' + escapeHtml(item.argsSummary) + '</div>' : '',
|
||||
item.resultSummary ? '<div class="wolai-page-ai-tool-meta">结果 ' + escapeHtml(item.resultSummary) + '</div>' : '',
|
||||
item.changedFiles && item.changedFiles.length ? '<pre class="wolai-page-ai-tool-meta wolai-page-ai-changed-files">' + escapeHtml(pageAiFormatChangedFiles(item.changedFiles)) + '</pre>' : '',
|
||||
(item.traceId || item.auditId) ? '<div class="wolai-page-ai-tool-meta">' + escapeHtml([item.traceId, item.auditId].filter(Boolean).join(' · ')) + '</div>' : ''
|
||||
].filter(Boolean).join('');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-tool-card data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" data-page-ai-tool-status="' + escapeHtml(item.status || '') + '">' +
|
||||
'<div class="wolai-page-ai-message-role">' + escapeHtml(roleLabel) + '</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<details class="wolai-page-ai-tool-details">' +
|
||||
'<summary>' +
|
||||
'<strong>' + escapeHtml(item.toolName || item.content || 'tool') + '</strong>' +
|
||||
'<span>' + escapeHtml([statusLabel, item.toolKind, item.toolCallId].filter(Boolean).join(' · ')) + '</span>' +
|
||||
'</summary>' +
|
||||
(detailRows || '<div class="wolai-page-ai-tool-meta">暂无参数或结果详情。</div>') +
|
||||
'</details>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
if (item.kind === 'thought') {
|
||||
return '' +
|
||||
'<details class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-thought-delta="true">' +
|
||||
'<summary class="wolai-page-ai-message-role" style="cursor:pointer;user-select:none;opacity:0.7;font-size:0.85em">思考过程</summary>' +
|
||||
'<div class="wolai-page-ai-message-text" style="margin-top:4px;opacity:0.6;font-size:0.9em;border-left:2px solid var(--wolai-border-color, #ddd);padding-left:8px">' + escapeHtml(item.content || '') + '</div>' +
|
||||
'</details>';
|
||||
}
|
||||
if (item.kind === 'permission') {
|
||||
var permissionActions = item.resolved ? '' : (
|
||||
'<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-id="' + escapeHtml(item.permissionId || '') + '">允许</button>' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="deny" data-page-ai-permission-id="' + escapeHtml(item.permissionId || '') + '">拒绝</button>' +
|
||||
'</div>'
|
||||
);
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-permission-request="' + escapeHtml(item.permissionId || '') + '">' +
|
||||
'<div class="wolai-page-ai-message-role">权限</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<strong>' + escapeHtml(item.toolName || 'session/request_permission') + '</strong><br />' +
|
||||
'<span>' + escapeHtml(item.argsSummary || item.content || '') + '</span>' +
|
||||
permissionActions +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
if (item.kind === 'plan') {
|
||||
var planEntries = Array.isArray(item.entries) ? item.entries : [];
|
||||
var listHtml = planEntries.map(function(entry, idx) {
|
||||
return '<li style="margin:2px 0">' + escapeHtml(String(entry || '')) + '</li>';
|
||||
}).join('');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-plan="true">' +
|
||||
'<details class="wolai-page-ai-plan-details" open>' +
|
||||
'<summary class="wolai-page-ai-message-role" style="cursor:pointer;user-select:none;opacity:0.8;font-size:0.85em">执行计划 · ' + planEntries.length + ' 步</summary>' +
|
||||
'<div class="wolai-page-ai-message-text" style="margin-top:4px;opacity:0.75;font-size:0.9em;border-left:2px solid var(--wolai-border-color, #ddd);padding-left:8px">' +
|
||||
'<ol style="margin:4px 0;padding-left:20px">' + listHtml + '</ol>' +
|
||||
'</div>' +
|
||||
'</details>' +
|
||||
'</div>';
|
||||
}
|
||||
var streamingAttr = item.streaming ? ' data-page-ai-streaming="true"' : '';
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--' + escapeHtml(item.role || 'assistant') + '"' + streamingAttr + '>' +
|
||||
'<div class="wolai-page-ai-message-role">' + escapeHtml(roleLabel) + '</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' + (item.role === 'assistant' ? renderPageAiMarkdown(item.content || '') : escapeHtml(item.content || '')) + '</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
conversation.scrollTop = conversation.scrollHeight;
|
||||
var shouldStickToBottom = pageAiConversationShouldStickToBottom(conversation);
|
||||
var previousScrollTop = conversation.scrollTop;
|
||||
var openToolDetails = pageAiCaptureOpenToolDetails(conversation);
|
||||
conversation.innerHTML = pageAiConversationRenderHtml(pageUiState.pageAiMessages);
|
||||
pageAiRestoreOpenToolDetails(conversation, openToolDetails);
|
||||
if (shouldStickToBottom) {
|
||||
conversation.scrollTop = conversation.scrollHeight;
|
||||
} else {
|
||||
var maxScrollTop = Math.max(0, conversation.scrollHeight - conversation.clientHeight);
|
||||
conversation.scrollTop = Math.min(previousScrollTop, maxScrollTop);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -724,6 +724,89 @@ export function createSidebarPageAiRuntime(context) {
|
||||
});
|
||||
}
|
||||
|
||||
function pageAiEvidenceRangeFromParam(value) {
|
||||
var text = String(value || '').trim();
|
||||
if (!text) return null;
|
||||
var match = text.match(/^(\d+)[-:,](\d+)$/);
|
||||
if (!match) return text;
|
||||
return { start: Number(match[1]), end: Number(match[2]) };
|
||||
}
|
||||
|
||||
function pageAiEvidenceBboxFromParam(value) {
|
||||
var text = String(value || '').trim();
|
||||
if (!text) return null;
|
||||
var parts = text.split(',').map(function(part) { return Number(part.trim()); });
|
||||
if (parts.length < 4 || parts.slice(0, 4).some(function(part) { return !Number.isFinite(part); })) return text;
|
||||
return { x0: parts[0], y0: parts[1], x1: parts[2], y1: parts[3] };
|
||||
}
|
||||
|
||||
function pageAiDocumentIdFromUrl(url) {
|
||||
var prefix = '/documents/';
|
||||
if (!url.pathname.startsWith(prefix)) return '';
|
||||
try {
|
||||
return decodeURIComponent(url.pathname.slice(prefix.length).split('/')[0] || '');
|
||||
} catch (_error) {
|
||||
return url.pathname.slice(prefix.length).split('/')[0] || '';
|
||||
}
|
||||
}
|
||||
|
||||
function pageAiCitationResourcePath(url, rootUri) {
|
||||
var explicitPath = String(url.searchParams.get('resourcePath') || '').trim();
|
||||
if (explicitPath) return explicitPath;
|
||||
var raw = String(url.searchParams.get('resourceTab') || '').trim();
|
||||
if (raw.indexOf('::') >= 0) raw = raw.slice(raw.indexOf('::') + 2);
|
||||
if (!raw.startsWith('resource:file:')) return '';
|
||||
var rest = raw.slice('resource:file:'.length);
|
||||
var prefix = String(rootUri || '').trim() + ':';
|
||||
if (!prefix.trim() || !rest.startsWith(prefix)) return '';
|
||||
return rest.slice(prefix.length).replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
function pageAiOpenCitationUrl(href) {
|
||||
var url;
|
||||
try {
|
||||
url = new URL(String(href || ''), window.location.origin);
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
if (url.origin !== window.location.origin || !url.pathname.startsWith('/documents/')) return false;
|
||||
var rootUri = String(url.searchParams.get('rootUri') || currentRootUri() || '').trim();
|
||||
var resourcePath = pageAiCitationResourcePath(url, rootUri);
|
||||
var documentId = pageAiDocumentIdFromUrl(url) || currentDocumentId() || '';
|
||||
var workspaceId = String(url.searchParams.get('workspaceId') || resolveWorkspaceId(document.body) || '').trim();
|
||||
if (resourcePath) {
|
||||
void openLocalResourceInActiveTab({
|
||||
path: resourcePath,
|
||||
rootUri: rootUri,
|
||||
documentId: documentId,
|
||||
workspaceId: workspaceId,
|
||||
sourceKind: String(url.searchParams.get('sourceKind') || currentSourceKind() || 'local_folder').trim(),
|
||||
page: url.searchParams.get('page') || undefined,
|
||||
bbox: pageAiEvidenceBboxFromParam(url.searchParams.get('bbox')),
|
||||
sourceMapPath: String(url.searchParams.get('sourceMapPath') || '').trim(),
|
||||
blockId: String(url.searchParams.get('blockId') || '').trim(),
|
||||
lineRange: pageAiEvidenceRangeFromParam(url.searchParams.get('lineRange')),
|
||||
charRange: pageAiEvidenceRangeFromParam(url.searchParams.get('charRange')),
|
||||
openTarget: 'active-tab',
|
||||
paneRole: 'primary'
|
||||
}).then(function(opened) {
|
||||
if (!opened) window.location.assign(url.pathname + url.search + url.hash);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (typeof window.__mnoteDocumentPaneRuntime?.openPrimaryDocument === 'function') {
|
||||
void window.__mnoteDocumentPaneRuntime.openPrimaryDocument({
|
||||
documentId: documentId,
|
||||
workspaceId: workspaceId,
|
||||
sourceKind: String(url.searchParams.get('sourceKind') || currentSourceKind() || '').trim(),
|
||||
rootUri: rootUri,
|
||||
url: url
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function pageAiChatOnlyProfileEntries() {
|
||||
var profiles = pageAiNormalizeArray(pageUiState.pageAiProfiles);
|
||||
return PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY.map(function(spec) {
|
||||
@@ -786,10 +869,13 @@ export function createSidebarPageAiRuntime(context) {
|
||||
var archived = pageAiNormalizeArray(upstream && upstream.archived ? upstream.archived : []);
|
||||
return {
|
||||
categories: categories.map(function(category) {
|
||||
var skillRows = pageAiNormalizeArray(category && (category.skills || category.capabilities));
|
||||
return {
|
||||
name: String(category && category.name || '').trim() || 'misc',
|
||||
description: String(category && category.description || '').trim(),
|
||||
skills: pageAiNormalizeArray(category && category.skills).map(function(skill) {
|
||||
title: String(category && category.title || '').trim(),
|
||||
capabilities: pageAiNormalizeArray(category && category.capabilities),
|
||||
skills: skillRows.map(function(skill) {
|
||||
return {
|
||||
name: String(skill && skill.name || '').trim(),
|
||||
id: String(skill && skill.id || skill && skill.name || '').trim(),
|
||||
@@ -807,8 +893,18 @@ export function createSidebarPageAiRuntime(context) {
|
||||
configScope: String(skill && skill.configScope || '').trim(),
|
||||
skillKind: String(skill && skill.skillKind || '').trim(),
|
||||
profileId: String(skill && skill.profileId || '').trim(),
|
||||
toolNames: pageAiNormalizeArray(skill && skill.toolNames),
|
||||
tools: pageAiNormalizeArray(skill && skill.tools),
|
||||
toolCount: Number(skill && skill.toolCount || 0),
|
||||
disabledToolCount: Number(skill && skill.disabledToolCount || 0),
|
||||
status: String(skill && skill.status || '').trim(),
|
||||
capabilityId: String(skill && skill.capabilityId || '').trim(),
|
||||
capabilityKind: String(skill && skill.capabilityKind || '').trim(),
|
||||
uiKind: String(skill && skill.uiKind || '').trim(),
|
||||
requiresContextRefs: pageAiNormalizeArray(skill && skill.requiresContextRefs),
|
||||
readOnly: Boolean(skill && (skill.readOnly || skill.readonly || skill.configurable === false)),
|
||||
category: String(category && category.name || '').trim()
|
||||
category: String(skill && skill.category || category && category.name || '').trim(),
|
||||
categoryTitle: String(skill && skill.categoryTitle || category && category.title || category && category.name || '').trim()
|
||||
};
|
||||
})
|
||||
};
|
||||
@@ -831,6 +927,16 @@ export function createSidebarPageAiRuntime(context) {
|
||||
configScope: String(skill && skill.configScope || '').trim(),
|
||||
skillKind: String(skill && skill.skillKind || '').trim(),
|
||||
profileId: String(skill && skill.profileId || '').trim(),
|
||||
toolNames: pageAiNormalizeArray(skill && skill.toolNames),
|
||||
tools: pageAiNormalizeArray(skill && skill.tools),
|
||||
toolCount: Number(skill && skill.toolCount || 0),
|
||||
disabledToolCount: Number(skill && skill.disabledToolCount || 0),
|
||||
status: String(skill && skill.status || '').trim(),
|
||||
capabilityId: String(skill && skill.capabilityId || '').trim(),
|
||||
capabilityKind: String(skill && skill.capabilityKind || '').trim(),
|
||||
uiKind: String(skill && skill.uiKind || '').trim(),
|
||||
requiresContextRefs: pageAiNormalizeArray(skill && skill.requiresContextRefs),
|
||||
categoryTitle: String(skill && skill.categoryTitle || '').trim(),
|
||||
readOnly: Boolean(skill && (skill.readOnly || skill.readonly || skill.configurable === false))
|
||||
};
|
||||
})
|
||||
@@ -859,6 +965,16 @@ export function createSidebarPageAiRuntime(context) {
|
||||
configScope: skill.configScope || '',
|
||||
skillKind: skill.skillKind || '',
|
||||
profileId: skill.profileId || '',
|
||||
toolNames: skill.toolNames || [],
|
||||
tools: skill.tools || [],
|
||||
toolCount: Number(skill.toolCount || 0),
|
||||
disabledToolCount: Number(skill.disabledToolCount || 0),
|
||||
status: skill.status || '',
|
||||
capabilityId: skill.capabilityId || '',
|
||||
capabilityKind: skill.capabilityKind || '',
|
||||
uiKind: skill.uiKind || '',
|
||||
requiresContextRefs: skill.requiresContextRefs || [],
|
||||
categoryTitle: skill.categoryTitle || category.title || category.name || '',
|
||||
readOnly: Boolean(skill.readOnly || skill.readonly || skill.configurable === false)
|
||||
});
|
||||
});
|
||||
@@ -1310,18 +1426,21 @@ export function createSidebarPageAiRuntime(context) {
|
||||
function pageAiLoadSkillCatalog(runtime, profile) {
|
||||
var params = new URLSearchParams();
|
||||
params.set('runtime', runtime);
|
||||
var endpoint = '/api/hermes/client/skills';
|
||||
if (runtime === 'mnote') {
|
||||
params.set('agentId', pageUiState.pageAiAgentId || 'reasonix');
|
||||
endpoint = '/api/hermes/client/capabilities';
|
||||
params.set('agentId', 'reasonix');
|
||||
if (profile) params.set('profile', profile);
|
||||
} else if (runtime === 'reasonix') {
|
||||
params.set('runtime', 'reasonix');
|
||||
} else if (runtime === 'hermes' && profile) {
|
||||
params.set('profileId', profile);
|
||||
}
|
||||
return fetch('/api/hermes/client/skills?' + params.toString(), {
|
||||
return fetch(endpoint + '?' + params.toString(), {
|
||||
headers: { 'accept': 'application/json' }
|
||||
}).then(function(response) {
|
||||
return response.json().catch(function(){ return null; }).then(function(payload) {
|
||||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'skills_failed_' + response.status));
|
||||
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'capabilities_failed_' + response.status));
|
||||
return pageAiNormalizeSkills(payload);
|
||||
});
|
||||
});
|
||||
@@ -1478,21 +1597,30 @@ export function createSidebarPageAiRuntime(context) {
|
||||
}
|
||||
if (skillGroup === 'mnote') {
|
||||
try {
|
||||
var mnoteResponse = await fetch('/api/hermes/client/skills/toggle', {
|
||||
var mnoteResponse = await fetch('/api/hermes/client/capabilities/toggle', {
|
||||
method: 'PUT',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
skillKind: 'mnote_builtin',
|
||||
name: name,
|
||||
runtime: 'mnote',
|
||||
profile: skillProfile || pageAiCurrentProfile(),
|
||||
id: name,
|
||||
enabled: Boolean(enabled)
|
||||
})
|
||||
});
|
||||
var mnotePayload = await mnoteResponse.json().catch(function(){ return null; });
|
||||
if (!mnoteResponse.ok) throw new Error(pageAiErrorMessage(mnotePayload, 'mnote_skill_toggle_failed_' + mnoteResponse.status));
|
||||
if (!mnoteResponse.ok) throw new Error(pageAiErrorMessage(mnotePayload, 'mnote_capability_toggle_failed_' + mnoteResponse.status));
|
||||
pageAiSetSkillPreference(skillGroup, name, Boolean(enabled), skillProfile);
|
||||
pageAiNormalizeArray(pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs.mnote && pageUiState.pageAiSkillCatalogs.mnote.categories).forEach(function(category) {
|
||||
pageAiNormalizeArray(category.skills).forEach(function(skill) {
|
||||
if (skill.id === name || skill.name === name) skill.enabled = Boolean(enabled);
|
||||
if (skill.id === name || skill.name === name) {
|
||||
skill.enabled = Boolean(enabled);
|
||||
skill.status = enabled ? 'available' : 'disabled';
|
||||
pageAiNormalizeArray(skill.tools).forEach(function(tool) {
|
||||
tool.enabled = Boolean(enabled);
|
||||
tool.status = enabled ? 'available' : 'disabled';
|
||||
});
|
||||
skill.disabledToolCount = enabled ? 0 : Number(skill.toolCount || pageAiNormalizeArray(skill.tools).length || 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
document.documentElement.setAttribute('data-mnote-page-ai-skill-toggled', skillGroup + ':' + name);
|
||||
@@ -1612,8 +1740,6 @@ export function createSidebarPageAiRuntime(context) {
|
||||
pageAiLoadBackendSessions()
|
||||
]).then(function() {
|
||||
renderPageAiControls();
|
||||
}).catch(function() {}).then(function() {
|
||||
return pageAiEnsureHermesSession();
|
||||
}).then(function() {
|
||||
return pageAiRestoreHermesSession();
|
||||
}).then(function() {
|
||||
@@ -2119,6 +2245,14 @@ export function createSidebarPageAiRuntime(context) {
|
||||
function handlePageAiClick(event, helpers) {
|
||||
var closestAction = helpers && helpers.closestAction;
|
||||
if (typeof closestAction !== 'function') return false;
|
||||
var pageAiCitationLink = closestAction(event.target, 'a[data-page-ai-citation-link="true"]');
|
||||
if (pageAiCitationLink && !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
|
||||
var href = pageAiCitationLink.getAttribute('href') || '';
|
||||
if (pageAiOpenCitationUrl(href)) {
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
var pageAiClose = closestAction(event.target, '[data-page-ai-action="close"]');
|
||||
if (pageAiClose) {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -60,9 +60,9 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
acpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
source: 'local',
|
||||
source: 'draft',
|
||||
usage: null,
|
||||
status: 'idle',
|
||||
status: 'draft',
|
||||
messages: []
|
||||
};
|
||||
}
|
||||
@@ -117,6 +117,10 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
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 status = String(row.status || (row.runtime && row.runtime.status) || '').trim();
|
||||
var hasConversationSignal = String(payload.message || payload.input || row.snippet || '').trim()
|
||||
|| pageAiNormalizeArray(row.messages).length > 0;
|
||||
if (status === 'session.created' && !hasConversationSignal) return null;
|
||||
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';
|
||||
@@ -143,7 +147,7 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
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(),
|
||||
status: status,
|
||||
usage: row.usage && typeof row.usage === 'object' ? row.usage : null,
|
||||
preview: String(payload.message || row.snippet || '').trim(),
|
||||
messages: []
|
||||
@@ -164,11 +168,24 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
return pageAiNormalizeSessions(Object.keys(byId).map(function(id) { return byId[id]; }));
|
||||
}
|
||||
|
||||
function pageAiDedupeSessions(sessions) {
|
||||
var byId = {};
|
||||
var ordered = [];
|
||||
pageAiNormalizeSessions(sessions).forEach(function(session) {
|
||||
var id = String(session && session.id || '').trim();
|
||||
if (!id || byId[id]) return;
|
||||
byId[id] = true;
|
||||
ordered.push(session);
|
||||
});
|
||||
return ordered;
|
||||
}
|
||||
|
||||
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 === 'sqlite_control_plane' || persistence === 'sqlite_acp_runtime_store') return '账号会话';
|
||||
if (storage === 'cloud' || persistence === 'convex_acp_runtime_store') return '云端会话';
|
||||
if (persistence === 'local_ai_session_jsonl') return '本地私有';
|
||||
return String(session && session.source || '').trim() === 'acp' ? '云端会话' : '本地私有';
|
||||
@@ -179,30 +196,11 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
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];
|
||||
@@ -219,8 +217,24 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
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);
|
||||
var draftSessions = pageAiNormalizeArray(pageUiState.pageAiSessions).filter(function(session) {
|
||||
var id = String(session && session.id || '').trim();
|
||||
return id && !id.startsWith('mnote_')
|
||||
&& (id === pageUiState.pageAiActiveSessionId || (Array.isArray(session.messages) && session.messages.length > 0));
|
||||
});
|
||||
if (!backendSessions.length) {
|
||||
if (!draftSessions.length && !pageUiState.pageAiActiveSessionId) {
|
||||
var fresh = pageAiNewSession();
|
||||
pageUiState.pageAiSessions = [fresh];
|
||||
pageUiState.pageAiActiveSessionId = fresh.id;
|
||||
pageUiState.pageAiMessages = [];
|
||||
}
|
||||
pageUiState.pageAiSessionError = '';
|
||||
renderPageAiConversation();
|
||||
renderPageAiControls();
|
||||
return [];
|
||||
}
|
||||
pageUiState.pageAiSessions = pageAiDedupeSessions(backendSessions.concat(draftSessions));
|
||||
if (!pageUiState.pageAiActiveSessionId || !pageUiState.pageAiSessions.find(function(session) { return session.id === pageUiState.pageAiActiveSessionId; })) {
|
||||
pageUiState.pageAiActiveSessionId = pageUiState.pageAiSessions[0].id;
|
||||
}
|
||||
@@ -242,7 +256,7 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
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();
|
||||
var delta = String(payload.delta || payload.text || payload.output_text || '');
|
||||
return delta ? { role: 'assistant', content: delta } : null;
|
||||
}
|
||||
if (eventType === 'thought.delta') {
|
||||
@@ -301,10 +315,34 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
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();
|
||||
var assistantDelta = '';
|
||||
var completedOutput = '';
|
||||
function flushAssistantDelta() {
|
||||
var content = assistantDelta.trim();
|
||||
if (content) messages.push({ role: 'assistant', content: content });
|
||||
assistantDelta = '';
|
||||
}
|
||||
pageAiNormalizeArray(eventsByRunId[runId]).forEach(function(event) {
|
||||
var message = pageAiMessageFromRuntimeEvent(event);
|
||||
if (message) messages.push(message);
|
||||
if (!message) return;
|
||||
if (message.role === 'assistant' && !message.kind) {
|
||||
var eventType = String(event && (event.eventType || event.event_type || event.event) || '').trim();
|
||||
if (eventType === 'run.completed') {
|
||||
completedOutput = String(message.content || '').trim();
|
||||
return;
|
||||
}
|
||||
assistantDelta += String(message.content || '');
|
||||
return;
|
||||
}
|
||||
flushAssistantDelta();
|
||||
messages.push(message);
|
||||
});
|
||||
flushAssistantDelta();
|
||||
if (completedOutput && !messages.some(function(message) {
|
||||
return message.role === 'assistant' && String(message.content || '').trim() === completedOutput;
|
||||
})) {
|
||||
messages.push({ role: 'assistant', content: completedOutput });
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!messages.length) messages = storedMessages;
|
||||
@@ -381,8 +419,7 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
version: sessionStorageVersion,
|
||||
activeSessionId: pageUiState.pageAiActiveSessionId,
|
||||
activeProfileName: pageAiCurrentProfile(),
|
||||
activeAcpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix',
|
||||
sessions: pageAiNormalizeSessions(pageUiState.pageAiSessions)
|
||||
activeAcpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix'
|
||||
}));
|
||||
} catch (_) {}
|
||||
}
|
||||
@@ -427,7 +464,12 @@ export function createSidebarPageAiSessionRuntime(context) {
|
||||
updatedAt: Date.now(),
|
||||
messages: pageUiState.pageAiMessages.slice()
|
||||
};
|
||||
pageUiState.pageAiSessions = pageAiNormalizeSessions([session]);
|
||||
var previousSessionId = String(current && current.id || '').trim();
|
||||
var retainedSessions = pageAiNormalizeArray(pageUiState.pageAiSessions).filter(function(item) {
|
||||
var itemId = String(item && item.id || '').trim();
|
||||
return itemId && itemId !== session.id && itemId !== previousSessionId;
|
||||
});
|
||||
pageUiState.pageAiSessions = pageAiNormalizeSessions([session].concat(retainedSessions));
|
||||
pageUiState.pageAiActiveSessionId = session.id;
|
||||
pageAiPersistSessions();
|
||||
renderPageAiConversation();
|
||||
|
||||
@@ -13,30 +13,39 @@ export function createSidebarPageAiSkillRuntime(context) {
|
||||
|
||||
function pageAiSkillSourceOptions() {
|
||||
var options = [
|
||||
{ value: 'mnote', group: 'mnote', label: 'mnote', profile: '' },
|
||||
{ value: 'reasonix', group: 'reasonix', label: 'reasonix', profile: '' }
|
||||
{ value: 'mnote', group: 'mnote', label: 'MNote 公共能力', profile: '' },
|
||||
{ value: 'reasonix', group: 'reasonix', label: 'Reasonix skill(查看)', profile: '', readonly: true }
|
||||
];
|
||||
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();
|
||||
if (!profileId || pageAiProfileIsChatOnlySkillSource(profile)) return;
|
||||
var label = profile.kind === 'shared' ? 'Hermes 共享 skill(查看)' : 'Hermes skill(查看)';
|
||||
var alias = String(profile.alias || profile.displayName || '').trim();
|
||||
options.push({
|
||||
value: 'hermes:' + profileId,
|
||||
group: 'hermes',
|
||||
profile: profileId,
|
||||
label: alias && alias !== label ? label + ' · ' + alias : label,
|
||||
readonly: profile.readonly === true
|
||||
readonly: true
|
||||
});
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
function pageAiProfileIsChatOnlySkillSource(profile) {
|
||||
var profileId = String(pageAiProfileValue(profile) || '').trim().toLowerCase();
|
||||
var baseProfile = String(profile && profile.baseProfile || '').trim().toLowerCase();
|
||||
var label = String(profile && (profile.displayName || profile.alias || profile.name) || '').trim().toLowerCase();
|
||||
var providerKind = String(profile && profile.providerKind || '').trim().toLowerCase();
|
||||
return profileId.indexOf('chat') >= 0
|
||||
|| baseProfile.indexOf('chat') >= 0
|
||||
|| label.indexOf('chat') >= 0
|
||||
|| providerKind.indexOf('chat') >= 0
|
||||
|| profileId === 'shared_lite'
|
||||
|| baseProfile === 'lite';
|
||||
}
|
||||
|
||||
function pageAiDefaultSkillSource() {
|
||||
if (pageAiCurrentAgentId() === 'hermes') return 'hermes:' + pageAiCurrentProfile();
|
||||
if (pageAiCurrentAgentId() === 'reasonix') return 'reasonix';
|
||||
return 'mnote';
|
||||
}
|
||||
|
||||
@@ -172,21 +181,29 @@ export function createSidebarPageAiSkillRuntime(context) {
|
||||
group: group,
|
||||
profile: profile || '',
|
||||
category: category.name,
|
||||
categoryTitle: skill.categoryTitle || category.title || 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,
|
||||
enabled: group === 'mnote' ? (skill.enabled !== false && overrides[id] !== false) : skill.enabled !== false,
|
||||
toggleable: group === 'mnote' && skill.toggleable !== false,
|
||||
source: skill.source || group,
|
||||
origin: skill.origin || '',
|
||||
readOnly: skill.readOnly === true || skill.readonly === true || skill.configurable === false,
|
||||
readOnly: group !== 'mnote' || 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 || [],
|
||||
tools: skill.tools || [],
|
||||
toolCount: Number(skill.toolCount || 0),
|
||||
disabledToolCount: Number(skill.disabledToolCount || 0),
|
||||
status: skill.status || '',
|
||||
capabilityId: skill.capabilityId || '',
|
||||
capabilityKind: skill.capabilityKind || '',
|
||||
uiKind: skill.uiKind || '',
|
||||
requiresContextRefs: skill.requiresContextRefs || []
|
||||
});
|
||||
});
|
||||
@@ -198,21 +215,29 @@ export function createSidebarPageAiSkillRuntime(context) {
|
||||
group: group,
|
||||
profile: profile || '',
|
||||
category: 'archived',
|
||||
categoryTitle: skill.categoryTitle || '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,
|
||||
enabled: group === 'mnote' ? (skill.enabled !== false && overrides[id] !== false) : skill.enabled !== false,
|
||||
toggleable: group === 'mnote' && skill.toggleable !== false,
|
||||
source: skill.source || group,
|
||||
origin: skill.origin || '',
|
||||
readOnly: skill.readOnly === true || skill.readonly === true || skill.configurable === false,
|
||||
readOnly: group !== 'mnote' || 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 || [],
|
||||
tools: skill.tools || [],
|
||||
toolCount: Number(skill.toolCount || 0),
|
||||
disabledToolCount: Number(skill.disabledToolCount || 0),
|
||||
status: skill.status || '',
|
||||
capabilityId: skill.capabilityId || '',
|
||||
capabilityKind: skill.capabilityKind || '',
|
||||
uiKind: skill.uiKind || '',
|
||||
requiresContextRefs: skill.requiresContextRefs || []
|
||||
});
|
||||
});
|
||||
|
||||
@@ -59,13 +59,26 @@ export function createSidebarPageAiTargetRuntime(context) {
|
||||
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' || /\.(doc|docx|ppt|pptx|xls|xlsx)$/.test(path)) return 'only_office';
|
||||
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 : {};
|
||||
@@ -113,6 +126,7 @@ export function createSidebarPageAiTargetRuntime(context) {
|
||||
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
|
||||
@@ -137,6 +151,7 @@ export function createSidebarPageAiTargetRuntime(context) {
|
||||
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
|
||||
@@ -661,7 +676,7 @@ export function createSidebarPageAiTargetRuntime(context) {
|
||||
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) {
|
||||
if (pageAiIsOnlyOfficeLiveTarget(editorTarget) && !onlyofficeSessionId) {
|
||||
var sessionError = new Error('ONLYOFFICE 资源仍在连接 MNote bridge,请等待 Office 页面加载完成后再让 AI 操作。');
|
||||
sessionError.code = 'page_ai_onlyoffice_bridge_not_ready';
|
||||
throw sessionError;
|
||||
|
||||
@@ -740,8 +740,15 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
|
||||
var status = summary.status || {};
|
||||
var settings = status.settings && typeof status.settings === 'object' ? status.settings : {};
|
||||
var includePaths = Array.isArray(settings.includePaths) ? settings.includePaths : ['.'];
|
||||
statusNode.textContent = '索引 ' + Number(status.documentCount || 0) + ' 个页面 / ' + Number(status.resourceCount || 0) + ' 个资源 / ' + Number(status.evidenceBlockCount || 0) + ' 个证据块' + (status.scheduledDue ? ',已到计划时间' : '');
|
||||
var savedIncludePaths = Array.isArray(settings.includePaths) ? settings.includePaths : [];
|
||||
var indexedPaths = Array.isArray(status.indexedPaths) ? status.indexedPaths : [];
|
||||
var includePaths = savedIncludePaths.length ? savedIncludePaths : indexedPaths;
|
||||
var hasIndexCache = status.indexExists === true || status.evidenceIndexExists === true;
|
||||
if (!savedIncludePaths.length && !hasIndexCache) {
|
||||
statusNode.textContent = '未设置索引范围';
|
||||
} else {
|
||||
statusNode.textContent = '索引 ' + Number(status.documentCount || 0) + ' 个页面 / ' + Number(status.resourceCount || 0) + ' 个资源 / ' + Number(status.evidenceBlockCount || 0) + ' 个证据块' + (status.scheduledDue ? ',已到计划时间' : '');
|
||||
}
|
||||
if (!(rangesNode instanceof HTMLElement) || !rangesNode.contains(document.activeElement)) {
|
||||
renderLocalIndexRangeRows(popover, includePaths, localIndexStatusKind(status), false, status);
|
||||
}
|
||||
@@ -762,6 +769,9 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
|
||||
function localIndexStatusKind(status) {
|
||||
if (!status || typeof status !== 'object') return 'fault';
|
||||
var settings = status.settings && typeof status.settings === 'object' ? status.settings : {};
|
||||
var includePaths = Array.isArray(settings.includePaths) ? settings.includePaths : [];
|
||||
if (!includePaths.length && status.indexExists !== true && status.evidenceIndexExists !== true) return 'indexed';
|
||||
if (status.indexExists !== true) return 'fault';
|
||||
if (status.cacheMatchesSettings === true && status.scheduledDue !== true) return 'indexed';
|
||||
return 'indexing';
|
||||
@@ -774,10 +784,10 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
}
|
||||
|
||||
function localIndexPathStatusKind(path, status, fallbackKind) {
|
||||
if (fallbackKind === 'fault' || fallbackKind === 'indexing') return fallbackKind;
|
||||
var indexedPaths = Array.isArray(status && status.indexedPaths) ? status.indexedPaths : [];
|
||||
var normalizedPath = String(path || '').trim() || '.';
|
||||
if (indexedPaths.indexOf(normalizedPath) >= 0 && status.cacheMatchesSettings === true) return 'indexed';
|
||||
if (indexedPaths.indexOf(normalizedPath) >= 0 && status.indexExists === true) return 'indexed';
|
||||
if (fallbackKind === 'fault' || fallbackKind === 'indexing') return fallbackKind;
|
||||
if (status.indexExists === true) return 'indexing';
|
||||
return 'fault';
|
||||
}
|
||||
@@ -789,13 +799,26 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
return String(value || '').trim();
|
||||
}) : [];
|
||||
var paths = disabled ? rawPaths.filter(Boolean) : rawPaths;
|
||||
if (!paths.length && !disabled) paths = ['.'];
|
||||
var savedPaths = Array.isArray(status && status.settings && status.settings.includePaths)
|
||||
? status.settings.includePaths.map(function(value) { return String(value || '').trim(); }).filter(Boolean)
|
||||
: [];
|
||||
var indexedPaths = Array.isArray(status && status.indexedPaths)
|
||||
? status.indexedPaths.map(function(value) { return String(value || '').trim(); }).filter(Boolean)
|
||||
: [];
|
||||
rangesNode.innerHTML = paths.map(function(path, index) {
|
||||
var isPersisted = savedPaths.indexOf(path) >= 0 || indexedPaths.indexOf(path) >= 0;
|
||||
var inputDisabled = disabled || isPersisted;
|
||||
var kind = localIndexPathStatusKind(path, status || {}, fallbackKind || 'fault');
|
||||
var normalizedPath = String(path || '').trim() || '.';
|
||||
var displayPath = inputDisabled && normalizedPath === '.' ? '工作区根目录(全部)' : path;
|
||||
var rootScopeHint = normalizedPath === '.'
|
||||
? '<span class="wolai-page-settings-index-root-hint">当前工作区全部</span>'
|
||||
: '';
|
||||
return '' +
|
||||
'<div class="wolai-page-settings-index-range-row" data-local-index-range-row="true">' +
|
||||
'<span class="wolai-page-settings-index-status-dot" data-index-status="' + kind + '" title="' + escapeHtml(localIndexStatusLabel(kind)) + '"></span>' +
|
||||
'<input type="text" class="wolai-page-settings-index-path" data-local-index-range-input="true" value="' + escapeHtml(path) + '" spellcheck="false"' + (disabled ? ' disabled' : '') + ' />' +
|
||||
'<input type="text" class="wolai-page-settings-index-path" data-local-index-range-input="true" data-local-index-value="' + escapeHtml(normalizedPath) + '" value="' + escapeHtml(displayPath) + '" spellcheck="false"' + (inputDisabled ? ' disabled' : '') + (isPersisted ? ' data-local-index-persisted="true" title="已保存的索引目录不能直接修改;删除后重新新增范围"' : '') + ' />' +
|
||||
rootScopeHint +
|
||||
'<button type="button" class="wolai-page-settings-index-remove" data-local-index-action="remove-path" data-local-index-path-index="' + String(index) + '"' + (disabled ? ' disabled' : '') + ' aria-label="删除索引范围">×</button>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
@@ -860,16 +883,17 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
|
||||
function localIndexIncludePathsFromForm() {
|
||||
var popover = ensureLocalIndexSettingsPopover();
|
||||
var values = currentLocalIndexRangeValues(popover).map(function(value) {
|
||||
return currentLocalIndexRangeValues(popover).map(function(value) {
|
||||
return value.trim();
|
||||
}).filter(Boolean);
|
||||
return values.length ? values : ['.'];
|
||||
}
|
||||
|
||||
function currentLocalIndexRangeValues(popover) {
|
||||
var root = popover || ensureLocalIndexSettingsPopover();
|
||||
return Array.from(root.querySelectorAll('[data-local-index-range-input]')).map(function(input) {
|
||||
return input instanceof HTMLInputElement ? input.value : '';
|
||||
if (!(input instanceof HTMLInputElement)) return '';
|
||||
if (input.disabled && input.dataset.localIndexValue) return input.dataset.localIndexValue;
|
||||
return input.value;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -901,7 +925,6 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
var summary = pageUiState.localIndexSummary || {};
|
||||
var values = currentLocalIndexRangeValues(popover);
|
||||
values.splice(Number(index || 0), 1);
|
||||
if (!values.length) values = [''];
|
||||
renderLocalIndexRangeRows(popover, values, localIndexStatusKind(summary.status || {}), false, summary.status || {});
|
||||
}
|
||||
|
||||
|
||||
@@ -1162,6 +1162,8 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
var objectIdentity = fileObjectIdentity(item);
|
||||
var ownerDocumentId = fileOwnerDocumentId(item, documentId, objectIdentity);
|
||||
var iconKind = iconKindOf(item);
|
||||
var indexStatus = String((item && item.indexStatus) || (item && item.resourceMeta && item.resourceMeta.indexStatus) || '').trim();
|
||||
if (indexStatus !== 'indexed' && indexStatus !== 'failed') indexStatus = '';
|
||||
var cachedChildren = relativePath ? cachedFileTreeRows(relativePath) : [];
|
||||
var title = isFileTreeProjectionPageRow(rowKind, assetId)
|
||||
? fileTreePageTitle(rawTitle)
|
||||
@@ -1185,7 +1187,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
var childHtml = expandable && expanded && childRowsHtml
|
||||
? '<ul class="tree-children">' + childRowsHtml + '</ul>'
|
||||
: '';
|
||||
return '<li class="tree-node" data-node-id="' + escapeHtml(nodeId) + '"><div class="tree-row" role="treeitem" aria-level="' + (depth + 1) + '" aria-expanded="' + String(expandable && expanded) + '" data-rust-rendered-row="filetree" data-testid="' + testId + '" data-row-id="' + escapeHtml(rowId) + '" data-row-kind="' + escapeHtml(rowKind) + '" data-node-id="' + escapeHtml(nodeId) + '"' + (parent ? ' data-parent-id="' + escapeHtml(parent) + '"' : '') + ' data-document-id="' + escapeHtml(documentId) + '" data-doc-id="' + escapeHtml(documentId) + '" data-owner-document-id="' + escapeHtml(ownerDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '" data-local-relative-path="' + escapeHtml(relativePath) + '" data-object-identity="' + escapeHtml(objectIdentityAttr(objectIdentity)) + '" data-object-kind="' + escapeHtml(String(objectIdentity.objectKind || '')) + '" data-capabilities="' + escapeHtml(fileCapabilitiesAttr(item)) + '" data-shell-mode="filetree" data-selected="' + String(selected) + '" data-active="false" draggable="true">' + toggle + '<span class="tree-kind-badge" data-kind="' + escapeHtml(iconKind) + '" aria-hidden="true"></span><button type="button" class="tree-link" data-rust-action="open" data-row-id="' + escapeHtml(rowId) + '" data-document-id="' + escapeHtml(documentId) + '" data-owner-document-id="' + escapeHtml(ownerDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '" data-local-relative-path="' + escapeHtml(relativePath) + '" title="' + escapeHtml(title) + '"><span class="tree-link-title" title="' + escapeHtml(title) + '">' + escapeHtml(title) + '</span></button><div class="tree-actions">' + createAction + '<button type="button" class="tree-action" data-testid="filetree-action-menu" data-rust-action="menu" data-row-id="' + escapeHtml(rowId) + '" aria-label="更多操作">…</button></div></div>' + childHtml + '</li>';
|
||||
return '<li class="tree-node" data-node-id="' + escapeHtml(nodeId) + '"><div class="tree-row" role="treeitem" aria-level="' + (depth + 1) + '" aria-expanded="' + String(expandable && expanded) + '" data-rust-rendered-row="filetree" data-testid="' + testId + '" data-row-id="' + escapeHtml(rowId) + '" data-row-kind="' + escapeHtml(rowKind) + '" data-node-id="' + escapeHtml(nodeId) + '"' + (parent ? ' data-parent-id="' + escapeHtml(parent) + '"' : '') + ' data-document-id="' + escapeHtml(documentId) + '" data-doc-id="' + escapeHtml(documentId) + '" data-owner-document-id="' + escapeHtml(ownerDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '" data-local-relative-path="' + escapeHtml(relativePath) + '" data-object-identity="' + escapeHtml(objectIdentityAttr(objectIdentity)) + '" data-object-kind="' + escapeHtml(String(objectIdentity.objectKind || '')) + '"' + (indexStatus ? ' data-index-status="' + escapeHtml(indexStatus) + '"' : '') + ' data-capabilities="' + escapeHtml(fileCapabilitiesAttr(item)) + '" data-shell-mode="filetree" data-selected="' + String(selected) + '" data-active="false" draggable="true">' + toggle + '<span class="tree-kind-badge" data-kind="' + escapeHtml(iconKind) + '" aria-hidden="true"></span><button type="button" class="tree-link" data-rust-action="open" data-row-id="' + escapeHtml(rowId) + '" data-document-id="' + escapeHtml(documentId) + '" data-owner-document-id="' + escapeHtml(ownerDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '" data-local-relative-path="' + escapeHtml(relativePath) + '" title="' + escapeHtml(title) + '"><span class="tree-link-title" title="' + escapeHtml(title) + '">' + escapeHtml(title) + '</span></button><div class="tree-actions">' + createAction + '<button type="button" class="tree-action" data-testid="filetree-action-menu" data-rust-action="menu" data-row-id="' + escapeHtml(rowId) + '" aria-label="更多操作">…</button></div></div>' + childHtml + '</li>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
|
||||
@@ -874,6 +874,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
const inferOnlyOfficeFileType = (...args) => sidebarFileTreeOpen.inferOnlyOfficeFileType(...args);
|
||||
const buildOnlyOfficeOpenUrl = (...args) => sidebarFileTreeOpen.buildOnlyOfficeOpenUrl(...args);
|
||||
const buildOnlyOfficeOpenPath = (...args) => sidebarFileTreeOpen.buildOnlyOfficeOpenPath(...args);
|
||||
const buildOfficePreviewOpenUrl = (...args) => sidebarFileTreeOpen.buildOfficePreviewOpenUrl(...args);
|
||||
const buildPdfPreviewOpenUrl = (...args) => sidebarFileTreeOpen.buildPdfPreviewOpenUrl(...args);
|
||||
const buildLocalOnlyOfficeOpenUrl = (...args) => sidebarFileTreeOpen.buildLocalOnlyOfficeOpenUrl(...args);
|
||||
const openLocalOfficeFileInActiveTab = (...args) => sidebarFileTreeOpen.openLocalOfficeFileInActiveTab(...args);
|
||||
@@ -2198,26 +2199,57 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return searchText(shell && shell.getAttribute('data-document-id')) || searchText(bodyId);
|
||||
}
|
||||
|
||||
function localResourceDocumentIdFromPath(path) {
|
||||
var normalized = searchText(path).replace(/\\/g, '/');
|
||||
return normalized ? 'local-resource:' + normalized.replace(/\//g, '~2F') : '';
|
||||
}
|
||||
|
||||
function currentSearchDocumentId() {
|
||||
var activePanel = document.querySelector('[data-mnote-resource-tab-panel][data-pane-role="primary"]:not([hidden])');
|
||||
if (activePanel instanceof HTMLElement) {
|
||||
var resourceKind = searchText(activePanel.getAttribute('data-resource-kind')).toLowerCase();
|
||||
var resourcePath = searchText(activePanel.getAttribute('data-resource-path'));
|
||||
if (resourceKind && resourceKind !== 'markdown' && resourcePath) return localResourceDocumentIdFromPath(resourcePath);
|
||||
try {
|
||||
var locator = JSON.parse(activePanel.getAttribute('data-mnote-evidence-locator') || 'null');
|
||||
var locatorDoc = searchText(locator && (locator.ownerDocumentId || locator.owner_document_id));
|
||||
if (locatorDoc) return locatorDoc;
|
||||
} catch (_) {}
|
||||
}
|
||||
return currentDocumentId();
|
||||
}
|
||||
|
||||
function searchSwitchValue(overlay, name) {
|
||||
var button = overlay.querySelector('[data-search-switch="' + name + '"]');
|
||||
return button instanceof HTMLElement && button.getAttribute('aria-checked') === 'true';
|
||||
}
|
||||
|
||||
function highlightedHtml(value) {
|
||||
return escapeHtml(value)
|
||||
.replace(/<mark>/g, '<mark>')
|
||||
.replace(/<\/mark>/g, '</mark>');
|
||||
}
|
||||
|
||||
function highlightSearchTitle(title, query) {
|
||||
var cleanTitle = searchText(title);
|
||||
function highlightSearchText(value, query, exact) {
|
||||
var text = searchText(value);
|
||||
var cleanQuery = searchText(query);
|
||||
if (!cleanQuery) return escapeHtml(cleanTitle);
|
||||
var index = cleanTitle.toLowerCase().indexOf(cleanQuery.toLowerCase());
|
||||
if (index < 0) return escapeHtml(cleanTitle);
|
||||
return escapeHtml(cleanTitle.slice(0, index)) +
|
||||
'<mark>' + escapeHtml(cleanTitle.slice(index, index + cleanQuery.length)) + '</mark>' +
|
||||
escapeHtml(cleanTitle.slice(index + cleanQuery.length));
|
||||
if (!text || !cleanQuery) return escapeHtml(text);
|
||||
var lower = text.toLowerCase();
|
||||
var lowerQuery = cleanQuery.toLowerCase();
|
||||
var exactIndex = lower.indexOf(lowerQuery);
|
||||
if (exact || exactIndex >= 0) {
|
||||
if (exactIndex < 0) return escapeHtml(text);
|
||||
return escapeHtml(text.slice(0, exactIndex)) +
|
||||
'<mark>' + escapeHtml(text.slice(exactIndex, exactIndex + cleanQuery.length)) + '</mark>' +
|
||||
escapeHtml(text.slice(exactIndex + cleanQuery.length));
|
||||
}
|
||||
var chars = Array.from(cleanQuery.replace(/\s+/g, '').toLowerCase());
|
||||
if (!chars.length) return escapeHtml(text);
|
||||
var next = 0;
|
||||
var html = '';
|
||||
Array.from(text).forEach(function(ch) {
|
||||
if (next < chars.length && ch.toLowerCase() === chars[next]) {
|
||||
html += '<mark>' + escapeHtml(ch) + '</mark>';
|
||||
next += 1;
|
||||
} else {
|
||||
html += escapeHtml(ch);
|
||||
}
|
||||
});
|
||||
return next >= chars.length ? html : escapeHtml(text);
|
||||
}
|
||||
|
||||
function searchResultEvidenceLocator(item) {
|
||||
@@ -2243,6 +2275,38 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return searchText(action && action.url);
|
||||
}
|
||||
|
||||
function evidenceLocatorLineRange(locator) {
|
||||
return locator && (locator.lineRange || locator.line_range) || null;
|
||||
}
|
||||
|
||||
function evidenceLocatorCharRange(locator) {
|
||||
return locator && (locator.charRange || locator.char_range) || null;
|
||||
}
|
||||
|
||||
function evidenceLocatorBlockId(locator) {
|
||||
return searchText(locator && (locator.blockId || locator.block_id));
|
||||
}
|
||||
|
||||
function resourceHrefForEvidenceLocator(resourceKind, resourcePath, fileName, ownerDocumentId, locator) {
|
||||
var localFileUrl = buildLocalFileOpenUrl(resourcePath, false);
|
||||
if (!localFileUrl) return '';
|
||||
if (resourceKind === 'pdf') return buildPdfPreviewOpenUrl(localFileUrl, fileName);
|
||||
if (resourceKind === 'office') {
|
||||
var fileType = inferOnlyOfficeFileType(fileName, '') || (fileName.indexOf('.') >= 0 ? fileName.split('.').pop() : 'docx');
|
||||
return buildOfficePreviewOpenUrl({
|
||||
fileUrl: localFileUrl,
|
||||
fileName: fileName,
|
||||
fileType: fileType,
|
||||
assetId: 'local-file:' + resourcePath,
|
||||
documentId: ownerDocumentId || currentDocumentId() || '',
|
||||
workspaceId: resolveWorkspaceId(document.body) || '',
|
||||
sourceKind: currentSourceKind() || 'local_folder',
|
||||
rootUri: searchText(locator && (locator.rootUri || locator.root_uri) || currentRootUri())
|
||||
});
|
||||
}
|
||||
return localFileUrl;
|
||||
}
|
||||
|
||||
async function openEvidenceSearchResult(item, event) {
|
||||
var locator = searchResultEvidenceLocator(item);
|
||||
if (!locator) {
|
||||
@@ -2254,18 +2318,20 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
var ownerDocumentId = searchText(locator.ownerDocumentId || locator.owner_document_id || item.documentId || '');
|
||||
var resourceKind = evidenceLocatorResourceKind(locator, item && item.resourceType);
|
||||
var openTarget = event && event.altKey ? 'side' : 'active-tab';
|
||||
if (resourcePath && resourceKind && resourceKind !== 'markdown') {
|
||||
if (resourcePath && resourceKind) {
|
||||
var fileName = resourcePath.split('/').filter(Boolean).pop() || resourcePath;
|
||||
var localFileUrl = buildLocalFileOpenUrl(resourcePath, false);
|
||||
var href = resourceKind === 'pdf' ? buildPdfPreviewOpenUrl(localFileUrl, fileName) : localFileUrl;
|
||||
await openLocalResourceInActiveTab({
|
||||
var normalizedKind = resourceKind === 'raw_file' || resourceKind === 'resource' ? fileTreeIconKindForFileName(fileName) || 'file' : resourceKind;
|
||||
var hostDocumentId = currentDocumentId() || ownerDocumentId || '';
|
||||
var href = resourceHrefForEvidenceLocator(normalizedKind, resourcePath, fileName, hostDocumentId, locator);
|
||||
var openedInResourceTab = await openLocalResourceInActiveTab({
|
||||
path: resourcePath,
|
||||
title: fileName,
|
||||
kind: resourceKind,
|
||||
kind: normalizedKind,
|
||||
href: href,
|
||||
officeUrl: normalizedKind === 'office' ? href : '',
|
||||
assetId: 'local-file:' + resourcePath,
|
||||
documentId: ownerDocumentId || currentDocumentId() || '',
|
||||
ownerDocumentId: ownerDocumentId || currentDocumentId() || '',
|
||||
documentId: hostDocumentId,
|
||||
ownerDocumentId: ownerDocumentId || hostDocumentId,
|
||||
workspaceId: resolveWorkspaceId(document.body) || '',
|
||||
sourceKind: currentSourceKind() || 'local_folder',
|
||||
rootUri: searchText(locator.rootUri || locator.root_uri || currentRootUri()),
|
||||
@@ -2274,10 +2340,11 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
page: locator.page,
|
||||
bbox: locator.bbox,
|
||||
sourceMapPath: searchText(locator.sourceMapPath || locator.source_map_path),
|
||||
blockId: searchText(locator.blockId || locator.block_id),
|
||||
lineRange: locator.lineRange || locator.line_range || null,
|
||||
charRange: locator.charRange || locator.char_range || null
|
||||
blockId: evidenceLocatorBlockId(locator),
|
||||
lineRange: evidenceLocatorLineRange(locator),
|
||||
charRange: evidenceLocatorCharRange(locator)
|
||||
});
|
||||
if (!openedInResourceTab) console.warn('mnote evidence 搜索结果无法在当前页面资源标签打开', locator);
|
||||
closeSearchModal();
|
||||
return;
|
||||
}
|
||||
@@ -2339,7 +2406,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
sourceKind: currentSourceKind() || null,
|
||||
rootUri: currentRootUri() || null,
|
||||
documentId: currentDocumentId() || null,
|
||||
documentId: searchSwitchValue(overlay, 'page') ? (currentSearchDocumentId() || null) : (currentDocumentId() || null),
|
||||
query: query,
|
||||
limit: 30,
|
||||
filters: {
|
||||
@@ -2368,10 +2435,11 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
var locator = searchResultEvidenceLocator(item);
|
||||
var locatorAttr = locator ? escapeHtml(JSON.stringify(locator)) : '';
|
||||
var index = items.indexOf(item);
|
||||
var exact = searchSwitchValue(overlay, 'exact');
|
||||
return '<button type="button" class="wolai-search-result-row" data-testid="wolai-search-result-row" data-search-result-owner="rust-kernel" data-search-result-index="' + String(index) + '" data-document-id="' + escapeHtml(item.documentId || item.nodeId || item.id || '') + '" data-resource-type="' + escapeHtml(resourceType) + '"' + (locatorAttr ? ' data-evidence-locator="' + locatorAttr + '"' : '') + '>' +
|
||||
'<span class="material-symbols-outlined wolai-search-result-icon" data-icon="article" aria-hidden="true"></span>' +
|
||||
'<span class="wolai-search-result-main"><span class="wolai-search-result-title">' + highlightSearchTitle(title, query) + '</span>' +
|
||||
'<span class="wolai-search-result-snippet">' + (snippet ? highlightedHtml(snippet) : escapeHtml(path)) + '</span>' +
|
||||
'<span class="wolai-search-result-main"><span class="wolai-search-result-title">' + highlightSearchText(title, query, exact) + '</span>' +
|
||||
'<span class="wolai-search-result-snippet">' + (snippet ? highlightSearchText(snippet, query, exact) : escapeHtml(path)) + '</span>' +
|
||||
'<span class="wolai-search-result-path"><span>' + escapeHtml(path) + '</span><span class="wolai-search-result-type">' + escapeHtml(resourceType) + '</span></span></span>' +
|
||||
'</button>';
|
||||
}).join('');
|
||||
@@ -2583,7 +2651,16 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return;
|
||||
}
|
||||
|
||||
var ocrSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-ocr-settings"], [data-mnote-action="toggle-ocr-tasks"]');
|
||||
var ocrTaskTrigger = closestAction(e.target, '[data-mnote-action="toggle-ocr-tasks"]');
|
||||
if (ocrTaskTrigger) {
|
||||
e.preventDefault();
|
||||
window.dispatchEvent(new CustomEvent('mnote:local-ocr-settings-action', {
|
||||
detail: { action: 'tasks' }
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
var ocrSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-ocr-settings"]');
|
||||
if (ocrSettingsTrigger) {
|
||||
e.preventDefault();
|
||||
openLocalOcrSettingsPopover();
|
||||
|
||||
Reference in New Issue
Block a user