| name | html-build |
| scope | co-deck |
| version | 1.5.0 |
| description | Generates HTML slides from slide_deck.md and design_spec.md. Applies theme (data-theme attribute), binds images from image-manifest.json, inserts speaker intro and contact slides, checks balance. Responds to "make HTML", "generate slides", "add images", "add speaker intro" (Korean: "HTML ๋ง๋ค์ด์ค", "์ฌ๋ผ์ด๋ ์์ฑํด์ค", "์ด๋ฏธ์ง ๋ฃ์ด์ค", "๊ฐ์ฐ์ ์๊ฐ ์ถ๊ฐํด์ค"). Stages 5-8 of the lecture workflow.
|
| status | active |
| owner | html-build |
| last_reviewed | "2026-06-24T00:00:00.000Z" |
| prerequisites | design |
Context
Generates a single HTML file from slide_deck.md + design_spec.md, applies theme from lecture-profile.md, binds images from image-manifest.json, inserts special pages (speaker intro, contact), and checks balance. Slide data is embedded as a strict-JSON array (const slideData = [...]) inside the HTML. Strict-JSON means: all keys double-quoted, all string values double-quoted, no trailing commas, no JavaScript comments, no single-quoted strings, no template literals. Invoked at Stages 5-8, after Gate 3 (design approval).
When to Use
- PM Agent dispatches after Gate 3 (design approval)
- User says "make HTML" / "HTML ๋ง๋ค์ด์ค" / "์ฌ๋ผ์ด๋ ์์ฑํด์ค"
- User says "add images" / "์ด๋ฏธ์ง ๋ฃ์ด์ค"
- User wants to edit specific slides
Execution Steps
Stage 5: HTML Slide Generation
File Structure: Single HTML file (lecture_[topic]_v1.html) + assets/images/ folder (photos) + assets/diagrams/ folder (SVG/PNG diagrams).
slideData Structure: Slide data lives as a strict-JSON array embedded as const slideData = [...] inside the HTML file. All keys and string values must use double-quotes; no trailing commas, no JS comments. This enables extract_slidedata.mjs to parse via JSON.parse directly without a transform step.
Korean example โ field keys stay in English; only the content values are Korean.
{ isTitleSlide: true, section: "", title: "๊ฐ์ฐ ์ ๋ชฉ", subtitle: "๋ถ์ ๋ชฉ",
meta: "๋ ์ง | ์ฃผ์ต", visualImage: "../assets/images/lecture-hall-professional.jpg",
backgroundImage: "../assets/images/bg-deck.jpg" }
{ isProfileSlide: true, section: "INTRODUCTION", title: "๊ฐ์ฐ์ ์๊ฐ",
speakerName: "์ด๋ฆ", speakerTitle: "์ง์ฑ
/ ์์", speakerBio: "์ฝ๋ ฅ (2-3์ค)",
visualImage: "../assets/images/speaker-portrait.jpg" }
{ isDividerSlide: true, section: "์น์
๋ช
", partNum: "PART 01",
title: "ํํธ ์ ๋ชฉ", desc: "์ด ํํธ์์ ๋ค๋ฃฐ ๋ด์ฉ ํ ์ค ์์ฝ",
visualImage: "../assets/images/ai-transformation-abstract.jpg",
backgroundImage: "../assets/images/bg-deck.jpg" }
{ section: "์น์
๋ช
", title: "์ฌ๋ผ์ด๋ ์ ๋ชฉ",
bullets: ["๋ถ๋ฆฟ 1", "๋ถ๋ฆฟ 2", "๋ถ๋ฆฟ 3"],
visualImage: "../assets/images/data-analysis-dashboard.jpg" }
{ section: "์น์
๋ช
", title: "์ฌ๋ผ์ด๋ ์ ๋ชฉ",
bullets: ["๋ถ๋ฆฟ 1", "๋ถ๋ฆฟ 2"],
visualTitle: "ํต์ฌ ํฌ์ธํธ", visualDisplay: "[ํ์ฌ ์ํฉ]\nโ ํญ๋ชฉ 1\nโ ํญ๋ชฉ 2\n\n[๊ฐ์ ๋ฐฉํฅ]\nโ ๋ฐฉํฅ 1\nโ ๋ฐฉํฅ 2" }
{ isContactSlide: true, section: "CLOSING", title: "๊ฐ์ฌํฉ๋๋ค",
contactEmail: "email@example.com", contactLinkedIn: "linkedin.com/in/...",
contactPhone: "010-XXXX-XXXX" }
{ isPunchlineSlide: true, section: "", title: "ํต์ฌ ๋ฉ์์ง" }
Use design_spec.md's CSS variables directly. Unify slide rendering through a single renderSlide(data) function. Do not hardcode color or font values.
Theme + Style injection (from lecture-profile.md โ presentation.theme + presentation.style):
<html lang="ko" data-theme="pitch-enhanced" data-style="premium-dark">
<link rel="stylesheet" href="../../docs/html-themes/styles/base.css">
<link rel="stylesheet" href="../../docs/html-themes/styles/premium-dark/style.css">
Available themes: outline | pitch | pitch-enhanced | vertical | zen. Available styles: classic | minimal | visual-heavy | academic | premium-dark. Defaults: pitch-enhanced + premium-dark.
Deterministic Builder (Task 4 โ pending implementation): A CLI build-theme-deck.ts will automate CSS injection, marker replacement, and ppt-engine.js inlining. Once available, html-build will invoke the builder instead of performing these steps manually. The builder handles: theme/style resolution, package validation, INJECT marker replacement, CSS link generation, ppt-engine.js inlining, strict-JSON slideData injection. html-build remains responsible for: slide content generation, image path binding, narration/auto-advance config assembly, and renderSlide() (template-provided).
Theme capabilities:
- All 5 themes support
visualImage, visualTitle/visualDisplay text panels, profile avatars, contactPhone, and isPunchlineSlide.
pitch and pitch-enhanced use slide-content grid (left text + right visual panel) for standard slides.
outline, pitch-enhanced, zen, vertical use slide-card (content + right-panel) for standard slides.
outline, pitch-enhanced, zen, vertical support PPT features (TOC drawer, transitions, timer, speaker notes, TTS).
pitch uses its own layout (TOC drawer, no transitions).
visual-heavy style uses --slide-bg-image CSS variable for full-bleed background images.
Narration and auto-advance config injection (from lecture-profile.md โ narration + auto_advance sections):
Read the narration and auto_advance blocks from lecture-profile.md and inject two independent config objects into the initPPT() call. This bridges lecture profile settings to the runtime NarrationEngine v2.3:
var narrationConfig = {
enabled: true,
autoPlay: false,
defaultLanguage: 'ko',
scriptLanguage: 'ko',
languages: ['ko']
};
var autoAdvanceConfig = {
enabled: true,
startAsAuto: false,
interval: 8
};
initPPT({ transition: 'fade', showTimer: true, showThumbnails: false,
narration: narrationConfig, autoAdvance: autoAdvanceConfig });
narration.enabled: false โ hides TTS play button, language dropdown, voice selector; disables 'P' keyboard shortcut
narration.autoPlay: true โ auto-starts TTS narration on page load
narration.scriptLanguage โ declares what language the primary script field is written in (defaults to 'ko'); read from script_language in lecture-profile.md, falls back to language field
auto_advance.enabled: false โ hides auto-advance toggle button; disables 'A' keyboard shortcut
auto_advance.startAsAuto: true โ starts auto-advance timer immediately on page load
auto_advance.interval โ configures the timer interval (seconds) for auto-advance slides
narration.languages โ populates the language dropdown (only languages with scripts in slideData are clickable)
- If
narration section is absent, set narration.enabled: false. If auto_advance section is absent, use defaults (enabled: true, startAsAuto: false, interval: 8)
- Backward compatibility: if the old single
narration.autoAdvance or narration.autoAdvanceInterval fields are present, silently ignore them
Image paths: Photos live in the shared pool at presentations/assets/images/. Use ../assets/images/<slug>.<ext> (relative from presentations/<project>/). Slug is the path field basename from image-manifest.json. No slide-number prefix. Diagrams (SVG/PNG) live in the shared pool at presentations/assets/diagrams/. Use ../assets/diagrams/<stem>.png for diagram slides. Paths are auto-rewritten by gen-visual-images.ts when run.
Stage 6: Image Binding
Read image-manifest.json and bind downloaded images to matching slideData entries by slide index. For slides where no image was found by the Image Curator:
- If slide has
image_role: background โ switch to visualTitle / visualDisplay text panel fallback
- If slide has
image_role: illustrative โ omit the right panel; switch to full-text layout
- Never use placeholder or generic images โ always use manifest paths or text fallback
Background image binding (when lecture-profile.md โ background_image.enabled is true):
- Read
background_image config from lecture-profile.md
- Read the global background image path from
image-manifest.json โ background_image.path
- Convert to relative path:
../assets/images/<slug>.<ext>
- Inject
backgroundImage field into slideData entries based on scope:
all โ every slide gets "backgroundImage": "../assets/images/bg-deck.jpg"
divider-cover โ only slides where isTitleSlide, isDividerSlide, or isPunchline is true
individual โ only slides with image_role: "background" in the manifest
- The template's
renderSlide() reads data.backgroundImage and sets --slide-bg-image CSS variable (consumed by visual-heavy/style.css for full-bleed backgrounds)
Stage 7: Balance Check
Self-check after generating the HTML:
If any issue, split or merge slides.
Stage 8: Special Pages
- Speaker intro: right after the cover (slide 2)
- Contact: last slide
Insert if either is missing. Populate with instructor fields from lecture-profile.md.
To open HTML locally: bunx serve . โ http://localhost:3000
Output Format
lecture_[topic]_v1.html โ single self-contained HTML file with embedded const slideData (strict JSON), theme data-theme attribute, base + override CSS links, and assets/images/ (photos) + assets/diagrams/ (SVG/PNG) alongside.
PDF pipeline note: scripts/co-deck/extract_slidedata.mjs parses the inline const slideData = [...] array via a bracket-depth state machine (not regex, not DOM). The slideData array MUST be strict JSON โ all keys double-quoted, string values double-quoted, no trailing commas, no JS comments, no single quotes. Non-JSON syntax will break the PDF pipeline.
Encoding: All output HTML files MUST be UTF-8 without BOM. Verify <meta charset="UTF-8"> is present in <head>. On Korean Windows systems, ensure chcp 65001 is active before writing files to prevent CP949 corruption of Korean text content.
Builder Boundary: See agents/html-build.md "Builder Boundary" section for the complete responsibility split between the builder and the html-build agent.
Related Skills
design โ provides design_spec.md CSS variables consumed by this skill
image-curator โ produces image-manifest.json that maps slide indices to image paths
measure โ next step; consumes the HTML output to extract pixel coordinates
version โ must be called before any HTML file edit