feat: add evidence search and stabilize pdf previews
- add document evidence parsing/search/open routes, Hermes tool wiring, local index settings/status, and the document-evidence skill plus design notes - fix PDF resource tabs by rendering PDFs inline with pdf.js canvases instead of iframe preview pages, release PDF documents on close, and document the fourth-PDF stall bug - keep PDF preview at 2x rendering while removing the previous lazy-load/placeholder direction, and make dev:hot bind loopback defaults externally reachable Verification: - node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js - node scripts/task-dev-hot-plan-test.js - cargo test -p mnote-web --manifest-path rust/Cargo.toml pdf_preview_page_does_not_render_visible_toolbar - cargo test -p mnote-web --manifest-path rust/Cargo.toml document_shell_returns_page_aggregate_snapshot - cargo build -p mnote-web --manifest-path rust/Cargo.toml - browser smoke: sequentially opened the four tea_seed_oil_cosmetic PDFs; fourth PDF rendered 15/15 canvases, iframeCount=0, browser errors=0
This commit is contained in:
@@ -200,17 +200,25 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
});
|
||||
const applyPageOptionsToShell = (...args) => sidebarPageSettings.applyPageOptionsToShell(...args);
|
||||
const closePageHistoryDrawer = (...args) => sidebarPageSettings.closePageHistoryDrawer(...args);
|
||||
const closeAllSettingsPopovers = (...args) => sidebarPageSettings.closeAllSettingsPopovers(...args);
|
||||
const closePageSettingsPopover = (...args) => sidebarPageSettings.closePageSettingsPopover(...args);
|
||||
const closePageShareDialog = (...args) => sidebarPageSettings.closePageShareDialog(...args);
|
||||
const currentPageOptions = (...args) => sidebarPageSettings.currentPageOptions(...args);
|
||||
const ensureHistorySnapshotsSeeded = (...args) => sidebarPageSettings.ensureHistorySnapshotsSeeded(...args);
|
||||
const isAnySettingsOpen = (...args) => sidebarPageSettings.isAnySettingsOpen(...args);
|
||||
const isPageSettingsOpen = (...args) => sidebarPageSettings.isPageSettingsOpen(...args);
|
||||
const openPageHistoryDrawer = (...args) => sidebarPageSettings.openPageHistoryDrawer(...args);
|
||||
const openPageIndexSettingsPopover = (...args) => sidebarPageSettings.openPageIndexSettingsPopover(...args);
|
||||
const openLocalOcrSettingsPopover = (...args) => sidebarPageSettings.openLocalOcrSettingsPopover(...args);
|
||||
const openPageShareDialog = (...args) => sidebarPageSettings.openPageShareDialog(...args);
|
||||
const pageOptionIsSupported = (...args) => sidebarPageSettings.pageOptionIsSupported(...args);
|
||||
const addLocalIndexRange = (...args) => sidebarPageSettings.addLocalIndexRange(...args);
|
||||
const persistLocalOcrAutoPreference = (...args) => sidebarPageSettings.persistLocalOcrAutoPreference(...args);
|
||||
const persistLocalIndexSettings = (...args) => sidebarPageSettings.persistLocalIndexSettings(...args);
|
||||
const persistPageOptionsPatch = (...args) => sidebarPageSettings.persistPageOptionsPatch(...args);
|
||||
const persistPageWidthPreference = (...args) => sidebarPageSettings.persistPageWidthPreference(...args);
|
||||
const refreshLocalIndex = (...args) => sidebarPageSettings.refreshLocalIndex(...args);
|
||||
const removeLocalIndexRange = (...args) => sidebarPageSettings.removeLocalIndexRange(...args);
|
||||
const recordPageHistorySnapshot = (...args) => sidebarPageSettings.recordPageHistorySnapshot(...args);
|
||||
const renderPageSettingsPopover = (...args) => sidebarPageSettings.renderPageSettingsPopover(...args);
|
||||
const setActivePageSettingsTab = (...args) => sidebarPageSettings.setActivePageSettingsTab(...args);
|
||||
@@ -351,6 +359,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
const applyRemoveDocumentDelta = (...args) => sidebarTreeLiveApply.applyRemoveDocumentDelta(...args);
|
||||
const setTreeLiveApplyError = (...args) => sidebarTreeLiveApply.setTreeLiveApplyError(...args);
|
||||
const objectIdentityAttr = (...args) => sidebarTreeLiveApply.objectIdentityAttr(...args);
|
||||
const renderPageProjection = (...args) => sidebarTreeLiveApply.renderPageProjection(...args);
|
||||
const renderSidebarSnapshot = (...args) => sidebarTreeLiveApply.renderSidebarSnapshot(...args);
|
||||
const refreshLocalFolderSidebarSnapshot = (...args) => sidebarTreeLiveApply.refreshLocalFolderSidebarSnapshot(...args);
|
||||
const startLocalFolderSidebarWatch = (...args) => sidebarTreeLiveApply.startLocalFolderSidebarWatch(...args);
|
||||
@@ -432,6 +441,21 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return true;
|
||||
}
|
||||
|
||||
function openCurrentNavigationPage(trigger) {
|
||||
if (currentSourceKind() === 'local_folder') {
|
||||
var rootUri = currentRootUri();
|
||||
var scope = currentFileTreeScope();
|
||||
var workspaceId = currentWorkspaceId() || sidebarShortcutWorkspaceId() || resolveWorkspaceId(trigger || document.body);
|
||||
var title = scope ? scope.split('/').filter(Boolean).pop() : '本地文件夹';
|
||||
return openNavigationPageForFolder(rootUri, scope, workspaceId, title || scope || '本地文件夹');
|
||||
}
|
||||
var targetUrl = new URL('/', window.location.origin);
|
||||
var workspaceId = currentWorkspaceId() || resolveWorkspaceId(trigger || document.body);
|
||||
if (workspaceId) targetUrl.searchParams.set('workspaceId', workspaceId);
|
||||
window.location.assign(targetUrl.pathname + targetUrl.search);
|
||||
return true;
|
||||
}
|
||||
|
||||
function currentTopbarTitle() {
|
||||
var title = document.querySelector('[data-page-title-current="true"]');
|
||||
return title && title.textContent ? title.textContent.trim() : '无标题';
|
||||
@@ -821,9 +845,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
var sidebarPayload = await sidebarResponse.json().catch(function() { return null; });
|
||||
if (!sidebarResponse.ok) throw new Error(sidebarPayload && (sidebarPayload.error || sidebarPayload.message) || 'local_page_tree_failed_' + sidebarResponse.status);
|
||||
var sidebarProjection = sidebarPayload && (sidebarPayload.result || sidebarPayload) || {};
|
||||
var renderedPage = renderSidebarSnapshot(Object.assign({}, sidebarProjection, {
|
||||
dataset: Object.assign({}, sidebarProjection.dataset || {})
|
||||
}));
|
||||
var renderedPage = sidebarProjection ? await renderPageProjection(sidebarProjection) : false;
|
||||
root = document.getElementById('sidebar-file-tree-root');
|
||||
if (root instanceof HTMLElement) {
|
||||
root.setAttribute('data-workspace-id', workspaceId);
|
||||
@@ -2198,6 +2220,83 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
escapeHtml(cleanTitle.slice(index + cleanQuery.length));
|
||||
}
|
||||
|
||||
function searchResultEvidenceLocator(item) {
|
||||
if (item && item.evidence && item.evidence.source && typeof item.evidence.source === 'object') return item.evidence.source;
|
||||
if (item && item.source && item.source.locator && typeof item.source.locator === 'object') return item.source.locator;
|
||||
if (item && Array.isArray(item.evidence) && item.evidence[0] && item.evidence[0].source && typeof item.evidence[0].source === 'object') return item.evidence[0].source;
|
||||
return null;
|
||||
}
|
||||
|
||||
function evidenceLocatorResourceKind(locator, fallback) {
|
||||
return searchText(locator && (locator.resourceKind || locator.resource_kind) || fallback || '').toLowerCase();
|
||||
}
|
||||
|
||||
function evidenceLocatorBbox(locator) {
|
||||
var bbox = locator && locator.bbox;
|
||||
if (Array.isArray(bbox)) return bbox.slice(0, 4).join(',');
|
||||
if (bbox && typeof bbox === 'object') return [bbox.x0, bbox.y0, bbox.x1, bbox.y1].join(',');
|
||||
return '';
|
||||
}
|
||||
|
||||
function evidenceLocatorOpenUrl(locator) {
|
||||
var action = locator && locator.openAction && typeof locator.openAction === 'object' ? locator.openAction : null;
|
||||
return searchText(action && action.url);
|
||||
}
|
||||
|
||||
async function openEvidenceSearchResult(item, event) {
|
||||
var locator = searchResultEvidenceLocator(item);
|
||||
if (!locator) {
|
||||
var fallbackId = searchText(item && (item.documentId || item.nodeId || item.id));
|
||||
if (fallbackId) window.location.assign('/documents/' + encodeURIComponent(fallbackId));
|
||||
return;
|
||||
}
|
||||
var resourcePath = searchText(locator.resourcePath || locator.resource_path || locator.ownerDocumentPath || locator.owner_document_path);
|
||||
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') {
|
||||
var fileName = resourcePath.split('/').filter(Boolean).pop() || resourcePath;
|
||||
var localFileUrl = buildLocalFileOpenUrl(resourcePath, false);
|
||||
var href = resourceKind === 'pdf' ? buildPdfPreviewOpenUrl(localFileUrl, fileName) : localFileUrl;
|
||||
await openLocalResourceInActiveTab({
|
||||
path: resourcePath,
|
||||
title: fileName,
|
||||
kind: resourceKind,
|
||||
href: href,
|
||||
assetId: 'local-file:' + resourcePath,
|
||||
documentId: ownerDocumentId || currentDocumentId() || '',
|
||||
ownerDocumentId: ownerDocumentId || currentDocumentId() || '',
|
||||
workspaceId: resolveWorkspaceId(document.body) || '',
|
||||
sourceKind: currentSourceKind() || 'local_folder',
|
||||
rootUri: searchText(locator.rootUri || locator.root_uri || currentRootUri()),
|
||||
paneRole: openTarget === 'side' ? 'secondary' : 'primary',
|
||||
evidenceLocator: locator,
|
||||
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
|
||||
});
|
||||
closeSearchModal();
|
||||
return;
|
||||
}
|
||||
var url = evidenceLocatorOpenUrl(locator) || ('/documents/' + encodeURIComponent(ownerDocumentId || currentDocumentId() || ''));
|
||||
try {
|
||||
var target = new URL(url, window.location.origin);
|
||||
var blockId = searchText(locator.blockId || locator.block_id);
|
||||
var sourceMapPath = searchText(locator.sourceMapPath || locator.source_map_path);
|
||||
if (blockId) target.searchParams.set('blockId', blockId);
|
||||
if (locator.page) target.searchParams.set('page', String(locator.page));
|
||||
var bbox = evidenceLocatorBbox(locator);
|
||||
if (bbox) target.searchParams.set('bbox', bbox);
|
||||
if (sourceMapPath) target.searchParams.set('sourceMapPath', sourceMapPath);
|
||||
window.location.assign(target.pathname + target.search + target.hash);
|
||||
} catch (_) {
|
||||
window.location.assign(url);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSearchRecentState(overlay) {
|
||||
var options = overlay.querySelector('[data-testid="wolai-search-options"]');
|
||||
var meta = overlay.querySelector('[data-testid="wolai-search-result-meta"]');
|
||||
@@ -2246,7 +2345,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
filters: {
|
||||
titleOnly: searchSwitchValue(overlay, 'title'),
|
||||
exact: searchSwitchValue(overlay, 'exact'),
|
||||
includeOcr: false,
|
||||
includeOcr: true,
|
||||
onlyCurrentPage: searchSwitchValue(overlay, 'page'),
|
||||
timeRange: 'any',
|
||||
timeField: 'updated'
|
||||
@@ -2263,16 +2362,20 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
}
|
||||
results.innerHTML = items.map(function(item) {
|
||||
var title = searchText(item.title || item.name || item.documentTitle || '无标题');
|
||||
var snippet = searchText(item.snippet || (Array.isArray(item.evidence) && item.evidence[0] && item.evidence[0].snippet) || '');
|
||||
var snippet = searchText(item.snippet || item.evidence && item.evidence.quote || (Array.isArray(item.evidence) && item.evidence[0] && (item.evidence[0].quote || item.evidence[0].snippet)) || '');
|
||||
var path = searchText(item.path || item.parentTitle || item.workspaceName || currentWorkspaceName());
|
||||
var resourceType = searchText(item.resourceType || item.resourceKind || 'page');
|
||||
return '<button type="button" class="wolai-search-result-row" data-testid="wolai-search-result-row" data-search-result-owner="rust-kernel" data-document-id="' + escapeHtml(item.documentId || item.nodeId || item.id || '') + '" data-resource-type="' + escapeHtml(resourceType) + '">' +
|
||||
var locator = searchResultEvidenceLocator(item);
|
||||
var locatorAttr = locator ? escapeHtml(JSON.stringify(locator)) : '';
|
||||
var index = items.indexOf(item);
|
||||
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-path"><span>' + escapeHtml(path) + '</span><span class="wolai-search-result-type">' + escapeHtml(resourceType) + '</span></span></span>' +
|
||||
'</button>';
|
||||
}).join('');
|
||||
window.__mnoteSearchResults = items;
|
||||
} catch (error) {
|
||||
if (requestId !== activeSearchRequestId) return;
|
||||
meta.innerHTML = '<span>共 0 条匹配结果</span><span>Ctrl + Enter 新窗口打开 / Alt + Enter 右侧边栏打开</span>';
|
||||
@@ -2473,9 +2576,30 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return;
|
||||
}
|
||||
|
||||
var indexSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-index-settings"]');
|
||||
if (indexSettingsTrigger) {
|
||||
e.preventDefault();
|
||||
openPageIndexSettingsPopover();
|
||||
return;
|
||||
}
|
||||
|
||||
var ocrSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-ocr-settings"], [data-mnote-action="toggle-ocr-tasks"]');
|
||||
if (ocrSettingsTrigger) {
|
||||
e.preventDefault();
|
||||
openLocalOcrSettingsPopover();
|
||||
return;
|
||||
}
|
||||
|
||||
var settingsClose = closestAction(e.target, '[data-settings-action="close"]');
|
||||
if (settingsClose) {
|
||||
e.preventDefault();
|
||||
closeAllSettingsPopovers();
|
||||
return;
|
||||
}
|
||||
|
||||
var pageSettingsPanel = closestAction(e.target, '.wolai-page-settings-panel');
|
||||
if (isPageSettingsOpen() && !pageSettingsPanel) {
|
||||
closePageSettingsPopover();
|
||||
if (isAnySettingsOpen() && !pageSettingsPanel) {
|
||||
closeAllSettingsPopovers();
|
||||
}
|
||||
|
||||
var pageSettingsTab = closestAction(e.target, '[data-page-settings-tab]');
|
||||
@@ -2485,6 +2609,39 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return;
|
||||
}
|
||||
|
||||
var localIndexAction = closestAction(e.target, '[data-local-index-action]');
|
||||
if (localIndexAction) {
|
||||
e.preventDefault();
|
||||
var localIndexActionName = localIndexAction.getAttribute('data-local-index-action') || '';
|
||||
if (localIndexActionName === 'save') {
|
||||
void persistLocalIndexSettings();
|
||||
return;
|
||||
}
|
||||
if (localIndexActionName === 'refresh') {
|
||||
void refreshLocalIndex();
|
||||
return;
|
||||
}
|
||||
if (localIndexActionName === 'add-path') {
|
||||
addLocalIndexRange();
|
||||
return;
|
||||
}
|
||||
if (localIndexActionName === 'remove-path') {
|
||||
removeLocalIndexRange(localIndexAction.getAttribute('data-local-index-path-index') || '0');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var localOcrSettingsAction = closestAction(e.target, '[data-local-ocr-settings-action]');
|
||||
if (localOcrSettingsAction) {
|
||||
e.preventDefault();
|
||||
var localOcrSettingsActionName = localOcrSettingsAction.getAttribute('data-local-ocr-settings-action') || '';
|
||||
closeAllSettingsPopovers();
|
||||
window.dispatchEvent(new CustomEvent('mnote:local-ocr-settings-action', {
|
||||
detail: { action: localOcrSettingsActionName }
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
var pageSettingsAction = closestAction(e.target, '[data-page-settings-action]');
|
||||
if (pageSettingsAction) {
|
||||
e.preventDefault();
|
||||
@@ -2507,6 +2664,13 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return;
|
||||
}
|
||||
|
||||
var navigationPageTrigger = closestAction(e.target, '[data-mnote-action="open-navigation-page"]');
|
||||
if (navigationPageTrigger) {
|
||||
e.preventDefault();
|
||||
openCurrentNavigationPage(navigationPageTrigger);
|
||||
return;
|
||||
}
|
||||
|
||||
var searchTrigger = closestAction(e.target, '[data-mnote-action="open-search-modal"]');
|
||||
if (searchTrigger) {
|
||||
e.preventDefault();
|
||||
@@ -2518,6 +2682,24 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
closeSearchModal();
|
||||
return;
|
||||
}
|
||||
var searchResultRow = closestAction(e.target, '[data-testid="wolai-search-result-row"]');
|
||||
if (searchResultRow) {
|
||||
e.preventDefault();
|
||||
var index = Number(searchResultRow.getAttribute('data-search-result-index') || -1);
|
||||
var item = Array.isArray(window.__mnoteSearchResults) && index >= 0 ? window.__mnoteSearchResults[index] : null;
|
||||
if (!item) {
|
||||
var locatorPayload = searchResultRow.getAttribute('data-evidence-locator') || '';
|
||||
try {
|
||||
var locator = locatorPayload ? JSON.parse(locatorPayload) : null;
|
||||
item = locator ? { evidence: { source: locator }, documentId: searchResultRow.getAttribute('data-document-id') || '' } : null;
|
||||
} catch (_) {
|
||||
item = null;
|
||||
}
|
||||
}
|
||||
void openEvidenceSearchResult(item || { documentId: searchResultRow.getAttribute('data-document-id') || '' }, e)
|
||||
.catch(function(error) { console.warn('mnote evidence 搜索结果打开失败', error); });
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceMenuTrigger = closestAction(e.target, '[data-mnote-action="open-workspace-source-menu"]');
|
||||
if (sourceMenuTrigger) {
|
||||
@@ -2847,9 +3029,9 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.key === 'Escape' && isPageSettingsOpen()) {
|
||||
if (event.key === 'Escape' && isAnySettingsOpen()) {
|
||||
event.preventDefault();
|
||||
closePageSettingsPopover();
|
||||
closeAllSettingsPopovers();
|
||||
return;
|
||||
}
|
||||
if ((event.metaKey || event.ctrlKey) && !event.shiftKey && event.key && event.key.toLowerCase() === 'p') {
|
||||
|
||||
Reference in New Issue
Block a user