← All examples

PDF Viewer: Inline & Fullscreen

EmbedPDF (@embedpdf/svelte-pdf-viewer v2.15) — the PDFium/WASM engine, virtualized scroll (89-page doc), landscape pages, full-UI toolbar+sidebar, a $state-driven document selector, a fullscreen daisyUI modal (destroy on close), a feature-toggles panel with a Read-only ⇄ Editor master mode (the UX contract: hide mutation affordances — insert, form, redact, comments, protection, close/open document — keep reading affordances), a headless EmbedPDF core viewer (render+scroll+zoom only) as the no-chrome contrast, and an EDITABLE server-persisted document: annotate → autosave → versioned saves on disk → reload persists (real save path via the export plugin’s saveAsCopy), with Ctrl+S/Cmd+S save-intent interception and an honest "Saving… / All changes saved" toast (editor mode only).

Inline viewer — full-UI PDFViewer

viewer: loading… mounts: 0

The viewer mounts client-side (WASM = browser-only). The toolbar includes the document menu, sidebar toggle, zoom controls, page navigation, and search — all driven by the disabledCategories config below. Changing documents or toggles remounts the viewer (the library reads its config once on mount).

Document selector — $state drives the viewer

Short 3-page portrait document (116KB). The minimal case — loads instantly, all pages visible without virtualization pressure.

Switching the document changes the config.src — the {#key} wrapper remounts the PDFViewer component with the new document. Watch the mount counter above increment on each switch.

Fullscreen modal — daisyUI dialog

Opens a modal dialog sized near the viewport. Close via X, Escape, or backdrop click. The viewer is destroyed on close (no orphan canvases).

Unlike the floating-chat window (which should NOT close on backdrop/escape — you don't want to lose an in-progress message), a PDF modal SHOULD close on escape/backdrop — that's standard modal UX. The same document and toggles apply.

Master is a preset over the individual toggles below — it sets them all at once, and any individual mutation toggle flips the master's state back. One behavior, one source of truth.

Feature toggles — disabledCategories

The centerpiece: disabledCategories in the viewer config hides UI elements. Disabling a parent category (e.g. annotation) removes all children. This is the full-UI path — no need to go headless just to hide features. Mutation toggles (first group) start OFF because read-only is the default; reading toggles start ON.

category: document-open disabled
category: annotation disabled
category: insert disabled
category: form disabled
category: redaction disabled
category: annotation-comment disabled
category: document-protect disabled
category: document-close disabled
category: document-print enabled
category: document-export enabled
category: zoom enabled
category: panel-sidebar enabled

Current disabledCategories

["document-open","annotation","insert","form","redaction","annotation-comment","panel-comment","document-protect","security","document-close"]

How local-file loading was disabled

The Captain's explicit example: online viewers should not offer "open file". Open local file is part of the mutation preset, so it starts OFF in read-only mode — the document-open category sits in disabledCategories, and the "Open document" entry is gone from the toolbar's document menu. Flip the master or the individual toggle to see it return. This is the full-UI path: the disabledCategories config removes the UI element without needing to go headless. The headless path (below) achieves the same effect by simply not registering the feature's plugins — but for a single menu entry, the config toggle is the right tool.

Document menu tester — prove the toggle

Browsers only allow real user gestures to open a file picker, so a headless test cannot literally click "Open document" and watch a picker appear. The next best proof: open the toolbar's document menu and check whether the data-epdf-i="document:open" entry is rendered. The viewer renders in an open shadow root, so the menu contents are inspectable.

With "Open local file" enabled you'll see the Open entry; with it disabled the whole entry is gone — not merely greyed out. Close the menu with Escape or by clicking anywhere outside it.

Headless contrast — EmbedPDF core (no chrome)

The full-UI viewer above registers ALL plugins (zoom, search, annotation, print, export, thumbnails, etc.). This headless viewer registers ONLY 4: document-manager, viewport, scroll, and render. No toolbar, no sidebar, no "open file" entry — just rendered pages in a scrollable viewport. The zoom engine works (pinch/scroll) but has no zoom buttons. This is the composition model: you get ONLY what you register.