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:
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user