feat(viewer): preview text by its bytes, not its extension — and render PDFs (#95)

The viewer decided what to preview from a filename regex, so every
extensionless file an agent writes — Dockerfile, LICENSE, .bdriveignore —
and every unlisted extension (main.tf, schema.graphql) hit a dead "No
preview for this file type." card. That bites hardest in the core use
case: an agent writes an artifact, a teammate opens the hub to read it,
and the hub refuses.

The unmatched path now fetches once and decides on the bytes. The logic
already existed for the history diff — 1 MB cap, Content-Length cheap-out,
8 KB NUL scan, fatal UTF-8 decode — so the pure half moves to
lib/sniff.ts (importable by node --test, no React Query) and both
DiffView and FileView call it. Exactly one sniffer, per the spec.

.pdf gets the browser's own viewer in an iframe, in the wide page column
(768px is unreadable for a PDF page). No sandbox attribute, deliberately:
the PDF viewer is not this page's JS realm, so it cannot reach the hub
API or its cookies, and sandboxing without allow-same-origin breaks
Firefox's pdf.js.

Files that already previewed (md/html/img/.txt) are untouched and issue
no extra fetch.

BEA-44
This commit is contained in:
Snow Lee (Sungwon)
2026-07-30 16:25:21 +09:00
committed by GitHub
parent 7b863a4684
commit 951c6b9de2
12 changed files with 262 additions and 54 deletions
+4 -2
View File
@@ -62,9 +62,9 @@ classDiagram
+useConfig
+useHub
+useBrowse
+useBlobText (sha-keyed, immutable)
+useTextAt (any URL) → useBlobText (sha-keyed, immutable)
}
note for hooks "TanStack Query wrappers over the viewer APIs"
note for hooks "TanStack Query wrappers over the viewer APIs. useTextAt fetches any URL and sniffs it — the Content-Length cheap-out lives here (HTTP), the byte decision in lib/sniff.ts (pure). A live path must not be cached immutable; a sha can be"
class components {
FileView FolderListing FileTree
@@ -78,6 +78,7 @@ classDiagram
class lib {
+diff.ts splitLines lcsDiff diffText
+sniff.ts sniffBytes BlobText MAX_BYTES
+utils.ts
}
note for lib "pure, no React, unit-tested on node (npm test) — the line diff is ~40 lines, cheaper than auditing a diff package"
@@ -92,6 +93,7 @@ classDiagram
HubApp --> components
components --> nav : linkProps navigate
components --> lib : diffText
hooks --> lib : sniffBytes
hooks --> api
Browser --> hooks
HubApp --> hooks