fix local office resource editing

- add local-folder OnlyOffice sign/callback writeback and edit-tab handling

- align main resource tabs, attachment edit menu, slash isolation, and filetree context behavior

- record Sidex/Hermes gap reviews and Reasonix task checklists
This commit is contained in:
lix-2026
2026-05-21 05:40:06 +08:00
parent a29d9868f6
commit eba1010191
50 changed files with 4309 additions and 283 deletions
+416 -31
View File
@@ -1253,6 +1253,9 @@ const SIDEBAR_TREE_JS: &str = r##"
var treeView = normalizeSidebarTreeMode(options && options.treeView ? options.treeView : activeSidebarTreeMode());
persistSidebarTreeMode(treeView);
if (currentDocumentId() === nodeId && window.location.pathname.indexOf('/documents/') === 0) {
if (typeof window.__mnoteDocumentPaneRuntime?.activatePageTab === 'function') {
window.__mnoteDocumentPaneRuntime.activatePageTab();
}
document.querySelectorAll('.tree-row[data-active="true"], .tree-row[data-selected="true"]').forEach(function(row) {
if (row instanceof HTMLElement) {
row.setAttribute('data-active', 'false');
@@ -1268,6 +1271,7 @@ const SIDEBAR_TREE_JS: &str = r##"
}
}
});
selectSidebarFileTreeDocument(nodeId, { scrollIntoView: true });
return;
}
var targetUrl = new URL('/documents/' + encodeURIComponent(nodeId), window.location.origin);
@@ -1289,6 +1293,7 @@ const SIDEBAR_TREE_JS: &str = r##"
}).then(function(){
if (mnoteNavigationInFlight === url) mnoteNavigationInFlight = '';
document.documentElement.removeAttribute('data-mnote-navigation-pending');
selectSidebarFileTreeDocument(nodeId, { scrollIntoView: true });
}).catch(function(error){
console.warn('mnote pane 退', error);
if (window.__mnoteTreeLiveEventSource && typeof window.__mnoteTreeLiveEventSource.close === 'function') {
@@ -1323,9 +1328,32 @@ const SIDEBAR_TREE_JS: &str = r##"
window.__mnoteLocalFolderSelfChangeSuppressions = window.__mnoteLocalFolderSelfChangeSuppressions || new Map();
window.__mnoteLocalFolderSelfChangeSuppressions.set(nextDocumentId, Date.now() + 5000);
}
if (nextDocumentId && currentSourceKind() === 'local_folder') {
await refreshLocalFolderSidebarSnapshot();
selectSidebarFileTreeDocument(nextDocumentId, { scrollIntoView: true });
document.documentElement.setAttribute('data-mnote-create-page-selected-document-id', nextDocumentId);
}
navigateToDocument(nextDocumentId, nextWorkspaceId, { treeView: activeSidebarTreeMode() });
}
async function createFileTreeFolder(trigger, parentId) {
if (currentSourceKind() !== 'local_folder') return false;
var workspaceId = resolveWorkspaceId(trigger || document.body);
var effectiveParentId = String(parentId || '').trim();
var title = window.prompt('', '');
if (!title || !title.trim()) return false;
var result = await dispatchTreeCommand(trigger || document.body, {
action: 'create_folder',
workspaceId: workspaceId,
parentId: effectiveParentId || null,
title: title.trim()
});
document.documentElement.setAttribute('data-mnote-filetree-folder-created', 'true');
document.documentElement.setAttribute('data-mnote-filetree-folder-created-id', commandDocumentId(result, result.id || ''));
void refreshLocalFolderSidebarSnapshot();
return true;
}
function applySidebarTreeTab(mode, shell) {
mode = persistSidebarTreeMode(mode);
shell = shell || document.querySelector('[data-testid="wolai-sidebar-page-tree-shell"]');
@@ -2063,7 +2091,7 @@ const SIDEBAR_TREE_JS: &str = r##"
if (input.assetId) target.searchParams.set('assetId', input.assetId);
if (input.documentId) target.searchParams.set('documentId', input.documentId);
if (input.userId) target.searchParams.set('userId', input.userId);
target.searchParams.set('mode', input.mode || 'edit');
target.searchParams.set('mode', input.mode || 'view');
return target.toString();
}
@@ -2075,7 +2103,7 @@ const SIDEBAR_TREE_JS: &str = r##"
if (input.assetId) params.set('assetId', input.assetId);
if (input.documentId) params.set('documentId', input.documentId);
if (input.userId) params.set('userId', input.userId);
params.set('mode', input.mode || 'edit');
params.set('mode', input.mode || 'view');
if (input.documentId && input.assetId) {
return '/office/' + encodeURIComponent(input.documentId) + '/' + encodeURIComponent(input.assetId) + '?' + params.toString();
}
@@ -2094,7 +2122,7 @@ const SIDEBAR_TREE_JS: &str = r##"
assetId: assetId || ('local-file:' + relativePath),
documentId: documentId || currentDocumentId() || '',
userId: '',
mode: mode || 'edit'
mode: mode || 'view'
});
}
@@ -2224,6 +2252,7 @@ const SIDEBAR_TREE_JS: &str = r##"
if (!assetId) return;
var openTarget = String(detail && detail.openTarget || '').trim().toLowerCase();
var forceNewWindow = openTarget === 'new-window';
var forceEditMode = openTarget === 'edit-mode';
if (openTarget === 'side') {
if (typeof window.__mnoteDocumentPaneRuntime?.openResourceAsSideTarget === 'function') {
void window.__mnoteDocumentPaneRuntime.openResourceAsSideTarget({
@@ -2261,7 +2290,7 @@ const SIDEBAR_TREE_JS: &str = r##"
navigateToMindmapObject(String(detail && detail.documentId || currentDocumentId() || '').trim(), assetId, String(detail.workspaceId || '').trim());
return;
}
var localOfficeUrl = buildLocalOnlyOfficeOpenUrl(localFilePath, localFileName, String(detail && detail.documentId || currentDocumentId() || '').trim(), assetId, forceNewWindow ? 'edit' : 'view');
var localOfficeUrl = buildLocalOnlyOfficeOpenUrl(localFilePath, localFileName, String(detail && detail.documentId || currentDocumentId() || '').trim(), assetId, forceEditMode ? 'edit' : 'view');
if (localOfficeUrl) {
if (!forceNewWindow && await openLocalResourceInActiveTab({
path: localFilePath,
@@ -2328,15 +2357,29 @@ const SIDEBAR_TREE_JS: &str = r##"
var fileType = inferOnlyOfficeFileType(fileName, asset.mime_type);
if (fileType) {
var userId = await fetchCurrentOnlyOfficeUserId();
window.open(buildOnlyOfficeOpenUrl({
var officeUrl = buildOnlyOfficeOpenUrl({
fileUrl: fileUrl,
fileName: fileName,
fileType: fileType,
assetId: assetId,
documentId: String(asset.document_id || detail.documentId || '').trim(),
userId: userId,
mode: 'edit'
}), '_blank', 'noopener,noreferrer');
mode: forceEditMode ? 'edit' : 'view'
});
if (typeof window.__mnoteDocumentPaneRuntime?.openResourceInActiveTab === 'function') {
var didOpen = await window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({
objectIdentity: 'resource:onlyoffice:' + String(asset.document_id || detail.documentId || '').trim() + ':' + assetId,
assetId: assetId,
title: fileName,
fileName: fileName,
kind: 'office',
officeUrl: officeUrl,
documentId: String(asset.document_id || detail.documentId || '').trim(),
workspaceId: String(detail.workspaceId || '').trim()
});
if (didOpen) return;
}
window.open(officeUrl, '_blank', 'noopener,noreferrer');
return;
}
if (isCodeAttachmentFileName(fileName) && String(asset.document_id || detail.documentId || '').trim() === currentDocumentId()) {
@@ -2695,7 +2738,7 @@ const SIDEBAR_TREE_JS: &str = r##"
assetId: assetId,
documentId: String(asset && (asset.document_id || asset.documentId) || currentDocumentId() || '').trim(),
userId: userId || '',
mode: 'edit'
mode: 'view'
});
}
return buildOnlyOfficeOpenUrl({
@@ -2705,7 +2748,7 @@ const SIDEBAR_TREE_JS: &str = r##"
assetId: assetId,
documentId: String(asset && (asset.document_id || asset.documentId) || currentDocumentId() || '').trim(),
userId: userId || '',
mode: 'edit'
mode: 'view'
});
}
@@ -2803,7 +2846,7 @@ const SIDEBAR_TREE_JS: &str = r##"
assetId: detail.assetId,
documentId: detail.documentId || currentDocumentId() || '',
userId: '',
mode: 'edit'
mode: 'view'
}));
link.setAttribute('data-mnote-attachment-link', 'true');
link.setAttribute('data-asset-id', detail.assetId);
@@ -3172,7 +3215,7 @@ const SIDEBAR_TREE_JS: &str = r##"
assetId: assetId,
documentId: String(asset && (asset.document_id || asset.documentId) || currentDocumentId() || '').trim(),
userId: userId || '',
mode: 'edit'
mode: 'view'
}))
: href;
var inserted = editor.chain().focus().insertContent({
@@ -3557,6 +3600,57 @@ const SIDEBAR_TREE_JS: &str = r##"
});
}
function fileTreeCopyPath(detail, trigger) {
// 对 local_folder 复制真实相对路径,不复制标题
if (currentSourceKind() === 'local_folder') {
if (detail.assetId) {
var path = localFilePathFromAssetId(detail.assetId);
if (path) {
try {
return decodeURIComponent(path.replace(/~2F/g, '/'));
} catch (_) {
return path.replace(/~2F/g, '/');
}
}
}
if (detail.documentId) {
var docPath = String(detail.documentId || '')
.replace(/^local-md:/, '')
.replace(/^local-dir:/, '')
.replace(/~2F/g, '/');
if (docPath) {
try {
return decodeURIComponent(docPath);
} catch (_) {
return docPath;
}
}
}
}
return detail.title || '';
}
function fileTreeMenuTargetParentId(detail, trigger) {
var rowKind = String(detail && detail.rowKind || '').trim();
var rowId = String(detail && detail.rowId || '').trim();
var documentId = String(detail && detail.documentId || '').trim();
if (rowKind === 'folder' && rowId) return rowId;
if (rowKind === 'directory' && rowId) return rowId;
if (documentId) return documentId;
if (trigger && typeof trigger.getAttribute === 'function') {
var triggerKind = String(trigger.getAttribute('data-row-kind') || '').trim();
var triggerRowId = String(trigger.getAttribute('data-row-id') || '').trim();
if ((triggerKind === 'folder' || triggerKind === 'directory') && triggerRowId) return triggerRowId;
}
return '';
}
function withOfficeEditModeGuard(callback) {
document.documentElement.setAttribute('data-mnote-last-office-edit-mode-requested', 'true');
document.documentElement.setAttribute('data-mnote-last-office-edit-mode-guard', 'silent');
callback();
}
function handleTreeContextMenuAction(action, detail, trigger) {
closeTreeContextMenu();
detail = detail || {};
@@ -3577,6 +3671,18 @@ const SIDEBAR_TREE_JS: &str = r##"
openEditorAttachmentNewWindow(detail);
return;
}
if (action === 'new-window-edit') {
withOfficeEditModeGuard(function() {
openEditorAttachmentNewWindow(detail, 'edit');
});
return;
}
if (action === 'open-edit-mode') {
withOfficeEditModeGuard(function() {
void openEditorAttachmentEditTab(detail);
});
return;
}
if (action === 'right-preview') {
dispatchSidebarEvent('tree.attachment.open-right', detail);
return;
@@ -3597,6 +3703,13 @@ const SIDEBAR_TREE_JS: &str = r##"
void openConvexAssetFromFileTree({ ...detail, openTarget: 'new-window' });
return;
}
if (isAsset && action === 'open-edit-mode') {
withOfficeEditModeGuard(function() {
recordFileTreeAction('open-edit-mode', detail);
void openConvexAssetFromFileTree({ ...detail, openTarget: 'edit-mode' });
});
return;
}
if (isAsset && action === 'open-right') {
recordFileTreeAction('open-right', detail);
void openConvexAssetFromFileTree({ ...detail, openTarget: 'side' });
@@ -3645,7 +3758,17 @@ const SIDEBAR_TREE_JS: &str = r##"
return;
}
if (action === 'new-file') {
void createPage(trigger || document.body, documentId || null);
void createPage(trigger || document.body, fileTreeMenuTargetParentId(detail, trigger) || null);
return;
}
if (action === 'new-folder') {
recordFileTreeAction('new-folder', detail);
void createFileTreeFolder(trigger || document.body, fileTreeMenuTargetParentId(detail, trigger) || null).then(function(ok) {
recordFileTreeActionStatus(ok ? 'created' : 'skipped', detail);
}).catch(function(error) {
recordFileTreeActionStatus('failed', Object.assign({}, detail, { fallback: 'alert' }));
window.alert(error && error.message ? error.message : '');
});
return;
}
if (action === 'paste-into') {
@@ -3660,7 +3783,7 @@ const SIDEBAR_TREE_JS: &str = r##"
return;
}
if (action === 'copy-path') {
void copyTreeContextValue(title, 'copy-path');
void copyTreeContextValue(fileTreeCopyPath(detail, trigger), 'copy-path');
return;
}
if (action === 'refresh') {
@@ -3790,6 +3913,8 @@ const SIDEBAR_TREE_JS: &str = r##"
{ separator: true },
{ action: 'popup-preview', icon: 'preview', label: '' },
{ action: 'right-preview', icon: 'right_panel_open', label: '' },
{ action: 'open-edit-mode', icon: 'edit_note', label: '' },
{ action: 'new-window-edit', icon: 'open_in_new', label: '' },
{ action: 'new-window', icon: 'open_in_new', label: '' },
{ action: 'download', icon: 'download', label: '' },
{ action: 'replace-file', icon: 'sync', label: '' },
@@ -3799,7 +3924,7 @@ const SIDEBAR_TREE_JS: &str = r##"
{ separator: true },
{ action: 'color', icon: 'format_paint', label: '' }
] : isAsset ? [
{ action: 'open-right', icon: 'open_in_new', label: '', shortcut: 'Alt + O' },
{ action: 'open-edit-mode', icon: 'edit_note', label: '使' },
{ action: 'new-window', icon: 'open_in_new', label: '' },
{ action: 'rename', icon: 'edit', label: '', shortcut: 'F2' },
{ action: 'copy-id', icon: 'tag', label: ' ID' },
@@ -3817,14 +3942,12 @@ const SIDEBAR_TREE_JS: &str = r##"
{ action: 'copy-path', icon: 'content_copy', label: 'Copy Path' },
{ separator: true },
{ action: 'new-file', icon: 'note_add', label: 'New File' },
{ action: 'new-folder', icon: 'create_new_folder', label: 'New Folder', disabled: true, title: 'Convex tree command' },
{ action: 'new-folder', icon: 'create_new_folder', label: 'New Folder', disabled: currentSourceKind() !== 'local_folder', title: currentSourceKind() === 'local_folder' ? '' : ' local folder ' },
{ action: 'paste-into', icon: 'content_paste', label: '', disabled: !sidebarFileTreeClipboard, title: sidebarFileTreeClipboard ? '' : '' },
{ action: 'refresh', icon: 'refresh', label: 'Refresh' },
{ action: 'collapse-all', icon: 'unfold_less', label: 'Collapse All' },
{ action: 'reveal', icon: 'my_location', label: 'Reveal' },
{ separator: true },
{ action: 'duplicate', icon: 'file_copy', label: '' },
{ separator: true },
{ action: 'rename', icon: 'edit', label: '', shortcut: 'F2' },
{ separator: true },
{ action: 'delete-trash', icon: 'delete', label: '', shortcut: 'Del', danger: true }
@@ -3835,8 +3958,6 @@ const SIDEBAR_TREE_JS: &str = r##"
{ action: 'copy-reference-inline', icon: 'content_copy', label: '' },
{ action: 'copy-id', icon: 'tag', label: 'ID' },
{ separator: true },
{ action: 'duplicate', icon: 'file_copy', label: '' },
{ separator: true },
{ action: 'rename', icon: 'edit', label: '' },
{ separator: true },
{ action: 'delete-trash', icon: 'delete', label: '', shortcut: 'Del', danger: true }
@@ -3929,6 +4050,33 @@ const SIDEBAR_TREE_JS: &str = r##"
return Array.from(selected);
}
function selectSidebarFileTreeDocument(documentId, options) {
var id = String(documentId || '').trim();
if (!id) return false;
var row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + id) + '"]')
|| document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-document-id="' + cssEscape(id) + '"]')
|| document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-doc-id="' + cssEscape(id) + '"]');
if (!(row instanceof HTMLElement)) return false;
var rowId = row.getAttribute('data-row-id') || '';
if (!rowId) return false;
sidebarFileTreeSelection.selectedRowIds = new Set([rowId]);
sidebarFileTreeSelection.anchorRowId = rowId;
sidebarFileTreeSelection.focusedRowId = rowId;
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-active="true"]').forEach(function(activeRow) {
if (activeRow instanceof HTMLElement) activeRow.setAttribute('data-active', 'false');
});
syncSidebarFileTreeSelection();
row.setAttribute('data-active', 'true');
if (options && options.scrollIntoView !== false) {
try {
row.scrollIntoView({ block: 'nearest' });
} catch (_) {
row.scrollIntoView();
}
}
return true;
}
function selectedSidebarFileTreeRowIdsForDrag(row) {
var rowId = row instanceof HTMLElement ? row.getAttribute('data-row-id') || '' : '';
if (rowId && sidebarFileTreeSelection.selectedRowIds.has(rowId)) {
@@ -4773,6 +4921,22 @@ const SIDEBAR_TREE_JS: &str = r##"
function pageAiResolvePermission(permissionId, decision) {
permissionId = String(permissionId || '').trim();
if (!permissionId) return;
// 调用后端 resolve-permission 端点,让 ACP agent 得到真实响应
var runId = pageUiState.pageAiCurrentRunId;
if (runId) {
fetch('/api/hermes/client/runs/' + encodeURIComponent(runId) + '/resolve-permission', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ permissionId: permissionId, decision: decision })
}).then(function(response) {
if (!response.ok) console.warn('resolve-permission ', response.status);
}).catch(function(err) {
console.warn('resolve-permission ', err);
});
} else {
console.warn('resolve-permission: runId');
}
// 本地乐观更新 UI
pageUiState.pageAiMessages.forEach(function(item) {
if (item.kind === 'permission' && item.permissionId === permissionId) {
item.resolved = true;
@@ -4986,6 +5150,7 @@ const SIDEBAR_TREE_JS: &str = r##"
}
if (eventType === 'tool.started' || eventType === 'tool.completed' || eventType === 'tool.failed') {
var toolName = String(payload.toolName || payload.tool || payload.name || eventType).trim();
var rawLocations = payload.locations;
return {
role: 'tool',
content: toolName,
@@ -4995,6 +5160,7 @@ const SIDEBAR_TREE_JS: &str = r##"
status: eventType === 'tool.completed' ? 'completed' : (eventType === 'tool.failed' ? 'failed' : 'running'),
argsSummary: pageAiPreviewValue(payload.args || payload.arguments || payload.input),
resultSummary: pageAiPreviewValue(payload.summary || payload.result || payload.output || payload.error),
locations: Array.isArray(rawLocations) ? rawLocations.filter(function(l) { return typeof l === 'string' || (typeof l === 'object' && l && l.path); }).map(function(l) { return typeof l === 'string' ? l : l.path; }) : [],
traceId: String(payload.traceId || payload.trace_id || ''),
auditId: String(payload.auditId || payload.audit_id || '')
};
@@ -5610,6 +5776,8 @@ const SIDEBAR_TREE_JS: &str = r##"
traceId: traceId,
auditId: auditId
};
var rawLocations = toolEvent && toolEvent.locations;
var locations = Array.isArray(rawLocations) ? rawLocations.filter(function(l) { return typeof l === 'string' || (typeof l === 'object' && l && l.path); }).map(function(l) { return typeof l === 'string' ? l : l.path; }) : [];
var existing = pageUiState.pageAiMessages.find(function(item) {
return item.role === 'tool' && item.toolCallId === toolCallId;
});
@@ -5624,6 +5792,7 @@ const SIDEBAR_TREE_JS: &str = r##"
status: status,
argsSummary: '',
resultSummary: '',
locations: locations,
traceId: traceId,
auditId: auditId
};
@@ -5637,6 +5806,7 @@ const SIDEBAR_TREE_JS: &str = r##"
existing.auditId = auditId || existing.auditId || '';
if (argsSummary) existing.argsSummary = argsSummary;
if (resultSummary) existing.resultSummary = resultSummary;
if (locations.length) existing.locations = locations;
if (status === 'completed') {
pageAiNotifyToolWriteCompleted(toolName, toolEvent, runId, runTraceId);
}
@@ -6585,7 +6755,16 @@ const SIDEBAR_TREE_JS: &str = r##"
var roleLabel = item.role === 'user' ? '你' : (item.role === 'tool' ? '' : 'AI');
if (item.role === 'tool') {
var statusLabel = item.status === 'completed' ? '' : (item.status === 'failed' ? '' : '');
var locationRows = Array.isArray(item.locations) && item.locations.length
? '<div class="wolai-page-ai-tool-meta">' + item.locations.map(function(loc, idx) {
return '<span style="display:inline-flex;align-items:center;gap:4px;margin-right:8px">' +
'<span>' + escapeHtml(loc) + '</span>' +
'<button type="button" class="wolai-page-ai-ghost" style="font-size:0.85em;padding:0 4px;min-width:unset" data-page-ai-open-location="' + escapeHtml(loc) + '" data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" title="打开文件"></button>' +
'</span>';
}).join('') + '</div>'
: '';
var detailRows = [
locationRows,
item.argsSummary ? '<div class="wolai-page-ai-tool-meta"> ' + escapeHtml(item.argsSummary) + '</div>' : '',
item.resultSummary ? '<div class="wolai-page-ai-tool-meta"> ' + escapeHtml(item.resultSummary) + '</div>' : '',
item.changedFiles && item.changedFiles.length ? '<pre class="wolai-page-ai-tool-meta wolai-page-ai-changed-files">' + escapeHtml(pageAiFormatChangedFiles(item.changedFiles)) + '</pre>' : '',
@@ -6629,6 +6808,21 @@ const SIDEBAR_TREE_JS: &str = r##"
'</div>' +
'</div>';
}
if (item.kind === 'plan') {
var planEntries = Array.isArray(item.entries) ? item.entries : [];
var listHtml = planEntries.map(function(entry, idx) {
return '<li style="margin:2px 0">' + escapeHtml(String(entry || '')) + '</li>';
}).join('');
return '' +
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-plan="true">' +
'<details class="wolai-page-ai-plan-details" open>' +
'<summary class="wolai-page-ai-message-role" style="cursor:pointer;user-select:none;opacity:0.8;font-size:0.85em"> · ' + planEntries.length + ' </summary>' +
'<div class="wolai-page-ai-message-text" style="margin-top:4px;opacity:0.75;font-size:0.9em;border-left:2px solid var(--wolai-border-color, #ddd);padding-left:8px">' +
'<ol style="margin:4px 0;padding-left:20px">' + listHtml + '</ol>' +
'</div>' +
'</details>' +
'</div>';
}
var streamingAttr = item.streaming ? ' data-page-ai-streaming="true"' : '';
return '' +
'<div class="wolai-page-ai-message wolai-page-ai-message--' + escapeHtml(item.role || 'assistant') + '"' + streamingAttr + '>' +
@@ -7046,6 +7240,40 @@ const SIDEBAR_TREE_JS: &str = r##"
pageUiState.pageAiStoppedRunIds[runId] = true;
pageAiSetRunStatus('aborted', runId);
}
if (eventName === 'session.info.updated') {
try {
var infoPayload = JSON.parse(payloadText || 'null') || {};
var newTitle = String(infoPayload.title || '').trim();
if (newTitle) {
var sessionForTitle = pageAiCurrentSession();
if (sessionForTitle) {
sessionForTitle.title = newTitle;
sessionForTitle.updatedAt = Date.now();
pageAiPersistSessions();
renderPageAiControls();
}
}
} catch (_) {}
}
if (eventName === 'plan.updated') {
try {
var planPayload = JSON.parse(payloadText || 'null') || {};
var planEntries = Array.isArray(planPayload.entries) ? planPayload.entries : [];
if (planEntries.length) {
var planMsgs = pageUiState.pageAiMessages;
var existingPlan = planMsgs.length > 0 && planMsgs[planMsgs.length - 1].kind === 'plan' ? planMsgs[planMsgs.length - 1] : null;
if (existingPlan) {
existingPlan.entries = planEntries;
existingPlan.updatedAt = Date.now();
} else {
planMsgs.push({ role: 'system', kind: 'plan', entries: planEntries, createdAt: Date.now(), updatedAt: Date.now() });
}
pageAiSyncCurrentSessionMessages();
pageAiPersistSessions();
renderPageAiConversation();
}
} catch (_) {}
}
if (eventName === 'tool.started' || eventName === 'tool.completed' || eventName === 'tool.failed') {
pageAiApplyToolEvent(eventName, payloadText, runId, runTraceId);
pageAiSyncCurrentSessionMessages();
@@ -7424,7 +7652,7 @@ const SIDEBAR_TREE_JS: &str = r##"
assetId: url.searchParams.get('assetId') || '',
documentId: url.searchParams.get('documentId') || currentDocumentId() || '',
userId: url.searchParams.get('userId') || '',
mode: url.searchParams.get('mode') || 'edit'
mode: url.searchParams.get('mode') || 'view'
});
} catch (_) {
return String(href || '');
@@ -7453,7 +7681,7 @@ const SIDEBAR_TREE_JS: &str = r##"
assetId: assetId,
documentId: currentDocumentId() || '',
userId: '',
mode: 'edit'
mode: 'view'
});
} else if (isOnlyOfficeAttachmentHref(rawHref)) {
href = normalizeOnlyOfficeAttachmentHref(rawHref);
@@ -7494,7 +7722,7 @@ const SIDEBAR_TREE_JS: &str = r##"
assetId: assetId,
documentId: params.get('documentId') || currentDocumentId() || '',
userId: params.get('userId') || '',
mode: params.get('mode') || 'edit'
mode: params.get('mode') || 'view'
}));
}
attachmentClassForFileName(fileName).split(/\s+/).forEach(function(name) {
@@ -7575,20 +7803,110 @@ const SIDEBAR_TREE_JS: &str = r##"
void openCodeEditorAttachment(detail);
return;
}
var fileType = String(detail.fileType || '').trim();
if (fileType && typeof window.__mnoteDocumentPaneRuntime?.openResourceInActiveTab === 'function') {
void window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({
objectIdentity: 'resource:onlyoffice:' + (detail.documentId || '') + ':' + (detail.assetId || ''),
assetId: detail.assetId || '',
title: detail.fileName || '',
fileName: detail.fileName || '',
kind: 'office',
officeUrl: detail.href,
documentId: detail.documentId || '',
workspaceId: detail.workspaceId || ''
});
return;
}
window.open(detail.href, '_blank', 'noopener,noreferrer');
}
function openEditorAttachmentNewWindow(detail) {
function openEditorAttachmentNewWindow(detail, mode) {
if (!detail) return;
var requestedMode = mode === 'edit' ? 'edit' : 'view';
var localFilePath = localFilePathFromAssetId(detail.assetId);
if (localFilePath) {
var localFileName = localFilePath.split('/').pop() || localFilePath;
var localOfficeUrl = buildLocalOnlyOfficeOpenUrl(localFilePath, localFileName, String(detail.documentId || currentDocumentId() || '').trim(), detail.assetId, 'edit');
var localOfficeUrl = buildLocalOnlyOfficeOpenUrl(localFilePath, localFileName, String(detail.documentId || currentDocumentId() || '').trim(), detail.assetId, requestedMode);
var localFileUrl = buildLocalFileOpenUrl(localFilePath, false);
window.open(localOfficeUrl || localFileUrl || detail.href, '_blank', 'noopener,noreferrer');
return;
}
window.open(detail.href || detail.fileUrl, '_blank', 'noopener,noreferrer');
var href = detail.href || detail.fileUrl;
if (detail.fileType && isOnlyOfficeAttachmentHref(href)) {
try {
var url = new URL(href, window.location.origin);
url.searchParams.set('mode', requestedMode);
href = url.toString();
} catch (_) {}
} else if (detail.fileType) {
href = buildOnlyOfficeOpenUrl({
fileUrl: detail.fileUrl || href || '',
fileName: detail.fileName || '',
fileType: detail.fileType,
assetId: detail.assetId || '',
documentId: detail.documentId || currentDocumentId() || '',
userId: '',
mode: requestedMode
});
}
window.open(href, '_blank', 'noopener,noreferrer');
}
async function openEditorAttachmentEditTab(detail) {
if (!detail) return false;
var localFilePath = localFilePathFromAssetId(detail.assetId);
if (localFilePath) {
var localFileName = localFilePath.split('/').pop() || localFilePath;
var localOfficeUrl = buildLocalOnlyOfficeOpenUrl(localFilePath, localFileName, String(detail.documentId || currentDocumentId() || '').trim(), detail.assetId, 'edit');
if (localOfficeUrl) {
var didOpenLocalEditTab = await openLocalResourceInActiveTab({
path: localFilePath,
title: detail.fileName || localFileName,
kind: 'office',
assetId: detail.assetId,
documentId: detail.documentId || currentDocumentId() || '',
workspaceId: detail.workspaceId || resolveWorkspaceId(document.body) || '',
href: buildLocalFileOpenUrl(localFilePath, false),
officeUrl: localOfficeUrl
});
if (!didOpenLocalEditTab) window.open(localOfficeUrl, '_blank', 'noopener,noreferrer');
return didOpenLocalEditTab;
}
}
var href = detail.href || detail.fileUrl;
if (detail.fileType && isOnlyOfficeAttachmentHref(href)) {
try {
var url = new URL(href, window.location.origin);
url.searchParams.set('mode', 'edit');
href = url.toString();
} catch (_) {}
} else if (detail.fileType) {
href = buildOnlyOfficeOpenUrl({
fileUrl: detail.fileUrl || href || '',
fileName: detail.fileName || '',
fileType: detail.fileType,
assetId: detail.assetId || '',
documentId: detail.documentId || currentDocumentId() || '',
userId: '',
mode: 'edit'
});
}
if (detail.fileType && typeof window.__mnoteDocumentPaneRuntime?.openResourceInActiveTab === 'function') {
var didOpenEditTab = await window.__mnoteDocumentPaneRuntime.openResourceInActiveTab({
objectIdentity: 'resource:onlyoffice:' + (detail.documentId || '') + ':' + (detail.assetId || ''),
assetId: detail.assetId || '',
title: detail.fileName || '',
fileName: detail.fileName || '',
kind: 'office',
officeUrl: href,
documentId: detail.documentId || '',
workspaceId: detail.workspaceId || ''
});
if (!didOpenEditTab && href) window.open(href, '_blank', 'noopener,noreferrer');
return didOpenEditTab;
}
if (href) window.open(href, '_blank', 'noopener,noreferrer');
return false;
}
async function resolveEditorAttachmentUrl(detail) {
@@ -7999,6 +8317,21 @@ const SIDEBAR_TREE_JS: &str = r##"
return;
}
var pageAiOpenLocation = closestAction(e.target, '[data-page-ai-open-location]');
if (pageAiOpenLocation) {
e.preventDefault();
var loc = String(pageAiOpenLocation.getAttribute('data-page-ai-open-location') || '').trim();
if (loc) {
openLocalResourceInActiveTab({ path: loc }).then(function(opened) {
if (!opened) {
var href = buildLocalFileOpenUrl(loc, false);
if (href) window.open(href, '_blank', 'noopener,noreferrer');
}
});
}
return;
}
var pageAiSession = closestAction(e.target, '[data-page-ai-session]');
if (pageAiSession) {
e.preventDefault();
@@ -8181,10 +8514,11 @@ const SIDEBAR_TREE_JS: &str = r##"
var tree = document.getElementById('sidebar-tree-root');
if (!tree || !tree.contains(e.target)) return;
var pageRow = closestAction(e.target, '.tree-row[data-shell-mode="page"]');
var btn = closestAction(e.target, '[data-rust-action]');
if (!btn) return;
var nodeId = btn.getAttribute('data-node-id');
var action = btn.getAttribute('data-rust-action');
if (!btn && !pageRow) return;
var nodeId = (btn && btn.getAttribute('data-node-id')) || (pageRow && pageRow.getAttribute('data-node-id')) || '';
var action = btn ? btn.getAttribute('data-rust-action') : 'open';
if (action === 'toggle') {
var row = btn.closest('.tree-row');
@@ -8192,11 +8526,12 @@ const SIDEBAR_TREE_JS: &str = r##"
toggleChildren(row, btn);
e.preventDefault();
} else if (action === 'open') {
if (btn.getAttribute('data-page-openable') === 'false') {
var openTrigger = btn || pageRow;
if (openTrigger && openTrigger.getAttribute('data-page-openable') === 'false') {
e.preventDefault();
return;
}
var workspaceId = resolveWorkspaceId(btn);
var workspaceId = resolveWorkspaceId(openTrigger);
navigateToDocument(nodeId, workspaceId, { treeView: 'page' });
e.preventDefault();
} else if (action === 'create') {
@@ -8714,6 +9049,10 @@ const SIDEBAR_TREE_JS: &str = r##"
sidebarFileTreeSelection.focusedRowId = rowId;
});
syncSidebarFileTreeSelection();
window.addEventListener('mnote:primary-document-activated', function(event) {
var detail = event && event.detail ? event.detail : {};
selectSidebarFileTreeDocument(detail.documentId, { scrollIntoView: false });
});
restoreSidebarTreeTab();
startLocalFolderSidebarWatch();
})();
@@ -9162,7 +9501,7 @@ mod tests {
assert!(SIDEBAR_TREE_JS.contains("/api/auth/whoami"));
assert!(SIDEBAR_TREE_JS.contains("buildOnlyOfficeOpenUrl"));
assert!(SIDEBAR_TREE_JS.contains("target.searchParams.set('userId'"));
assert!(SIDEBAR_TREE_JS.contains("window.open(buildOnlyOfficeOpenUrl"));
assert!(SIDEBAR_TREE_JS.contains("window.open(officeUrl,"));
assert!(SIDEBAR_TREE_JS.contains("tree.filetree.internal-drop"));
assert!(SIDEBAR_TREE_JS.contains("tree.filetree.external-drop"));
assert!(SIDEBAR_TREE_JS.contains("beginFileTreeInlineRename"));
@@ -9240,6 +9579,22 @@ mod tests {
!SIDEBAR_TREE_JS.contains("(item.resolved ? ' disabled' : '')"),
"已决 ACP permission 事件不能继续展示假审批按钮"
);
assert!(
SIDEBAR_TREE_JS.contains("session.info.updated"),
"ACP SessionInfoUpdate 事件应通过 session.info.updated SSE 转发到前端"
);
assert!(
SIDEBAR_TREE_JS.contains("plan.updated"),
"ACP PlanUpdate 事件应通过 plan.updated SSE 转发到前端"
);
assert!(
SIDEBAR_TREE_JS.contains("data-page-ai-plan"),
"plan 消息应渲染为 data-page-ai-plan 标记的轻量系统状态面板"
);
assert!(
SIDEBAR_TREE_JS.contains("执行计划 · "),
"plan 面板标题应显示执行计划和步数"
);
}
#[test]
@@ -9358,6 +9713,18 @@ mod tests {
assert!(SIDEBAR_TREE_JS.contains("mnote-tree-context-menu"));
assert!(SIDEBAR_TREE_JS.contains("复制访问链接"));
assert!(SIDEBAR_TREE_JS.contains("删除到垃圾桶"));
assert!(SIDEBAR_TREE_JS.contains("function createFileTreeFolder"));
assert!(SIDEBAR_TREE_JS.contains("action: 'create_folder'"));
assert!(SIDEBAR_TREE_JS.contains("function fileTreeMenuTargetParentId"));
assert!(SIDEBAR_TREE_JS.contains("if (action === 'new-folder')"));
assert!(SIDEBAR_TREE_JS.contains("fileTreeCopyPath(detail, trigger)"));
assert!(SIDEBAR_TREE_JS.contains(
"var pageRow = closestAction(e.target, '.tree-row[data-shell-mode=\"page\"]');"
));
assert!(SIDEBAR_TREE_JS
.contains("var action = btn ? btn.getAttribute('data-rust-action') : 'open';"));
assert!(SIDEBAR_TREE_JS.contains("var openTrigger = btn || pageRow;"));
assert!(SIDEBAR_TREE_JS.contains("var workspaceId = resolveWorkspaceId(openTrigger);"));
assert!(SIDEBAR_TREE_JS.contains(
r#".tree-row[data-shell-mode="page"][data-node-id="' + escaped + '"] > .tree-link > .tree-link-title"#
));
@@ -9491,6 +9858,24 @@ mod tests {
"return '/office/' + encodeURIComponent(input.documentId) + '/' + encodeURIComponent(input.assetId)"
));
assert!(SIDEBAR_TREE_JS.contains("return '/onlyoffice?' + params.toString();"));
assert!(SIDEBAR_TREE_JS.contains("target.searchParams.set('mode', input.mode || 'view');"));
assert!(SIDEBAR_TREE_JS.contains("params.set('mode', input.mode || 'view');"));
assert!(SIDEBAR_TREE_JS.contains(
"{ action: 'open-edit-mode', icon: 'edit_note', label: '使用编辑模式打开' }"
));
assert!(SIDEBAR_TREE_JS
.contains("{ action: 'open-edit-mode', icon: 'edit_note', label: '弹窗编辑' }"));
assert!(SIDEBAR_TREE_JS
.contains("{ action: 'new-window-edit', icon: 'open_in_new', label: '新窗口编辑' }"));
assert!(SIDEBAR_TREE_JS.contains("function withOfficeEditModeGuard(callback)"));
assert!(SIDEBAR_TREE_JS.contains("data-mnote-last-office-edit-mode-requested"));
assert!(SIDEBAR_TREE_JS.contains("function openEditorAttachmentEditTab(detail)"));
assert!(SIDEBAR_TREE_JS.contains("void openEditorAttachmentEditTab(detail);"));
assert!(SIDEBAR_TREE_JS.contains("buildLocalOnlyOfficeOpenUrl(localFilePath, localFileName, String(detail.documentId || currentDocumentId() || '').trim(), detail.assetId, 'edit')"));
assert!(SIDEBAR_TREE_JS.contains("openEditorAttachmentNewWindow(detail, 'edit')"));
assert!(SIDEBAR_TREE_JS.contains("forceEditMode ? 'edit' : 'view'"));
assert!(SIDEBAR_TREE_JS.contains("url.searchParams.set('mode', requestedMode);"));
assert!(SIDEBAR_TREE_JS.contains("mode: requestedMode"));
}
#[test]