specpipe 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +1319 -0
  2. package/bin/devkit.js +3 -0
  3. package/package.json +61 -0
  4. package/src/cli.js +76 -0
  5. package/src/commands/check.js +33 -0
  6. package/src/commands/diff.js +84 -0
  7. package/src/commands/init-adopt.js +54 -0
  8. package/src/commands/init-agents.js +118 -0
  9. package/src/commands/init-global.js +102 -0
  10. package/src/commands/init.js +311 -0
  11. package/src/commands/list.js +54 -0
  12. package/src/commands/remove.js +133 -0
  13. package/src/commands/upgrade.js +215 -0
  14. package/src/lib/agent-guards.js +100 -0
  15. package/src/lib/agent-install.js +161 -0
  16. package/src/lib/agents.js +280 -0
  17. package/src/lib/claude-global.js +183 -0
  18. package/src/lib/detector.js +93 -0
  19. package/src/lib/hasher.js +21 -0
  20. package/src/lib/installer.js +213 -0
  21. package/src/lib/logger.js +16 -0
  22. package/src/lib/manifest.js +102 -0
  23. package/src/lib/reconcile.js +56 -0
  24. package/templates/.claude/CLAUDE.md +79 -0
  25. package/templates/.claude/hooks/comment-guard.js +126 -0
  26. package/templates/.claude/hooks/file-guard.js +216 -0
  27. package/templates/.claude/hooks/glob-guard.js +104 -0
  28. package/templates/.claude/hooks/path-guard.sh +118 -0
  29. package/templates/.claude/hooks/self-review.sh +27 -0
  30. package/templates/.claude/hooks/sensitive-guard.sh +227 -0
  31. package/templates/.claude/settings.json +68 -0
  32. package/templates/docs/WORKFLOW.md +325 -0
  33. package/templates/docs/specs/.gitkeep +0 -0
  34. package/templates/hooks/specpipe-read-guard.sh +42 -0
  35. package/templates/hooks/specpipe-shell-guard.sh +65 -0
  36. package/templates/rules/specpipe-guards.md +40 -0
  37. package/templates/scripts/test-hooks.sh +66 -0
  38. package/templates/skills/sp-build/SKILL.md +776 -0
  39. package/templates/skills/sp-challenge/SKILL.md +255 -0
  40. package/templates/skills/sp-commit/SKILL.md +174 -0
  41. package/templates/skills/sp-explore/SKILL.md +730 -0
  42. package/templates/skills/sp-fix/SKILL.md +266 -0
  43. package/templates/skills/sp-humanize/SKILL.md +212 -0
  44. package/templates/skills/sp-investigate/SKILL.md +648 -0
  45. package/templates/skills/sp-md-render/SKILL.md +200 -0
  46. package/templates/skills/sp-md-render/components.md +415 -0
  47. package/templates/skills/sp-md-render/template.html +283 -0
  48. package/templates/skills/sp-plan/SKILL.md +947 -0
  49. package/templates/skills/sp-review/SKILL.md +268 -0
  50. package/templates/skills/sp-scaffold/SKILL.md +237 -0
  51. package/templates/skills/sp-scaffold/references/ARCHITECTURE.md.tmpl +228 -0
  52. package/templates/skills/sp-scaffold/references/DESIGN.md.tmpl +113 -0
  53. package/templates/skills/sp-scaffold/references/adr/NNNN-template.md +92 -0
  54. package/templates/skills/sp-scaffold/references/stack-profiles/react.md +36 -0
  55. package/templates/skills/sp-spec-render/SKILL.md +254 -0
  56. package/templates/skills/sp-spec-render/components.md +418 -0
  57. package/templates/skills/sp-spec-render/examples/user-auth.html +749 -0
  58. package/templates/skills/sp-spec-render/examples/user-auth.md +114 -0
  59. package/templates/skills/sp-spec-render/template.html +222 -0
  60. package/templates/skills/sp-voices/SKILL.md +1184 -0
