78 lines
5.5 KiB
Markdown
78 lines
5.5 KiB
Markdown
# CHUNK #6 — Export Pipeline
|
||
|
||
**Status:** ✅ Complete — committed `63f34b3` (feat(export): Chunk #6 Export Pipeline). All 7 exporters, the ExportDialog, and the test suite are green.
|
||
|
||
**Duration:** ~3 days · **Prereqs:** CHUNK #3 (editor, ProseMirror JSON) · **Follows:** CHUNK #5 · **Unblocks:** CHUNK #11 (packaging ships exports)
|
||
|
||
**Purpose:** Ship a single export engine that turns a project's ProseMirror JSON into Markdown, single-file HTML, EPUB 3, DOCX, ODT, and PDF — all with title/author/cover/TOC/styling options. Everything shares one flat content model so formats stay consistent and tests are golden-file based.
|
||
|
||
---
|
||
|
||
## Steps
|
||
|
||
### 1. Shared flat model (`packages/exporters/src/model.ts`)
|
||
1. `flattenProject(project)` → a linear list of `Block`s: `{ type: 'heading'|'paragraph'|'sceneBreak'|'blockquote'|'codeBlock'|'list'|'footnote', text, marks[], level?, children? }`, plus frontmatter `{ title, author, language, cover, chapters: [{title, order}] }`.
|
||
2. This is the single source every exporter consumes — no exporter touches raw ProseMirror JSON directly.
|
||
|
||
### 2. Markdown & HTML (`packages/exporters/src/md.ts`, `html.ts`)
|
||
1. **Markdown export** (`exportMarkdown(blocks)`): headings (`#`–`###`), scene breaks as `***`, blockquotes, code fences, lists, footnotes as `[1]` refs with a notes section. Round-trips with Chunk 3's importer.
|
||
2. **Single-file HTML** (`exportHtml(blocks, opts)`): one self-contained `.html` with inline CSS (theme-aware: Ink/Parchment/Nocturne/Terminal), a generated TOC (anchored headings), print styles (`@page` margins), and a cover page. No external assets.
|
||
|
||
### 3. EPUB 3 (`packages/exporters/src/epub.ts`)
|
||
1. Build a valid EPUB 3 container **by hand** (no runtime deps):
|
||
- `mimetype` (uncompressed, `application/epub+zip`) first in the zip.
|
||
- `META-INF/container.xml` → `content.opf`.
|
||
- `content.opf`: metadata (title, author, language, UUID, cover), manifest (`chapterN.xhtml`, `toc.ncx`, `cover.svg`, `style.css`), spine (chapter order).
|
||
- `chapterN.xhtml`: one file per chapter, semantic HTML5 (`<h1>`, `<p>`, `<hr>` for scene breaks), a `nav` TOC.
|
||
- `cover.svg`: renders the project's cover color + title/author.
|
||
2. Zip with `jszip` (already a dep). Output `.epub`.
|
||
3. **Validation**: golden-file test + `epubcheck` (run in CI via a small Node wrapper or Docker image).
|
||
|
||
### 4. DOCX (`packages/exporters/src/docx.ts`)
|
||
1. Use the maintained **`docx`** npm package: `Document` → `Section` → `Paragraph`/`Heading`/`Quote`; add a `TableOfContents` (field `TOC`), a cover paragraph, and page breaks between chapters.
|
||
2. Styles: map themes to Word styles (font, size, heading colors). Export `.docx`.
|
||
|
||
### 5. ODT (`packages/exporters/src/odt.ts`)
|
||
1. Build a valid OpenDocument container (parallel to EPUB):
|
||
- `mimetype` (`application/vnd.oasis.opendocument.text`) first, uncompressed.
|
||
- `content.xml`: `text:p` paragraphs, `text:h` headings, `text:section`, with `style:name` references.
|
||
- `manifest.xml`, `meta.xml` (title/author), `styles.xml` (basic paragraph styles), `thumbs/thumbnail.png` (optional).
|
||
2. Zip with `jszip`. Output `.odt`. (LibreOffice-openable; golden-file unzip-validate in CI.)
|
||
|
||
### 6. PDF (`packages/exporters/src/pdf.ts`)
|
||
1. **Two backends** (chosen by env):
|
||
- **Server/desktop:** headless Chromium `page.pdf()` over the single-file HTML → pixel-perfect, themes intact, real TOC. This is the default.
|
||
- **Browser-only fallback:** `jspdf` + `html2canvas` (lower fidelity, no TOC automation) — used when no server is available.
|
||
2. `exportPdf(blocks, opts)` returns a `Uint8Array`. Add title page, page numbers (`- {page}` footer), and `@page` margins from the HTML path.
|
||
|
||
### 7. Export dialog (`apps/web/src/features/export/ExportDialog.tsx`)
|
||
1. Select: format (multi-select), scope (current chapter / whole book / whole project), options (cover, TOC, theme, page size).
|
||
2. Live preview thumbnail (HTML/EPUB), progress bar, download button(s). Desktop uses Tauri FS save dialog; web uses native download.
|
||
3. **Batch export**: "Export all formats" → zips results.
|
||
|
||
### 8. Tests
|
||
1. **Golden-file tests**: for each format, snapshot the output bytes of the seed project; assert stable across runs (whitespace-normalized for HTML/PDF).
|
||
2. **Round-trip**: MD export → import → MD export is identical.
|
||
3. **Validation**: `epubcheck` on every EPUB; unzip + XML well-formedness on ODT/EPUB.
|
||
4. **CI**: run epubcheck; unzip-validate all archives.
|
||
|
||
---
|
||
|
||
## Acceptance criteria (all must pass)
|
||
- [x] Export a project to MD, HTML, EPUB, DOCX, ODT, PDF — all succeed from the dialog.
|
||
- [x] EPUB passes `epubcheck` with zero errors; opens in Calibre/Readers.
|
||
- [x] ODT opens cleanly in LibreOffice; styles preserved.
|
||
- [x] DOCX opens in Word; TOC field present (update-on-open).
|
||
- [x] PDF has title page, TOC (HTML backend), page numbers, correct margins.
|
||
- [x] Theme applied in HTML/EPUB/PDF (Ink dark / Parchment light, etc.).
|
||
- [x] Golden-file tests stable; round-trip MD identical.
|
||
- [x] CI validates EPUB + unzip-validates all archives.
|
||
|
||
## Definition of done
|
||
An author can export a finished manuscript to every major format from one dialog, with consistent styling and validated archives.
|
||
|
||
## Risks & fallbacks
|
||
- **EPUB/ODT spec edge cases** → golden-file tests + `epubcheck` in CI; keep the container builders tiny and unit-tested.
|
||
- **PDF fidelity without a server** → prefer headless Chromium; fall back to `jspdf` only when needed and note the limitation.
|
||
- **Cover image sizing** → normalize covers to EPUB-recommended dimensions (e.g. 1600×2560) before embedding.
|