feat(rag): harden post-LightRAG runtime

Retire legacy OCR/media/evidence fallbacks, add local-folder event bus and Page Aggregate guards, and archive completed design checklists.

Validation: cargo test -p mnote-web -- --test-threads=1; cargo test --workspace -- --test-threads=1; git diff --check; codegraph sync .; codegraph_status.
This commit is contained in:
lix-2026
2026-06-07 10:35:21 +08:00
parent 22a92edcda
commit 9551d4c1dc
59 changed files with 4053 additions and 5249 deletions
@@ -842,6 +842,8 @@ import {
setStatus(runtimeDescriptor, 'ready');
}
if (session.pageBodySource) runtimeDescriptor.root.setAttribute('data-mnote-page-body-source', session.pageBodySource);
runtimeDescriptor.root.setAttribute('data-mnote-page-body-local-compat-fallback', session.pageBodyLocalCompatFallback ? 'true' : 'false');
if (session.pageBodyHardGuard) runtimeDescriptor.root.setAttribute('data-mnote-page-body-hard-guard', session.pageBodyHardGuard);
if (session.projectionSource) runtimeDescriptor.root.setAttribute('data-mnote-projection-source', session.projectionSource);
if (session.blockProjectionVersion) runtimeDescriptor.root.setAttribute('data-mnote-block-projection-version', session.blockProjectionVersion);
enhanceEditorAttachmentLinksSoon();
File diff suppressed because it is too large Load Diff
@@ -125,6 +125,8 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
return `${String(session?.rootUri || '').trim()}#${scope}`;
};
const localFolderEventBusChannelKey = (session) => `${String(session?.rootUri || '').trim()}#event-bus`;
const localMarkdownRelativePathFromDocumentId = (documentId) => {
const value = String(documentId || '').trim();
if (!value.startsWith('local-md:')) return '';
@@ -136,6 +138,25 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
|| localMarkdownRelativePathFromDocumentId(session?.documentId)
);
const localFolderEventItems = (payload) => {
if (!payload || typeof payload !== 'object') return [];
const changedPaths = Array.isArray(payload.changedPaths)
? payload.changedPaths
: (Array.isArray(payload.changed_paths) ? payload.changed_paths : []);
if (changedPaths.length > 0) {
return changedPaths.map((item) => {
if (typeof item === 'string') return { relativePath: item };
if (!item || typeof item !== 'object') return null;
return {
relativePath: String(item.relativePath || item.relative_path || item.path || '').trim(),
documentId: String(item.documentId || item.document_id || '').trim(),
eventKind: String(item.eventKind || item.event_kind || item.changeType || item.change_type || '').trim(),
};
}).filter(Boolean);
}
return [payload];
};
const sessionBufferStateUrl = (session) => {
if (!session || session.sourceKind !== 'local_folder' || !session.rootUri || !session.documentId) return null;
const url = new URL('/api/documents/buffer-state', window.location.origin);
@@ -206,10 +227,14 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
if (!channel) return;
channel.sessions.delete(session.key);
if (channel.sessions.size === 0) {
try {
channel.eventSource.close();
} catch (_) {
// noop
if (typeof channel.unsubscribe === 'function') {
channel.unsubscribe();
} else if (channel.eventSource && typeof channel.eventSource.close === 'function') {
try {
channel.eventSource.close();
} catch (_) {
// noop
}
}
localFolderEventRegistry.delete(channel.key);
}
@@ -385,7 +410,53 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
}, source || 'mnote-web-document-session-meta');
};
const pageBodySourceMeta = (sourceKind, pageBody) => {
const pageBodySource = pageBodyTiptapDocumentSource(pageBody || {}, '');
const projectionSource = String(pageBody?.projectionSource || pageBody?.projection_source || '').trim();
const blockProjectionVersion = String(pageBody?.blockProjectionVersion || pageBody?.block_projection_version || '').trim();
const localCompatFallback = sourceKind === 'local_folder' && pageBodySource === 'compat.legacy_content';
return {
pageBodySource,
projectionSource,
blockProjectionVersion,
localCompatFallback,
hardGuard: localCompatFallback
? 'local_compat_fallback'
: (sourceKind === 'local_folder' ? 'local_ok' : 'compat_allowed'),
};
};
const applyPageBodySourceMetaToSession = (session, pageBody) => {
const meta = pageBodySourceMeta(session.sourceKind, pageBody || {});
session.pageBodySource = meta.pageBodySource;
session.projectionSource = meta.projectionSource;
session.blockProjectionVersion = meta.blockProjectionVersion;
session.pageBodyLocalCompatFallback = meta.localCompatFallback;
session.pageBodyHardGuard = meta.hardGuard;
};
const syncPageBodySourceDiagnosticsToViews = (session) => {
sessionViews(session).forEach((view) => {
const root = view?.runtimeDescriptor?.root;
if (!(root instanceof HTMLElement)) return;
root.setAttribute('data-mnote-page-body-source', session.pageBodySource || '');
root.setAttribute('data-mnote-page-body-local-compat-fallback', session.pageBodyLocalCompatFallback ? 'true' : 'false');
root.setAttribute('data-mnote-page-body-hard-guard', session.pageBodyHardGuard || '');
if (session.projectionSource) {
root.setAttribute('data-mnote-projection-source', session.projectionSource);
} else {
root.removeAttribute('data-mnote-projection-source');
}
if (session.blockProjectionVersion) {
root.setAttribute('data-mnote-block-projection-version', session.blockProjectionVersion);
} else {
root.removeAttribute('data-mnote-block-projection-version');
}
});
};
const syncSessionMetaToViews = (session) => {
syncPageBodySourceDiagnosticsToViews(session);
sessionViews(session).forEach((view) => {
if (view.mountId != null) dispatchSessionMetaToView(session, view);
});
@@ -543,6 +614,7 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
const nextRevision = Number.isInteger(nextBody.revision) ? nextBody.revision : revisionFromConflictKey(nextConflictKey);
session.latestAggregate = nextAggregate;
syncPageAggregateScript(session, nextAggregate);
applyPageBodySourceMetaToSession(session, nextBody);
session.title = nextAggregate?.head?.title || session.title;
session.currentTiptapDocument = nextTiptapDocument;
session.currentSerialized = nextSerialized;
@@ -1168,15 +1240,22 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
const nextPermissions = nextAggregate?.head?.permissions || {};
const nextConflictKey = conflictDetectionKeyFromBody(nextBody);
const nextTiptapDocument = pageBodyTiptapDocument(nextBody, '', session);
const nextPageBodyMeta = pageBodySourceMeta(session.sourceKind, nextBody);
const nextSerialized = JSON.stringify(nextTiptapDocument);
const contentChanged = nextSerialized !== session.currentSerialized;
session.externalChangePending = false;
session.bufferDirtyState = 'Clean';
if (!nextConflictKey || !session.lastExternalConflictDetectionKey) {
session.lastExternalConflictDetectionKey = nextConflictKey || session.lastExternalConflictDetectionKey;
if (!contentChanged) return;
if (!contentChanged) {
applyPageBodySourceMetaToSession(session, nextBody);
syncPageBodySourceDiagnosticsToViews(session);
return;
}
}
if (nextConflictKey === session.lastExternalConflictDetectionKey) {
applyPageBodySourceMetaToSession(session, nextBody);
syncPageBodySourceDiagnosticsToViews(session);
if (nextConflictKey && session.conflictDetectionKey !== nextConflictKey) {
session.conflictDetectionKey = nextConflictKey;
session.fileVersion = nextConflictKey;
@@ -1187,6 +1266,11 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
const nextRevision = Number.isInteger(nextBody.revision) ? nextBody.revision : revisionFromConflictKey(nextConflictKey);
session.latestAggregate = nextAggregate;
syncPageAggregateScript(session, nextAggregate);
session.pageBodySource = nextPageBodyMeta.pageBodySource;
session.projectionSource = nextPageBodyMeta.projectionSource;
session.blockProjectionVersion = nextPageBodyMeta.blockProjectionVersion;
session.pageBodyLocalCompatFallback = nextPageBodyMeta.localCompatFallback;
session.pageBodyHardGuard = nextPageBodyMeta.hardGuard;
session.title = nextAggregate?.head?.title || session.title;
session.currentTiptapDocument = nextTiptapDocument;
session.currentSerialized = nextSerialized;
@@ -1284,10 +1368,94 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
await refreshSessionFromExternalChange(session, 'mnote-web-local-folder-watch');
};
const handleLocalFolderEventPayloadForSessions = (channel, payload) => {
if (!payload) return;
localFolderEventItems(payload).forEach((item) => {
Array.from(channel.sessions.values()).forEach((targetSession) => {
if (!targetSession || targetSession.views.size === 0) return;
const documentId = typeof item.documentId === 'string' ? item.documentId.trim() : '';
const relativePath = typeof item.relativePath === 'string' ? item.relativePath.trim() : '';
if (targetSession.sessionKind === 'resource') {
if (!relativePath || relativePath !== resourceWatchPath(targetSession)) return;
if (targetSession.saving) {
targetSession.externalChangePending = false;
targetSession.lastSelfSaveSignalAt = Date.now();
return;
}
targetSession.lastExternalChangeSignalAt = Date.now();
targetSession.externalChangePending = true;
scheduleSessionExternalRefresh(targetSession, 'mnote-web-local-resource-watch');
return;
}
const eventKind = String(item.eventKind || '');
const relativeTarget = sessionRelativePath(targetSession);
const targetsCurrentDocument = Boolean(
(documentId && documentId === targetSession.documentId)
|| (!documentId && relativePath && relativeTarget && relativePath === relativeTarget)
);
if (!targetsCurrentDocument) return;
if (shouldSuppressLocalFolderSelfChange(targetSession.documentId, eventKind)) {
targetSession.externalChangePending = false;
targetSession.lastSelfSaveSignalAt = Date.now();
return;
}
if (targetSession.saving) {
targetSession.externalChangePending = false;
targetSession.lastSelfSaveSignalAt = Date.now();
return;
}
targetSession.lastExternalChangeSignalAt = Date.now();
targetSession.externalChangePending = true;
scheduleSessionExternalRefresh(targetSession, 'mnote-web-local-folder-watch');
});
});
};
const ensureLocalFolderEventChannel = (session) => {
if (session.sourceKind !== 'local_folder' || !session.rootUri || typeof window.EventSource !== 'function') {
if (session.sourceKind !== 'local_folder' || !session.rootUri) {
return;
}
const eventBus = window.__mnoteLocalFolderEventBus;
if (eventBus && typeof eventBus.startLocalFolderWatcher === 'function') {
const channelKey = localFolderEventBusChannelKey(session);
let channel = localFolderEventRegistry.get(channelKey);
if (!channel) {
const rootUri = String(session.rootUri || '').trim();
const workspaceId = String(session.workspaceId || '').trim();
const handler = (event) => {
const detail = event?.detail || {};
const eventRootUri = String(detail.rootUri || detail.payload?.rootUri || '').trim();
if (eventRootUri && eventRootUri !== rootUri) return;
handleLocalFolderEventPayloadForSessions(channel, detail.payload || detail);
};
const handle = eventBus.startLocalFolderWatcher({
rootUri,
workspaceId,
bootstrap: {
schema: 'mnote.document_session.local_folder_event_bus.v1',
transport: 'local-folder-events',
workspaceId,
},
});
channel = {
key: channelKey,
rootUri,
documentId: '',
resourcePath: '',
eventSource: handle,
sessions: new Map(),
unsubscribe: () => window.removeEventListener('mnote:local-folder:document-changed', handler),
};
window.addEventListener('mnote:local-folder:document-changed', handler);
localFolderEventRegistry.set(channelKey, channel);
}
channel.sessions.set(session.key, session);
session.localFolderChannel = channel;
return;
}
if (typeof window.EventSource !== 'function') return;
const channelKey = localFolderEventChannelKey(session);
let channel = localFolderEventRegistry.get(channelKey);
if (!channel) {
@@ -1308,42 +1476,7 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
sessions: new Map(),
};
eventSource.addEventListener('change', (event) => {
const payload = parseLocalFolderEventPayload(event);
if (!payload) return;
Array.from(channel.sessions.values()).forEach((targetSession) => {
if (!targetSession || targetSession.views.size === 0) return;
const documentId = typeof payload.documentId === 'string' ? payload.documentId.trim() : '';
const relativePath = typeof payload.relativePath === 'string' ? payload.relativePath.trim() : '';
if (targetSession.sessionKind === 'resource') {
if (!relativePath || relativePath !== resourceWatchPath(targetSession)) return;
if (targetSession.saving) {
targetSession.externalChangePending = false;
targetSession.lastSelfSaveSignalAt = Date.now();
return;
}
targetSession.lastExternalChangeSignalAt = Date.now();
targetSession.externalChangePending = true;
scheduleSessionExternalRefresh(targetSession, 'mnote-web-local-resource-watch');
return;
}
if (!documentId) return;
const eventKind = String(payload.eventKind || '');
const targetsCurrentDocument = Boolean(documentId && documentId === targetSession.documentId);
if (documentId && !targetsCurrentDocument) return;
if (targetsCurrentDocument && shouldSuppressLocalFolderSelfChange(documentId, eventKind)) {
targetSession.externalChangePending = false;
targetSession.lastSelfSaveSignalAt = Date.now();
return;
}
if (targetsCurrentDocument && targetSession.saving) {
targetSession.externalChangePending = false;
targetSession.lastSelfSaveSignalAt = Date.now();
return;
}
targetSession.lastExternalChangeSignalAt = Date.now();
targetSession.externalChangePending = true;
scheduleSessionExternalRefresh(targetSession, 'mnote-web-local-folder-watch');
});
handleLocalFolderEventPayloadForSessions(channel, parseLocalFolderEventPayload(event));
});
eventSource.onerror = () => {
console.warn('mnote local folder 外部更新事件流中断,将等待浏览器自动重连');
@@ -1609,7 +1742,7 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
const keyRevision = revisionFromConflictKey(conflictDetectionKey);
const sourceKind = normalizeSessionSourceKind(runtimeDescriptor.bootstrap);
const tiptapDocument = pageBodyTiptapDocument(pageBody, '', runtimeDescriptor.bootstrap);
const pageBodySource = pageBodyTiptapDocumentSource(pageBody, '');
const pageBodyMeta = pageBodySourceMeta(sourceKind, pageBody);
const session = {
key: buildDocumentSessionKey(runtimeDescriptor.bootstrap),
documentId: runtimeDescriptor.bootstrap.documentId,
@@ -1620,9 +1753,11 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
pageAggregateScriptId: runtimeDescriptor.bootstrap.pageAggregateScriptId || '__MNOTE_PAGE_AGGREGATE__',
latestAggregate: runtimeDescriptor.aggregate,
title: runtimeDescriptor.aggregate.head?.title || '无标题',
pageBodySource,
projectionSource: String(pageBody.projectionSource || pageBody.projection_source || '').trim(),
blockProjectionVersion: String(pageBody.blockProjectionVersion || pageBody.block_projection_version || '').trim(),
pageBodySource: pageBodyMeta.pageBodySource,
projectionSource: pageBodyMeta.projectionSource,
blockProjectionVersion: pageBodyMeta.blockProjectionVersion,
pageBodyLocalCompatFallback: pageBodyMeta.localCompatFallback,
pageBodyHardGuard: pageBodyMeta.hardGuard,
currentTiptapDocument: tiptapDocument,
currentSerialized: JSON.stringify(tiptapDocument),
lastPersistedSerialized: JSON.stringify(tiptapDocument),
@@ -0,0 +1,355 @@
(function(){
'use strict';
if (window.__mnoteLocalFolderEventBus) {
return;
}
var connections = new Map();
var sidebarRefreshQueues = new Map();
var lastSource = '';
var lastReason = '';
function root() {
return document.documentElement;
}
function setDiagnostics(source, reason) {
lastSource = source || lastSource || '';
lastReason = reason || lastReason || '';
root().setAttribute('data-mnote-local-folder-event-bus', 'ready');
root().setAttribute('data-mnote-local-folder-event-bus-connections', String(connections.size));
root().setAttribute('data-mnote-local-folder-event-bus-last-source', lastSource);
root().setAttribute('data-mnote-local-folder-event-bus-last-reason', lastReason);
}
function normalizeRootUri(rootUri) {
return String(rootUri || '').trim();
}
function normalizeWorkspaceId(workspaceId) {
return String(workspaceId || 'default').trim() || 'default';
}
function emit(name, detail) {
window.dispatchEvent(new CustomEvent(name, { detail: detail || {} }));
}
function parseEventPayload(event) {
try {
return JSON.parse((event && event.data) || '{}') || {};
} catch (_) {
return {};
}
}
function revisionOf(payload, event) {
return payload.revision || payload.cursor || (event && event.lastEventId) || null;
}
function arrayOf(value) {
if (!value) return [];
if (Array.isArray(value)) return value.filter(Boolean).map(String);
return [String(value)].filter(Boolean);
}
function pathArrayOf(value) {
if (!value) return [];
var list = Array.isArray(value) ? value : [value];
return list.map(function(item) {
if (typeof item === 'string') return item.trim();
if (!item || typeof item !== 'object') return '';
return String(item.relativePath || item.relative_path || item.path || item.sourcePath || item.source_path || '').trim();
}).filter(Boolean);
}
function collectChangedPaths(payload) {
var paths = []
.concat(pathArrayOf(payload.changedPaths))
.concat(pathArrayOf(payload.changed_paths))
.concat(pathArrayOf(payload.paths));
var events = Array.isArray(payload.events) ? payload.events : [];
events.forEach(function(item) {
if (!item || typeof item !== 'object') return;
paths = paths
.concat(arrayOf(item.path))
.concat(arrayOf(item.relativePath))
.concat(arrayOf(item.relative_path))
.concat(arrayOf(item.sourcePath))
.concat(arrayOf(item.source_path));
});
return Array.from(new Set(paths));
}
function collectChangedPathItems(payload, reason) {
var raw = []
.concat(Array.isArray(payload.changedPaths) ? payload.changedPaths : [])
.concat(Array.isArray(payload.changed_paths) ? payload.changed_paths : [])
.concat(Array.isArray(payload.paths) ? payload.paths : [])
.concat(Array.isArray(payload.events) ? payload.events : []);
var seen = new Set();
var items = [];
raw.forEach(function(item) {
var relativePath = typeof item === 'string'
? item.trim()
: String(item && (item.relativePath || item.relative_path || item.path || item.sourcePath || item.source_path || '') || '').trim();
if (!relativePath || seen.has(relativePath)) return;
seen.add(relativePath);
items.push({
relativePath: relativePath,
reason: reason || 'event-bus',
kind: typeof item === 'object' && item ? String(item.kind || '') : '',
eventKind: typeof item === 'object' && item ? String(item.eventKind || item.event_kind || item.changeType || item.change_type || '') : ''
});
});
if (items.length) return items;
return pathItems(collectChangedPaths(payload), reason);
}
function collectAffectedParents(payload) {
var parents = []
.concat(pathArrayOf(payload.affectedParents))
.concat(pathArrayOf(payload.affected_parents))
.concat(pathArrayOf(payload.parentRelativePaths))
.concat(pathArrayOf(payload.parent_relative_paths));
return Array.from(new Set(parents));
}
function pathItems(paths, reason) {
return (paths || []).map(function(relativePath) {
return {
relativePath: String(relativePath || '').trim(),
reason: reason || 'event-bus'
};
}).filter(function(item) { return item.relativePath || item.relativePath === ''; });
}
function queueSidebarRefresh(detail) {
var key = String(detail.rootUri || '').trim();
if (!key) return;
var queue = sidebarRefreshQueues.get(key);
if (!queue) {
queue = {
rootUri: detail.rootUri,
workspaceId: detail.workspaceId,
revisions: new Set(),
reasons: new Set(),
changedPaths: new Map(),
affectedParents: new Set(),
resyncRequired: false,
timer: 0
};
sidebarRefreshQueues.set(key, queue);
}
if (detail.revision) queue.revisions.add(String(detail.revision));
if (detail.reason) queue.reasons.add(String(detail.reason));
(detail.changedPaths || []).forEach(function(item) {
var path = typeof item === 'string' ? item : String(item && (item.relativePath || item.relative_path || '') || '');
if (!path && path !== '') return;
var existing = queue.changedPaths.get(path) || { relativePath: path };
queue.changedPaths.set(path, {
relativePath: path,
reason: String((item && item.reason) || existing.reason || detail.reason || 'event-bus-orchestrated'),
kind: String((item && item.kind) || existing.kind || ''),
eventKind: String((item && item.eventKind) || (item && item.event_kind) || existing.eventKind || '')
});
});
(detail.affectedParents || []).forEach(function(item) {
var parent = typeof item === 'string' ? item : String(item && (item.relativePath || item.relative_path || '') || '');
if (parent || parent === '') queue.affectedParents.add(parent);
});
queue.resyncRequired = queue.resyncRequired || detail.resyncRequired === true;
if (!queue.timer) {
queue.timer = window.setTimeout(function() {
flushSidebarRefresh(key);
}, 0);
}
root().setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-pending', String(queue.affectedParents.size));
}
function flushSidebarRefresh(key) {
var queue = sidebarRefreshQueues.get(key);
if (!queue) return;
sidebarRefreshQueues.delete(key);
var reasons = Array.from(queue.reasons);
var detail = {
schema: 'mnote.local_folder.event_bus.sidebar_refresh.v1',
source: 'event_bus_orchestrator',
reason: reasons.join(',') || 'watch_batch',
rootUri: queue.rootUri,
workspaceId: queue.workspaceId,
revision: Array.from(queue.revisions).pop() || null,
reasons: reasons,
changedPaths: Array.from(queue.changedPaths.values()),
affectedParents: pathItems(Array.from(queue.affectedParents), 'event-bus-orchestrated'),
resyncRequired: queue.resyncRequired,
viaEventBus: true
};
root().setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-applied', detail.reason);
root().setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-parents', String(detail.affectedParents.length));
emit('mnote:local-folder:sidebar-refresh-requested', detail);
}
function dispatchWatchBatch(entry, event, payload, meta) {
var revision = revisionOf(payload, event);
var changedPaths = collectChangedPaths(payload);
var affectedParents = collectAffectedParents(payload);
var source = String((meta && meta.source) || payload.source || 'watcher_sse').trim() || 'watcher_sse';
var reason = String((meta && meta.reason) || payload.reason || 'watch_batch').trim() || 'watch_batch';
var resyncRequired = payload.fallbackResync === true
|| payload.requiresResync === true
|| payload.resyncRequired === true
|| payload.resync_required === true;
var detail = {
schema: 'mnote.local_folder.event_bus.watch_batch.v1',
source: source,
reason: reason,
rootUri: entry.rootUri,
workspaceId: entry.workspaceId,
revision: revision,
payload: payload,
bootstrap: entry.bootstrap || null,
changedPaths: collectChangedPathItems(payload, reason),
affectedParents: pathItems(affectedParents, reason),
resyncRequired: resyncRequired,
viaEventBus: true
};
setDiagnostics(source, reason);
root().setAttribute('data-mnote-tree-live-revision', String(revision || ''));
emit('mnote:local-folder:watch-batch', detail);
emit('tree:local-folder-watch-batch', detail);
if (affectedParents.length > 0) {
emit('mnote:local-folder:filetree-parent-changed', detail);
}
if (changedPaths.length > 0) {
emit('mnote:local-folder:document-changed', detail);
emit('mnote:local-folder:resource-changed', detail);
emit('mnote:local-folder:knowledge-rag-source-updated', detail);
}
queueSidebarRefresh(detail);
if (resyncRequired) {
emit('mnote:local-folder:resync-required', detail);
}
}
function closeEntry(entry) {
if (!entry || !entry.source || typeof entry.source.close !== 'function') return;
entry.source.close();
}
function startLocalFolderWatcher(options) {
var rootUri = normalizeRootUri(options && options.rootUri);
if (!rootUri || typeof window.EventSource !== 'function') return null;
var workspaceId = normalizeWorkspaceId(options && options.workspaceId);
var key = rootUri;
if (connections.has(key)) {
var existing = connections.get(key);
if ((!existing.workspaceId || existing.workspaceId === 'default') && workspaceId && workspaceId !== 'default') {
existing.workspaceId = workspaceId;
if (existing.handle) existing.handle.workspaceId = workspaceId;
}
setDiagnostics(existing.lastSource || 'watcher_sse', 'reuse_connection');
return existing.handle;
}
var url = new URL('/api/local-folder/events', window.location.origin);
url.searchParams.set('rootUri', rootUri);
url.searchParams.set('treeLive', 'true');
var eventSource = new EventSource(url.toString());
var entry = {
key: key,
rootUri: rootUri,
workspaceId: workspaceId,
bootstrap: (options && options.bootstrap) || null,
source: eventSource,
lastSource: 'watcher_sse',
close: function() {
connections.delete(key);
closeEntry(entry);
setDiagnostics('watcher_sse', 'closed');
}
};
entry.handle = {
key: key,
rootUri: rootUri,
workspaceId: workspaceId,
source: eventSource,
close: entry.close
};
connections.set(key, entry);
setDiagnostics('watcher_sse', 'connect');
emit('mnote:local-folder:event-bus-ready', {
schema: 'mnote.local_folder.event_bus.ready.v1',
source: 'watcher_sse',
reason: 'connect',
rootUri: rootUri,
workspaceId: workspaceId,
connections: connections.size
});
eventSource.addEventListener('open', function() {
setDiagnostics('watcher_sse', 'open');
});
eventSource.addEventListener('watch_batch', function(event) {
dispatchWatchBatch(entry, event, parseEventPayload(event), {
source: 'watcher_sse',
reason: 'watch_batch'
});
});
eventSource.addEventListener('tree_error', function(event) {
var payload = parseEventPayload(event);
setDiagnostics('watcher_sse', 'tree_error');
emit('tree:error', { payload: payload, bootstrap: entry.bootstrap || null, viaEventBus: true });
});
eventSource.onerror = function() {
setDiagnostics('watcher_sse', 'error');
};
return entry.handle;
}
function emitSyntheticWatchBatch(detail) {
var payload = detail && detail.payload ? detail.payload : (detail || {});
var rootUri = normalizeRootUri(detail && detail.rootUri);
var workspaceId = normalizeWorkspaceId(detail && detail.workspaceId);
var entry = {
rootUri: rootUri,
workspaceId: workspaceId,
bootstrap: (detail && detail.bootstrap) || null
};
dispatchWatchBatch(entry, { lastEventId: detail && detail.revision }, payload, {
source: (detail && detail.source) || 'synthetic',
reason: (detail && detail.reason) || payload.source || 'synthetic_watch_batch'
});
}
function closeAll() {
Array.from(connections.values()).forEach(function(entry) {
closeEntry(entry);
});
connections.clear();
setDiagnostics('watcher_sse', 'closed');
}
window.__mnoteLocalFolderEventBus = {
startLocalFolderWatcher: startLocalFolderWatcher,
emitSyntheticWatchBatch: emitSyntheticWatchBatch,
closeAll: closeAll,
flushSidebarRefresh: function(rootUri) {
flushSidebarRefresh(normalizeRootUri(rootUri));
},
connectionCount: function() { return connections.size; },
diagnostics: function() {
return {
connections: connections.size,
lastSource: lastSource,
lastReason: lastReason
};
}
};
setDiagnostics('', 'ready');
})();
@@ -413,23 +413,8 @@ async function uploadLocalFolderAsset(file, plan, context) {
}
async function uploadMediaAsset(file, plan, context) {
var form = new FormData();
form.append('file', file);
form.append('workspaceId', plan && plan.workspaceId || '');
form.append('documentId', plan && plan.targetDocumentId || '');
if (plan && plan.targetMindmapId) form.append('mindmapId', plan.targetMindmapId);
var response = await fetchWithTimeout('/api/media/upload', {
method: 'POST',
credentials: 'include',
body: form
}, Number(context && context.timeoutMs) || 15000, '上传');
var payload = await response.json().catch(function() { return null; });
if (!response.ok || !payload || !payload.asset) {
throw new Error(payload && payload.error ? payload.error : '上传失败');
}
return {
asset: payload.asset
};
document.documentElement.setAttribute('data-mnote-media-upload-retired', 'true');
throw new Error('旧 Convex Files 上传链已退役;local-first 附件请使用本地文件夹上传目标。');
}
async function insertUploadedAssetIntoEditor(asset, targetRoot, deps) {
@@ -749,23 +749,7 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
};
}
}
if (assetId) {
var response = await fetch('/api/media/sign?assetId=' + encodeURIComponent(assetId), {
method: 'GET',
credentials: 'include',
cache: 'no-store'
});
var payload = await response.json().catch(function() { return null; });
if (!response.ok || !payload) {
throw new Error(payload && payload.error ? payload.error : '生成签名链接失败');
}
var signedUrl = String(payload && payload.signedUrl || '').trim();
if (!signedUrl) throw new Error('附件链接不可用');
return {
url: signedUrl,
asset: payload.asset && typeof payload.asset === 'object' ? payload.asset : {}
};
}
if (assetId) document.documentElement.setAttribute('data-mnote-media-sign-retired', 'true');
var url = String(detail && (detail.fileUrl || detail.href) || '').trim();
if (!url) throw new Error('附件链接不可用');
return { url: url, asset: {} };
@@ -855,20 +839,7 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
return;
}
}
if (detail.assetId) {
try {
var response = await fetch('/api/media/sign?assetId=' + encodeURIComponent(detail.assetId), {
method: 'GET',
credentials: 'include'
});
var payload = await response.json().catch(function() { return null; });
var signedUrl = String(payload && payload.signedUrl || '').trim();
if (response.ok && signedUrl) {
window.open(signedUrl, '_blank', 'noopener,noreferrer');
return;
}
} catch (_) {}
}
if (detail.assetId) document.documentElement.setAttribute('data-mnote-media-sign-retired', 'true');
var localRelativePath = String(detail.localRelativePath || '').trim();
if (localRelativePath) {
var localPathDownloadUrl = buildLocalFileOpenUrl(localRelativePath, true);
@@ -380,11 +380,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
callback();
}
function isLocalOcrSourceFileName(fileName) {
return /\.(png|jpe?g|webp|gif|bmp|tiff?|pdf)$/i.test(String(fileName || '').trim());
}
function localOcrSourceRelativePath(detail) {
function knowledgeRagSourceRelativePath(detail) {
var workspacePath = detail && detail.workspacePath && typeof detail.workspacePath === 'object' ? detail.workspacePath : null;
var relativePath = String(
detail && detail.localRelativePath
@@ -395,7 +391,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
return relativePath.replace(/^\/+/, '');
}
function localOcrRootUri(detail, trigger) {
function knowledgeRagRootUri(detail, trigger) {
var workspacePath = detail && detail.workspacePath && typeof detail.workspacePath === 'object' ? detail.workspacePath : null;
var rootUri = String(
detail && (detail.localRootUri || detail.rootUri)
@@ -409,39 +405,15 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
return rootUri || currentRootUri() || '';
}
function supportsLocalOcr(detail) {
var path = localOcrSourceRelativePath(detail);
var title = String(detail && (detail.title || detail.fileName) || '').trim() || path.split('/').pop() || '';
return Boolean(path && localOcrRootUri(detail, null) && isLocalOcrSourceFileName(title || path));
}
function localOcrProvider() {
var override = String(window.__MNOTE_LOCAL_OCR_PROVIDER || '').trim().toLowerCase();
return override === 'mock' ? 'mock' : 'mineru';
}
async function runLocalOcrForDetail(detail, trigger) {
document.documentElement.setAttribute('data-mnote-local-ocr-menu-status', 'running');
var payload = await ingestKnowledgeRagForDetail(detail, trigger).catch(function(error) {
document.documentElement.setAttribute('data-mnote-local-ocr-menu-status', 'failed');
throw error;
});
var sourceRootRelativePath = localOcrSourceRelativePath(detail)
|| String(detail && (detail.localRelativePath || detail.path || detail.title) || '').trim();
var rootUri = localOcrRootUri(detail, trigger);
var status = payload && payload.retryRequired ? 'retry' : 'done';
document.documentElement.setAttribute('data-mnote-local-ocr-menu-status', status);
document.documentElement.setAttribute('data-mnote-local-ocr-menu-path', sourceRootRelativePath);
window.dispatchEvent(new CustomEvent('mnote:local-ocr-job-updated', {
detail: { status: status, job: payload, rootUri: rootUri, sourceRootRelativePath: sourceRootRelativePath }
}));
return payload;
function supportsKnowledgeRagSource(detail) {
var path = knowledgeRagSourceRelativePath(detail);
return Boolean(path && knowledgeRagRootUri(detail, null));
}
async function ingestKnowledgeRagForDetail(detail, trigger) {
var sourceRootRelativePath = localOcrSourceRelativePath(detail)
var sourceRootRelativePath = knowledgeRagSourceRelativePath(detail)
|| String(detail && (detail.localRelativePath || detail.path || detail.title) || '').trim();
var rootUri = localOcrRootUri(detail, trigger);
var rootUri = knowledgeRagRootUri(detail, trigger);
if (!sourceRootRelativePath || !rootUri) {
throw new Error('缺少资料库来源或 rootUri');
}
@@ -473,17 +445,6 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
function handleTreeContextMenuAction(action, detail, trigger) {
closeTreeContextMenu();
detail = detail || {};
if (action === 'local-ocr') {
recordFileTreeAction('knowledge-rag-index', detail);
recordFileTreeActionStatus('pending', detail);
void runLocalOcrForDetail(detail, trigger).then(function(job) {
recordFileTreeActionStatus(job && job.retryRequired ? 'retry' : 'done', detail);
}).catch(function(error) {
recordFileTreeActionStatus('failed', Object.assign({}, detail, { fallback: 'alert' }));
window.alert(error && error.message ? error.message : '资料库索引失败');
});
return;
}
if (action === 'knowledge-rag-index') {
recordFileTreeAction('knowledge-rag-index', detail);
recordFileTreeActionStatus('pending', detail);
@@ -954,7 +915,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
menu.setAttribute('data-command-context-target-kind', String(ctx['tree.targetResourceKind'] || ''));
menu.setAttribute('data-command-context-editor-dirty', String(ctx['editor.dirty'] === true));
menu.setAttribute('data-command-context-ai-can-write', String(ctx['ai.canWrite'] === true));
var localOcrSupported = supportsLocalOcr(detail);
var knowledgeRagSupported = supportsKnowledgeRagSource(detail);
var items = isAttachment ? [
{ action: 'duplicate', icon: 'file_copy', label: '拷贝副本', shortcut: 'Ctrl + D' },
{ action: 'delete-trash', icon: 'delete', label: '删除', shortcut: 'Del', danger: true, destructive: true, requiresApproval: true, when: '!workspace.readonly' },
@@ -1017,7 +978,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
{ separator: true },
{ action: 'delete-trash', icon: 'delete', label: '删除', shortcut: 'Del', danger: true, destructive: true, requiresApproval: true, when: '!workspace.readonly && !editor.dirty' }
];
if (currentSourceKind() === 'local_folder' && detail.rowKind !== 'index') {
if (currentSourceKind() === 'local_folder' && detail.rowKind !== 'index' && knowledgeRagSupported) {
var ragItem = { action: 'knowledge-rag-index', icon: 'travel_explore', label: '加入资料库索引', when: '!workspace.readonly' };
var insertAt = isAttachment ? 11 : isAsset ? 3 : 3;
if (insertAt >= 0) items.splice(insertAt, 0, ragItem);
@@ -479,92 +479,8 @@ export const createSidebarFileTreeOpenRuntime = (dependencies = {}) => {
navigateToMindmapObject(documentId, assetId, String(detail.workspaceId || '').trim());
return;
}
try {
var response = await fetch('/api/media/sign?assetId=' + encodeURIComponent(assetId), {
method: 'GET',
credentials: 'include'
});
var payload = await response.json().catch(function() { return null; });
if (!response.ok) {
throw new Error(payload && payload.error ? payload.error : '生成签名链接失败');
}
var asset = payload && payload.asset && typeof payload.asset === 'object' ? payload.asset : {};
var fileUrl = String(payload && payload.signedUrl || asset.file_url || asset.signed_url || '').trim();
if (!fileUrl) throw new Error('附件链接不可用');
var fileName = String(asset.file_name || detail.fileName || '未命名资源').trim() || '未命名资源';
var fileType = inferOnlyOfficeFileType(fileName, asset.mime_type);
if (fileType) {
var userId = await fetchCurrentOnlyOfficeUserId();
var officeUrl = buildOnlyOfficeOpenUrl({
fileUrl: fileUrl,
fileName: fileName,
fileType: fileType,
assetId: assetId,
documentId: String(asset.document_id || detail.documentId || '').trim(),
userId: userId,
mode: forceEditMode ? 'edit' : 'view'
});
if (typeof window.__mnoteDocumentPaneRuntime?.openResourceInActiveTab === 'function') {
var didOpen = await window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({
objectIdentity: resourceObjectIdentityFromWorkspacePath({
workspacePath: detailWorkspacePath,
objectKind: 'only_office',
documentId: String(asset.document_id || detail.documentId || '').trim(),
assetId: assetId
}),
assetId: assetId,
title: fileName,
fileName: fileName,
kind: 'office',
officeUrl: officeUrl,
documentId: String(asset.document_id || detail.documentId || '').trim(),
workspaceId: String(detail.workspaceId || '').trim(),
workspacePath: detailWorkspacePath
});
if (didOpen) return;
}
window.open(officeUrl, '_blank', 'noopener,noreferrer');
return;
}
if (isPdfAttachmentFileName(fileName)) {
var pdfPreviewUrl = buildPdfPreviewOpenUrl(fileUrl, fileName);
if (typeof window.__mnoteDocumentPaneRuntime?.openResourceInActiveTab === 'function') {
var didOpenPdf = await window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({
objectIdentity: resourceObjectIdentityFromWorkspacePath({
workspacePath: detailWorkspacePath,
objectKind: 'pdf',
documentId: String(asset.document_id || detail.documentId || '').trim(),
assetId: assetId
}),
assetId: assetId,
title: fileName,
fileName: fileName,
kind: 'pdf',
href: pdfPreviewUrl,
documentId: String(asset.document_id || detail.documentId || '').trim(),
workspaceId: String(detail.workspaceId || '').trim(),
workspacePath: detailWorkspacePath
});
if (didOpenPdf) return;
}
window.open(pdfPreviewUrl || fileUrl, '_blank', 'noopener,noreferrer');
return;
}
if (isCodeAttachmentFileName(fileName) && String(asset.document_id || detail.documentId || '').trim() === currentDocumentId()) {
await openCodeEditorAttachment({
href: fileUrl,
fileUrl: fileUrl,
fileName: fileName,
assetId: assetId,
documentId: String(asset.document_id || detail.documentId || '').trim(),
fileSize: uploadedFileSize(asset)
});
return;
}
window.open(fileUrl, '_blank', 'noopener,noreferrer');
} catch (error) {
window.alert(error && error.message ? error.message : '打开附件失败');
}
document.documentElement.setAttribute('data-mnote-media-sign-retired', 'true');
window.alert('旧 Convex Files 附件签名链已退役;local-first 附件请通过本地文件夹资源打开。');
}
window.addEventListener('tree.asset.open', function(event) {
@@ -469,7 +469,6 @@ export function createSidebarPageAiRuntime(context) {
const pageAiWorkspacePathForTarget = (...args) => pageAiTargetRuntime.pageAiWorkspacePathForTarget(...args);
const pageAiBuildAllowedRoots = (...args) => pageAiTargetRuntime.pageAiBuildAllowedRoots(...args);
const pageAiBuildContextRefs = (...args) => pageAiTargetRuntime.pageAiBuildContextRefs(...args);
const pageAiEnrichOcrContextRefs = (...args) => pageAiTargetRuntime.pageAiEnrichOcrContextRefs(...args);
const pageAiBuildRunTargetSnapshot = (...args) => pageAiTargetRuntime.pageAiBuildRunTargetSnapshot(...args);
const pageAiPageContextForRefs = (...args) => pageAiTargetRuntime.pageAiPageContextForRefs(...args);
const pageAiSetRunTargetSnapshot = (...args) => pageAiTargetRuntime.pageAiSetRunTargetSnapshot(...args);
@@ -1085,19 +1084,29 @@ export function createSidebarPageAiRuntime(context) {
return index === list.findIndex(function(candidate) { return candidate.relativePath === item.relativePath; });
});
if (changedPaths.length) {
var rootUri = String((receipt && receipt.rootUri) || (fallbackAudit && fallbackAudit.rootUri) || currentRootUri() || '');
var syntheticPayload = {
schema: 'mnote.local_folder.watch_batch.v1',
source: 'agent_run_receipt',
runId: runId,
rootUri: rootUri,
changedPaths: changedPaths,
affectedParents: affectedParents
};
document.documentElement.setAttribute('data-mnote-page-ai-receipt-filetree-refresh', 'true');
window.dispatchEvent(new CustomEvent('tree:local-folder-watch-batch', {
detail: {
payload: {
schema: 'mnote.local_folder.watch_batch.v1',
source: 'agent_run_receipt',
runId: runId,
rootUri: String((receipt && receipt.rootUri) || (fallbackAudit && fallbackAudit.rootUri) || currentRootUri() || ''),
changedPaths: changedPaths,
affectedParents: affectedParents
}
}
}));
if (window.__mnoteLocalFolderEventBus && typeof window.__mnoteLocalFolderEventBus.emitSyntheticWatchBatch === 'function') {
window.__mnoteLocalFolderEventBus.emitSyntheticWatchBatch({
source: 'synthetic_page_ai_receipt',
reason: 'agent_run_receipt',
rootUri: rootUri,
workspaceId: resolveWorkspaceId(document.body),
payload: syntheticPayload
});
} else {
window.dispatchEvent(new CustomEvent('tree:local-folder-watch-batch', {
detail: { payload: syntheticPayload }
}));
}
}
}
if (refresh.touchesCurrentFile === true) {
@@ -1975,11 +1984,6 @@ export function createSidebarPageAiRuntime(context) {
var allowedRoots = pageAiBuildAllowedRoots();
var agentTargetPackage = pageAiBuildAgentTargetPackage(scopedContext, runTargetSnapshot);
assertPageAiLocalWritePermission(prompt, agentTargetPackage);
if (typeof pageAiEnrichOcrContextRefs === 'function') {
var ocrContext = await pageAiEnrichOcrContextRefs(contextRefs, agentTargetPackage, scopedContext.editorTarget);
contextRefs = ocrContext.contextRefs || contextRefs;
agentTargetPackage = ocrContext.agentTargetPackage || agentTargetPackage;
}
if (scopedContext.pageContext && scopedContext.pageContext.aiContext) {
scopedContext.pageContext.aiContext.runTargetSnapshot = runTargetSnapshot;
scopedContext.pageContext.aiContext.agentTargetPackage = agentTargetPackage;
@@ -427,48 +427,6 @@ export function createSidebarPageAiTargetRuntime(context) {
});
}
function pageAiOcrEligibleResourceKind(value) {
var normalized = String(value || '').trim().toLowerCase();
return normalized === 'image' || normalized === 'pdf' || normalized === 'attachment' || normalized === 'resource';
}
function pageAiOcrEligiblePath(value) {
var path = String(value || '').trim().toLowerCase();
return /\.(png|jpg|jpeg|webp|bmp|tif|tiff|pdf)$/.test(path);
}
function pageAiOcrBodyPreview(markdown) {
var body = String(markdown || '').replace(/^---\n[\s\S]*?\n---\n?/, '').trim();
return body.slice(0, 1600);
}
async function fetchPageAiOcrSidecarContext(editorTarget) {
return null;
}
async function pageAiEnrichOcrContextRefs(contextRefs, agentTargetPackage, editorTarget) {
var ocrContext = await fetchPageAiOcrSidecarContext(editorTarget);
if (!ocrContext) return { contextRefs, agentTargetPackage };
var nextRefs = pageAiNormalizeArray(contextRefs).map(function(ref) {
if (ref && ref.kind === 'active_editor') return Object.assign({}, ref, { ocrContext: ocrContext });
return ref;
});
var nextPackage = agentTargetPackage && typeof agentTargetPackage === 'object'
? Object.assign({}, agentTargetPackage, { ocrContext: ocrContext })
: agentTargetPackage;
if (nextPackage && nextPackage.currentFile && typeof nextPackage.currentFile === 'object') {
nextPackage.currentFile = Object.assign({}, nextPackage.currentFile, { ocrRootRelativePath: ocrContext.ocrRootRelativePath });
}
if (nextPackage && Array.isArray(nextPackage.targets)) {
nextPackage.targets = nextPackage.targets.map(function(target, index) {
return index === 0 && target && typeof target === 'object'
? Object.assign({}, target, { ocrContext: ocrContext })
: target;
});
}
return { contextRefs: nextRefs, agentTargetPackage: nextPackage };
}
function pageAiBuildAllowedRoots() {
return pageAiNormalizeArray(pageUiState.pageAiAllowedRoots).map(function(root) {
return {
@@ -790,7 +748,6 @@ export function createSidebarPageAiTargetRuntime(context) {
pageAiBuildAgentTargetPackage,
pageAiBuildAllowedRoots,
pageAiBuildContextRefs,
pageAiEnrichOcrContextRefs,
pageAiBuildRunTargetSnapshot,
pageAiCloneJson,
pageAiContextKindsFromRefs,
@@ -67,10 +67,6 @@ export function createSidebarPageSettingsRuntime(context) {
return Object.assign({}, DEFAULT_PAGE_WIDTH_PREFERENCES, pageUiState.pageWidthPreferences || {});
}
function currentLocalOcrPreferences() {
return Object.assign({ 'localOcr.autoEnabled': false }, pageUiState.localOcrPreferences || {});
}
function currentKnowledgeRagSummary() {
return pageUiState.knowledgeRagSummary || {};
}
@@ -340,12 +336,6 @@ export function createSidebarPageSettingsRuntime(context) {
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');
}
var ragTrigger = document.querySelector('[data-testid="mnote-knowledge-rag-settings-toggle"]');
var ragOpen = isKnowledgeRagSettingsOpen();
if (ragTrigger instanceof HTMLElement) {
@@ -391,17 +381,6 @@ export function createSidebarPageSettingsRuntime(context) {
'</label>';
}
function createLocalOcrAutoRow() {
return '' +
'<label class="wolai-page-setting-row" data-page-setting-row="localOcrAutoEnabled">' +
'<span class="wolai-page-setting-copy">' +
'<span class="wolai-page-setting-label">资料库自动索引</span>' +
'<span class="wolai-page-setting-hint">LiteParse/OCR sidecar 已退役;图片、PDF、Office 统一由 LightRAG 资料库处理</span>' +
'</span>' +
'<input type="checkbox" class="wolai-page-setting-checkbox" data-local-ocr-option-checkbox="autoEnabled" />' +
'</label>';
}
function createPageWidthSelectRow(type) {
var options = type === 'default'
? [
@@ -456,11 +435,8 @@ 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 markRetiredLocalOcrPreferenceSurface() {
document.documentElement.setAttribute('data-mnote-local-ocr-auto-retired', 'true');
}
function createPageFontRow() {
@@ -673,10 +649,6 @@ export function createSidebarPageSettingsRuntime(context) {
return popover;
}
function ensureLocalOcrSettingsPopover() {
return ensureKnowledgeRagSettingsPopover();
}
function createKnowledgeRagSettingsPanelHtml() {
return '' +
'<div class="wolai-page-settings-panel mnote-settings-panel mnote-knowledge-rag-settings-panel" role="dialog" aria-modal="false" aria-label="资料库问答设置">' +
@@ -1809,51 +1781,12 @@ export function createSidebarPageSettingsRuntime(context) {
var payload = await response.json().catch(function(){ return null; });
if (!response.ok || !payload || payload.ok !== true) return;
pageUiState.pageWidthPreferences = normalizePageWidthPreferences(payload.result && payload.result.pageWidthPreferences);
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));
markRetiredLocalOcrPreferenceSurface();
applyPageOptionsToShell();
if (isPageSettingsOpen()) renderPageSettingsPopover();
if (isLocalOcrSettingsOpen()) renderLocalOcrOptions(ensureLocalOcrSettingsPopover());
} catch (_) {}
}
async function persistLocalOcrAutoPreference(enabled) {
var previous = currentLocalOcrPreferences();
var next = Object.assign({}, previous, { 'localOcr.autoEnabled': Boolean(enabled) });
pageUiState.localOcrPreferences = next;
window.__MNOTE_LOCAL_OCR_PREFERENCES = Object.assign({}, next);
if (isPageSettingsOpen()) renderPageSettingsPopover();
if (isLocalOcrSettingsOpen()) renderLocalOcrOptions(ensureLocalOcrSettingsPopover());
try {
var response = await fetch('/api/ui/preferences', {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
documentId: currentDocumentId(),
workspaceId: resolveWorkspaceId(document.body),
...currentWorkspaceSourcePayload(),
updates: { 'localOcr.autoEnabled': Boolean(enabled) }
})
});
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_ocr_preference_save_failed_' + response.status);
}
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));
if (isPageSettingsOpen()) renderPageSettingsPopover();
if (isLocalOcrSettingsOpen()) renderLocalOcrOptions(ensureLocalOcrSettingsPopover());
} catch (error) {
pageUiState.localOcrPreferences = previous;
window.__MNOTE_LOCAL_OCR_PREFERENCES = Object.assign({}, previous);
if (isPageSettingsOpen()) renderPageSettingsPopover();
if (isLocalOcrSettingsOpen()) renderLocalOcrOptions(ensureLocalOcrSettingsPopover());
document.documentElement.setAttribute('data-mnote-local-ocr-preference-error', error instanceof Error ? error.message : String(error));
}
}
async function persistPageWidthPreference(type, mode) {
if (PAGE_WIDTH_TYPES.indexOf(type) < 0) return;
var previous = pageUiState.pageWidthPreferences;
@@ -1908,24 +1841,18 @@ export function createSidebarPageSettingsRuntime(context) {
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 isKnowledgeRagSettingsOpen() {
var popover = document.querySelector('[data-testid="mnote-knowledge-rag-settings-popover"]');
return popover instanceof HTMLElement && !popover.hidden;
}
function isAnySettingsOpen() {
return isPageSettingsOpen() || isLocalIndexSettingsOpen() || isLocalOcrSettingsOpen() || isKnowledgeRagSettingsOpen();
return isPageSettingsOpen() || isLocalIndexSettingsOpen() || isKnowledgeRagSettingsOpen();
}
function openPageSettingsPopover(initialTab) {
if (!currentDocumentId()) return;
closeLocalIndexSettingsPopover();
closeLocalOcrSettingsPopover();
closeKnowledgeRagSettingsPopover();
var popover = ensurePageSettingsPopover();
renderPageSettingsPopover();
@@ -1937,7 +1864,6 @@ export function createSidebarPageSettingsRuntime(context) {
function openPageIndexSettingsPopover() {
closePageSettingsPopover();
closeLocalOcrSettingsPopover();
closeKnowledgeRagSettingsPopover();
var popover = ensureLocalIndexSettingsPopover();
renderPageSettingsLocalIndex(popover);
@@ -1946,14 +1872,9 @@ export function createSidebarPageSettingsRuntime(context) {
updateStandaloneSettingsTriggerState();
}
function openLocalOcrSettingsPopover() {
openKnowledgeRagSettingsPopover();
}
function openKnowledgeRagSettingsPopover() {
closePageSettingsPopover();
closeLocalIndexSettingsPopover();
closeLocalOcrSettingsPopover();
var popover = ensureKnowledgeRagSettingsPopover();
renderKnowledgeRagSettings(popover);
popover.hidden = false;
@@ -1978,12 +1899,6 @@ export function createSidebarPageSettingsRuntime(context) {
updateStandaloneSettingsTriggerState();
}
function closeLocalOcrSettingsPopover() {
var popover = document.querySelector('[data-testid="mnote-local-ocr-settings-popover"]');
if (popover instanceof HTMLElement) popover.hidden = true;
updateStandaloneSettingsTriggerState();
}
function closeKnowledgeRagSettingsPopover() {
var popover = document.querySelector('[data-testid="mnote-knowledge-rag-settings-popover"]');
if (popover instanceof HTMLElement) popover.hidden = true;
@@ -1993,7 +1908,6 @@ export function createSidebarPageSettingsRuntime(context) {
function closeAllSettingsPopovers() {
closePageSettingsPopover();
closeLocalIndexSettingsPopover();
closeLocalOcrSettingsPopover();
closeKnowledgeRagSettingsPopover();
}
@@ -2006,10 +1920,6 @@ export function createSidebarPageSettingsRuntime(context) {
applyPageOptionsToShell();
});
window.addEventListener('mnote:open-local-ocr-settings', function() {
openLocalOcrSettingsPopover();
});
window.addEventListener('mnote:knowledge-rag-source-updated', function() {
if (isKnowledgeRagSettingsOpen()) void loadKnowledgeRagStatus(true);
});
@@ -2021,32 +1931,26 @@ export function createSidebarPageSettingsRuntime(context) {
closeKnowledgeRagSettingsPopover,
closePageHistoryDrawer,
closeLocalIndexSettingsPopover,
closeLocalOcrSettingsPopover,
closePageSettingsPopover,
closePageShareDialog,
currentLocalOcrPreferences,
currentKnowledgeRagSummary,
currentPageOptions,
ensureHistorySnapshotsSeeded,
ensureKnowledgeRagSettingsPopover,
ensureLocalIndexSettingsPopover,
ensureLocalOcrSettingsPopover,
ensurePageHistoryDrawer,
ensurePageSettingsPopover,
ensurePageShareDialog,
isAnySettingsOpen,
isKnowledgeRagSettingsOpen,
isLocalIndexSettingsOpen,
isLocalOcrSettingsOpen,
isPageSettingsOpen,
openKnowledgeRagSettingsPopover,
openPageHistoryDrawer,
openLocalOcrSettingsPopover,
openPageSettingsPopover,
openPageIndexSettingsPopover,
openPageShareDialog,
pageOptionIsSupported,
persistLocalOcrAutoPreference,
persistLocalIndexSettings,
persistPageOptionsPatch,
persistPageWidthPreference,
@@ -2004,7 +2004,17 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
setTreeLiveApplyError('tree_resync_missing_projection_payload');
});
window.addEventListener('mnote:local-folder:sidebar-refresh-requested', function(event) {
var detail = event.detail || {};
document.documentElement.setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-received', String(detail.reason || 'event-bus'));
applyLocalFolderWatchBatch(detail);
});
window.addEventListener('tree:local-folder-watch-batch', function(event) {
if (event.detail && event.detail.viaEventBus === true) {
document.documentElement.setAttribute('data-mnote-local-folder-watch-batch-skipped', 'event-bus-orchestrated');
return;
}
var payload = event.detail && event.detail.payload ? event.detail.payload : event.detail;
applyLocalFolderWatchBatch(payload);
});
@@ -209,7 +209,6 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
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 openKnowledgeRagSettingsPopover = (...args) => sidebarPageSettings.openKnowledgeRagSettingsPopover(...args);
const openPageShareDialog = (...args) => sidebarPageSettings.openPageShareDialog(...args);
const pageOptionIsSupported = (...args) => sidebarPageSettings.pageOptionIsSupported(...args);
@@ -223,7 +222,6 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
const pruneKnowledgeRagRegistry = (...args) => sidebarPageSettings.pruneKnowledgeRagRegistry(...args);
const setKnowledgeRagSourceFilter = (...args) => sidebarPageSettings.setKnowledgeRagSourceFilter(...args);
const useKnowledgeRagFileTreeSelection = (...args) => sidebarPageSettings.useKnowledgeRagFileTreeSelection(...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);
@@ -477,7 +475,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
return title && title.textContent ? title.textContent.trim() : (fallback || '文件夹');
}
function isLocalOcrMarkdownPath(relativePath) {
function isRetiredOcrSidecarMarkdownPath(relativePath) {
var normalized = String(relativePath || '').trim().replace(/\\/g, '/').replace(/^\/+/, '');
return /(^|\/)[^/]+\.ocr\/[^/]+\.ocr\.md$/i.test(normalized);
}
@@ -1298,24 +1296,8 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
applyEditorAttachmentMeta(link, attachmentMetaCache[assetId]);
return;
}
attachmentMetaPending[assetId] = fetch('/api/media/sign?assetId=' + encodeURIComponent(assetId), {
method: 'GET',
credentials: 'include',
cache: 'no-store'
}).then(function(response) {
return response.json().catch(function() { return null; }).then(function(payload) {
if (!response.ok || !payload) return null;
var asset = payload.asset && typeof payload.asset === 'object' ? payload.asset : {};
var meta = {
assetId: assetId,
fileSize: uploadedFileSize(asset)
};
attachmentMetaCache[assetId] = meta;
return meta;
});
}).catch(function() {
return null;
}).finally(function() {
document.documentElement.setAttribute('data-mnote-media-sign-retired', 'true');
attachmentMetaPending[assetId] = Promise.resolve(null).finally(function() {
delete attachmentMetaPending[assetId];
});
try {
@@ -1899,20 +1881,8 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
throw new Error('上传失败');
}
} else {
var form = new FormData();
form.append('file', file);
form.append('workspaceId', plan.workspaceId);
form.append('documentId', plan.targetDocumentId);
if (plan.targetMindmapId) form.append('mindmapId', plan.targetMindmapId);
var response = await fetchWithTimeout('/api/media/upload', {
method: 'POST',
credentials: 'include',
body: form
}, 15000, '上传');
payload = await response.json().catch(function() { return null; });
if (!response.ok || !payload || !payload.asset) {
throw new Error(payload && payload.error ? payload.error : '上传失败');
}
document.documentElement.setAttribute('data-mnote-media-upload-retired', 'true');
throw new Error('旧 Convex Files 上传链已退役;local-first 附件请使用本地文件夹上传目标。');
}
appendUploadedAssetRow(payload.asset, plan.targetDocumentId);
if (options && options.insertIntoEditor) {
@@ -2695,20 +2665,6 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
return;
}
var ocrTaskTrigger = closestAction(e.target, '[data-mnote-action="toggle-ocr-tasks"]');
if (ocrTaskTrigger) {
e.preventDefault();
openKnowledgeRagSettingsPopover();
return;
}
var ocrSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-ocr-settings"]');
if (ocrSettingsTrigger) {
e.preventDefault();
openKnowledgeRagSettingsPopover();
return;
}
var settingsClose = closestAction(e.target, '[data-settings-action="close"]');
if (settingsClose) {
e.preventDefault();
@@ -2750,17 +2706,6 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
}
}
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 knowledgeRagAction = closestAction(e.target, '[data-knowledge-rag-action]');
if (knowledgeRagAction) {
e.preventDefault();
@@ -3042,14 +2987,14 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
if (e.shiftKey || e.ctrlKey || e.metaKey) {
return;
}
if ((rowKind === 'document' || rowKind === 'index' || rowKind === 'markdown') && isLocalOcrMarkdownPath(localRelativePath)) {
document.documentElement.setAttribute('data-mnote-local-ocr-filetree-open', 'resource-tab');
var ocrResourceInput = {
if ((rowKind === 'document' || rowKind === 'index' || rowKind === 'markdown') && isRetiredOcrSidecarMarkdownPath(localRelativePath)) {
document.documentElement.setAttribute('data-mnote-retired-ocr-sidecar-filetree-open', 'resource-tab');
var retiredOcrSidecarResourceInput = {
path: localRelativePath,
title: fileTreeRowTitleForShortcut(fileRow, localRelativePath),
kind: 'markdown',
objectIdentity: 'local-ocr:' + localRelativePath,
assetId: 'local-ocr:' + localRelativePath,
objectIdentity: 'retired-ocr-sidecar:' + localRelativePath,
assetId: 'retired-ocr-sidecar:' + localRelativePath,
documentId: documentId || ownerDocumentId || null,
workspaceId: resolveWorkspaceId(fileRow),
sourceKind: 'local_folder',
@@ -3059,9 +3004,9 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
paneRole: 'primary'
};
if (typeof window.__mnoteDocumentPaneRuntime?.openResourceInActiveTab === 'function') {
void window.__mnoteDocumentPaneRuntime.openResourceInActiveTab(ocrResourceInput);
void window.__mnoteDocumentPaneRuntime.openResourceInActiveTab(retiredOcrSidecarResourceInput);
} else {
void openLocalResourceInActiveTab(ocrResourceInput);
void openLocalResourceInActiveTab(retiredOcrSidecarResourceInput);
}
return;
}
@@ -3219,11 +3164,6 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
renderPageSettingsPopover();
return;
}
var localOcrCheckbox = closestAction(event.target, '[data-local-ocr-option-checkbox="autoEnabled"]');
if (localOcrCheckbox instanceof HTMLInputElement) {
void persistLocalOcrAutoPreference(localOcrCheckbox.checked);
return;
}
var checkbox = closestAction(event.target, '[data-page-option-checkbox]');
if (checkbox instanceof HTMLInputElement) {
var key = checkbox.getAttribute('data-page-option-checkbox') || '';
@@ -194,6 +194,19 @@
var localRootUri = (params.get('rootUri') || '').trim()
|| (document.body instanceof HTMLElement ? (document.body.getAttribute('data-mnote-root-uri') || '').trim() : '');
if (localRootUri && 'EventSource' in window) {
var localBus = window.__mnoteLocalFolderEventBus;
if (localBus && typeof localBus.startLocalFolderWatcher === 'function') {
var localHandle = localBus.startLocalFolderWatcher({
rootUri: localRootUri,
workspaceId: bootstrap.workspaceId || resolveWorkspaceId(),
bootstrap: bootstrap
});
if (localHandle) {
window.__mnoteTreeLiveEventSource = localHandle;
applyStatus('connected');
return;
}
}
var url = new URL('/api/local-folder/events', window.location.origin);
url.searchParams.set('rootUri', localRootUri);
url.searchParams.set('treeLive', 'true');