@@ -0,0 +1,200 @@
1
+ ---
2
+ description: |
3
+ Render any markdown file into a self-contained, scannable HTML view:
4
+ sidebar TOC with scroll-spy, anchored headings, code copy buttons,
5
+ Mermaid diagrams, callouts (note/warn/danger/tip), step cards,
6
+ comparison cards, collapsible long sections, light/dark theme, TOC search.
7
+
8
+ Use when asked to "render markdown", "md sang html", "xem md đẹp",
9
+ "preview this doc", "make this readable", "md2html", or for any
10
+ long-form markdown that is not a /sp-plan spec (investigation reports,
11
+ explore docs, design notes, retros, READMEs, RFCs, meeting notes).
12
+
13
+ Proactively suggest when the user is reading a long .md in chat
14
+ ("hard to scan", "lots to read", "tl;dr this") or after a skill that
15
+ writes a long markdown artefact finishes (e.g. /sp-investigate,
16
+ /sp-explore, /retro).
17
+
18
+ For /sp-plan spec files (structured stories + acceptance scenarios),
19
+ use [[sp-spec-render]] instead — it understands S-NNN / AS-NNN /
20
+ C-NNN / P-badges. This skill is the generic counterpart for arbitrary
21
+ markdown without a fixed schema.
22
+
23
+ Idempotent: re-rendering overwrites the previous .html. Safe anytime.
24
+ allowed-tools: Read, Write, Bash, Glob, Grep
25
+ ---
26
+
27
+ Render an arbitrary markdown file into a self-contained `<file>.html` next to it.
28
+
29
+ The source `.md` is the truth. The HTML is a **view layer**: regenerable, never hand-edited. `template.html` and `components.md` are read-only inputs.
30
+
31
+ ---
32
+
33
+ ## Inputs
34
+
35
+ `$ARGUMENTS` may be:
36
+
37
+ - **Path to a .md file** (`docs/investigate/payment-bug-2026-05-16.md`) — render to `<file>.html` next to it.
38
+ - **`--out <path>`** — custom output destination.
39
+ - **Path to a directory** — list `*.md` files inside and prompt the user to pick.
40
+ - **Empty** — prompt the user for a file path.
41
+
42
+ Output is written to `<file>.html` (or `--out`) and overwrites any existing file.
43
+
44
+ **Refuse politely** if the file looks like a /sp-plan spec (has `### S-\d{3}:` headings) — tell the user to run `/sp-spec-render` instead. Quick check: `grep -q '^### S-[0-9]\{3\}:' <file>`.
45
+
46
+ ---
47
+
48
+ ## Skill files (read once before generating)
49
+
50
+ Resolved relative to this `SKILL.md`:
51
+
52
+ - `template.html` — HTML skeleton with embedded CSS, JS, Mermaid CDN, SVG sprite. Contains `{{PLACEHOLDER}}` strings and `<!-- TOC_ENTRIES -->`, `<!-- CONTENT_START -->`, `<!-- CONTENT_END -->` slots. **Read once, never modified.**
53
+ - `components.md` — Catalog of HTML snippets to copy verbatim. **Read once.**
54
+ - `examples/` — Reference input/output pairs (if any). If empty, proceed without — the catalog is enough.
55
+
56
+ **Read all available inputs before producing any output.** Do not invent CSS classes, do not generate component markup from scratch, do not add new `<style>` or `<script>` tags.
57
+
58
+ ---
59
+
60
+ ## Workflow
61
+
62
+ ### Step 1 — Resolve and classify input
63
+
64
+ 1. Parse `$ARGUMENTS` to find the source `.md`.
65
+ 2. Reject if it looks like a /sp-plan spec (see above).
66
+ 3. Read the source fully. Read `template.html` and `components.md`.
67
+ 4. **Detect language** of the source prose. Default `vi` if Vietnamese dominates, `en` for English, otherwise the source's language. UI labels translate to match — see placeholder table in Step 3.
68
+
69
+ ### Step 2 — Analyze content (analyzer pattern)
70
+
71
+ Walk the markdown and decide, for each chunk, the best component (see `components.md`). This is the core difference from `sp-spec-render`: there is no fixed schema. Use judgment.
72
+
73
+ Heuristics:
74
+
75
+ | Source pattern | Component |
76
+ |---|---|
77
+ | First `# Title` | Page title (template `<h1>`) |
78
+ | First paragraph after H1, ≤ 200 chars | Subtitle (`{{SUBTITLE}}`) |
79
+ | `## Section` | `<h2 id="...">` section anchor |
80
+ | `### Subsection` | `<h3 id="...">` |
81
+ | Prose paragraphs | `<p>` |
82
+ | Numbered actions ("Step 1: …", `1. Do X` chains ≥ 3 with imperative verbs) | Step cards (§4) |
83
+ | Fenced ```mermaid block | Mermaid diagram (§7) |
84
+ | Other fenced ```lang code | Code block with copy button (§6) |
85
+ | `> [!NOTE]` / `> [!TIP]` / `> [!WARNING]` / `> [!DANGER]` / `> [!IMPORTANT]` (GFM admonitions) | Callout (§5) |
86
+ | Blockquote starting with "Don't" / "Never" / "Cảnh báo" / "Lưu ý" | Mapped DANGER / WARN / NOTE callout |
87
+ | GFM table | `<table class="md-table">` (§8) |
88
+ | Pros/cons or "X vs Y" pair | Comparison cards (§9) |
89
+ | Inline `<details>` | Pass through |
90
+ | H3 section > 80 lines, or "Appendix" / "Full log" / "Phụ lục" | Wrap in `<details class="collapsible">` (§10) |
91
+ | Plain `ul` / `ol` not matching step heuristic | `<ul>` / `<ol>` (§11) |
92
+ | Inline `` `code` `` | `<code>` verbatim |
93
+ | Source has `## TL;DR` / `## Tóm tắt` | TL;DR card (§3) at top |
94
+
95
+ **TL;DR is optional here** (unlike spec-render). Only render if the source has it.
96
+
97
+ **Counts:** count H2 sections, code blocks, Mermaid diagrams → topbar meta.
98
+
99
+ ### Step 3 — Build output buffer
100
+
101
+ Copy `template.html` into an in-memory string. Replace placeholders:
102
+
103
+ | Placeholder | Value |
104
+ |---|---|
105
+ | `{{LANG}}` | ISO 639-1 from detection |
106
+ | `{{TITLE}}` | First H1 text, or filename slug if absent |
107
+ | `{{SUBTITLE}}` | First paragraph after H1 (≤ 200 chars); empty if none |
108
+ | `{{DOC_TYPE}}` | `DOC` / `INVESTIGATION` / `EXPLORE` / `RFC` / `NOTES` / `RETRO` (infer from filename prefix; fallback `DOC`) |
109
+ | `{{DOC_TYPE_CLASS}}` | lowercase of above |
110
+ | `{{LAST_UPDATED}}` | ISO date from frontmatter (`date:` / `**Last updated**:`) else `$(date +%Y-%m-%d)` |
111
+ | `{{UPDATED_LABEL}}` | "updated" / "cập nhật" / matched language |
112
+ | `{{META_EXTRA}}` | `<span class="sep">·</span><span>N sections</span>` + optional diagrams / code blocks; skip zero counts |
113
+ | `{{TOC_LABEL}}` | "Contents" / "Mục lục" / matched |
114
+ | `{{SEARCH_PLACEHOLDER}}` | "Search…" / "Tìm trong trang…" / matched |
115
+ | `{{SKIP_LABEL}}` | "Skip to content" / "Bỏ qua menu" / matched |
116
+ | `{{THEME_TIP}}` | "Toggle theme" / "Đổi theme" / matched |
117
+ | `{{COPY_LABEL}}` | "Copy" / "Sao chép" / matched |
118
+ | `{{COPIED_LABEL}}` | "Copied" / "Đã chép" / matched |
119
+
120
+ ### Step 4 — Render TOC
121
+
122
+ Replace `<!-- TOC_ENTRIES -->`. One entry per H2; H3 nested with `.toc a.h3` indent. Flat list ordered by appearance:
123
+
124
+ ```html
125
+ <a href="#section-id" data-target="section-id">Section title</a>
126
+ <a href="#sub-id" data-target="sub-id" class="h3">Subsection</a>
127
+ ```
128
+
129
+ ### Step 5 — Render body
130
+
131
+ Replace the slot between `<!-- CONTENT_START -->` and `<!-- CONTENT_END -->` with body markup in source order. Use only components in `components.md`.
132
+
133
+ **Identifier preservation:** function names, file paths, routes, env vars, CLI flags stay verbatim in `<code>`.
134
+
135
+ **Mermaid:** ```mermaid block → `<div class="mermaid">…</div>`. CDN script in template renders it.
136
+
137
+ ### Step 6 — Write file
138
+
139
+ Write the buffer to `<file>.html` (or `--out`). **One Write call.**
140
+
141
+ ### Step 7 — Verify
142
+
143
+ Re-read the written file and check:
144
+
145
+ - [ ] No leftover `{{PLACEHOLDER}}` strings.
146
+ - [ ] No leftover slot comments.
147
+ - [ ] Every TOC `href="#x"` resolves to an element with `id="x"` (sample 5).
148
+ - [ ] Count of `<h2 id` matches H2 count from source.
149
+ - [ ] Each ```mermaid block produced one `<div class="mermaid">`.
150
+
151
+ Fix with targeted Edits if needed — don't rewrite the file.
152
+
153
+ Report:
154
+
155
+ ```
156
+ ✓ Rendered <filename> → <path>
157
+ <N sections> · <N diagrams> · <N code blocks> · lang: <xx>
158
+ Open: open <path>
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Rules
164
+
165
+ 1. **Source is truth.** Never hand-edit HTML.
166
+ 2. **One Write call.** Build buffer in memory, write once.
167
+ 3. **No new CSS or components.** Use only template classes and `components.md` snippets.
168
+ 4. **Never paraphrase technical content.** Identifiers, code, paths verbatim in `<code>`.
169
+ 5. **Don't invent TL;DR.** Only render if source has it.
170
+ 6. **No emojis in chrome.** Use SVG icons from template sprite. Source emojis pass through inside prose only.
171
+ 7. **Long sections collapse.** > 80 lines under one H3 → wrap in `<details>`.
172
+
173
+ ---
174
+
175
+ ## Edge cases
176
+
177
+ - **Empty file / no headings** → render the prose as a single block, no TOC entries.
178
+ - **Heading ID collision** → suffix `-2`, `-3`; mirror in TOC.
179
+ - **Mermaid syntax invalid** → still emit `<div class="mermaid">`; the CDN shows its own inline error.
180
+ - **Frontmatter** → strip from body; read `date:` / `**Last updated**:` only.
181
+ - **Raw HTML in source** → pass through except `<script>` and `<style>` (strip for safety).
182
+ - **Source is a /sp-plan spec** → STOP, tell user to run `/sp-spec-render`.
183
+
184
+ ---
185
+
186
+ ## Anti-patterns
187
+
188
+ - ❌ Edit loop to assemble HTML — build buffer, Write once.
189
+ - ❌ New `<style>` blocks or inline CSS attributes.
190
+ - ❌ Inventing components not in `components.md`.
191
+ - ❌ Translating identifiers, routes, function names, CLI flags.
192
+ - ❌ Adding emojis to UI chrome.
193
+ - ❌ Rendering a spec file — defer to [[sp-spec-render]].
194
+
195
+ ---
196
+
197
+ ## Relationship with other skills
198
+
199
+ - [[sp-spec-render]] — sibling for /sp-plan specs (story cards, AS dl/dt, P-badges). This skill is the generic fallback.
200
+ - `/sp-investigate`, `/sp-explore`, `/retro` produce long markdown artefacts; suggest this skill at the end so the user can render an HTML view.
@@ -0,0 +1,415 @@
1
+ # Components catalog for sp-md-render
2
+
3
+ Copy these snippets verbatim into the body slot. Do not invent new CSS classes. Do not add `<style>` or `<script>` tags — the template already has them.
4
+
5
+ All `id` attributes must be kebab-case slugs derived from the heading text, deduplicated by appending `-2`, `-3` on collision.
6
+
7
+ ---
8
+
9
+ ## §1. Section headings
10
+
11
+ ```html
12
+ <h2 id="kebab-slug">Section title</h2>
13
+ <h3 id="kebab-slug-sub">Subsection</h3>
14
+ <h4>Eyebrow / non-anchored label</h4>
15
+ ```
16
+
17
+ H1 is reserved for the page title (template). Don't emit additional H1s.
18
+
19
+ ---
20
+
21
+ ## §2. TOC entries (sidebar)
22
+
23
+ Placed inside `<nav class="toc">`. One link per H2; H3 nested via `.h3` class.
24
+
25
+ ```html
26
+ <a href="#section-id" data-target="section-id">Section title</a>
27
+ <a href="#section-id-sub" data-target="section-id-sub" class="h3">Subsection</a>
28
+ ```
29
+
30
+ `data-target` MUST match the element `id` exactly — the scroll-spy uses it.
31
+
32
+ ---
33
+
34
+ ## §3. TL;DR card
35
+
36
+ Render only if the source has a `## TL;DR` / `## Tóm tắt` / `## Summary` section. Place before the first H2.
37
+
38
+ ```html
39
+ <aside class="tldr">
40
+ <div class="tldr-label">TL;DR</div>
41
+ <ul>
42
+ <li>Bullet one.</li>
43
+ <li>Bullet two.</li>
44
+ </ul>
45
+ </aside>
46
+ ```
47
+
48
+ Max ~10 bullets. Keep each ≤ 1 line. If source is prose, render as `<p>` inside the card instead of `<ul>`.
49
+
50
+ ---
51
+
52
+ ## §4. Step cards
53
+
54
+ Use when source has 3+ sequential imperative items (numbered list with verbs, "Step N:", "Bước N:").
55
+
56
+ ```html
57
+ <ol class="steps">
58
+ <li class="step">
59
+ <div class="step-title">Open the dashboard</div>
60
+ <div class="step-body">Navigate to <code>/admin/dashboard</code> and sign in.</div>
61
+ </li>
62
+ <li class="step">
63
+ <div class="step-title">Run the migration</div>
64
+ <div class="step-body">
65
+ <p>Execute <code>bin/migrate up</code>. Wait for the green check.</p>
66
+ </div>
67
+ </li>
68
+ </ol>
69
+ ```
70
+
71
+ The numbered badge is generated via CSS counter. Don't hardcode "1.", "2." in the title.
72
+
73
+ ---
74
+
75
+ ## §5. Callouts
76
+
77
+ Map GFM admonitions to the matching variant. Source emojis in callout body pass through; do NOT add emojis to the title.
78
+
79
+ ### NOTE — neutral aside
80
+
81
+ ```html
82
+ <div class="callout note">
83
+ <svg class="ico" aria-hidden="true"><use href="#i-note"/></svg>
84
+ <div>
85
+ <div class="callout-title">Note</div>
86
+ <p>Body text. Identifiers like <code>SHA256</code> stay verbatim.</p>
87
+ </div>
88
+ </div>
89
+ ```
90
+
91
+ ### TIP — recommendation
92
+
93
+ ```html
94
+ <div class="callout tip">
95
+ <svg class="ico" aria-hidden="true"><use href="#i-tip"/></svg>
96
+ <div>
97
+ <div class="callout-title">Tip</div>
98
+ <p>Body text.</p>
99
+ </div>
100
+ </div>
101
+ ```
102
+
103
+ ### WARN — caution
104
+
105
+ ```html
106
+ <div class="callout warn">
107
+ <svg class="ico" aria-hidden="true"><use href="#i-warn"/></svg>
108
+ <div>
109
+ <div class="callout-title">Warning</div>
110
+ <p>Body text.</p>
111
+ </div>
112
+ </div>
113
+ ```
114
+
115
+ ### DANGER — destructive / irreversible
116
+
117
+ ```html
118
+ <div class="callout danger">
119
+ <svg class="ico" aria-hidden="true"><use href="#i-danger"/></svg>
120
+ <div>
121
+ <div class="callout-title">Danger</div>
122
+ <p>Body text.</p>
123
+ </div>
124
+ </div>
125
+ ```
126
+
127
+ **Title translation:** match `{{LANG}}` — vi: Ghi chú / Mẹo / Cảnh báo / Nguy hiểm. en: Note / Tip / Warning / Danger.
128
+
129
+ ---
130
+
131
+ ## §6. Code blocks
132
+
133
+ The copy button is auto-injected by template JS. Optional language label appears top-left.
134
+
135
+ ```html
136
+ <pre class="code"><span class="lang">bash</span><code>cd /tmp
137
+ ./build.sh --release</code></pre>
138
+ ```
139
+
140
+ No language? Omit the `<span class="lang">`:
141
+
142
+ ```html
143
+ <pre class="code"><code>raw text without language hint</code></pre>
144
+ ```
145
+
146
+ **HTML-escape** `<`, `>`, `&` inside the `<code>` body. Do not syntax-highlight (no extra spans).
147
+
148
+ ---
149
+
150
+ ## §7. Mermaid diagram
151
+
152
+ For fenced ```mermaid blocks. The CDN script in the template renders them on load.
153
+
154
+ ```html
155
+ <div class="mermaid">
156
+ graph TD
157
+ A[Start] --> B{Decision}
158
+ B -->|yes| C[Do thing]
159
+ B -->|no| D[Skip]
160
+ </div>
161
+ ```
162
+
163
+ Preserve source Mermaid syntax verbatim — do NOT HTML-escape inside `.mermaid`.
164
+
165
+ ---
166
+
167
+ ## §8. Tables
168
+
169
+ GFM table → HTML table. Wraps with horizontal scroll on narrow screens via CSS.
170
+
171
+ ```html
172
+ <table class="md-table">
173
+ <thead>
174
+ <tr><th>Column A</th><th>Column B</th></tr>
175
+ </thead>
176
+ <tbody>
177
+ <tr><td>value 1</td><td><code>identifier</code></td></tr>
178
+ <tr><td>value 2</td><td>value 3</td></tr>
179
+ </tbody>
180
+ </table>
181
+ ```
182
+
183
+ ---
184
+
185
+ ## §9. Comparison cards
186
+
187
+ For "X vs Y", pros/cons, or two-column trade-off discussions.
188
+
189
+ ### Generic two-column
190
+
191
+ ```html
192
+ <div class="compare">
193
+ <div>
194
+ <h4>Option A</h4>
195
+ <p>Body for A.</p>
196
+ </div>
197
+ <div>
198
+ <h4>Option B</h4>
199
+ <p>Body for B.</p>
200
+ </div>
201
+ </div>
202
+ ```
203
+
204
+ ### Pros / cons (left=good, right=bad)
205
+
206
+ ```html
207
+ <div class="compare pro">
208
+ <div>
209
+ <h4>Pros</h4>
210
+ <ul><li>Fast</li><li>Cheap</li></ul>
211
+ </div>
212
+ <div>
213
+ <h4>Cons</h4>
214
+ <ul><li>Risky migration</li></ul>
215
+ </div>
216
+ </div>
217
+ ```
218
+
219
+ The `.pro` variant adds green/red left borders.
220
+
221
+ ---
222
+
223
+ ## §10. Collapsible section
224
+
225
+ Use for appendices, full logs, long detail sections (> 80 lines under one H3), or anything labeled "Details" / "Phụ lục" / "Full log".
226
+
227
+ ```html
228
+ <details class="collapsible">
229
+ <summary>Show full stack trace</summary>
230
+ <div>
231
+ <pre class="code"><code>... long output ...</code></pre>
232
+ </div>
233
+ </details>
234
+ ```
235
+
236
+ Defaults closed. Open by default only if the source explicitly opens it.
237
+
238
+ ---
239
+
240
+ ## §11. Plain lists
241
+
242
+ Use when content does not match the step-card heuristic — short scannable items, mixed prose, non-sequential.
243
+
244
+ ```html
245
+ <ul>
246
+ <li>First item with <code>some-code</code>.</li>
247
+ <li>Second item.
248
+ <ul>
249
+ <li>Nested.</li>
250
+ </ul>
251
+ </li>
252
+ </ul>
253
+ ```
254
+
255
+ Ordered:
256
+
257
+ ```html
258
+ <ol>
259
+ <li>One.</li>
260
+ <li>Two.</li>
261
+ </ol>
262
+ ```
263
+
264
+ ---
265
+
266
+ ## §12. Blockquote (non-callout)
267
+
268
+ For quotations, citations, or "as said by X" passages that are NOT admonitions.
269
+
270
+ ```html
271
+ <blockquote>
272
+ <p>The best code is no code at all.</p>
273
+ </blockquote>
274
+ ```
275
+
276
+ ---
277
+
278
+ ## §13. Horizontal rule
279
+
280
+ ```html
281
+ <hr>
282
+ ```
283
+
284
+ Use sparingly — markdown `---` between major sections only.
285
+
286
+ ---
287
+
288
+ ## §14. Inline elements
289
+
290
+ - Inline code: `<code>identifier</code>`
291
+ - Bold: `<strong>important</strong>`
292
+ - Italic: `<em>nuance</em>`
293
+ - Link: `<a href="https://example.com">label</a>` (external links open in same tab; do not auto-add `target="_blank"`)
294
+ - Image: `<img src="path/to/image.png" alt="alt text">`
295
+
296
+ ---
297
+
298
+ ## §16. Task list (GFM `- [ ]`)
299
+
300
+ Map `- [ ]` / `- [x]` lines (GitHub-flavored markdown task list). Render checked items strikethrough automatically via CSS.
301
+
302
+ ```html
303
+ <ul class="task-list">
304
+ <li class="task-item">
305
+ <input type="checkbox" disabled>
306
+ <span class="task-text">Open ticket and reproduce locally.</span>
307
+ </li>
308
+ <li class="task-item">
309
+ <input type="checkbox" disabled checked>
310
+ <span class="task-text">Write failing test in <code>tests/billing.spec.ts</code>.</span>
311
+ </li>
312
+ </ul>
313
+ ```
314
+
315
+ Always emit `disabled` on the checkbox — the HTML is a view, not interactive state. `checked` reflects the source `- [x]`.
316
+
317
+ ---
318
+
319
+ ## §17. Strikethrough
320
+
321
+ Map markdown `~~text~~` → `<del>`. Inline element, lives inside paragraphs/lists.
322
+
323
+ ```html
324
+ <p>Migration was <del>scheduled for Friday</del> pushed to Monday.</p>
325
+ ```
326
+
327
+ Use `<del>` (semantic) over `<s>` (presentational). Color is muted via CSS — do not add inline styles.
328
+
329
+ ---
330
+
331
+ ## §18. Footnotes
332
+
333
+ Map Pandoc/GFM style: inline `[^1]` → superscript ref linking to a footnotes section at the bottom of the page.
334
+
335
+ **Inline reference (in body):**
336
+
337
+ ```html
338
+ <sup class="fn-ref"><a href="#fn-1" id="fnref-1">1</a></sup>
339
+ ```
340
+
341
+ **Footnotes section (once, at end of `<main>` after all body content):**
342
+
343
+ ```html
344
+ <section class="footnotes" aria-label="Footnotes">
345
+ <h4>Footnotes</h4>
346
+ <ol>
347
+ <li id="fn-1">
348
+ The full citation or note. Identifiers stay verbatim: <code>RFC 8707</code>.
349
+ <a class="fn-back" href="#fnref-1" aria-label="Back to text">↩</a>
350
+ </li>
351
+ <li id="fn-2">
352
+ Second note.
353
+ <a class="fn-back" href="#fnref-2" aria-label="Back to text">↩</a>
354
+ </li>
355
+ </ol>
356
+ </section>
357
+ ```
358
+
359
+ Numbering must match between body refs and list items. Heading label "Footnotes" translates per `{{LANG}}` — vi: "Chú thích". The target item (when navigated via `#fn-N`) gets a highlight ring via `:target` CSS — no extra markup needed.
360
+
361
+ ---
362
+
363
+ ## §19. Figure with caption
364
+
365
+ Wrap an image that has a caption (markdown `![alt](src "title")` or explicit caption prose right after) into `<figure>`.
366
+
367
+ ```html
368
+ <figure>
369
+ <img src="diagrams/auth-flow.png" alt="OAuth flow with PKCE">
370
+ <figcaption>Figure 1: OAuth 2.1 + PKCE handshake. Token exchange happens server-side.</figcaption>
371
+ </figure>
372
+ ```
373
+
374
+ If the source image has no caption, use plain `<img>` (§14) instead — do not invent caption text.
375
+
376
+ ---
377
+
378
+ ## §20. Anchor copy link (auto-injected)
379
+
380
+ **You do not emit this markup.** Template JS auto-adds a `<a class="anchor" href="#id">#</a>` to every `<h2 id>` and `<h3 id>` on page load. Hover the heading to reveal it; click to copy the deep link to clipboard.
381
+
382
+ What you must ensure:
383
+
384
+ - Every section heading you want copyable has an `id` attribute.
385
+ - Do not emit your own `.anchor` element — the JS handles it.
386
+
387
+ CSS hides it by default and shows on `:hover` of the parent heading; on mobile it stays inline at .45 opacity so it's discoverable without hover.
388
+
389
+ ---
390
+
391
+ ## §21. UI label translations
392
+
393
+ UI chrome labels go into placeholders (template). Provide values matching `{{LANG}}`:
394
+
395
+ | Placeholder | en | vi |
396
+ |---|---|---|
397
+ | `{{TOC_LABEL}}` | Contents | Mục lục |
398
+ | `{{SEARCH_PLACEHOLDER}}` | Search… | Tìm trong trang… |
399
+ | `{{SKIP_LABEL}}` | Skip to content | Bỏ qua menu |
400
+ | `{{THEME_TIP}}` | Toggle theme | Đổi theme |
401
+ | `{{UPDATED_LABEL}}` | updated | cập nhật |
402
+ | `{{COPY_LABEL}}` | Copy | Sao chép |
403
+ | `{{COPIED_LABEL}}` | Copied | Đã chép |
404
+ | Footnotes heading (§18) | Footnotes | Chú thích |
405
+
406
+ Callout title translations (in body markup, not placeholders):
407
+
408
+ | Variant | en | vi |
409
+ |---|---|---|
410
+ | note | Note | Ghi chú |
411
+ | tip | Tip | Mẹo |
412
+ | warn | Warning | Cảnh báo |
413
+ | danger | Danger | Nguy hiểm |
414
+
415
+ For other source languages, translate consistently across all labels and titles.