Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pulpitum

Part of the Petrarca Project

Pulpitum is the distributed digital library front-end of the Petrarca Project: an Astro static site that turns a folder of exported document editions (HTML transcription + PDF facsimile + TEI-XML source) into a browsable, static, dependency-free digital library — a synchronized side-by-side reading room where a diplomatic HTML transcription and its original page-image facsimile stay in lockstep as you read.

This project is the standalone, directly-editable engine behind that library. It is also bundled as-is inside Praelum, the Electron app that validates a set of exported document folders and runs this engine's build for you — but nothing here depends on Praelum, and the engine can be built and run entirely on its own.

A Pulpitum demo is here.


Table of Contents


Data model

Pulpitum reads its content from two folders at the project root, populated before running astro build (normally by exporting from Scriptorium, or by copying in already-exported document folders):

  • /json — one metadata file per document, named [format].[uuid].[name].json, where format is html (a fully readable edition, with transcription) or another format for facsimile-only entries. An html document's JSON declares uuid, title, language, htmlPath, pdfPath, and xmlPath; other formats only need pdfPath and xmlPath.
  • /repo — the actual files referenced by the JSON metadata, split into html/, pdf/, and xml/ subfolders.

Only html-format documents get a reading page generated for them (loadHtmlDocuments() in src/lib/metadata.js); at build time, each one's HTML transcription is read directly off disk and embedded into its static page, while the PDF is left to be fetched by the browser at runtime from its statically-served URL.


Library index

The homepage (src/pages/index.astro) lists every available html-format document: title, language tag, and a warning badge if the PDF or XML companion file declared in its metadata is missing. If /json is empty, it shows a hint to either run the build helper's dummy-content generator or export real documents from Scriptorium.


Document viewer

Each document gets its own statically pre-rendered page at /doc/[uuid] (getStaticPaths iterates every html-format entry in /json), hosting the DocumentViewer Vue island: a split-panel reading view with the HTML transcription on one side and the PDF facsimile on the other, kept in sync.

  • Draggable divider — the split between the two panels can be resized by dragging (mouse, touch, or arrow keys when focused), clamped between 25% and 75% so neither panel disappears entirely.
  • Bidirectional page sync — scrolling the HTML transcription past a page break moves the PDF to the matching page, and navigating the PDF scrolls the HTML transcription to the corresponding page break; each viewer tags its own updates with a source identifier so it never re-applies a page change it just caused itself (no feedback loops).
  • Responsive single-view mode — below a 992px-wide panel (not window — the check is based on the actual rendered panel width, since a panel in a resized split can be narrow even on a wide screen), the layout switches to one full-width panel at a time with a toggle button ("Show facsimile" / "Show text") instead of the draggable split.
  • Shared toolbar (SynchronizationControls) — back-to-index link, document title/language, a page indicator using the philological verso/recto convention (labelled V./R.), download links for the HTML/PDF/XML source files, and pop-out links to open either panel alone in its own window.

HTML reading panel

