MCPInstall

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
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads course-ebook-publishing automatically when relevant.

  3. 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.COURSE object 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 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:

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 ![alt](path) 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:

FlagBehavior
(no flag)Build both PDF + DOCX
--md-onlyStop after composing markdown (debug content)
--no-docxSkip DOCX (faster iteration on PDF)
--no-pdfSkip PDF (faster iteration on DOCX)
--keep-htmlDon't delete intermediate HTML (debug CSS)
--output PATHCustom 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-authoring instead.
  • 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}.pdf and optionally .docx are 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

View all →

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.

65 installs

Popular docs & writing skills

View category →

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.

Docs & Writing1.2M installs

lark-markdown

open.feishu.cn

飞书 Markdown:查看、创建、上传、编辑和比较飞书中的原生 Markdown 文件。当用户要操作飞书 Markdown 文件,或比较其远端版本及本地草稿时使用。纯本地 Markdown 文件操作不触发本 skill。不负责将 Markdown 导入为飞书在线文档,也不负责文件搜索、权限、评论、移动、删除等云空间管理操作。

Docs & Writing778.7K installs

writing-beats

mattpocock/skills

Writing, exploit; assemble raw material into a journey of beats, grounding each term before a beat leans on it.

Docs & Writing490.7K installs

writing-shape

mattpocock/skills

Writing, exploit: shape raw material into an article, paragraph by paragraph.

Docs & Writing490.2K installs

writing-fragments

mattpocock/skills

Writing, explore: mine raw fragments, no structure yet.

Docs & Writing489.6K installs

writing-for-agents

mattpocock/skills

Writing documents for agents. Use when creating or editing skills, or modifying AGENTS.md or CLAUDE.md.

Docs & Writing479.3K installs