
course-ebook-publishing
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.
- Installs
- 77
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/kevintsai1202/teaching-site-skills --skill course-ebook-publishing- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads course-ebook-publishing automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Course Ebook Publishing
Schema authority: this skill reads the live
window.COURSEobject whose shape is defined in_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 underdist/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-editionto 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:
import vm from 'node:vm';
async function loadCourseFromIndexHtml(htmlPath) {
const html = await fs.readFile(htmlPath, 'utf8');
const match = html.match(/<script>\s*window\.COURSE\s*=\s*\{[\s\S]*?\};\s*<\/script>/);
if (!match) throw new Error('window.COURSE 區塊找不到');
const code = match[0].replace(/<\/?script>/g, '');
const sandbox = { window: {}, console };
vm.createContext(sandbox);
vm.runInContext(code, sandbox);
return sandbox.window.COURSE;
}
Why vm sandbox not eval: vm isolates the script's globals from the build process; an accidental side-effect can't leak.
PDF Rendering: Playwright page.pdf() (not Chrome CLI)
Original implementation: pandoc → HTML → msedge --headless --print-to-pdf. Switch to Playwright — Chrome CLI doesn't support footerTemplate (no page numbers).
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(`file://${path.resolve(htmlPath)}`, { waitUntil: 'networkidle' });
await page.pdf({
path: outputPath,
format: 'A4',
printBackground: true,
displayHeaderFooter: true,
headerTemplate: '<div></div>',
footerTemplate: `
<div style="font-size:9pt; width:100%; text-align:center; color:#666;">
<span class="pageNumber"></span> / <span class="totalPages"></span>
</div>`,
margin: { top: '20mm', right: '20mm', bottom: '20mm', left: '20mm' },
});
await browser.close();
Cover page without page number
The cover should be number-less. CSS:
@page { margin: 20mm; }
@page :first { margin: 0; } /* full bleed cover */
Playwright respects @page :first — the footer doesn't print on it.
Book-Style Layout (style-ebook.css)
The example workshop's "book-grade typography" commit was driven by aesthetics, not just formality. Key rules worth porting:
/* 章首跨頁分頁 */
h1.chapter { page-break-before: always; }
/* 圖文並排(Grid 排版) */
.figure-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12mm;
}
.figure-grid .three-up { grid-template-columns: repeat(3, 1fr); }
/* 提示詞區塊:橘色 PROMPT 徽章 + 暗底 code */
.prompt-block {
background: #1a1a1a; color: #e0e0e0;
border-left: 4px solid #ff8c00;
padding: 12pt; margin: 8pt 0;
}
.prompt-block::before {
content: 'PROMPT'; display: inline-block;
background: #ff8c00; color: white;
padding: 2pt 8pt; border-radius: 3pt;
font-size: 8pt; margin-bottom: 6pt;
}
/* 任務勾選方框(純印刷) */
.task::before {
content: '☐'; margin-right: 8pt;
font-size: 14pt; color: #888;
}
/* 學習目標綠色徽章 */
.goal::before {
content: '✓'; margin-right: 6pt;
color: #2e7d32; font-weight: bold;
}
DOCX via pandoc + reference template
const args = [
mdPath,
'-f', 'gfm+attributes+raw_html',
'-t', 'docx',
'--standalone',
'--toc', '--toc-depth=2',
'--resource-path', path.dirname(mdPath),
'--reference-doc', 'scripts/lib/reference.docx',
'-o', outPath,
];
spawn('pandoc', args);
reference.docx is a one-time-generated template with your chosen fonts (e.g. Microsoft JhengHei UI for Chinese), heading colors, paragraph spacing. Generate via scripts/gen-reference-docx.mjs — pandoc copies the styles.
Caveat: raw HTML support in DOCX is partial. <figure-grid> blocks won't render as grids in Word — they degrade to vertical stack. Use markdown  syntax for images so they survive both formats.
Compose Module Anatomy
compose-ebook.mjs exports one function per chapter type:
export async function loadSources() { /* read COURSE + materials + assets */ }
export async function composeCover(meta) { /* big title, day list, meta card */ }
export async function composeOverview(meta) { /* TOC + course overview */ }
export async function composeChapter(day, assetIndex) { /* hero image + units */ }
export async function composeAppendixMaterials(materials) { /* full-text material appendix */ }
export async function composeAppendixSkills(skills) { /* QR-coded skill links */ }
Each returns a markdown string. build-ebook.mjs concatenates them with \n\n---\n\n.
Asset Path Strategy
Inside the composed markdown, images use paths relative to the markdown file (./assets/illustrations/foo.png). Pandoc's --resource-path resolves them; Playwright's file:// HTML loader does too. No absolute paths.
For corporate editions with asset fallback, resolve the actual file location at compose time and embed the resolved path:
async function pickAsset(name, roots) {
for (const root of roots) {
const full = path.join(root, name);
try { await fs.access(full); return path.relative(mdDir, full); }
catch { continue; }
}
return null; // caller decides whether to drop the image or fall back to placeholder
}
What to Filter Out
Some content lives on the website but should not be in the ebook:
- Quiz items: leaking exam questions defeats the assessment. The example workshop filters out the entire quiz chapter.
- Pre-test / post-test sections: same reason.
- Interactive-only features: "點擊複製" buttons make no sense in print. Render the raw prompt text, not the button.
- Dynamic illustrations: anything generated client-side via canvas/JS won't render.
Add filter conditions to the compose functions; don't try to render-then-strip.
Verification
After generating, verify:
// scripts/verify-ebook.mjs
// - PDF file exists and is > 100KB
// - First page (cover) has no page number
// - TOC pages have entries
// - Every image reference resolved (no broken-image placeholders)
For DOCX, open in Word once and visually check: TOC linked, fonts applied, no missing-image squares.
CLI Conventions
The example workshop's build:ebook script supports:
| Flag | Behavior |
|---|---|
| (no flag) | Build both PDF + DOCX |
--md-only | Stop after composing markdown (debug content) |
--no-docx | Skip DOCX (faster iteration on PDF) |
--no-pdf | Skip PDF (faster iteration on DOCX) |
--keep-html | Don't delete intermediate HTML (debug CSS) |
--output PATH | Custom PDF output path |
Provide these from day one — you'll iterate the layout many times.
Anti-Patterns
- Re-authoring content in the ebook builder — the ebook is a derivative. If you find yourself adding new prose in the compose functions, that prose belongs in
course-content-authoringinstead. - Using Chrome CLI for PDF — switch to Playwright early; you'll want footers eventually.
- Hardcoding image paths — use
pickAsset()with a list of roots, especially when supporting corporate edition fallbacks. - Forgetting
@page :first { margin: 0 }— cover ends up with a stray page number in the footer. - Re-running the ebook build every time the site changes — only build at stable checkpoints; the intermediate
dist/folder is large.
Hand-off
When this skill finishes:
dist/{course-name}.pdfand optionally.docxare produced.- Verify scripts pass.
- If for a client, the file goes into the deliverable bundle.
Tell the user: "ebook ready. The site and ebook are now in sync; from now on, treat the site as canonical and rebuild the ebook only at release milestones — don't edit the ebook directly, it'll get overwritten."
FAQ
What does the course-ebook-publishing skill do?
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.
How do I install course-ebook-publishing?
Run npx skills add https://github.com/kevintsai1202/teaching-site-skills --skill course-ebook-publishing in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/course-ebook-publishing/ (personal) or .claude/skills/course-ebook-publishing/ (project).
Which AI agents can use course-ebook-publishing?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is course-ebook-publishing safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains course-ebook-publishing?
It is published by kevintsai1202 in kevintsai1202/teaching-site-skills. It has 77 recorded installs.
More from kevintsai1202/teaching-site-skills
course-outline-design
kevintsai1202/teaching-site-skills
Open the skill to read its SKILL.md instructions.
course-content-authoring
kevintsai1202/teaching-site-skills
Use this skill when an outline exists and you need to fill in the actual teaching content — lecture notes, sample materials (CSV / YAML / MD samples), quiz items, and any course-specific artifacts like prompt templates or worksheets. Triggers on phrases like "寫講義", "補素材", "出測驗題", "提示詞範本", "撰寫課程內容", "lecture notes", "course material", "quiz questions", "quiz authoring", "prompt templates". Always invoke AFTER course-outline-design is stable, BEFORE static-spa-conversion. This skill produces only the raw .md/.csv/.yaml artifacts — turning them into a website is the next stage's job.
teaching-site-design-system
kevintsai1202/teaching-site-skills
Use this skill whenever you're making visual / styling decisions on a teaching site or content-driven SPA — picking colors, fonts, badge styles, card layouts, hero treatments, dark mode tokens, or any "how should this look?" question. Triggers on phrases like "視覺風格", "設計系統", "色票", "字體", "暗色模式", "玻璃卡片", "提示詞徽章", "Day hero 怎麼設計", "design tokens", "design system", "color system", "glass cards", "typography", "look and feel", "視覺一致性", or any moment when the user evaluates aesthetics rather than behaviour. This is the cross-cutting visual authority — every stage (SPA, interactions, corporate edition, ebook) reads tokens from here so the brand stays coherent across formats.
course-corporate-edition
kevintsai1202/teaching-site-skills
Open the skill to read its SKILL.md instructions.
teaching-site
kevintsai1202/teaching-site-skills
Use this skill as the main entry point whenever the user wants to build, plan, or evolve an interactive teaching website / course microsite / workshop landing page — from a blank slate, from existing course materials, or any state in between. Triggers on broad phrases like "做課程網站", "做教學網頁", "做工作坊網站", "把講義變網頁", "course microsite", "workshop site", "interactive lesson page", "multi-day curriculum website", "做一套課程". This skill is the router for the whole teaching-site pipeline (outline → content → SPA → interactions → visuals → corporate / ebook) and dispatches to the 10 specialised sub-skills as needed. Prefer this when the user's request is broad or unclear about which stage they're at — sub-skills (e.g. course-ebook-publishing) are still triggerable directly for stage-specific requests.
web-visual-assets
kevintsai1202/teaching-site-skills
Open the skill to read its SKILL.md instructions.
Popular docs & writing skills
grill-with-docs
mattpocock/skills
A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.
lark-markdown
open.feishu.cn
飞书 Markdown:查看、创建、上传、编辑和比较飞书中的原生 Markdown 文件。当用户要操作飞书 Markdown 文件,或比较其远端版本及本地草稿时使用。纯本地 Markdown 文件操作不触发本 skill。不负责将 Markdown 导入为飞书在线文档,也不负责文件搜索、权限、评论、移动、删除等云空间管理操作。
writing-beats
mattpocock/skills
Writing, exploit; assemble raw material into a journey of beats, grounding each term before a beat leans on it.
writing-shape
mattpocock/skills
Writing, exploit: shape raw material into an article, paragraph by paragraph.
writing-fragments
mattpocock/skills
Writing, explore: mine raw fragments, no structure yet.
writing-for-agents
mattpocock/skills
Writing documents for agents. Use when creating or editing skills, or modifying AGENTS.md or CLAUDE.md.