Skip to content

Architecture

Papyrus is split into small layers so UI and engine can evolve independently.

Packages

PackageRole
@papyrus-sdk/typesShared contracts (DocumentEngine, Annotation, events).
@papyrus-sdk/coreStore + events (useViewerStore, papyrusEvents).
@papyrus-sdk/engine-pdfjsWeb engine adapter on top of PDF.js.
@papyrus-sdk/engine-cbzWeb CBZ adapter using ZIP/zip.js.
@papyrus-sdk/engine-cbrWeb CBR adapter using RAR/libarchive.
@papyrus-sdk/engine-cbz-rustExperimental CBZ adapter using Rust/WASM with a zip.js fallback.
@papyrus-sdk/ui-reactWeb UI for reader, search, and navigation.
@papyrus-sdk/engine-nativeiOS/Android bridge (PDFKit, PDFium).
@papyrus-sdk/ui-react-nativeMobile UI with sheets and toolbars.

Data flow

  1. Engine loads the document and exposes page count, outline, text.
  2. UI components call engine methods and update store state.
  3. papyrusEvents emits lifecycle and annotation events for your app.

Engine agnostic

UI components never import PDF.js or native code directly. They talk to the DocumentEngine interface so you can swap engines without changing UI logic.

For comics, the same boundary supports CBZEngine, CBREngine, and the experimental Rust/WASM CBZ adapter. The engine=rust-cbz demo route uses Rust to list and extract CBZ pages and falls back to zip.js if WASM initialization fails. The regular engine=cbz route remains available as the baseline.

CBR still uses libarchive.js. A Rust RAR/CBR engine is not integrated yet and requires a separate benchmark for compatibility, extraction, memory, and artifact size before it should replace the current path.

React Native monorepo integration

The SDK does not own the consumer app's Metro configuration. In a pnpm monorepo, configure Metro so @papyrus-sdk/core resolves to one physical installation shared by the app and @papyrus-sdk/ui-react-native (for example, with an explicit extraNodeModules/resolveRequest mapping). Duplicate core instances create separate viewer stores: the document can report a valid page count while the native PDF viewer remains blank because it is observing a different store. After changing package versions or Metro configuration, restart Metro with a clean cache and rebuild the native app.