Toggling HtmlPreview's securityMode updated the iframe `sandbox`
attribute but left `src` unchanged, so the browser did not reload
the iframe content — the new sandbox policy was never actually
applied. In `safe → trusted`, scripts stayed blocked; in the
reverse direction, already-running scripts kept running.
Use `key={securityMode}` to force React to unmount/remount the
iframe on mode switch, guaranteeing the new sandbox takes effect
on a freshly loaded document.
Also guard against the empty-`src` warning: render the iframe
only after `blobUrl` is set (the blob URL effect runs after the
first paint), instead of passing `src=""` on the first frame.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Build a FileInfo for a remote URL using metadata the caller already has
(name + size), so LargeFileGate can apply warning / confirm / block
thresholds before any network traffic starts. Unlike processRemoteUrl(),
this does not fetch — the download happens lazily when a preview plugin
calls readSourceAsArrayBuffer(source).
Trade-offs vs processRemoteUrl(): no magic-byte MIME sniffing (extension
only, or caller-supplied mimeType), no onProgress, no built-in 100 MB
hard cap (caller controls via largeFilePolicy.maxBytes), errors are
plain Error instead of typed RemoteUrlError.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Patch-level changeset covering the blob URL leak fix, ExcelJS ESM interop
fix, and the new HTML security toggle. Bumps
@lamberl-lee/file-preview from 0.5.0 → 0.5.1 once the Version Packages PR
is merged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three issues addressed:
1. HtmlPreview leaked blob URLs when `content` changed. The old `useMemo`
returned a fresh URL each render but the `useEffect` cleanup ran on
`[blobUrl]` so React StrictMode's double-invoke revoked URLs that were
still mounted, causing iframe flashes. Switched to `useState + useEffect`
with `[content]` deps so cleanup runs exactly once per content change.
2. ExcelJS dynamic import returned a `{ default: Module }` wrapper under
some bundler configs, making `EJS.Workbook` undefined and crashing
XLSX preview. Now resolves `mod.default?.Workbook ?? EJS.Workbook`
with a typed cast to handle both ESM-namespace and CJS-wrapper shapes.
3. HtmlPreview's `securityMode` was `useState<HtmlSecurityMode>("safe")`
with no setter exported — users had no way to enable scripts for trusted
HTML files. Added an explicit toggle in the view-mode bar (default
"safe", opt-in "trusted"). When trusted, a warning banner explains the
risk. Three new i18n strings: htmlEnableScripts / htmlDisableScripts /
htmlTrustedModeHint (zh-CN and en-US).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Covers the minor bump for the configurable largeFilePolicy API (5f88b11)
and the patch-level detached-ArrayBuffer fix (14a429f). Changesets bot
will open a "Version Packages" PR on push to main that bumps
@lamberl-lee/file-preview from 0.4.0 → 0.5.0 and regenerates CHANGELOG.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two demo-app improvements:
1. fetchBinaryDemoFiles previously logged `console.error` and skipped any
file under public/demo/ whose head looked like HTML — including the
legit `order-detail.html` preview target. HTML now flows through as
UTF-8 text (encoding: "utf8"), and page.tsx encodes it to an ArrayBuffer
via TextEncoder so it fits the same arrayBuffer source shape.
2. PluginPreviewRenderer's largeFilePolicy was the default 20/50/100 MB.
For a demo that ships small fixtures this is too lax — bumped down to
2 MB warn / 3 MB confirm / 5 MB block so users can see all three gate
states without needing multi-MB uploads.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`readSourceAsArrayBuffer` returned `source.buffer` directly for arrayBuffer
sources. Downstream consumers (pdf.js via `getDocument({ data })`) transfer
the buffer to a Web Worker, which detaches the original ArrayBuffer. A second
read on the same source — React StrictMode double-invoke, file re-selection,
source reuse across previews — then crashed with:
TypeError: Cannot perform Construct on a detached ArrayBuffer
The `file` and `blob` branches already returned fresh ArrayBuffers per call
(`File.arrayBuffer()` / `Blob.arrayBuffer()`); the arrayBuffer branch now
matches that behavior via `source.buffer.slice(0)`.
Tests cover both the copy semantics and the post-detach re-read path
(simulates worker transfer via `MessageChannel.postMessage(..., [buf])`).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Records the Stage 18.2 follow-up to the large file gate: configurable
LargeFilePolicy API, onError/renderLargeFileFallback props, i18n
externalization, downloadSource refactor, and bug fixes found in review
(blockReportedRef dedup, mimeType override, validate Infinity/NaN guard).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Verify Office demo archives (.xlsx/.docx/.pptx/.epub) are valid
ZIP packages during CI build, catching corruption before deploy
- Detect HTML responses in fetchBinaryDemoFiles to surface path
misconfiguration or 404 fallback pages early
- Log response status/statusText/url on fetch failure for easier
debugging
GitHub Pages deployed LFS pointer files (xlsx, pptx, docx, pdf, epub)
instead of actual binaries because actions/checkout@v4 defaults lfs:
to false. Browsers received the pointer text (200 OK), causing JSZip
"Can't find end of central directory" errors.
- Enable lfs: true in the checkout step
- Add verification step that scans public/demo/ for leftover pointers
- Add frontend LFS guard in fetchBinaryDemoFiles to skip/raise on
pointer content
- Improve FileReader error handling with explicit reject paths
Address PPTX review findings (issues 1-10):
- Reset semanticDeck/insight/isModeSwitching at source-mount start to
prevent cross-file fallback state bleed.
- View-mode effect depends only on viewMode via activeViewModeRef,
eliminating duplicate render after activeViewMode catches up.
- Enforce per-slide + total XML code-unit limits (break, not warn) in
readPptxInsight and readPptxSemanticDeck; rename constants to
...CodeUnits to reflect UTF-16 semantics.
- Skip readPptxInsight when semantic deck succeeds (avoid double parse).
- orderSlidesByPresentation appends rels-unmatched slides instead of
dropping them; readAttribute accepts single-quoted XML attrs.
- Sync latest initialZoom onto viewer after open so mid-load prop
updates are not lost.
- Promote safelyInvoke to core/ and apply to PluginPreviewRenderer
onError, PreviewErrorBoundary, and PptxPreviewAdapter reportError.
- Localized fallback description as primary text; raw error.message
moved into expandable details; add color-mix rgba fallback.
- Clean root entry: remove builtin/heavy plugin exports so the module
graph no longer reaches optional peers; heavy plugins live under
/full and /plugins/*. Playground switched to createFullPreviewRegistry.
- Bump version to 0.4.0; update README with base/full split and
migration note; add CHANGELOG entry.
Co-Authored-By: Claude <noreply@anthropic.com>
- order-slides.test.ts: 6 unit tests covering sldIdLst-driven ordering,
filename-fallback when sldIdLst/rels are missing, missing slide skip,
Target path normalisation, and unknown-rId fallback.
- PptxPreview.lifecycle.test.tsx: 4 jsdom tests using a vi.mock'd
@aiden0z/pptx-renderer to verify:
* destroy() is called when PptxViewer.open() rejects mid-construction
* a thrown onReady does NOT trigger fallback or onError
* renderList failure triggers onError (mode rollback path)
* onError fires exactly once after fallback completes
Co-Authored-By: Claude <noreply@anthropic.com>