Sniff docx ZIP magic and fall back to text rendering
Why: agent-generated ".docx" artifacts were historically plain text bytes written via string-only write tools; the docx preview loader fed them straight to mammoth/JSZip, which threw "Can't find end of central directory" on the meeting-notes docx from the review session. Evidence: docxLoader now checks the PK\x03\x04 (or empty-archive PK\x05\x06) magic before mammoth; non-ZIP bytes render as markdown with a readable note instead of an error. vitest 33/33 pass, tsc --noEmit clean. Impact: stock fake-docx artifacts become readable immediately; real docx previews unchanged. Rendering only, no API/contract change. Refs: .task/feature-preview-sniff-fallback.md
This commit is contained in:
+29
-2
@@ -2,6 +2,7 @@
|
||||
// 全部 dynamic import——对应格式首次预览时才下载 chunk,主 bundle 不含解析器。
|
||||
// 链接预览 fetcher 走服务端 /api/url-preview(SSRF 防护在服务端执行端)。
|
||||
import { configureFilePreviewLoaders, configureLinkPreviewFetcher } from '@ui'
|
||||
import { marked } from 'marked'
|
||||
import { auth } from './auth'
|
||||
import type { LinkPreviewData } from '@ui'
|
||||
|
||||
@@ -11,9 +12,35 @@ const fetchBuf = async (url: string | undefined): Promise<ArrayBuffer> => {
|
||||
return r.arrayBuffer()
|
||||
}
|
||||
|
||||
const docxLoader = async (el: HTMLElement, source: { contentUrl?: string }) => {
|
||||
// docx/OOXML 是 ZIP 容器,真文件必以 PK 魔数开头(PK\x03\x04 常规,PK\x05\x06 空包)。
|
||||
// 智能体生成侧可能写出"内容是文本的伪 docx",先嗅探再喂 mammoth:
|
||||
// 非 ZIP 直接按文本/markdown 回退渲染,不再抛 JSZip 底层错误。
|
||||
const isZipBytes = (buf: ArrayBuffer): boolean => {
|
||||
if (buf.byteLength < 4) return false
|
||||
const h = new Uint8Array(buf, 0, 4)
|
||||
return h[0] === 0x50 && h[1] === 0x4b &&
|
||||
((h[2] === 0x03 && h[3] === 0x04) || (h[2] === 0x05 && h[3] === 0x06))
|
||||
}
|
||||
|
||||
const renderTextFallback = (el: HTMLElement, buf: ArrayBuffer, name?: string) => {
|
||||
const note = document.createElement('div')
|
||||
note.className = 'ui-fp-note'
|
||||
note.textContent = `「${name ?? '该文件'}」不是有效的 docx 文件(缺少 ZIP 结构),已按文本内容展示;需要正式 Word 格式请让智能体以 docx 格式重新生成。`
|
||||
const body = document.createElement('div')
|
||||
body.className = 'doc-viewer'
|
||||
body.innerHTML = marked.parse(new TextDecoder().decode(buf)) as string
|
||||
el.innerHTML = ''
|
||||
el.append(note, body)
|
||||
}
|
||||
|
||||
const docxLoader = async (el: HTMLElement, source: { contentUrl?: string; name?: string }) => {
|
||||
const buf = await fetchBuf(source.contentUrl)
|
||||
if (!isZipBytes(buf)) {
|
||||
renderTextFallback(el, buf, source.name)
|
||||
return
|
||||
}
|
||||
const mammoth = await import('mammoth')
|
||||
const { value } = await mammoth.convertToHtml({ arrayBuffer: await fetchBuf(source.contentUrl) })
|
||||
const { value } = await mammoth.convertToHtml({ arrayBuffer: buf })
|
||||
el.innerHTML = value
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user