HtmlViewer is a read-only adaptation of Scriptorium's own document Viewer, reused directly rather than reimplemented: the search functionality, sidebar/TOC navigation, TEI-list browsing, header-metadata modal, and TEI processing logic are carried over largely verbatim from Scriptorium. The differences from the Scriptorium original:

  • The HTML content arrives as a prop (read from /repo/html at Astro build time) rather than from a Pinia store or Electron API — Pulpitum has no application state or IPC layer, since it's a static site.
  • Note editing is removed entirely — no note toolbar, no add/edit/delete logic. Notes already embedded in the exported TEI/HTML remain visible (click the note marker to view it), but Pulpitum is a reading surface, not an editing one.
  • Page-sync integration is added — the panel watches its own active page-break index and emits a page-change event upward, and exposes a goToPage() method so the parent DocumentViewer can drive it from the PDF side.
  • The container sizing was changed from a fixed full-viewport size (meant for Scriptorium's dedicated child windows) to height: 100%, so it correctly fills its half of the split-panel layout instead.

PDF panel

PdfViewer never talks to a PDF rendering library directly — it goes through a small internal adapter abstraction (src/components/pdf-viewer/PdfAdapter.js), currently implemented on top of PDF.js. The component itself only owns the container mount point, the toolbar (page indicator, zoom in/out, fit-to-width), and the same page-sync contract as the HTML panel (page-change event, goToPage() method). Isolating the concrete rendering engine behind this adapter is a deliberate choice to leave room for swapping in a different viewer technology later (the code comments specifically call out IIIF/Universal Viewer as a possible future backend) without touching the surrounding sync or toolbar logic.


Pop-out windows & cross-window sync

Every reading page also gets two statically pre-rendered pop-out variants — /view/html/[uuid] and /view/pdf/[uuid] — each showing just one panel, full-screen, in its own minimal layout (PopupLayout.astro). These are reachable from the main document page's toolbar ("Isola: HTM / PDF") and open in a new browser tab/window via standard links.

The page-sync mechanism (src/lib/sync.js) is built to keep these pop-outs in step with each other and with the main split view, using two layers:

  1. A per-document, module-scoped Vue ref shared by any viewers mounted in the same page (the two halves of the main split view).
  2. A BroadcastChannel, one per document uuid, that relays page-change messages to any other open browser tab/window showing the same document — so opening the HTML transcription and the PDF facsimile in two separate pop-out windows still keeps them synchronized as you navigate either one. Environments without BroadcastChannel support simply fall back to same-page-only sync.

Build helper

scripts/build-helper.js is a small CLI used both directly and as part of the Astro build itself:

node scripts/build-helper.js generate      # scaffold a 3-page dummy document (HTML + PDF + XML + JSON)
node scripts/build-helper.js validate       # check /json ↔ /repo consistency before building
node scripts/build-helper.js sync-public    # copy /repo and /json into /public (served statically)
  • generate produces a complete dummy document (a short "Fedro" text) — TEI-shaped HTML (<span class="tei-X" data-tag="X" data-*="...">, matching Scriptorium's own convertXmlStringToHtml() output format), a matching 3-page PDF built with pdf-lib (numbered pages, so end-to-end page-sync is actually testable), a TEI-XML source, and the corresponding JSON metadata entry — useful for developing or testing Pulpitum without needing a real exported edition on hand.
  • validate is the same internal consistency check Praelum's own validator performs, re-run here as a final safety net directly before astro build.
  • sync-public copies /repo and /json into Astro's public/ folder so they're included as static assets in the final build output; this runs automatically as part of npm run build.

Getting started

npm install
npm run generate:dummy   # optional: scaffold a sample document to preview the reading experience
npm run dev              # local dev server
npm run build             # sync:public + astro build → dist/
npm run preview           # serve the built dist/ locally

To populate the library with real content instead of the dummy document, place your exported [format].[uuid].[name].json files in /json and their referenced HTML/PDF/XML files under the matching /repo/html, /repo/pdf, /repo/xml folders, then run npm run validate followed by npm run build.


Tech Stack

  • Site generator: Astro (static output, per-document static paths via getStaticPaths)
  • Interactive islands: Vue 3 (@astrojs/vue), hydrated with client:load
  • PDF rendering: PDF.js (pdfjs-dist), behind an internal adapter abstraction
  • Cross-window sync: BroadcastChannel Web API
  • Build tooling: pdf-lib (dummy-content generation only, dev dependency)

Project Structure

Pulpitum/
├── astro.config.mjs
├── scripts/
│   └── build-helper.js         # generate / validate / sync-public CLI
├── src/
│   ├── layouts/
│   │   ├── BaseLayout.astro    # Shared page shell (library index, document pages)
│   │   └── PopupLayout.astro   # Minimal shell for pop-out single-panel windows
│   ├── pages/
│   │   ├── index.astro         # Library index
│   │   ├── doc/[uuid].astro    # Main split-panel document reading page
│   │   └── view/
│   │       ├── html/[uuid].astro   # Pop-out: HTML panel only
│   │       └── pdf/[uuid].astro    # Pop-out: PDF panel only
│   ├── components/
│   │   ├── DocumentViewer.vue         # Split layout, drag divider, responsive mode, sync wiring
│   │   ├── SynchronizationControls.vue # Toolbar: page indicator, downloads, pop-out links
│   │   ├── PdfViewer.vue               # PDF panel (toolbar + adapter-based rendering)
│   │   ├── PdfPopupViewer.vue          # PDF-only pop-out variant
│   │   ├── HtmlPopupViewer.vue         # HTML-only pop-out variant
│   │   ├── pdf-viewer/
│   │   │   └── PdfAdapter.js           # PDF.js-backed rendering adapter (swappable)
│   │   └── html-viewer/
│   │       ├── HtmlViewer.vue          # Read-only adaptation of Scriptorium's Viewer
│   │       ├── Sidebar.vue             # TOC / TEI-list navigation
│   │       ├── SearchSection.vue       # Full-text search
│   │       ├── HeaderModal.vue         # Document metadata (teiHeader) display
│   │       ├── NoteDisplay.vue         # Read-only note viewing
│   │       ├── modules/
│   │       │   ├── teiProcessor.js
│   │       │   ├── teiHeaderProcessor.js
│   │       │   ├── userNotes.js
│   │       │   ├── search.js
│   │       │   └── utils.js
│   │       └── styles/
│   │           ├── styles.css
│   │           └── tei-header.css
│   └── lib/
│       ├── metadata.js         # Build-time /json + /repo access (loadHtmlDocuments, etc.)
│       └── sync.js             # Page-sync state: same-page ref + BroadcastChannel
├── json/                        # Document metadata (populated before build)
├── repo/                        # html/ pdf/ xml/ source files (populated before build)
└── package.json

About

Pulpitum is the distributed digital library front-end of the Petrarca Project: an Astro static site that turns a folder of exported document editions into a browsable, static, dependency-free digital library

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages