ligarb 0.9.0 → 0.9.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10e90698ccef79745b7709265f5657df0354c86000ef85be7401b56cb443da0b
4
- data.tar.gz: 5edf902c6f4d06b0fe923f5eb24372b33379bfb50296cadeec04ef308c0cc217
3
+ metadata.gz: fff2a914d9209b69c1b9c35d4133accfe0d5dcc4e272779959baa9c3bba64cf0
4
+ data.tar.gz: da08e338979034ba836c2321adb4820f91231e942a6bedb0e7ffb31128271c52
5
5
  SHA512:
6
- metadata.gz: c2d201101b94c3b63405599c21e91c043ac98967fbf6b25035a0e959782c70fbbfa9948981caac86cdb9f25405ebe0e8f2d80c5e5bab6453912a62f1c212cf24
7
- data.tar.gz: ed28454394317ade969c4fd8e5dd974b3f34e732208f7089a5f603160a9e9f5172c78d11ef04f872d3b455de18811a56501b9e69e6983ce4d38942bae93d77d4
6
+ metadata.gz: 6880ee104849e3904dad11583a8b635dc3bf105acea215df197ab4acbe1dcffa432b5c45c0c471d84424f22c7f287c2b11dc553f987c99d2613948d1f01eb766
7
+ data.tar.gz: c230438379c7ff16c808b38835fe0ea764b88433e109ce0573caba710163ec3efddb86975251faa145525176946a543b70eb44670fc55718998fade9098bf7c7
@@ -2,11 +2,19 @@
2
2
  //
3
3
  // Usage: node mermaid_check.mjs <path/to/mermaid.min.js>
4
4
  // stdin: JSON array of {"id": <any>, "text": <mermaid source>}
5
- // stdout: JSON array of {"id": <any>, "error": <message or null>}
5
+ // stdout: JSON array of {"id": <any>, "error": <message or null>,
6
+ // "kind": "syntax" | "environment"}
6
7
  //
7
8
  // Loads the browser UMD bundle of mermaid in Node by stubbing just enough
8
9
  // of the DOM (mermaid.parse() only parses; it never renders, but DOMPurify
9
10
  // refuses to initialize without something that looks like a document).
11
+ //
12
+ // The DOM stub is intentionally minimal, so it cannot satisfy DOMPurify when a
13
+ // node label contains HTML (e.g. "A[1<br>2]"): sanitizing real markup needs a
14
+ // real DOM tree to walk, which the stub does not provide. That surfaces as a
15
+ // generic JS error (e.g. TypeError "Right-hand side of 'instanceof'..."), NOT a
16
+ // diagram syntax error. classifyError() tells the two apart so callers only
17
+ // warn about genuine mermaid problems and not these harness limitations.
10
18
 
11
19
  const noop = () => {};
12
20
 
@@ -73,14 +81,36 @@ if (!mermaid || typeof mermaid.parse !== "function") {
73
81
  process.exit(2);
74
82
  }
75
83
 
84
+ // Decide whether a thrown error is a genuine mermaid diagram problem
85
+ // ("syntax") or an artifact of our minimal DOM stub ("environment").
86
+ //
87
+ // - jison grammar errors carry a structured `.hash` -> syntax
88
+ // - mermaid's typed errors (e.g. UnknownDiagramError) -> syntax
89
+ // - generic JS runtime errors (TypeError/ReferenceError/RangeError/EvalError)
90
+ // with no hash come from the DOM stub -> environment
91
+ // - anything else is reported as syntax, erring toward visibility
92
+ function classifyError(e) {
93
+ if (e && e.hash !== undefined) return "syntax";
94
+ const name = e && e.name;
95
+ if (name && name.endsWith("DiagramError")) return "syntax";
96
+ if (["TypeError", "ReferenceError", "RangeError", "EvalError"].includes(name)) {
97
+ return "environment";
98
+ }
99
+ return "syntax";
100
+ }
101
+
76
102
  const blocks = JSON.parse(readFileSync(0, "utf8"));
77
103
  const results = [];
78
104
  for (const block of blocks) {
79
105
  try {
80
106
  await mermaid.parse(block.text);
81
- results.push({ id: block.id, error: null });
107
+ results.push({ id: block.id, error: null, kind: "syntax" });
82
108
  } catch (e) {
83
- results.push({ id: block.id, error: String(e && e.message ? e.message : e) });
109
+ results.push({
110
+ id: block.id,
111
+ error: String(e && e.message ? e.message : e),
112
+ kind: classifyError(e),
113
+ });
84
114
  }
85
115
  }
86
116
  console.log(JSON.stringify(results));
