--- name: course-ebook-publishing description: Use this skill ONLY AFTER a teaching site is feature-complete and stable, when the user wants to produce a printable / archivable / shareable version — typically a PDF ebook with cover, page numbers, table of contents, or a DOCX for editorial review. Triggers on phrases like "做電子書", "產 PDF", "ebook", "PDF ebook", "印給學員", "課程講義 PDF", "course handbook", "DOCX deliverable", "book-style layout", "build:ebook". Do NOT invoke during early site development — this is a downstream consumer that breaks if the site changes underneath. The skill produces PDF via Playwright `page.pdf()` and DOCX via pandoc, both from a single composed markdown source. --- # Course Ebook Publishing > **Schema authority**: this skill reads the live `window.COURSE` object whose shape is defined in [`_shared/domain-primitives.md`](../_shared/domain-primitives.md). Quiz / pre-test / post-test items are filtered OUT of the ebook (per §10 quiz item rules + ebook content policy). > > **Filename convention (English-first)**: outputs land in `dist/{name}.pdf` / `dist/{name}.docx`. Source markdown is composed under `dist/master.md`. This skill turns a finished teaching website into a book — PDF (primary) and optionally DOCX (for editorial review or further authoring). It is a **post-site** step: it consumes `window.COURSE` from the live site, never re-authoring content. ## When to Invoke - Site is feature-complete (Stages 1–5 done) and content is stable. - Stakeholders ask for a printable hand-out, an archivable record, or a deliverable for non-web channels. - Sometimes invoked after `course-corporate-edition` to produce a client deliverable bundle. **Do NOT invoke** when site is mid-development. The ebook pipeline reads `window.COURSE`; if the site changes daily, the ebook drifts daily. ## Architecture: Single Source → Two Outputs ``` window.COURSE ─┐ ├─→ compose-ebook.mjs ─→ master.md ─→ pandoc ─→ ebook.docx asset folders ─┤ └─→ Playwright page.pdf() ─→ ebook.pdf materials/ ─┘ ``` **Why a single composed markdown intermediate**: keeps PDF and DOCX content identical. Pandoc handles markdown → DOCX cleanly; Playwright renders the same markdown via an HTML wrapper → PDF. Two outputs, one source of truth. ## File Layout (Standard) ``` scripts/ ├── build-ebook.mjs ← CLI entry point (--md-only, --no-docx, --no-pdf, --output, --keep-html) ├── render-pdf.mjs ← markdown → HTML → PDF via Playwright └── lib/ ├── compose-ebook.mjs ← loadSources + composeXxx functions (cover, TOC, chapters, appendix) └── reference.docx ← pandoc style template (generated by gen-reference-docx.mjs) style-ebook.css ← @page rules, cover, page-number footer, print-only styles dist/ ← output: ebook.md, ebook.pdf, ebook.docx ``` ## Loading Source Data: `window.COURSE` from index.html When the site uses external `course-data.js`, just import it. When it uses **inlined** COURSE (corporate edition), extract via regex + vm sandbox: ```js import vm from 'node:vm'; async function loadCourseFromIndexHtml(htmlPath) { const html = await fs.readFile(htmlPath, 'utf8'); const match = html.match(/