${escapeHtml(paragraph)}
`) .join('')}import zlib from 'node:zlib'; const PREVIEWABLE_MIME_TYPES = new Set([ 'text/html', 'text/plain', 'text/markdown', 'text/csv', 'application/pdf', 'image/png', 'image/jpeg', 'image/webp', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', ]); const PREVIEW_SHELL_STYLE = ` html,body{margin:0;padding:0;background:#f5f0e5;color:#1f2937;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif} body{padding:24px;box-sizing:border-box;line-height:1.6} pre,code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace} pre{white-space:pre-wrap;word-break:break-word;background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:16px} table{border-collapse:collapse;width:100%;background:#fff;border-radius:12px;overflow:hidden} th,td{border:1px solid #e5e7eb;padding:8px 10px;text-align:left;font-size:14px} th{background:#faf7ef} .docx-preview h1{font-size:1.5rem;margin:0 0 16px} .docx-preview p{margin:0 0 12px;text-indent:2em} .docx-preview .meta{color:#6b7280;font-size:13px;margin-bottom:20px;text-indent:0} .pdf-frame,.image-frame{display:block;width:100%;min-height:calc(100vh - 48px);border:0;border-radius:12px;background:#fff} .image-frame{object-fit:contain;max-height:calc(100vh - 48px);width:auto;max-width:100%;margin:0 auto;cursor:zoom-in} .image-viewer{padding:0} .image-viewer h1,.image-viewer .meta{display:none} .image-viewer .image-frame{min-height:100vh;max-height:100vh;margin:0;border-radius:0;background:#0b100e} .image-lightbox[hidden]{display:none} .image-lightbox{position:fixed;inset:0;z-index:9999;display:grid;place-items:center;padding:max(16px,env(safe-area-inset-top)) max(16px,env(safe-area-inset-right)) max(16px,env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left));background:rgba(7,12,10,.9);cursor:zoom-out} .image-lightbox img{display:block;max-width:min(100%,1600px);max-height:calc(100vh - 32px);object-fit:contain;border-radius:8px;box-shadow:0 24px 80px rgba(0,0,0,.35);cursor:default} .image-lightbox-close{position:fixed;top:max(16px,env(safe-area-inset-top));right:max(16px,env(safe-area-inset-right));z-index:10000;display:grid;place-items:center;width:40px;height:40px;padding:0;border:0;border-radius:999px;color:#fffaf0;background:rgba(24,33,29,.72);box-shadow:0 8px 24px rgba(0,0,0,.28);cursor:pointer} .image-lightbox-close svg{display:block;width:18px;height:18px;stroke:currentColor;stroke-width:2;fill:none} .image-lightbox-close:hover{background:rgba(24,33,29,.92)} `; const IMAGE_LIGHTBOX_CLOSE_ICON = ''; const IMAGE_LIGHTBOX_SCRIPT = ``; function escapeHtml(text) { return String(text ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function previewDocument( title, bodyHtml, { downloadUrl = null, extraHead = '', allowScripts = false, bodyClass = '' } = {}, ) { const csp = [ "default-src 'none'", "style-src 'unsafe-inline'", "img-src 'self' data: https:", "font-src 'self' data:", "frame-src 'self'", "object-src 'self'", "base-uri 'none'", "form-action 'none'", allowScripts ? "script-src 'unsafe-inline'" : "script-src 'none'", ].join('; '); const downloadLink = downloadUrl ? `
` : ''; const bodyAttrs = bodyClass ? ` class="${escapeHtml(bodyClass)}"` : ''; return `$1');
html = html.replace(/\*\*(.+?)\*\*/g, '$1');
html = html.replace(/\*(.+?)\*/g, '$1');
html = html.replace(/\[([^\]]+)]\(([^)]+)\)/g, '$1');
return html;
}
function renderMarkdownDocument(text) {
const lines = String(text ?? '').split('\n');
const blocks = [];
let inCode = false;
let code = [];
for (const line of lines) {
if (line.startsWith('```')) {
if (inCode) {
blocks.push(`${escapeHtml(code.join('\n'))}`);
code = [];
inCode = false;
} else {
inCode = true;
}
continue;
}
if (inCode) {
code.push(line);
continue;
}
if (!line.trim()) continue;
const heading = line.match(/^(#{1,6})\s+(.+)$/);
if (heading) {
const level = heading[1].length;
blocks.push(`${renderInlineMarkdown(line)}
`); } if (inCode && code.length) { blocks.push(`${escapeHtml(code.join('\n'))}`);
}
return blocks.join('\n');
}
function parseCsvRows(text) {
const rows = [];
let row = [];
let cell = '';
let inQuotes = false;
for (let i = 0; i < text.length; i += 1) {
const ch = text[i];
const next = text[i + 1];
if (inQuotes) {
if (ch === '"' && next === '"') {
cell += '"';
i += 1;
} else if (ch === '"') {
inQuotes = false;
} else {
cell += ch;
}
continue;
}
if (ch === '"') {
inQuotes = true;
continue;
}
if (ch === ',') {
row.push(cell);
cell = '';
continue;
}
if (ch === '\n') {
row.push(cell);
rows.push(row);
row = [];
cell = '';
continue;
}
if (ch === '\r') continue;
cell += ch;
}
row.push(cell);
rows.push(row);
return rows.filter((item) => item.some((value) => String(value ?? '').trim()));
}
function renderCsvPreview(text) {
const rows = parseCsvRows(String(text ?? ''));
if (rows.length === 0) return '(空文件)'; const [head, ...body] = rows; const header = `
${escapeHtml(buffer.toString('utf8'))}`, { downloadUrl });
}
if (mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
const text = extractDocxText(buffer);
const body = text
? `${escapeHtml(paragraph)}
`) .join('')}