fix: use readonly preview for code resource tabs

This commit is contained in:
lix-2026
2026-06-04 23:00:53 +08:00
parent eccf399142
commit a1dcfc9f76
3 changed files with 142 additions and 2 deletions
@@ -64,6 +64,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
const resourceTabRegistry = new Map();
const resourceTabMru = { primary: [], secondary: [] };
const resourceTabMruMax = 20;
const readonlyCodePreviewMaxChars = 1_500_000;
const resourceTabCloseGuardAttribute = 'data-resource-tab-close-guarded';
let onlyofficeBridgeReadyListenerBound = false;
@@ -263,7 +264,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
const badgeKind = resourceTabBadgeKind(input, kind);
const rawTarget = String(input?.openTarget || '').trim().toLowerCase();
const openTarget = rawTarget === 'new-window' || rawTarget === 'side' ? rawTarget : 'active-tab';
const editable = kind === 'markdown' || kind === 'text' || kind === 'code';
const editable = kind === 'markdown' || kind === 'text';
const defaultOpenMode = kind === 'office' && openTarget === 'active-tab' ? 'active-tab-iframe' : openTarget;
const viewerUrl = kind === 'office'
? String(input?.officeUrl || input?.href || '').trim()
@@ -2064,6 +2065,46 @@ export const createResourceTabRuntime = (dependencies = {}) => {
markIntendedSlashRoot(entry);
};
const openReadonlyCodeResourceTab = async (entry, input) => {
entry.panel.innerHTML = '';
const shell = document.createElement('div');
shell.className = 'mnote-resource-tab-code-shell';
shell.setAttribute('data-mnote-resource-readonly-preview', 'true');
const header = document.createElement('div');
header.className = 'mnote-resource-tab-code-header';
const title = document.createElement('div');
title.className = 'mnote-resource-tab-code-title';
title.textContent = entry.title;
const meta = document.createElement('div');
meta.className = 'mnote-resource-tab-code-meta';
meta.textContent = '只读预览';
header.append(title, meta);
const pre = document.createElement('pre');
pre.className = 'mnote-resource-tab-code-preview';
const code = document.createElement('code');
pre.append(code);
shell.append(header, pre);
entry.panel.append(shell);
const response = await fetch(localResourceReadUrl(input.rootUri, input.path), { cache: 'no-store', headers: { accept: 'application/json' } });
const payload = await response.json().catch(() => null);
if (!response.ok || !payload || payload.ok !== true) throw new Error(payload?.error?.message || `resource_read_failed_${response.status}`);
const readResult = payload.result || {};
const rawText = typeof readResult.text === 'string' ? readResult.text : '';
const truncated = rawText.length > readonlyCodePreviewMaxChars;
code.textContent = truncated
? `${rawText.slice(0, readonlyCodePreviewMaxChars)}\n\n/* 预览已截断,文件过大,请用外部编辑器查看完整内容。 */`
: rawText;
const fileSize = new Blob([rawText]).size;
meta.textContent = truncated
? `只读预览 · 已截断 · ${fileSize.toLocaleString()} bytes`
: `只读预览 · ${fileSize.toLocaleString()} bytes`;
entry.session = null;
entry.view = null;
};
const ensureOnlyofficeBridgeReadyListener = () => {
if (onlyofficeBridgeReadyListenerBound) return;
onlyofficeBridgeReadyListenerBound = true;
@@ -2245,8 +2286,10 @@ export const createResourceTabRuntime = (dependencies = {}) => {
try {
if (entry.kind === 'mindmap') {
await openMindmapResourceTab(entry, input);
} else if (entry.kind === 'markdown' || entry.kind === 'text' || entry.kind === 'code') {
} else if (entry.kind === 'markdown' || entry.kind === 'text') {
await openTiptapResourceTab(entry, input);
} else if (entry.kind === 'code') {
await openReadonlyCodeResourceTab(entry, input);
} else {
await openPassiveResourceTab(entry, input);
}
+50
View File
@@ -2938,6 +2938,56 @@ body {
padding: 34px 48px;
}
.mnote-resource-tab-code-shell {
display: flex;
flex-direction: column;
width: 100%;
min-height: calc(100vh - 80px);
background: #FFF;
color: #37352F;
}
.mnote-resource-tab-code-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
height: 42px;
padding: 0 18px;
border-bottom: 1px solid rgba(55, 53, 47, 0.1);
background: #FAFAF8;
box-sizing: border-box;
}
.mnote-resource-tab-code-title {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
font-weight: 600;
}
.mnote-resource-tab-code-meta {
flex: 0 0 auto;
font-size: 12px;
color: #73726E;
}
.mnote-resource-tab-code-preview {
flex: 1 1 auto;
min-height: 0;
margin: 0;
padding: 18px 22px 48px;
overflow: auto;
background: #1F2328;
color: #F0F3F6;
font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
white-space: pre;
tab-size: 2;
box-sizing: border-box;
}
.mnote-resource-tab-error {
display: flex;
place-items: center;