feat(preview): inline .docx rendering via docx-preview (#297) #317

Merged
Cordy merged 4 commits from feat/docx-preview into main 2026-08-27 12:04:45 +00:00
Owner

First slice of #297 (office previews). Renders .docx inline in the preview panel instead of the "No preview — use Download" card.

Approach

  • Vendored, not CDN. vendor-jszip.min.js (JSZip 3.10.1, MIT, 97 KB) and vendor-docx-preview.min.js (docx-preview 0.3.5, MIT, 73 KB) live flat in web/static/, so the release cp web/static/* bundles them into the embedded webdist and Cairn serves them from its own origin (behind auth). No third-party runtime dependency.
  • Lazy-loaded. The two scripts are injected only on the first .docx preview (loadDocxLib()), so initial page load is unchanged. docx-preview's UMD reads a global JSZip and exports global docx, so jszip is loaded first.
  • Rendering. window.docx.renderAsync(blob, host, …) into a scrollable container; 25 MB size guard; every await re-checks the pvPath !== it.path staleness guard (same pattern as the text branch); any failure falls back to the existing pvNoPreview card.
  • Scope. Gated strictly on ext === "docx". .doc/.odt/.rtf still fall through to the card (docx-preview only handles OOXML). xlsx/pptx are the next slices of #297.

Verification

node --check on all page script blocks (passed in the build workflow). Functional rendering needs a logged-in browser with a real .docx, which is the operator's visual check on the dogfood after deploy.

First slice of #297 (office previews). Renders `.docx` inline in the preview panel instead of the "No preview — use Download" card. ## Approach - **Vendored, not CDN.** `vendor-jszip.min.js` (JSZip 3.10.1, MIT, 97 KB) and `vendor-docx-preview.min.js` (docx-preview 0.3.5, MIT, 73 KB) live flat in `web/static/`, so the release `cp web/static/*` bundles them into the embedded `webdist` and Cairn serves them from its own origin (behind auth). No third-party runtime dependency. - **Lazy-loaded.** The two scripts are injected only on the first `.docx` preview (`loadDocxLib()`), so initial page load is unchanged. docx-preview's UMD reads a global `JSZip` and exports global `docx`, so jszip is loaded first. - **Rendering.** `window.docx.renderAsync(blob, host, …)` into a scrollable container; 25 MB size guard; every `await` re-checks the `pvPath !== it.path` staleness guard (same pattern as the text branch); any failure falls back to the existing `pvNoPreview` card. - **Scope.** Gated strictly on `ext === "docx"`. `.doc`/`.odt`/`.rtf` still fall through to the card (docx-preview only handles OOXML). xlsx/pptx are the next slices of #297. ## Verification `node --check` on all page script blocks (passed in the build workflow). Functional rendering needs a logged-in browser with a real .docx, which is the operator's visual check on the dogfood after deploy.
Cordy merged commit 2caaf5faab into main 2026-08-27 12:04:45 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#317
No description provided.