Turn any PDF into an accessible web publication — audio, translations, structured HTML layouts — all generated from your source file, all editable, all yours.
- Extracts everything — text, figures, layout structure from any PDF.
- AI sectioning — labels chapters, headings, and pedagogic content automatically.
- Smart filtering — keeps what matters, drops what doesn't.
- Complex figure handling — detects and extracts multi-part figures, with multilingual support.
- WCAG-validated output — accessibility checked, not just claimed.
- Multiple render modes — readers choose how content is displayed.
- Context-aware alt text — image descriptions that actually describe the image.
- Localized text-to-speech — natural narration in the right voice for each language.
- Multilingual translation — one source, many languages.
- Comprehension quizzes — accessible, auto-generated from chapter content.
- Pedagogically aligned glossary — key terms surfaced and defined.
- Table of contents — navigable, structured, automatic.
- Interactive activities — static exercises converted into web-native interactions.
- Original-matching design — AI rebuilds the book's visual identity, accessibly.
- Visual editor — tweak design and content without touching code.
- Accessibility evaluator — flags issues before you publish.
- Major model support — works with the LLMs your team already uses.
- Desktop app — download, install, run locally. Windows and macOS.
Export to Web · WebPub · EPUB 3 · SCORM — drop into any LMS, library platform, or website.
These are live ADTs generated from real PDF source files. They span the spectrum from fully unedited AI output to textbook content with hands-on curation — pick one to get a feel for what the pipeline produces at each level of human involvement.
|
Multilingual reader from Bhutan |
Informative reader from Uruguay |
Grade 5 textbook with activities, Uruguay |
Desktop-first application for automated book production — extract content from PDFs, process through LLM pipelines, and generate formatted output bundles.
| Layer | Technology |
|---|---|
| Monorepo | pnpm workspaces |
| Language | TypeScript (strict mode) |
| Backend | Hono, node-sqlite3-wasm, Zod |
| Visual QA | Playwright (Chromium) |
| Frontend | React + Vite, TanStack (Router, Query, Table, Form), Tailwind CSS |
| Desktop | Electron (electron-vite + electron-builder) |
| Testing | Vitest |
The desktop wrapper is Electron-based and uses electron-vite for development and electron-builder for packaging. No Rust toolchain or platform-native build tools are required for app code; electron-builder fetches the right binaries for the host OS automatically.
Code signing and notarization are optional and only needed when producing distributable installers — see apps/desktop/README.md for the relevant environment variables (AZ_TOKEN, APPLEID, APPLEIDPASS, APPLEIDTEAM).
Just Docker — no Node.js, no cloning.
Option 1 — download docker-compose.yml from the latest release:
docker compose up # starts on http://localhost:8080
docker compose up -d # background
docker compose down # stopSet PORT=9000 in a .env file next to docker-compose.yml to change the port.
Option 2 — single command:
docker run -p 8080:80 -v ./books:/app/books ghcr.io/unicef/adt-studio:latestOpen http://localhost:8080. Book data persists in the local ./books/ directory.
Build from source
Requires cloning the repo and Docker.
git clone git@github.com:unicef/adt-studio.git
cd adt-studio
# Build and start (first build takes ~5 min)
docker compose up --buildTo change the port, copy .env.example to .env and set PORT=<your port>.
docker compose up --build -d # background
docker compose logs -f # logs
docker compose down # stopWindows one-click launcher
Download windows-setup-and-run.bat from the latest release and double-click it. The script will:
- Check that Git and Docker Desktop are installed (with download links if missing)
- Clone the repository (first run) or pull the latest changes
- Build and start the Docker containers
- Open
http://localhost:8080in your default browser
Prerequisites: Git and Docker Desktop.
Prerequisites: Node.js >= 20, pnpm >= 9, and Playwright Chromium (used by visual refinement in storyboard rendering).
# Clone the repository
git clone git@github.com:unicef/adt-studio.git
cd adt-studio
# Install dependencies (first time only)
pnpm install
# Install Playwright Chromium (required for visual refinement)
pnpm exec playwright install chromium
# Start dev servers — builds automatically, opens browser
pnpm devOn Linux, if Chromium system libraries are missing, run:
pnpm exec playwright install --with-deps chromiumThe browser opens automatically at http://localhost:5173. The API runs at http://localhost:3001.
On first run, pnpm dev compiles all packages (~1 min). Subsequent runs are fast (incremental build).
pnpm dev:desktopThis launches the Electron app via electron-vite dev, with HMR for the renderer (Studio SPA) and main/preload reloads for the Electron processes. The API server is started in-process by the Electron main process — there is no separate pnpm dev to run.
To package a distributable installer:
pnpm build:desktop # all-in-one
pnpm --filter @adt/desktop build:unpack # unpacked dir, no installer
pnpm --filter @adt/desktop build:win # Windows NSIS installer
pnpm --filter @adt/desktop build:mac # macOS DMG
pnpm --filter @adt/desktop build:linux # Linux AppImageadt-studio/
├── packages/ # Shared libraries (@adt/* workspace packages)
│ ├── types/ # Zod schemas — ALL types defined here
│ ├── pipeline/ # Extraction & generation — pure functions
│ ├── llm/ # LLM client, prompts, caching, cost tracking
│ ├── pdf/ # PDF extraction
│ └── output/ # Bundle packaging
│
├── apps/ # Application tier
│ ├── api/ # Hono HTTP server
│ ├── studio/ # React SPA (Vite + TanStack)
│ └── desktop/ # Electron desktop wrapper
│
├── templates/ # Layout templates
├── config/ # Global configuration
└── docs/ # Documentation
├── GUIDELINES.md # Coding standards & patterns
├── DECISIONS.md # Architecture decision records
└── architecture.html # Interactive architecture diagram
┌──────────────────────────────────────────┐
│ apps/studio (React) │ apps/desktop │
└────────────────┬─────────────────────────┘
│ HTTP only
▼
┌──────────────────────────────────────────┐
│ apps/api (Hono) │
└────────────────┬─────────────────────────┘
│ Direct imports
▼
┌──────────────────────────────────────────┐
│ packages/pipeline │ llm │ output │
└────────────────┬─────────────────────────┘
▼
┌──────────────────────────────────────────┐
│ packages/types │ pdf │
└──────────────────────────────────────────┘
Frontend apps communicate with the API over HTTP only — they never import from packages directly.
pnpm install # Install all dependencies
pnpm dev # Start dev servers (API + Studio)
pnpm dev:desktop # Launch the Electron desktop app (electron-vite dev, runs API in-process)
pnpm build # Build all packages and apps
pnpm test # Run tests
pnpm test:coverage # Run tests with coverage
pnpm typecheck # TypeScript strict mode check
pnpm lint # Lint all packages
pnpm a11y:regression # Run curated packaged-output accessibility regression (markdown)
pnpm a11y:regression:json # Same regression with JSON output to stdout
pnpm a11y:browser-recheck # Recheck manual-review items + contrast in Playwright
pnpm a11y:browser-recheck:json # Same browser recheck with JSON output to stdout
# i18n
pnpm --filter @adt/studio lint # Lint Studio only (includes lingui/no-unlocalized-strings)
pnpm --filter @adt/studio extract # Extract strings and update .po catalogs
OPENAI_API_KEY=<key> pnpm --filter @adt/studio translate:missing # Auto-translate missing strings (default: gpt-4o)
OPENAI_API_KEY=<key> TRANSLATE_MODEL=openai:gpt-4o-mini pnpm --filter @adt/studio translate:missing # Use a different model- Book-Level Storage — All book data isolated to a single, zippable directory
- Entity-Level Versioning — Never overwrite; always create new versions with rollback
- LLM-Level Caching — Hash inputs for cache keys; reruns are instant if unchanged
- Maximum Transparency — All LLM calls, prompts, and responses are user-inspectable
- Minimize Dependencies — Flat files over databases when sufficient
- Pure JS/TS Over Native — WASM over C/C++ bindings for cross-platform portability
ADT Studio now includes a curated packaged-output accessibility regression runner for checking systematic accessibility regressions in representative local books.
pnpm a11y:regressionThis command:
- runs
pnpm buildfirst - reads the curated allowlist from
scripts/curated-a11y-books.txt - packages temp copies of those local books
- runs the accessibility assessment before and after packaging
- prints a markdown summary to stdout
pnpm a11y:regression:jsonUse this when you want machine-readable output for local analysis or lightweight trend tracking.
You can also run the underlying script directly for custom inputs:
pnpm exec tsx scripts/run-curated-a11y-regression.ts --format markdown
pnpm exec tsx scripts/run-curated-a11y-regression.ts --format json --out .context/curated-a11y-regression.json
pnpm exec tsx scripts/run-curated-a11y-regression.ts --book lp-18-this-is-how-my-face-glows --book unicef-ai-strategy---main-paper-and-annexures_final
pnpm exec tsx scripts/run-curated-a11y-regression.ts --book-list scripts/curated-a11y-books.txtSupported options:
--build— runpnpm buildbefore the regression--quiet-build— suppress build output when used with--build--format markdown|json— choose report format--out <path>— write the report to a file instead of stdout--book <label>— run only specific local books--book-list <path>— use a different curated allowlist file--books-root <path>— override the local books directory--web-assets-dir <path>— override the packaged web assets directory
Use the Playwright-backed recheck when you want a second pass for JSDOM incomplete findings and a real-browser color-contrast audit:
pnpm a11y:browser-recheck
pnpm a11y:browser-recheck:json
pnpm exec tsx scripts/run-browser-a11y-recheck.ts --book lp-18-this-is-how-my-face-glows
pnpm exec tsx scripts/run-browser-a11y-recheck.ts --out .context/browser-a11y-recheck.mdThis tool:
- packages temp copies of curated local books
- runs the existing JSDOM accessibility assessment first
- rechecks only baseline
incompletepage/rule pairs in Playwright - also runs full-page browser checks for
color-contrastby default - reports how much of the manual-review queue becomes confirmed issues, resolved checks, or a smaller residual manual-review queue
Supported options:
--build— runpnpm buildbefore the recheck--quiet-build— suppress build output when used with--build--format markdown|json— choose report format--out <path>— write the report to a file instead of stdout--book <label>— run only specific local books--book-list <path>— use a different curated allowlist file--books-root <path>— override the local books directory--web-assets-dir <path>— override the packaged web assets directory--full-page-rule <id>— add a browser-only full-page rule in addition to the defaultcolor-contrast
These tools are intended as developer tooling, not as a guarantee that every accessibility issue in every book is fixed. They are designed to catch shared ADT Studio output regressions such as landmark, heading, image-alt, and browser-verifiable manual-review problems that can affect many exported ADTs.
Notes:
- They do not require the local Studio/API dev server to be running.
- They are heavier than a normal test pass because they rebuild and repackage books.
runAccessibilityAssessmentmay still emit jsdom canvas warnings to stderr during local runs.- The browser recheck requires Playwright Chromium to be installed.
| Document | Description |
|---|---|
| docs/ARCHITECTURE.md | System architecture, package graph, pipeline model, data flow |
| docs/RELEASING.md | Release flow, branching model (develop → beta, main → stable), and how the release pipeline works |
| docs/DEVELOPER-GUIDE.md | Hosting guide and developer extension reference for third-party teams |
| AGENTS.md | Project instructions for AI coding agents (Claude Code, Codex, Cursor, etc.) |
| docs/AGENT_ROLES.md | Reference role definitions for focused agent tasks |
| docs/GUIDELINES.md | Full coding standards, security, patterns |
| docs/DECISIONS.md | Architecture decision records with reasoning |
| docs/architecture.html | Interactive architecture diagram (open in browser) |
| docs/I18N_ADD_LANGUAGE.md | How to add a new UI language (Lingui i18n) |
This project is licensed under GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). See LICENSE for details.
Original author and copyright holder: UNICEF. See COPYRIGHT.