data/docs/help.md ADDED
@@ -0,0 +1,851 @@
1
+ # ligarb - Generate a single-page HTML book from Markdown files
2
+
3
+ ## Overview
4
+
5
+ ligarb converts multiple Markdown files into a self-contained index.html.
6
+ The generated HTML includes:
7
+ - A left sidebar with a searchable table of contents (h1-h3)
8
+ - Chapter-based content switching in the main area
9
+ - Permalink support via URL hash (#chapter-slug)
10
+ - Responsive design with print-friendly styles
11
+ - Syntax-highlighted code blocks
12
+ - Search with content highlighting
13
+ - Chapter and section numbering (configurable)
14
+ - Previous/Next chapter navigation
15
+ - Dark mode toggle (saved to localStorage)
16
+ - Custom CSS support
17
+ - "Edit on GitHub" links (optional)
18
+ - Footnotes (kramdown syntax)
19
+
20
+ **Intended use:** ligarb is a local CLI tool for authors who build books from
21
+ their own Markdown sources. The generated HTML is a static file meant to be
22
+ opened in a browser or deployed to a static hosting service. `ligarb serve`
23
+ is for local preview only and must not be exposed to the internet.
24
+
25
+ **Non-goals:** ligarb does not sanitize Markdown-derived HTML, CSS, or
26
+ bibliography URLs. It is not designed for untrusted third-party content,
27
+ public upload services, or automated pipelines that ingest unreviewed input.
28
+ If such use cases are needed, add HTML sanitization, URL validation, and CSP
29
+ headers externally.
30
+
31
+ ## Commands
32
+
33
+ ### `ligarb init [DIRECTORY]`
34
+
35
+ Create a new book project with scaffolding.
36
+ If DIRECTORY is given, creates and populates that directory.
37
+ If omitted, populates the current directory.
38
+ Generates book.yml, 01-introduction.md, and images/.
39
+ If .md files already exist, registers them as chapters.
40
+ Aborts if book.yml already exists.
41
+
42
+ ### `ligarb setup-github-review [DIRECTORY] [--owner NAME]`
43
+
44
+ Set up a GitHub-based review workflow in an existing project (book.yml must
45
+ exist). DIRECTORY defaults to the current directory. Composable: run it after
46
+ `ligarb init` or `ligarb write`, or on any hand-made project.
47
+
48
+ It generates `.github/`, `SETUP.md`, and `SETUP.sh` — GitHub Actions workflows
49
+ for publishing the book to GitHub Pages, checking that PRs still build, and
50
+ (opt-in) letting Claude triage reader-filed issues into pull requests — plus
51
+ issue forms for structured feedback and a gh CLI one-shot script (`SETUP.sh`)
52
+ that does the repo creation / secret / Pages / labels setup. It also:
53
+ - adds `github_review.enabled: true` to book.yml (the reader feedback UI) if
54
+ absent;
55
+ - seeds a default `repository:` (`https://github.com/<owner>/<dir>`) if absent,
56
+ so the templates and SETUP.sh get concrete values — edit it if the guess is
57
+ wrong and re-run. `<owner>` is the `--owner` (alias `--user`) flag when given
58
+ — useful for an organization — otherwise `$USER`. If `repository:` is already
59
+ set, passing `--owner` is an error (edit `repository:` in book.yml directly);
60
+ - seeds a default `site_url:` (the GitHub Pages URL derived from `repository`)
61
+ if absent, so the build emits `og:url`/canonical — edit it for a custom domain;
62
+ - creates a project `README.md` linking to the GitHub Pages site, unless one
63
+ already exists (your README is never overwritten).
64
+
65
+ Re-run it any time to update the scaffolding to the latest templates (e.g. after
66
+ upgrading ligarb): existing generated files are **overwritten**, so review the
67
+ changes with `git diff` and revert any local edits you want to keep. Your
68
+ book.yml is never overwritten. `__OWNER__` / `__REPO__` placeholders in the
69
+ templates are filled from book.yml's `repository:`; if it is unset the
70
+ placeholders are left as-is (and the Discussions link in
71
+ `.github/ISSUE_TEMPLATE/config.yml` is commented out). The generated files are
72
+ the only thing that depends on Claude or GitHub Actions — ligarb itself never
73
+ calls either at runtime. Read the generated `SETUP.md` for the manual steps
74
+ (token registration, repository settings) that cannot be generated; it includes
75
+ a gh CLI quickstart.
76
+
77
+ ### `ligarb build [CONFIG]`
78
+
79
+ Build the HTML book.
80
+ CONFIG defaults to 'book.yml' in the current directory.
81
+
82
+ The output directory contains index.html plus js/ and css/ subdirectories
83
+ for auto-downloaded libraries (highlight.js, mermaid, KaTeX, etc.).
84
+ Open index.html directly in a browser — no web server needed.
85
+
86
+ When `github_review.enabled` is true and `repository` is set (see
87
+ Configuration below), the build also injects a static "Report as issue"
88
+ feedback UI: readers select text, pick a type and add a comment, and a
89
+ prefilled GitHub Issue form opens in a new tab (chapter/section, quoted text,
90
+ source path, and current page URL are filled in). It is pure client-side JS
91
+ with no backend or tokens — it only builds a URL and opens it. If
92
+ `github_review.enabled` is true but `repository` is unset, build prints a
93
+ warning and skips the UI. Without `github_review`, nothing is injected.
94
+
95
+ ### `ligarb serve [CONFIG...]`
96
+
97
+ Start a local web server with live reload and review UI.
98
+ CONFIG defaults to 'book.yml' in the current directory.
99
+ Multiple CONFIG paths can be given to serve multiple books.
100
+
101
+ Options:
102
+ - `--port PORT` — Server port (default: 3000)
103
+ - `--host ADDR` — Bind address (default: 127.0.0.1, loopback only).
104
+ Use `0.0.0.0` (or a specific LAN IP) to reach the server from other
105
+ devices — e.g. from the Windows host when running under WSL, or a
106
+ phone on the same network. Binding beyond loopback prints a warning:
107
+ the review/feedback APIs (file writes, Claude runs) become reachable
108
+ by other hosts, so only do this on a trusted network. Same-origin
109
+ protection still applies (POSTs must come from the page's own origin).
110
+ - `--multi` — Force multi-book mode (even with 1 CONFIG)
111
+
112
+ **Single book mode** (1 CONFIG, without --multi):
113
+ - Serves the built HTML book at http://localhost:PORT
114
+
115
+ **Multi-book mode** (2+ CONFIGs, or --multi):
116
+ - Top page (/) shows a book index with links
117
+ - Each book is served at /\<directory-name\>/
118
+ - "Write a new book" button on the index page to generate
119
+ a new book via AI (posts a brief, runs Writer in background)
120
+ - Example: `ligarb serve */book.yml`
121
+
122
+ Features:
123
+ - Injects a reload button that pulses when build output changes
124
+ - Injects a review UI for commenting on book text
125
+ - Review comments are saved to .ligarb/reviews/\*.json
126
+ (in each book's directory)
127
+ - If 'claude' CLI is installed, comments are sent to Claude
128
+ for review suggestions, and approved changes are applied
129
+ to the source Markdown files and the book is rebuilt
130
+ - Review patches can span multiple chapters and the
131
+ bibliography file (Claude reads book.yml to find all files)
132
+
133
+ ### `ligarb librarium`
134
+
135
+ Start a multi-book library server.
136
+ Automatically discovers \*/book.yml in the current directory.
137
+ Equivalent to: `ligarb serve --multi */book.yml`
138
+
139
+ Options:
140
+ - `--port PORT` — Server port (default: 3000)
141
+ - `--host ADDR` — Bind address (default: 127.0.0.1; see `serve` above)
142
+
143
+ ### `ligarb write [BRIEF]`
144
+
145
+ Generate a complete book using AI (Claude).
146
+ BRIEF defaults to 'brief.yml' in the current directory.
147
+ Reads the brief, sends a prompt to Claude, and builds
148
+ the generated book. Files are created in the same
149
+ directory as brief.yml.
150
+
151
+ - `ligarb write --init [DIR]` — Create a brief.yml template.
152
+ If DIR is given, creates DIR/brief.yml (mkdir as needed).
153
+ If omitted, creates brief.yml in the current directory.
154
+ - `ligarb write --no-build` — Generate files only, skip the build step.
155
+
156
+ Requires the 'claude' CLI to be installed.
157
+ See the "Write Command" section below for brief.yml details.
158
+
159
+ ### `ligarb help`
160
+
161
+ Show this detailed specification.
162
+
163
+ ### `ligarb --help`
164
+
165
+ Show short usage summary.
166
+
167
+ ### `ligarb version`
168
+
169
+ Show the version number.
170
+
171
+ ## Configuration: book.yml
172
+
173
+ The configuration file is a YAML file with the following fields:
174
+
175
+ | Field | Required | Default | Description |
176
+ |-------|----------|---------|-------------|
177
+ | `title` | required | — | The book title displayed in the header and \<title\> tag. |
178
+ | `description` | optional | — | Short description for the `<meta name="description">` and Open Graph (`og:description`) tags. When omitted, it is auto-extracted from the first prose paragraph of the cover (or first) chapter. Not inherited by translations — set it per language. |
179
+ | `site_url` | optional | — | Canonical published URL of the book. When set, emits `og:url` and `<link rel="canonical">`. Used verbatim, so set it to where this build is actually served. `setup-github-review` seeds it from `repository` (the GitHub Pages URL) if absent; edit it for custom domains or other hosting. |
180
+ | `author` | optional | empty | Author name displayed in the header. |
181
+ | `language` | optional | `"en"` | HTML lang attribute value. |
182
+ | `output_dir` | optional | `"build"` | Output directory relative to book.yml. |
183
+ | `chapter_numbers` | optional | `true` | Show chapter/section numbers (e.g. "1.", "1.1", "1.1.1"). |
184
+ | `style` | optional | — | Path to a custom CSS file relative to book.yml. Loaded after the default styles, so it can override any rule. |
185
+ | `repository` | optional | — | GitHub repository URL (e.g. "https://github.com/user/repo"). When set, each chapter shows a "View on GitHub" link pointing to `{repository}/blob/HEAD/{path-from-git-root}`. |
186
+ | `ai_generated` | optional | `false` | Mark the book as AI-generated content. When true: adds an "AI Generated" badge in the sidebar header, adds a default disclaimer footer to each chapter, and adds noindex/noai meta tags. The footer text can be overridden with the `footer` field. |
187
+ | `footer` | optional | — | Custom text displayed at the bottom of each chapter. Overrides the default ai_generated disclaimer if both are set. |
188
+ | `bibliography` | optional | — | Path to a bibliography file (.yml or .bib) relative to book.yml. See "Bibliography" section. |
189
+ | `sources` | optional | — | Reference files for AI context. Array of strings or `{path:, label:}` objects. Used by the `write` command and review AI. |
190
+ | `github_review` | optional | — | A mapping enabling the static reader feedback UI. `enabled: true` injects a "Report as issue" UI into `build` output (requires `repository`). Optional sub-keys: `issue_template` (default `book-feedback.yml`) and `labels` (default `[feedback]`) prefilled into the GitHub Issue URL. Usually set by `ligarb setup-github-review`. |
191
+ | `translations` | optional | — | A mapping of language codes to config file paths. Enables multi-language support. See "Translations" section. |
192
+ | `inherit` | optional | — | Path to a parent config file. Inheritable settings (author, language, style, etc.) are loaded as defaults. Used by per-language configs for standalone builds. |
193
+ | `chapters` | required | — | Book structure. An array that can contain: a cover, chapter strings, parts, and appendix. |
194
+
195
+ ### chapters array
196
+
197
+ All paths in the chapters array are relative to book.yml.
198
+
199
+ The chapters array supports four element types:
200
+
201
+ **1. Cover** (object with 'cover' key):
202
+
203
+ ```yaml
204
+ chapters:
205
+ - cover: cover.md # Markdown file: landing page shown when the book is opened
206
+ # Not shown in the TOC sidebar.
207
+ ```
208
+
209
+ **2. Plain chapter** (string):
210
+
211
+ ```yaml
212
+ chapters:
213
+ - 01-introduction.md
214
+ ```
215
+
216
+ **3. Part** (object with 'part' and 'chapters' keys):
217
+
218
+ ```yaml
219
+ chapters:
220
+ - part: part1.md
221
+ chapters:
222
+ - 01-introduction.md
223
+ - 02-getting-started.md
224
+ ```
225
+
226
+ The part file's h1 becomes the part title, and the body text (if any)
227
+ is shown as the part's opening page.
228
+
229
+ **4. Appendix** (object with 'appendix' key, value is array of chapter files):
230
+
231
+ ```yaml
232
+ chapters:
233
+ - appendix:
234
+ - a1-references.md
235
+ - a2-glossary.md
236
+ ```
237
+
238
+ These can be combined freely:
239
+
240
+ ```yaml
241
+ chapters:
242
+ - cover: cover.md
243
+ - part: part1.md
244
+ chapters:
245
+ - 01-introduction.md
246
+ - 02-getting-started.md
247
+ - part: part2.md
248
+ chapters:
249
+ - 03-advanced.md
250
+ - appendix:
251
+ - a1-references.md
252
+ ```
253
+
254
+ Part numbering is sequential across parts (1, 2, 3, ...).
255
+ Appendix numbering uses letters (A, B, C, ...).
256
+
257
+ ### Social metadata (Open Graph)
258
+
259
+ Every build emits Open Graph tags in `<head>` so the generated `index.html`
260
+ previews nicely when shared: `og:type` (`book`), `og:title` and `og:site_name`
261
+ (the book title), `og:locale` (derived from `language`, e.g. `ja` → `ja_JP`),
262
+ and `book:author` when `author` is set. If a description is available (the
263
+ `description` field, or auto-extracted from the cover/first chapter) it is also
264
+ emitted as `<meta name="description">` and `og:description`. A
265
+ `twitter:card` (`summary`) tag is always included. No image tag is produced.
266
+
267
+ When `site_url` is set, `og:url` and `<link rel="canonical">` are emitted with
268
+ that URL verbatim (the book is a single page, so it has one canonical URL).
269
+ Running `setup-github-review` seeds `site_url` in book.yml from `repository`
270
+ (the GitHub Pages URL — `https://<owner>.github.io/<repo>/`, or
271
+ `https://<owner>.github.io/` for an `<owner>.github.io` repo), so books deployed
272
+ that way get `og:url` automatically; edit it for a custom domain.
273
+
274
+ ### Example book.yml (simple)
275
+
276
+ ```yaml
277
+ title: "My Software Guide"
278
+ author: "Author Name"
279
+ language: "ja"
280
+ chapters:
281
+ - 01-introduction.md
282
+ - 02-getting-started.md
283
+ - 03-advanced.md
284
+ ```
285
+
286
+ ### Example book.yml (with parts and appendix)
287
+
288
+ ```yaml
289
+ title: "My Software Guide"
290
+ author: "Author Name"
291
+ language: "ja"
292
+ chapters:
293
+ - part: part1.md
294
+ chapters:
295
+ - 01-introduction.md
296
+ - 02-getting-started.md
297
+ - part: part2.md
298
+ chapters:
299
+ - 03-advanced.md
300
+ - 04-deployment.md
301
+ - appendix:
302
+ - a1-config-reference.md
303
+ ```
304
+
305
+ ## Translations (Multi-Language)
306
+
307
+ ligarb supports building the same book in multiple languages. A parent
308
+ config file (hub) defines shared settings and points to per-language
309
+ config files.
310
+
311
+ ### Hub config (book.yml)
312
+
313
+ ```yaml
314
+ repository: "https://github.com/user/repo"
315
+ ai_generated: true
316
+ translations:
317
+ ja: book.ja.yml
318
+ en: book.en.yml
319
+ ```
320
+
321
+ ### Per-language config (book.ja.yml)
322
+
323
+ ```yaml
324
+ title: "マニュアル"
325
+ language: "ja"
326
+ inherit: book.yml
327
+ chapters:
328
+ - chapters/ja/01-intro.md
329
+ ```
330
+
331
+ ### Per-language config (book.en.yml)
332
+
333
+ ```yaml
334
+ title: "Manual"
335
+ language: "en"
336
+ inherit: book.yml
337
+ chapters:
338
+ - chapters/en/01-intro.md
339
+ ```
340
+
341
+ ### Building
342
+
343
+ There are two ways to build a multi-language book:
344
+
345
+ **Hub build** — pass the hub config to build all languages at once.
346
+ Produces a single output with a language switcher linking all translations:
347
+
348
+ ```
349
+ ligarb build book.yml # Builds all languages, adds language switcher
350
+ ```
351
+
352
+ **Standalone build** — pass a per-language config to build one language.
353
+ No language switcher is generated. The `inherit` field loads the hub's
354
+ inheritable settings (repository, style, ai_generated, etc.) as defaults:
355
+
356
+ ```
357
+ ligarb build book.ja.yml # Builds only Japanese, no language switcher
358
+ ```
359
+
360
+ ### Inheritance rules
361
+
362
+ - The hub's settings (repository, style, ai_generated, etc.) are
363
+ inherited by each per-language config as defaults.
364
+ - Per-language configs can override any inherited setting.
365
+ - title, language, and chapters are always per-language (required in
366
+ each per-language config).
367
+ - output_dir is NOT inherited — it always comes from the config file
368
+ passed directly to `ligarb build`.
369
+
370
+ The hub config does not need 'title' or 'chapters' fields — it only
371
+ needs 'translations'. If the hub has no 'chapters', it is treated
372
+ purely as a translations hub.
373
+
374
+ ### Language switcher
375
+
376
+ - Hub build produces a single index.html containing all languages.
377
+ A language switcher in the sidebar header (e.g. `[JA | EN]`) toggles
378
+ visibility via JavaScript.
379
+ - The current language button is highlighted; others switch the display.
380
+
381
+ ## Directory Structure
382
+
383
+ A typical book project has this structure:
384
+
385
+ ```
386
+ my-book/
387
+ ├── book.yml # Configuration file
388
+ ├── part1.md # Part opening page (optional)
389
+ ├── 01-introduction.md # Markdown source files
390
+ ├── 02-getting-started.md
391
+ ├── 03-advanced.md
392
+ └── images/ # Image files (optional)
393
+ ├── screenshot.png
394
+ └── diagram.svg
395
+ ```
396
+
397
+ After running `ligarb build`, the output is:
398
+
399
+ ```
400
+ my-book/
401
+ └── build/
402
+ ├── index.html # Single-page HTML book
403
+ ├── js/ # Auto-downloaded (only if needed)
404
+ ├── css/ # Auto-downloaded (only if needed)
405
+ └── images/ # Copied image files
406
+ ```
407
+
408
+ ## Markdown Authoring
409
+
410
+ ### Basics
411
+
412
+ Each Markdown file represents one chapter. ligarb uses GitHub Flavored
413
+ Markdown (GFM) via kramdown. Supported syntax includes:
414
+
415
+ - Headings (# h1, ## h2, ### h3) — used for TOC generation
416
+ - Code blocks with language-specific syntax highlighting (\`\`\` fenced blocks)
417
+ - Tables, task lists, strikethrough, and other GFM extensions
418
+ - Inline HTML
419
+
420
+ Each file must start with an h1 heading (`#`). This becomes the chapter
421
+ title in the TOC. Use h2 (`##`) and h3 (`###`) for sections within the chapter.
422
+
423
+ ### Images
424
+
425
+ Place image files in the `images/` directory next to book.yml.
426
+ Reference them from Markdown with relative paths:
427
+
428
+ ```markdown
429
+ ![Screenshot](images/screenshot.png)
430
+ ```
431
+
432
+ ligarb rewrites all local image paths to `images/{basename}` in the output
433
+ (directory components are stripped). Images are copied from the `images/`
434
+ directory next to book.yml — only top-level files, not subdirectories.
435
+
436
+ This means:
437
+ - `![img](images/foo.png)` and `![img](sub/dir/foo.png)` both become
438
+ `images/foo.png` in the output.
439
+ - All image filenames must be unique regardless of source directory.
440
+ - Remote URLs (http://, https://, data:) are left unchanged.
441
+ - In multi-language builds, all languages share the same `images/` pool.
442
+
443
+ ### Footnotes
444
+
445
+ Footnotes use kramdown syntax:
446
+
447
+ ```markdown
448
+ This is a sentence with a footnote[^1].
449
+
450
+ [^1]: This is the footnote content.
451
+ ```
452
+
453
+ Footnote IDs are scoped per chapter to avoid collisions in the single-page
454
+ output.
455
+
456
+ ### Admonitions
457
+
458
+ GFM-style blockquote alerts are converted to styled admonition boxes.
459
+ Five types are supported: NOTE, TIP, WARNING, CAUTION, IMPORTANT.
460
+
461
+ Syntax:
462
+
463
+ ```markdown
464
+ > [!NOTE]
465
+ > This is a note.
466
+
467
+ > [!TIP]
468
+ > Helpful advice here.
469
+
470
+ > [!WARNING]
471
+ > Be careful about this.
472
+
473
+ > [!CAUTION]
474
+ > Dangerous operation.
475
+
476
+ > [!IMPORTANT]
477
+ > Critical information.
478
+ ```
479
+
480
+ Each type renders with a distinct color and icon:
481
+
482
+ | Type | Color | Icon |
483
+ |------|-------|------|
484
+ | NOTE | blue | info |
485
+ | TIP | green | lightbulb |
486
+ | WARNING | yellow | warning |
487
+ | CAUTION | red | stop |
488
+ | IMPORTANT | purple | exclamation |
489
+
490
+ ### Cross-References
491
+
492
+ Link to other chapters or headings using standard Markdown relative links.
493
+ ligarb resolves .md file references to internal anchors in the single-page
494
+ output.
495
+
496
+ Syntax:
497
+
498
+ ```markdown
499
+ [link text](other-chapter.md) Link to a chapter
500
+ [link text](other-chapter.md#Heading) Link to a specific heading
501
+ [](other-chapter.md) Auto-fill with chapter title
502
+ [](other-chapter.md#Heading) Auto-fill with heading text
503
+ ```
504
+
505
+ The .md path is resolved relative to the current Markdown file's directory.
506
+ The heading fragment is matched against heading IDs (case-insensitive,
507
+ normalized the same way heading slugs are generated).
508
+
509
+ When the link text is empty, ligarb fills it with the target's display text:
510
+ - Chapter link: the chapter's display title (e.g. "3. Config Guide")
511
+ - Heading link: the heading's display text (e.g. "3.2 Setup")
512
+
513
+ If a referenced chapter or heading does not exist, the build fails with an
514
+ error message indicating the broken reference and its source file.
515
+
516
+ External URLs ending in .md (e.g. https://example.com/README.md) are not
517
+ affected — only relative paths are resolved.
518
+
519
+ ### Index
520
+
521
+ Mark terms for the book index using Markdown link syntax with `#index`:
522
+
523
+ ```markdown
524
+ [Ruby](#index) Index the link text as-is
525
+ [dynamic typing](#index:動的型付け) Index under a specific term
526
+ [Ruby](#index:Ruby,Languages/Ruby) Multiple index entries (comma-separated)
527
+ [Ruby](#index:Languages/Ruby) Hierarchical: Languages > Ruby
528
+ ```
529
+
530
+ The link is rendered as plain text in the output (no link styling).
531
+ An "Index" section is automatically appended at the end of the book,
532
+ with terms sorted alphabetically and grouped by first character.
533
+
534
+ Clicking an index entry navigates to the exact location in the chapter.
535
+
536
+ ### Bibliography
537
+
538
+ Cite references in the text using Markdown link syntax with `#cite`:
539
+
540
+ ```markdown
541
+ [Ruby](#cite:matz1995) Cite by key; rendered as Ruby[Matsumoto, 1995]
542
+ ```
543
+
544
+ Define a bibliography data file in book.yml:
545
+
546
+ ```yaml
547
+ bibliography: references.yml # YAML format
548
+ bibliography: references.bib # BibTeX format
549
+ ```
550
+
551
+ The format is auto-detected by file extension (.bib = BibTeX, otherwise YAML).
552
+
553
+ **YAML format** — maps keys to reference data:
554
+
555
+ ```yaml
556
+ matz1995:
557
+ author: "Yukihiro Matsumoto"
558
+ title: "The Ruby Programming Language"
559
+ year: 1995
560
+ url: "https://www.ruby-lang.org"
561
+ publisher: "O'Reilly"
562
+ doi: "10.1234/example"
563
+ ```
564
+
565
+ **BibTeX format:**
566
+
567
+ ```bibtex
568
+ @book{matz1995,
569
+ author = {Yukihiro Matsumoto},
570
+ title = {The Ruby Programming Language},
571
+ year = {1995},
572
+ publisher = {O'Reilly},
573
+ url = {https://www.ruby-lang.org}
574
+ }
575
+ ```
576
+
577
+ BibTeX notes:
578
+ - Entry types (@book, @article, etc.) are preserved for formatting
579
+ - Field values can use {braces} or "quotes"
580
+ - Nested braces are supported one level deep (`{The {Ruby} Language}`)
581
+ - Lines starting with `%` are comments
582
+
583
+ Supported fields (YAML and BibTeX):
584
+ author, title, year, url, publisher, journal, booktitle, volume,
585
+ number, pages, edition, doi, editor, note.
586
+
587
+ Formatting by type:
588
+
589
+ | Type | Format |
590
+ |------|--------|
591
+ | book | Author. *Title*. Edition. Publisher, Year. |
592
+ | article | Author. "Title". Journal, Volume(Number), Pages, Year. |
593
+ | inproceedings | Author. "Title". In Booktitle, Pages, Year. |
594
+ | other/YAML | Author. Title. Publisher/Journal, Volume, Pages, Year. |
595
+
596
+ If url is present, the title becomes a link. If doi is present, a DOI link
597
+ is appended.
598
+
599
+ The citation is rendered as a superscript [author, year] link that navigates
600
+ to the "Bibliography" section at the end of the book. Hovering the link shows
601
+ the full reference. The bibliography section lists all cited entries sorted by
602
+ author and year.
603
+
604
+ A warning is printed and the citation is rendered as [key?] (highlighted in
605
+ red) if a cite key is not found in the bibliography file.
606
+ If no bibliography file is configured, `#cite:` links are converted to
607
+ plain text spans (the link text is preserved but no citation label is added).
608
+
609
+ ## Fenced Code Blocks
610
+
611
+ The following fenced code block types are automatically detected and
612
+ rendered. Required JS/CSS is auto-downloaded on first build to build/js/
613
+ and build/css/.
614
+
615
+ | Block type | Description |
616
+ |-----------|-------------|
617
+ | `` ```ruby ``, `` ```python ``, etc. | Syntax highlighting (highlight.js, BSD-3-Clause) |
618
+ | `` ```mermaid `` | Diagrams: flowcharts, sequence, bar/line/pie charts, gantt, mindmap, etc. (mermaid, MIT) |
619
+ | `` ```math `` | LaTeX math equations (KaTeX, MIT) |
620
+ | `` ```functionplot `` | Function graphs (function-plot + d3, MIT) |
621
+
622
+ These are rendered visually in the output HTML — use them freely.
623
+
624
+ ### Mermaid syntax checking
625
+
626
+ When a build contains `` ```mermaid `` blocks and Node.js is available, ligarb
627
+ validates each diagram with the bundled mermaid parser at build time. Diagrams
628
+ that fail to parse are reported as warnings with their `file:line` location, for
629
+ example:
630
+
631
+ ```
632
+ Warning: mermaid syntax error in chapters/05-diagrams.md:42
633
+ Parse error on line 2:
634
+ ...
635
+ ```
636
+
637
+ The build still succeeds (the broken diagram renders an error box in the output
638
+ instead of an SVG). The check is skipped silently when Node.js is not installed.
639
+ HTML inside node labels (such as `<br>`) is exempt from the check and never warns.
640
+
641
+ ### Mermaid examples
642
+
643
+ Flowchart:
644
+
645
+ ````markdown
646
+ ```mermaid
647
+ graph TD
648
+ A[Start] --> B{Check}
649
+ B -->|Yes| C[OK]
650
+ B -->|No| D[Retry]
651
+ ```
652
+ ````
653
+
654
+ Sequence diagram:
655
+
656
+ ````markdown
657
+ ```mermaid
658
+ sequenceDiagram
659
+ Client->>Server: Request
660
+ Server-->>Client: Response
661
+ ```
662
+ ````
663
+
664
+ Bar chart:
665
+
666
+ ````markdown
667
+ ```mermaid
668
+ xychart
669
+ title "Monthly Sales"
670
+ x-axis ["Jan", "Feb", "Mar", "Apr", "May"]
671
+ y-axis "Revenue" 0 --> 500
672
+ bar [120, 230, 180, 350, 410]
673
+ line [120, 230, 180, 350, 410]
674
+ ```
675
+ ````
676
+
677
+ Line chart (line only):
678
+
679
+ ````markdown
680
+ ```mermaid
681
+ xychart
682
+ title "Temperature"
683
+ x-axis ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
684
+ y-axis "°C" -5 --> 30
685
+ line [2, 4, 10, 16, 22, 26]
686
+ ```
687
+ ````
688
+
689
+ Pie chart:
690
+
691
+ ````markdown
692
+ ```mermaid
693
+ pie title Browser Share
694
+ "Chrome" : 65
695
+ "Safari" : 19
696
+ "Firefox" : 4
697
+ "Other" : 12
698
+ ```
699
+ ````
700
+
701
+ Gantt chart:
702
+
703
+ ````markdown
704
+ ```mermaid
705
+ gantt
706
+ title Project Plan
707
+ dateFormat YYYY-MM-DD
708
+ section Design
709
+ Requirements :a1, 2025-01-01, 14d
710
+ Architecture :a2, after a1, 10d
711
+ section Dev
712
+ Implementation :b1, after a2, 21d
713
+ Testing :b2, after b1, 14d
714
+ ```
715
+ ````
716
+
717
+ Mindmap:
718
+
719
+ ````markdown
720
+ ```mermaid
721
+ mindmap
722
+ root((Project))
723
+ Frontend
724
+ React
725
+ CSS
726
+ Backend
727
+ API
728
+ Database
729
+ ```
730
+ ````
731
+
732
+ ### Math (KaTeX, LaTeX syntax)
733
+
734
+ ````markdown
735
+ ```math
736
+ E = mc^2
737
+ ```
738
+ ````
739
+
740
+ Inline math uses `$...$` syntax within text:
741
+
742
+ ```
743
+ The equation $E = mc^2$ is well-known.
744
+ ```
745
+
746
+ Rules for inline math:
747
+ - `$$` is not matched (use `` ```math `` for display math)
748
+ - `$` followed by a space is not matched (e.g. $10)
749
+ - `$` preceded by a space is not matched
750
+ - Content inside `<code>` and `<pre>` is not affected
751
+ - The content is rendered with KaTeX (displayMode: false)
752
+
753
+ ### Function plot
754
+
755
+ ````markdown
756
+ ```functionplot
757
+ y = sin(x)
758
+ y = x^2 - 1
759
+ range: [-2pi, 2pi]
760
+ yrange: [-3, 3]
761
+ ```
762
+ ````
763
+
764
+ Function plot syntax:
765
+ - `y = <expr>` — Standard function (e.g. `y = sin(x)`)
766
+ - `r = <expr>` — Polar function (e.g. `r = cos(2*theta)`)
767
+ - `parametric: <x>, <y>` — Parametric curve (e.g. `parametric: cos(t), sin(t)`)
768
+ - Bare expression — Treated as `y = <expr>`
769
+
770
+ Options (one per line):
771
+ - `range` / `xrange: [min, max]` — X-axis range (supports pi, e.g. `[-2pi, 2pi]`)
772
+ - `yrange: [min, max]` — Y-axis range
773
+ - `width: <pixels>` — Plot width (default: 600)
774
+ - `height: <pixels>` — Plot height (default: 400)
775
+ - `title: <text>` — Plot title
776
+ - `grid: true` — Show grid lines
777
+
778
+ ## Output Customization
779
+
780
+ ### Custom CSS
781
+
782
+ Add a `style` field to book.yml to inject custom CSS:
783
+
784
+ ```yaml
785
+ style: "custom.css"
786
+ ```
787
+
788
+ The custom CSS file is read and embedded as-is into a `<style>` tag in the
789
+ generated HTML, after the default styles. No sanitization or escaping is
790
+ applied — this is safe under the trusted authoring model, but means the CSS
791
+ file should be treated as part of the trusted source. You can override any
792
+ CSS custom property (e.g. colors, fonts, sidebar width) or add new rules.
793
+
794
+ Example custom.css:
795
+
796
+ ```css
797
+ :root {
798
+ --color-accent: #e63946;
799
+ --sidebar-width: 320px;
800
+ }
801
+ ```
802
+
803
+ ### Dark Mode
804
+
805
+ The generated HTML includes a dark mode toggle button (moon icon) in the
806
+ sidebar header. The user's preference is saved to localStorage and persists
807
+ across page reloads.
808
+
809
+ Custom CSS can override dark mode colors using the `[data-theme="dark"]`
810
+ selector.
811
+
812
+ ### Edit on GitHub
813
+
814
+ Add a `repository` field to book.yml:
815
+
816
+ ```yaml
817
+ repository: "https://github.com/user/repo"
818
+ ```
819
+
820
+ Each chapter will show a "View on GitHub" link pointing to:
821
+ `{repository}/blob/HEAD/{path-from-git-root}`
822
+
823
+ The chapter path is resolved relative to the Git repository root.
824
+
825
+ ### Previous/Next Navigation
826
+
827
+ Each chapter displays Previous and Next navigation links at the bottom.
828
+ These follow the flat chapter order (including across parts and appendix).
829
+ Cover pages do not show navigation.
830
+
831
+ ## Write Command
832
+
833
+ The `write` command generates a complete book using AI (Claude CLI).
834
+
835
+ ### brief.yml fields
836
+
837
+ | Field | Required | Default | Description |
838
+ |-------|----------|---------|-------------|
839
+ | `title` | required | — | The book title. |
840
+ | `language` | optional | `"ja"` | Language. |
841
+ | `audience` | optional | — | Target audience (used in the prompt). |
842
+ | `notes` | optional | — | Additional instructions for Claude (free text). |
843
+ | `sources` | optional | — | Reference files for AI context. Array of strings or `{path:, label:}` objects. Paths relative to brief.yml. |
844
+ | `author` | optional | — | Passed through to book.yml. |
845
+ | `output_dir` | optional | — | Passed through to book.yml. |
846
+ | `chapter_numbers` | optional | — | Passed through to book.yml. |
847
+ | `style` | optional | — | Passed through to book.yml. |
848
+ | `repository` | optional | — | Passed through to book.yml. |
849
+
850
+ The book is generated in the directory containing brief.yml.
851
+ Example: `ligarb write ruby_book/brief.yml` creates files in ruby_book/.
@@ -39,7 +39,10 @@ module Ligarb
39
39
  error_count = 0
40
40
  results.each do |result|
41
41
  error = result["error"]
42
- next unless error
42
+ # "environment" errors are limitations of the Node DOM stub (e.g. a node
43
+ # label containing HTML like <br>), not diagram syntax errors, so they
44
+ # would be false positives — skip them. See assets/mermaid_check.mjs.
45
+ next unless error && result["kind"] == "syntax"
43
46
 
44
47
  error_count += 1
45
48
  block = blocks[result["id"]]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ligarb
4
- VERSION = "0.9.0"
4
+ VERSION = "0.9.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ligarb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ligarb contributors
@@ -65,6 +65,20 @@ dependencies:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
67
  version: '1.1'
68
+ - !ruby/object:Gem::Dependency
69
+ name: base64
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0.1'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0.1'
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: rake
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -107,6 +121,7 @@ files:
107
121
  - assets/review.js
108
122
  - assets/serve.js
109
123
  - assets/style.css
124
+ - docs/help.md
110
125
  - exe/ligarb
111
126
  - lib/ligarb/asset_manager.rb
112
127
  - lib/ligarb/builder.rb