@dvina/artifact-renderer
v0.0.40
Published
Framework-agnostic SDK for embedding the Dvina Artifact Renderer in an iframe.
Readme
@dvina/artifact-renderer
@dvina/artifact-renderer is an iframe-first SDK for embedding the Dvina Artifact Renderer inside your application.
It is designed for products that need to preview user uploads, generated artifacts, and private documents without rendering untrusted content directly inside the host app DOM.
Install
npm install @dvina/artifact-rendererImportant
This package is the host-side SDK. You also need to deploy the self-hosted viewer image to a dedicated origin such as https://viewer.example.com/.
docker run --pull always -p 8080:8080 kantist/artifact-renderer-viewer:latestQuick Start
import { createViewer, type ViewerDocument } from "@dvina/artifact-renderer";
const container = document.querySelector("#viewer-slot");
if (!(container instanceof HTMLElement)) {
throw new Error("Viewer container not found.");
}
const documentToPreview: ViewerDocument = {
id: "artifact-123",
name: "analysis.pdf",
format: "pdf",
source: {
kind: "url",
url: "https://cdn.example.com/previews/analysis.pdf",
},
};
createViewer({
container,
viewerUrl: "https://viewer.example.com/",
allowedOrigin: "https://viewer.example.com",
document: documentToPreview,
});Private Files
For private files, use a session-backed document and provide getSession().
createViewer({
container,
viewerUrl: "https://viewer.example.com/",
allowedOrigin: "https://viewer.example.com",
document: {
id: "private-doc-7",
name: "board-packet.docx",
format: "docx",
source: {
kind: "session",
sessionId: "board-packet-session",
url: "https://api.example.com/viewer/files/private-doc-7/preview",
},
},
getSession: async () => {
const response = await fetch("https://api.example.com/viewer/sessions", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
documentId: "private-doc-7",
scope: "artifacts:read",
}),
credentials: "include",
});
if (!response.ok) {
throw new Error("Failed to create viewer session.");
}
return response.json();
},
});Main API
createViewer(options)controller.loadDocument(document, options?)controller.setTheme(theme)controller.setSafeArea(safeArea)controller.goToPage(page)controller.sendArtifactData(data, options?)controller.destroy()controller.getDiagnostics()
Theme Updates
Pass theme to createViewer(...) for the initial render, then call setTheme(...) when your app theme changes.
const controller = createViewer({
container,
viewerUrl: "https://viewer.example.com/",
allowedOrigin: "https://viewer.example.com",
document: documentToPreview,
theme: { mode: "light" },
});
controller.setTheme({
mode: "dark",
tokens: {
surface: "#0f1115",
text: "#f3f5f8",
},
});setTheme(...) updates the viewer appearance without reloading the current document.
Safe Area
Pass overlay and device insets through createViewer. Each edge is a finite,
non-negative number in CSS pixels; omitted edges default to 0.
const controller = createViewer({
container,
viewerUrl: "https://viewer.example.com/",
allowedOrigin: "https://viewer.example.com",
document: documentToPreview,
safeArea: { top: 64, right: 0, bottom: 34, left: 0 },
});
controller.setSafeArea({ top: 80, bottom: 24 });
controller.setSafeArea({}); // Reset all edges.setSafeArea replaces all edges without reloading the document. The latest value
is retained across document loads, HTML streams, and calls made before readiness.
The consumer should measure overlays and resolve device safe-area values in its
own window (including env(safe-area-inset-*) when needed); these are not read
from the parent window or added automatically.
PDF, PPTX, DOCX, text, Markdown, code, media, and archive viewers apply insets to their scrolling surfaces, so content can scroll beneath an overlaid host header. Spreadsheet headings and the footer respect insets; EPUB reserves space around its navigation and chapter frame.
Bootstrapped HTML applies insets inside the document and exposes
--viewer-safe-area-top/right/bottom/left, window.artifactRenderer.safeArea,
and the dvina-artifact-renderer:safe-area window event. Custom fixed/sticky
layouts or nested scrollers can use these values themselves. Direct-URL HTML,
disabled bootstrap, and script-disabled sandboxes instead inset the iframe
inside its wrapper, because document-level styling is unavailable there.
Scroll progress
const controller = createViewer({
container,
viewerUrl,
allowedOrigin,
document,
scrollProgressDistance: 48, // optional; CSS pixels, default 48
onScroll(progress) {
header.style.setProperty("--title-scroll", String(progress));
},
});progress is how far the document has scrolled from its top, from 0 to 1 over the first
scrollProgressDistance pixels, rounded to hundredths. The viewer follows whichever
element scrolls vertically (the Markdown page, the PDF viewport, a sheet's table, DOCX,
PPTX, code, archives, EPUB); sideways scrolls are ignored. Bootstrapped HTML reports its
own document's scroll through the bridge. It is sent only when it changes, at most once a
frame, so scrolling past the distance sends nothing more, and it resets to 0 when a new
document loads. The same value arrives in onEvent as viewer.scroll; it is not recorded
in diagnostics.
Runtime Notes
- browser-only package
- exact
allowedOriginis required - private files should use short-lived bearer-token sessions
Related Packages
@dvina/artifact-renderer-core@dvina/artifact-renderer-protocol
Documentation
See the repository documentation for:
- React integration
- Angular 20+ integration
- NestJS backend integration
- viewer deployment
- release checklist
