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:
@@ -324,6 +324,21 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
trigger.setAttribute('aria-expanded', pageUiState.pageSettingsOpen ? 'true' : 'false');
|
||||
}
|
||||
|
||||
function updateStandaloneSettingsTriggerState() {
|
||||
var indexTrigger = document.querySelector('[data-testid="mnote-local-index-settings-toggle"]');
|
||||
var indexOpen = isLocalIndexSettingsOpen();
|
||||
if (indexTrigger instanceof HTMLElement) {
|
||||
indexTrigger.setAttribute('data-state', indexOpen ? 'open' : 'closed');
|
||||
indexTrigger.setAttribute('aria-expanded', indexOpen ? 'true' : 'false');
|
||||
}
|
||||
var ocrTrigger = document.querySelector('[data-testid="mnote-local-ocr-task-toggle"]');
|
||||
var ocrOpen = isLocalOcrSettingsOpen();
|
||||
if (ocrTrigger instanceof HTMLElement) {
|
||||
ocrTrigger.setAttribute('data-state', ocrOpen ? 'open' : 'closed');
|
||||
ocrTrigger.setAttribute('aria-expanded', ocrOpen ? 'true' : 'false');
|
||||
}
|
||||
}
|
||||
|
||||
function createPageOptionRow(key, type) {
|
||||
var inputType = type || 'checkbox';
|
||||
var supported = pageOptionIsSupported(key);
|
||||
@@ -412,10 +427,6 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
popover.querySelectorAll('[data-global-option-checkbox="showHeadingNumbers"]').forEach(function(input) {
|
||||
input.checked = globalHeadingNumbers;
|
||||
});
|
||||
var localOcrPreferences = currentLocalOcrPreferences();
|
||||
popover.querySelectorAll('[data-local-ocr-option-checkbox="autoEnabled"]').forEach(function(input) {
|
||||
input.checked = localOcrPreferences['localOcr.autoEnabled'] === true;
|
||||
});
|
||||
var preferences = currentPageWidthPreferences();
|
||||
popover.querySelectorAll('[data-page-width-select]').forEach(function(select) {
|
||||
var type = select.getAttribute('data-page-width-select') || '';
|
||||
@@ -430,6 +441,13 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
});
|
||||
}
|
||||
|
||||
function renderLocalOcrOptions(popover) {
|
||||
var localOcrPreferences = currentLocalOcrPreferences();
|
||||
popover.querySelectorAll('[data-local-ocr-option-checkbox="autoEnabled"]').forEach(function(input) {
|
||||
input.checked = localOcrPreferences['localOcr.autoEnabled'] === true;
|
||||
});
|
||||
}
|
||||
|
||||
function createPageFontRow() {
|
||||
return '' +
|
||||
'<label class="wolai-page-setting-row" data-page-setting-row="pageFont">' +
|
||||
@@ -566,7 +584,6 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
'<div class="wolai-page-settings-tabs" data-testid="wolai-page-settings-tabs" role="tablist" aria-label="页面设置分组">' +
|
||||
'<button type="button" class="wolai-page-settings-tab is-active" role="tab" aria-selected="true" data-page-settings-tab="page">页面选项</button>' +
|
||||
'<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="custom">自定义页面</button>' +
|
||||
'<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="index">索引</button>' +
|
||||
'<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="global">全局选项</button>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-section" data-page-settings-panel="page">' +
|
||||
@@ -583,22 +600,8 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
createPageOptionRow('hideChildPages', 'checkbox') +
|
||||
createPageOptionRow('showBlockRefCount', 'checkbox') +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-section" data-page-settings-panel="index" hidden>' +
|
||||
'<div class="wolai-page-settings-index-panel" data-testid="wolai-page-settings-local-index-panel">' +
|
||||
'<div class="wolai-page-settings-index-status" data-testid="wolai-page-settings-local-index-status"></div>' +
|
||||
'<section class="wolai-page-settings-index-group">' +
|
||||
'<div class="wolai-page-settings-index-title">反链</div>' +
|
||||
'<div class="wolai-page-settings-index-list" data-testid="wolai-page-settings-local-index-backlinks"></div>' +
|
||||
'</section>' +
|
||||
'<section class="wolai-page-settings-index-group">' +
|
||||
'<div class="wolai-page-settings-index-title">标签</div>' +
|
||||
'<div class="wolai-page-settings-index-list" data-testid="wolai-page-settings-local-index-tags"></div>' +
|
||||
'</section>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-section" data-page-settings-panel="global" hidden>' +
|
||||
createGlobalHeadingNumbersRow() +
|
||||
createLocalOcrAutoRow() +
|
||||
createPageWidthRows() +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-actions">' +
|
||||
@@ -614,6 +617,73 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
return popover;
|
||||
}
|
||||
|
||||
function createLocalIndexSettingsPanelHtml() {
|
||||
return '' +
|
||||
'<div class="wolai-page-settings-panel mnote-settings-panel mnote-local-index-settings-panel" role="dialog" aria-modal="false" aria-label="索引设置">' +
|
||||
'<div class="mnote-settings-panel-head">' +
|
||||
'<strong>索引设置</strong>' +
|
||||
'<button type="button" class="mnote-settings-panel-close" data-settings-action="close" aria-label="关闭索引设置">×</button>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-index-panel" data-testid="wolai-page-settings-local-index-panel">' +
|
||||
'<div class="wolai-page-settings-index-status" data-testid="wolai-page-settings-local-index-status"></div>' +
|
||||
'<section class="wolai-page-settings-index-group">' +
|
||||
'<div class="wolai-page-settings-index-title">索引范围</div>' +
|
||||
'<div class="wolai-page-settings-index-range-list" data-testid="wolai-page-settings-local-index-ranges"></div>' +
|
||||
'<button type="button" class="wolai-page-settings-index-add" data-local-index-action="add-path">新增范围</button>' +
|
||||
'<div class="wolai-page-settings-index-schedule">' +
|
||||
'<label><span>索引时间</span><select data-testid="wolai-page-settings-local-index-schedule-mode"><option value="daily">每日</option><option value="once">指定日期</option><option value="manual">手动</option></select></label>' +
|
||||
'<label><span>时间</span><input type="time" data-testid="wolai-page-settings-local-index-schedule-time" value="02:00"></label>' +
|
||||
'<label><span>日期</span><input type="date" data-testid="wolai-page-settings-local-index-schedule-date"></label>' +
|
||||
'<label class="wolai-page-settings-index-inline"><input type="checkbox" data-testid="wolai-page-settings-local-index-run-on-change"><span>文档变化时立即索引</span></label>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-index-actions">' +
|
||||
'<button type="button" data-local-index-action="save">保存设置</button>' +
|
||||
'<button type="button" data-local-index-action="refresh">重建索引</button>' +
|
||||
'</div>' +
|
||||
'</section>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function ensureLocalIndexSettingsPopover() {
|
||||
var existing = document.querySelector('[data-testid="mnote-local-index-settings-popover"]');
|
||||
if (existing instanceof HTMLElement) return existing;
|
||||
var popover = document.createElement('div');
|
||||
popover.className = 'wolai-page-settings-popover mnote-local-index-settings-popover';
|
||||
popover.setAttribute('data-testid', 'mnote-local-index-settings-popover');
|
||||
popover.setAttribute('data-mnote-surface', 'local-index-settings');
|
||||
popover.hidden = true;
|
||||
popover.innerHTML = createLocalIndexSettingsPanelHtml();
|
||||
document.body.appendChild(popover);
|
||||
return popover;
|
||||
}
|
||||
|
||||
function ensureLocalOcrSettingsPopover() {
|
||||
var existing = document.querySelector('[data-testid="mnote-local-ocr-settings-popover"]');
|
||||
if (existing instanceof HTMLElement) return existing;
|
||||
var popover = document.createElement('div');
|
||||
popover.className = 'wolai-page-settings-popover mnote-local-ocr-settings-popover';
|
||||
popover.setAttribute('data-testid', 'mnote-local-ocr-settings-popover');
|
||||
popover.setAttribute('data-mnote-surface', 'local-ocr-settings');
|
||||
popover.hidden = true;
|
||||
popover.innerHTML = '' +
|
||||
'<div class="wolai-page-settings-panel mnote-settings-panel mnote-local-ocr-settings-panel" role="dialog" aria-modal="false" aria-label="OCR 设置">' +
|
||||
'<div class="mnote-settings-panel-head">' +
|
||||
'<strong>OCR 设置</strong>' +
|
||||
'<button type="button" class="mnote-settings-panel-close" data-settings-action="close" aria-label="关闭 OCR 设置">×</button>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-section">' +
|
||||
createLocalOcrAutoRow() +
|
||||
'<div class="wolai-page-settings-index-actions">' +
|
||||
'<button type="button" data-local-ocr-settings-action="run-active">识别当前资源</button>' +
|
||||
'<button type="button" data-local-ocr-settings-action="tasks">查看 OCR 任务</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
document.body.appendChild(popover);
|
||||
return popover;
|
||||
}
|
||||
|
||||
function pageSettingsLocalIndexScopeKey() {
|
||||
return [
|
||||
currentSourceKind(),
|
||||
@@ -624,79 +694,124 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
}
|
||||
|
||||
function pageSettingsLocalIndexIsAvailable() {
|
||||
return currentSourceKind() === 'local_folder' && Boolean(currentRootUri()) && Boolean(currentDocumentId());
|
||||
return currentSourceKind() === 'local_folder' && Boolean(currentRootUri());
|
||||
}
|
||||
|
||||
function pageSettingsLocalIndexEmpty(message) {
|
||||
return '<div class="wolai-page-settings-index-empty">' + escapeHtml(message) + '</div>';
|
||||
}
|
||||
|
||||
function renderPageSettingsLocalIndexList(items, kind) {
|
||||
var rows = Array.isArray(items) ? items : [];
|
||||
if (!rows.length) {
|
||||
return pageSettingsLocalIndexEmpty(kind === 'backlinks' ? '暂无反链' : '暂无标签');
|
||||
}
|
||||
if (kind === 'backlinks') {
|
||||
return rows.slice(0, 12).map(function(item) {
|
||||
var title = searchText(item && item.title) || searchText(item && item.path) || '未命名页面';
|
||||
var path = searchText(item && item.path);
|
||||
var snippet = searchText(item && item.snippet);
|
||||
return '' +
|
||||
'<div class="wolai-page-settings-index-row">' +
|
||||
'<div class="wolai-page-settings-index-row-title">' + escapeHtml(title) + '</div>' +
|
||||
(path ? '<div class="wolai-page-settings-index-row-meta">' + escapeHtml(path) + '</div>' : '') +
|
||||
(snippet ? '<div class="wolai-page-settings-index-row-snippet">' + escapeHtml(snippet) + '</div>' : '') +
|
||||
'</div>';
|
||||
}).join('');
|
||||
}
|
||||
return rows.slice(0, 16).map(function(item) {
|
||||
var tag = searchText(item && item.tag) || 'untagged';
|
||||
var count = Number(item && item.count || 0);
|
||||
return '' +
|
||||
'<div class="wolai-page-settings-index-row is-tag">' +
|
||||
'<div class="wolai-page-settings-index-row-title">#' + escapeHtml(tag) + '</div>' +
|
||||
'<div class="wolai-page-settings-index-row-meta">' + count + ' 个页面</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderPageSettingsLocalIndex(popover) {
|
||||
popover = popover || ensurePageSettingsPopover();
|
||||
popover = popover || ensureLocalIndexSettingsPopover();
|
||||
var statusNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-status"]');
|
||||
var backlinksNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-backlinks"]');
|
||||
var tagsNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-tags"]');
|
||||
if (!(statusNode instanceof HTMLElement) || !(backlinksNode instanceof HTMLElement) || !(tagsNode instanceof HTMLElement)) return;
|
||||
var rangesNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-ranges"]');
|
||||
var scheduleModeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-mode"]');
|
||||
var scheduleTimeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-time"]');
|
||||
var scheduleDateNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-date"]');
|
||||
var runOnChangeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-run-on-change"]');
|
||||
if (!(statusNode instanceof HTMLElement)) return;
|
||||
|
||||
if (!pageSettingsLocalIndexIsAvailable()) {
|
||||
statusNode.textContent = '本地索引仅在本地工作区页面可用';
|
||||
backlinksNode.innerHTML = pageSettingsLocalIndexEmpty('未连接本地 root');
|
||||
tagsNode.innerHTML = pageSettingsLocalIndexEmpty('未连接本地 root');
|
||||
statusNode.textContent = '本地索引仅在本地工作区可用';
|
||||
renderLocalIndexRangeRows(popover, [], 'fault', true, {});
|
||||
setLocalIndexScheduleControlsDisabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var summary = pageUiState.localIndexSummary || {};
|
||||
if (summary.loading) {
|
||||
statusNode.textContent = '正在读取本地索引...';
|
||||
backlinksNode.innerHTML = pageSettingsLocalIndexEmpty('加载中');
|
||||
tagsNode.innerHTML = pageSettingsLocalIndexEmpty('加载中');
|
||||
renderLocalIndexRangeRows(popover, currentLocalIndexRangeValues(popover), 'indexing', true, summary.status || {});
|
||||
setLocalIndexScheduleControlsDisabled(true);
|
||||
return;
|
||||
}
|
||||
if (summary.error) {
|
||||
statusNode.textContent = '本地索引读取失败';
|
||||
backlinksNode.innerHTML = pageSettingsLocalIndexEmpty(summary.error);
|
||||
tagsNode.innerHTML = pageSettingsLocalIndexEmpty(summary.error);
|
||||
renderLocalIndexRangeRows(popover, currentLocalIndexRangeValues(popover), 'fault', false, summary.status || {});
|
||||
setLocalIndexScheduleControlsDisabled(false);
|
||||
return;
|
||||
}
|
||||
if (summary.scopeKey !== pageSettingsLocalIndexScopeKey()) {
|
||||
statusNode.textContent = '切换到索引页签后读取本地索引';
|
||||
backlinksNode.innerHTML = pageSettingsLocalIndexEmpty('等待读取');
|
||||
tagsNode.innerHTML = pageSettingsLocalIndexEmpty('等待读取');
|
||||
statusNode.textContent = '打开索引设置后读取本地索引';
|
||||
renderLocalIndexRangeRows(popover, [], 'indexing', true, {});
|
||||
setLocalIndexScheduleControlsDisabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
statusNode.textContent = '来自当前授权 root 的 .mnote/index/search-index.json';
|
||||
backlinksNode.innerHTML = renderPageSettingsLocalIndexList(summary.backlinks, 'backlinks');
|
||||
tagsNode.innerHTML = renderPageSettingsLocalIndexList(summary.tags, 'tags');
|
||||
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 ? ',已到计划时间' : '');
|
||||
if (!(rangesNode instanceof HTMLElement) || !rangesNode.contains(document.activeElement)) {
|
||||
renderLocalIndexRangeRows(popover, includePaths, localIndexStatusKind(status), false, status);
|
||||
}
|
||||
if (scheduleModeNode instanceof HTMLSelectElement && document.activeElement !== scheduleModeNode) {
|
||||
scheduleModeNode.value = String(settings.scheduleMode || 'daily');
|
||||
}
|
||||
if (scheduleTimeNode instanceof HTMLInputElement && document.activeElement !== scheduleTimeNode) {
|
||||
scheduleTimeNode.value = String(settings.scheduleTime || '02:00');
|
||||
}
|
||||
if (scheduleDateNode instanceof HTMLInputElement && document.activeElement !== scheduleDateNode) {
|
||||
scheduleDateNode.value = String(settings.scheduleDate || '');
|
||||
}
|
||||
if (runOnChangeNode instanceof HTMLInputElement) {
|
||||
runOnChangeNode.checked = settings.runOnChange === true;
|
||||
}
|
||||
setLocalIndexScheduleControlsDisabled(false);
|
||||
}
|
||||
|
||||
function localIndexStatusKind(status) {
|
||||
if (!status || typeof status !== 'object') return 'fault';
|
||||
if (status.indexExists !== true) return 'fault';
|
||||
if (status.cacheMatchesSettings === true && status.scheduledDue !== true) return 'indexed';
|
||||
return 'indexing';
|
||||
}
|
||||
|
||||
function localIndexStatusLabel(kind) {
|
||||
if (kind === 'indexed') return '已索引';
|
||||
if (kind === 'indexing') return '索引中';
|
||||
return '索引故障';
|
||||
}
|
||||
|
||||
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 (status.indexExists === true) return 'indexing';
|
||||
return 'fault';
|
||||
}
|
||||
|
||||
function renderLocalIndexRangeRows(popover, includePaths, fallbackKind, disabled, status) {
|
||||
var rangesNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-ranges"]');
|
||||
if (!(rangesNode instanceof HTMLElement)) return;
|
||||
var rawPaths = Array.isArray(includePaths) ? includePaths.map(function(value) {
|
||||
return String(value || '').trim();
|
||||
}) : [];
|
||||
var paths = disabled ? rawPaths.filter(Boolean) : rawPaths;
|
||||
if (!paths.length && !disabled) paths = ['.'];
|
||||
rangesNode.innerHTML = paths.map(function(path, index) {
|
||||
var kind = localIndexPathStatusKind(path, status || {}, fallbackKind || 'fault');
|
||||
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' : '') + ' />' +
|
||||
'<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('');
|
||||
}
|
||||
|
||||
function setLocalIndexScheduleControlsDisabled(disabled) {
|
||||
var popover = ensureLocalIndexSettingsPopover();
|
||||
[
|
||||
'[data-testid="wolai-page-settings-local-index-schedule-mode"]',
|
||||
'[data-testid="wolai-page-settings-local-index-schedule-time"]',
|
||||
'[data-testid="wolai-page-settings-local-index-schedule-date"]',
|
||||
'[data-testid="wolai-page-settings-local-index-run-on-change"]'
|
||||
].forEach(function(selector) {
|
||||
var node = popover.querySelector(selector);
|
||||
if (node instanceof HTMLInputElement || node instanceof HTMLSelectElement) node.disabled = disabled;
|
||||
});
|
||||
}
|
||||
|
||||
async function loadPageSettingsLocalIndex(force) {
|
||||
@@ -713,50 +828,148 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
pageUiState.localIndexSummary = {
|
||||
scopeKey: scopeKey,
|
||||
loading: true,
|
||||
error: '',
|
||||
backlinks: null,
|
||||
tags: null
|
||||
error: ''
|
||||
};
|
||||
renderPageSettingsLocalIndex();
|
||||
try {
|
||||
var baseParams = new URLSearchParams();
|
||||
baseParams.set('workspaceId', resolveWorkspaceId(document.body));
|
||||
baseParams.set('rootUri', currentRootUri());
|
||||
var backlinksParams = new URLSearchParams(baseParams);
|
||||
backlinksParams.set('documentId', currentDocumentId());
|
||||
var backlinksUrl = '/api/search/local-index/backlinks?' + backlinksParams.toString();
|
||||
var tagsUrl = '/api/search/local-index/tags?' + baseParams.toString();
|
||||
var responses = await Promise.all([
|
||||
fetch(backlinksUrl, { headers: { accept: 'application/json' } }),
|
||||
fetch(tagsUrl, { headers: { accept: 'application/json' } })
|
||||
]);
|
||||
var backlinksPayload = await responses[0].json().catch(function(){ return null; });
|
||||
var tagsPayload = await responses[1].json().catch(function(){ return null; });
|
||||
if (!responses[0].ok || !backlinksPayload || backlinksPayload.ok !== true) {
|
||||
throw new Error('backlinks_' + responses[0].status);
|
||||
}
|
||||
if (!responses[1].ok || !tagsPayload || tagsPayload.ok !== true) {
|
||||
throw new Error('tags_' + responses[1].status);
|
||||
var statusUrl = '/api/search/local-index/status?' + baseParams.toString();
|
||||
var response = await fetch(statusUrl, { headers: { accept: 'application/json' } });
|
||||
var statusPayload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !statusPayload || statusPayload.ok !== true) {
|
||||
throw new Error('status_' + response.status);
|
||||
}
|
||||
pageUiState.localIndexSummary = {
|
||||
scopeKey: scopeKey,
|
||||
loading: false,
|
||||
error: '',
|
||||
backlinks: backlinksPayload.result && Array.isArray(backlinksPayload.result.backlinks) ? backlinksPayload.result.backlinks : [],
|
||||
tags: tagsPayload.result && Array.isArray(tagsPayload.result.tags) ? tagsPayload.result.tags : []
|
||||
status: statusPayload.result || {}
|
||||
};
|
||||
} catch (error) {
|
||||
pageUiState.localIndexSummary = {
|
||||
scopeKey: scopeKey,
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
backlinks: [],
|
||||
tags: []
|
||||
status: {}
|
||||
};
|
||||
}
|
||||
renderPageSettingsLocalIndex();
|
||||
}
|
||||
|
||||
function localIndexIncludePathsFromForm() {
|
||||
var popover = ensureLocalIndexSettingsPopover();
|
||||
var values = 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 : '';
|
||||
});
|
||||
}
|
||||
|
||||
function localIndexSchedulePayloadFromForm() {
|
||||
var popover = ensureLocalIndexSettingsPopover();
|
||||
var modeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-mode"]');
|
||||
var timeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-time"]');
|
||||
var dateNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-date"]');
|
||||
var runOnChangeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-run-on-change"]');
|
||||
return {
|
||||
scheduleMode: modeNode instanceof HTMLSelectElement ? modeNode.value : 'daily',
|
||||
scheduleTime: timeNode instanceof HTMLInputElement ? (timeNode.value || '02:00') : '02:00',
|
||||
scheduleDate: dateNode instanceof HTMLInputElement ? dateNode.value : '',
|
||||
runOnChange: runOnChangeNode instanceof HTMLInputElement ? runOnChangeNode.checked : false
|
||||
};
|
||||
}
|
||||
|
||||
function addLocalIndexRange() {
|
||||
var popover = ensureLocalIndexSettingsPopover();
|
||||
var summary = pageUiState.localIndexSummary || {};
|
||||
renderLocalIndexRangeRows(popover, currentLocalIndexRangeValues(popover).concat(['']), localIndexStatusKind(summary.status || {}), false, summary.status || {});
|
||||
var inputs = Array.from(popover.querySelectorAll('[data-local-index-range-input]'));
|
||||
var last = inputs[inputs.length - 1];
|
||||
if (last instanceof HTMLInputElement) last.focus();
|
||||
}
|
||||
|
||||
function removeLocalIndexRange(index) {
|
||||
var popover = ensureLocalIndexSettingsPopover();
|
||||
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 || {});
|
||||
}
|
||||
|
||||
async function persistLocalIndexSettings() {
|
||||
if (!pageSettingsLocalIndexIsAvailable()) return;
|
||||
pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
|
||||
scopeKey: pageSettingsLocalIndexScopeKey(),
|
||||
loading: true,
|
||||
error: ''
|
||||
});
|
||||
renderPageSettingsLocalIndex();
|
||||
try {
|
||||
var response = await fetch('/api/search/local-index/settings', {
|
||||
method: 'PUT',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify(Object.assign({
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
rootUri: currentRootUri(),
|
||||
includePaths: localIndexIncludePathsFromForm()
|
||||
}, localIndexSchedulePayloadFromForm()))
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload && payload.error && payload.error.message ? payload.error.message : 'local_index_settings_' + response.status);
|
||||
}
|
||||
await loadPageSettingsLocalIndex(true);
|
||||
} catch (error) {
|
||||
pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
|
||||
scopeKey: pageSettingsLocalIndexScopeKey(),
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
renderPageSettingsLocalIndex();
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshLocalIndex() {
|
||||
if (!pageSettingsLocalIndexIsAvailable()) return;
|
||||
pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
|
||||
scopeKey: pageSettingsLocalIndexScopeKey(),
|
||||
loading: true,
|
||||
error: ''
|
||||
});
|
||||
renderPageSettingsLocalIndex();
|
||||
try {
|
||||
var response = await fetch('/api/search/local-index/refresh', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify({
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
rootUri: currentRootUri()
|
||||
})
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload && payload.error && payload.error.message ? payload.error.message : 'local_index_refresh_' + response.status);
|
||||
}
|
||||
await loadPageSettingsLocalIndex(true);
|
||||
} catch (error) {
|
||||
pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
|
||||
scopeKey: pageSettingsLocalIndexScopeKey(),
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
renderPageSettingsLocalIndex();
|
||||
}
|
||||
}
|
||||
|
||||
function renderPageSettingsPopover() {
|
||||
var popover = ensurePageSettingsPopover();
|
||||
var options = currentPageOptions();
|
||||
@@ -780,7 +993,6 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
'<span>块数 ' + Number(stats.blockCount || 0) + '</span>' +
|
||||
'<span>待办 ' + Number(stats.todoDone || 0) + '/' + Number(stats.todoTotal || 0) + '</span>';
|
||||
}
|
||||
renderPageSettingsLocalIndex(popover);
|
||||
}
|
||||
|
||||
function setActivePageSettingsTab(tabName) {
|
||||
@@ -793,7 +1005,6 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
popover.querySelectorAll('[data-page-settings-panel]').forEach(function(panel) {
|
||||
panel.hidden = panel.getAttribute('data-page-settings-panel') !== tabName;
|
||||
});
|
||||
if (tabName === 'index') void loadPageSettingsLocalIndex(false);
|
||||
}
|
||||
|
||||
async function persistPageOptionsPatch(patch) {
|
||||
@@ -848,9 +1059,8 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
}
|
||||
|
||||
async function loadPageWidthPreferences() {
|
||||
if (!currentDocumentId()) return;
|
||||
var params = new URLSearchParams();
|
||||
params.set('documentId', currentDocumentId());
|
||||
if (currentDocumentId()) params.set('documentId', currentDocumentId());
|
||||
params.set('workspaceId', resolveWorkspaceId(document.body));
|
||||
var sourcePayload = currentWorkspaceSourcePayload();
|
||||
Object.keys(sourcePayload || {}).forEach(function(key) {
|
||||
@@ -868,6 +1078,7 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-auto-enabled', String(pageUiState.localOcrPreferences['localOcr.autoEnabled'] === true));
|
||||
applyPageOptionsToShell();
|
||||
if (isPageSettingsOpen()) renderPageSettingsPopover();
|
||||
if (isLocalOcrSettingsOpen()) renderLocalOcrOptions(ensureLocalOcrSettingsPopover());
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@@ -876,7 +1087,8 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
var next = Object.assign({}, previous, { 'localOcr.autoEnabled': Boolean(enabled) });
|
||||
pageUiState.localOcrPreferences = next;
|
||||
window.__MNOTE_LOCAL_OCR_PREFERENCES = Object.assign({}, next);
|
||||
renderPageSettingsPopover();
|
||||
if (isPageSettingsOpen()) renderPageSettingsPopover();
|
||||
if (isLocalOcrSettingsOpen()) renderLocalOcrOptions(ensureLocalOcrSettingsPopover());
|
||||
try {
|
||||
var response = await fetch('/api/ui/preferences', {
|
||||
method: 'PUT',
|
||||
@@ -895,11 +1107,13 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
pageUiState.localOcrPreferences = Object.assign({ 'localOcr.autoEnabled': false }, payload.result && payload.result.localOcrPreferences || {});
|
||||
window.__MNOTE_LOCAL_OCR_PREFERENCES = Object.assign({}, pageUiState.localOcrPreferences);
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-auto-enabled', String(pageUiState.localOcrPreferences['localOcr.autoEnabled'] === true));
|
||||
renderPageSettingsPopover();
|
||||
if (isPageSettingsOpen()) renderPageSettingsPopover();
|
||||
if (isLocalOcrSettingsOpen()) renderLocalOcrOptions(ensureLocalOcrSettingsPopover());
|
||||
} catch (error) {
|
||||
pageUiState.localOcrPreferences = previous;
|
||||
window.__MNOTE_LOCAL_OCR_PREFERENCES = Object.assign({}, previous);
|
||||
renderPageSettingsPopover();
|
||||
if (isPageSettingsOpen()) renderPageSettingsPopover();
|
||||
if (isLocalOcrSettingsOpen()) renderLocalOcrOptions(ensureLocalOcrSettingsPopover());
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-preference-error', error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
}
|
||||
@@ -953,16 +1167,51 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
return popover instanceof HTMLElement && !popover.hidden;
|
||||
}
|
||||
|
||||
function openPageSettingsPopover() {
|
||||
function isLocalIndexSettingsOpen() {
|
||||
var popover = document.querySelector('[data-testid="mnote-local-index-settings-popover"]');
|
||||
return popover instanceof HTMLElement && !popover.hidden;
|
||||
}
|
||||
|
||||
function isLocalOcrSettingsOpen() {
|
||||
var popover = document.querySelector('[data-testid="mnote-local-ocr-settings-popover"]');
|
||||
return popover instanceof HTMLElement && !popover.hidden;
|
||||
}
|
||||
|
||||
function isAnySettingsOpen() {
|
||||
return isPageSettingsOpen() || isLocalIndexSettingsOpen() || isLocalOcrSettingsOpen();
|
||||
}
|
||||
|
||||
function openPageSettingsPopover(initialTab) {
|
||||
if (!currentDocumentId()) return;
|
||||
closeLocalIndexSettingsPopover();
|
||||
closeLocalOcrSettingsPopover();
|
||||
var popover = ensurePageSettingsPopover();
|
||||
renderPageSettingsPopover();
|
||||
setActivePageSettingsTab('page');
|
||||
setActivePageSettingsTab(initialTab || 'page');
|
||||
popover.hidden = false;
|
||||
pageUiState.pageSettingsOpen = true;
|
||||
updatePageSettingsTriggerState();
|
||||
}
|
||||
|
||||
function openPageIndexSettingsPopover() {
|
||||
closePageSettingsPopover();
|
||||
closeLocalOcrSettingsPopover();
|
||||
var popover = ensureLocalIndexSettingsPopover();
|
||||
renderPageSettingsLocalIndex(popover);
|
||||
popover.hidden = false;
|
||||
void loadPageSettingsLocalIndex(false);
|
||||
updateStandaloneSettingsTriggerState();
|
||||
}
|
||||
|
||||
function openLocalOcrSettingsPopover() {
|
||||
closePageSettingsPopover();
|
||||
closeLocalIndexSettingsPopover();
|
||||
var popover = ensureLocalOcrSettingsPopover();
|
||||
renderLocalOcrOptions(popover);
|
||||
popover.hidden = false;
|
||||
updateStandaloneSettingsTriggerState();
|
||||
}
|
||||
|
||||
function closePageSettingsPopover() {
|
||||
var popover = document.querySelector('[data-testid="wolai-page-settings-popover"]');
|
||||
if (popover instanceof HTMLElement) popover.hidden = true;
|
||||
@@ -970,6 +1219,24 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
updatePageSettingsTriggerState();
|
||||
}
|
||||
|
||||
function closeLocalIndexSettingsPopover() {
|
||||
var popover = document.querySelector('[data-testid="mnote-local-index-settings-popover"]');
|
||||
if (popover instanceof HTMLElement) popover.hidden = true;
|
||||
updateStandaloneSettingsTriggerState();
|
||||
}
|
||||
|
||||
function closeLocalOcrSettingsPopover() {
|
||||
var popover = document.querySelector('[data-testid="mnote-local-ocr-settings-popover"]');
|
||||
if (popover instanceof HTMLElement) popover.hidden = true;
|
||||
updateStandaloneSettingsTriggerState();
|
||||
}
|
||||
|
||||
function closeAllSettingsPopovers() {
|
||||
closePageSettingsPopover();
|
||||
closeLocalIndexSettingsPopover();
|
||||
closeLocalOcrSettingsPopover();
|
||||
}
|
||||
|
||||
function togglePageSettingsPopover() {
|
||||
if (isPageSettingsOpen()) closePageSettingsPopover();
|
||||
else openPageSettingsPopover();
|
||||
@@ -979,28 +1246,46 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
applyPageOptionsToShell();
|
||||
});
|
||||
|
||||
window.addEventListener('mnote:open-local-ocr-settings', function() {
|
||||
openLocalOcrSettingsPopover();
|
||||
});
|
||||
|
||||
return {
|
||||
addLocalIndexRange,
|
||||
applyPageOptionsToShell,
|
||||
closeAllSettingsPopovers,
|
||||
closePageHistoryDrawer,
|
||||
closeLocalIndexSettingsPopover,
|
||||
closeLocalOcrSettingsPopover,
|
||||
closePageSettingsPopover,
|
||||
closePageShareDialog,
|
||||
currentLocalOcrPreferences,
|
||||
currentPageOptions,
|
||||
ensureHistorySnapshotsSeeded,
|
||||
ensureLocalIndexSettingsPopover,
|
||||
ensureLocalOcrSettingsPopover,
|
||||
ensurePageHistoryDrawer,
|
||||
ensurePageSettingsPopover,
|
||||
ensurePageShareDialog,
|
||||
isAnySettingsOpen,
|
||||
isLocalIndexSettingsOpen,
|
||||
isLocalOcrSettingsOpen,
|
||||
isPageSettingsOpen,
|
||||
openPageHistoryDrawer,
|
||||
openLocalOcrSettingsPopover,
|
||||
openPageSettingsPopover,
|
||||
openPageIndexSettingsPopover,
|
||||
openPageShareDialog,
|
||||
pageOptionIsSupported,
|
||||
persistLocalOcrAutoPreference,
|
||||
persistLocalIndexSettings,
|
||||
persistPageOptionsPatch,
|
||||
persistPageWidthPreference,
|
||||
recordPageHistorySnapshot,
|
||||
removeLocalIndexRange,
|
||||
renderPageSettingsPopover,
|
||||
setActivePageSettingsTab,
|
||||
refreshLocalIndex,
|
||||
togglePageSettingsPopover,
|
||||
updatePageSettingsTriggerState,
|
||||
writeGlobalShowHeadingNumbers,
|
||||
|
||||
Reference in New Issue
Block a user