Skip to content

Commit 90252e1

Browse files
phodalcodex
andcommitted
feat(studio): render PDF and virtualize large workbooks
Implements docs/specs/2026-08-24-studio-pdf-fbx-and-large-workbook-previews.md with a revision-bound PDF.js surface, bounded canvas allocation, and virtualized XLSX rows. Validated on Node 24 with Harness 162 tests, Studio 243 tests, 35 Playwright tests, package verification, and three responsive browser layouts. Co-authored-by: Codex (GPT 5.6 Sol) <codex@openai.com>
1 parent fabfc7e commit 90252e1

23 files changed

Lines changed: 986 additions & 15 deletions
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Studio PDF, FBX, and Large Workbook Previews
2+
3+
Status: Implemented
4+
Date: 2026-08-24
5+
6+
## Problem
7+
8+
Artifact View already dispatches through server-owned adapter and surface bindings, but PDF has no native binding, FBX has no activated Homology Provider contribution, and the XLSX projection drops cells outside a 200 by 64 preview window. Those gaps make the workbench look format-specific even though its extension points are general.
9+
10+
## Contract
11+
12+
- ArtifactView remains a renderer-agnostic dispatcher. It must not inspect PDF, FBX, Office, or Canvas payload details.
13+
- PDF is a Studio-owned, read-only data surface. The adapter binds the exact catalog revision, exposes only the PDF bytes plus bounded page metadata, and the browser renders pages with PDF.js. PDF JavaScript, form actions, attachments, and external network access are not executed.
14+
- FBX is a Provider-owned external-hosted surface. The Homology Provider binds the exact catalog revision, parses it with `@homology/diagram-fbx`, and owns the rendered document. Better Harness only validates and mounts the common opaque hosted surface.
15+
- XLSX remains a Studio-owned read-only data surface. The projection may expose up to the existing populated-cell budget, and the client virtualizes rows so a large sparse worksheet does not create one DOM row for every worksheet row.
16+
- Revision, adapter, renderer, hosted-runtime, provider fingerprint, capability, and security-profile identity continue to decide surface retention. Late data from an older revision must not replace the current view.
17+
18+
## Acceptance criteria
19+
20+
1. A real multipage PDF appears as a native Artifact View with page count, page navigation, zoom, keyboard operation, and virtualized page mounting.
21+
2. PDF bytes are served only from an immutable revision resource URI. Oversized files, excessive page counts, malformed files, password-protected files, and revision drift fail closed with a browser-safe diagnostic.
22+
3. A real ASCII or binary FBX appears through the existing external-hosted iframe lane, includes mesh/vertex/polygon metadata, and remains usable without adding an FBX branch to `ArtifactView.tsx` or the Studio surface registry.
23+
4. FBX provider activation is explicit and receipt-bound. Ordinary TSX/JSX, Canvas TSX, diagrams, notebooks, Office files, and unknown files retain their existing resolution rules.
24+
5. XLSX retains populated cells beyond row 200, renders only the visible row window plus overscan, preserves sheet and cell selection across compatible revisions, and supports keyboard navigation to an off-screen row.
25+
6. Unit tests cover model validation, catalog resolution, exact-revision resource reads, fail-closed inputs, provider receipts, and renderer selection. Browser tests cover wide, compact, and narrow layouts with no page errors, console errors, or unintended horizontal page overflow.
26+
27+
## Limits and non-goals
28+
29+
- This work is read-only. It does not add PDF editing, Office writeback, formula recalculation, chart/pivot fidelity, or native Office parity.
30+
- The FBX projection is a bounded model preview, not a claim of skinning, animation, audio, official Cursor parity, or pixel identity with a DCC application.
31+
- The opaque hosted provider profile remains network-denied. Hostile remote providers and publication certification remain separate trust and release work.
32+
33+
## Verification
34+
35+
- Better Harness Node 24 generated-code check, Harness build/Vitest (19 files, 162 tests), Studio build/Vitest (40 files, 243 tests), Playwright (35 tests), and package verification passed.
36+
- Homology `diagram-fbx` build and integration Provider check passed (4 files, 13 tests; pack 1,851,262 bytes / 6,056,207 bytes unpacked / 11 entries), plus the cross-repository `verify:studio` route check.
37+
- A real three-page PDF, binary FBX, and generated 420-row XLSX returned exact-revision snapshots/resources; PDF and XLSX used native bindings, while FBX used the receipt-bound opaque hosted Provider.
38+
- Browser inspection at 1440x900, 1024x768, and 390x844 found zero page overflow and zero final console warning/error. PDF rendered real canvases and navigated to page 2; XLSX mounted 37 visible rows and materialized A420 only near the bottom; FBX pan/zoom controls changed scale from 1.0 to 1.2.

package-lock.json

Lines changed: 278 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/harness-studio/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"esbuild-wasm": "0.28.1",
5252
"fast-xml-parser": "^5.11.0",
5353
"fflate": "^0.8.3",
54+
"pdfjs-dist": "^6.2.108",
5455
"react": "19.2.8",
5556
"react-dom": "19.2.8"
5657
},

packages/harness-studio/scripts/build-app.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ await Promise.all([
4545
copyFile(join(packageRoot, "src", "app", "styles", file), join(appDir, "assets", file)),
4646
),
4747
copyFile(join(inspectorAssetRoot, "workbench.css"), join(appDir, "assets", "inspector-workbench.css")),
48+
copyFile(join(repositoryRoot, "node_modules", "pdfjs-dist", "legacy", "build", "pdf.worker.mjs"), join(appDir, "assets", "pdf.worker.mjs")),
4849
]);
4950
process.stdout.write(`Built studio app into ${appDir}\n`);
5051
process.exit(0);

packages/harness-studio/src/app/artifacts/ArtifactSurfaceRegistry.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { DocxArtifactView } from "./docx/DocxArtifactView.js";
55
import { ExternalHostedArtifactView } from "./ExternalHostedArtifactView.js";
66
import { ImageArtifactView } from "./ImageArtifactView.js";
77
import { PptxArtifactView } from "./pptx/PptxArtifactView.js";
8+
import { PdfArtifactView } from "./pdf/PdfArtifactView.js";
89
import { TextArtifactView } from "./TextArtifactView.js";
910
import { XlsxArtifactView } from "./xlsx/XlsxArtifactView.js";
1011
import type { ArtifactSurfaceKind, ArtifactSurfaceMount } from "./ArtifactSurface.js";
@@ -46,6 +47,11 @@ export const ARTIFACT_SURFACE_MOUNTS: readonly ArtifactSurfaceMount[] = Object.f
4647
matches: (artifact) => normalizeArtifactSurfaceKind(artifact) === "native" && artifact.renderer.id === "studio.docx-dom",
4748
Component: DocxArtifactView,
4849
},
50+
{
51+
id: "studio.pdf-canvas",
52+
matches: (artifact) => normalizeArtifactSurfaceKind(artifact) === "native" && artifact.renderer.id === "studio.pdf-canvas",
53+
Component: PdfArtifactView,
54+
},
4955
{
5056
id: "studio.pptx-dom",
5157
matches: (artifact) => normalizeArtifactSurfaceKind(artifact) === "native" && artifact.renderer.id === "studio.pptx-dom",

0 commit comments

Comments
 (0)