fossbook 0.2.14 → 0.2.16

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.
package/README.md CHANGED
@@ -1,576 +1,755 @@
1
- # Fossbook
2
-
3
- A Markdown-based authoring and web-publishing tool for comics, illustrated
4
- stories, and articles. Fossbook extends familiar Markdown with comic panels,
5
- responsive layouts, image-linked dialogue, and reader-controlled transcripts,
6
- then generates a complete static site that can be published on GitHub Pages.
7
-
8
- Fossbook began as the publishing system for
9
- [F/OSS Comics](https://fosscomics.com). It is now an independent Node.js
10
- package for creators who want to keep words, artwork, translations, and site
11
- configuration in portable source files rather than a proprietary editor.
12
-
13
- ![A cartoonist assembles colorful blocks into a larger structure, representing Fossbook's composable publishing workflow.](docs/images/fossbook-comic-authoring.png)
14
-
15
- _Artwork from [F/OSS Comics](https://fosscomics.com)._
16
-
17
- ## Features
18
-
19
- - **Comic authoring in Markdown** — Combine artwork, captions, dialogue,
20
- narration, and links in readable text files
21
- - **Panels and responsive layouts** — Arrange images or mixed-content comic
22
- panels in grids that collapse for narrow screens
23
- - **Accessible transcripts** — Associate dialogue with artwork and let readers
24
- show or hide transcript text while keeping it available for printing
25
- - **Image presentation controls** — Set image size, alignment, captions, panel
26
- borders, dividers, backgrounds, and spacing with constrained Markdown syntax
27
- - **Multilingual publishing** — Keep translations beside the original work and
28
- generate language-specific pages, navigation, and `hreflang` metadata
29
- - **Static web output** — Generate fast, portable HTML with pagination, tags,
30
- SEO metadata, syntax highlighting, and Mermaid diagrams
31
- - **Creator-owned workflow** — Store Markdown and artwork in Git, preview
32
- locally, customize themes, and deploy to GitHub Pages
33
-
34
- Fossbook also works as a conventional static blog generator. Its comic
35
- extensions build on GitHub Flavored Markdown instead of replacing it.
36
-
37
- ## Quick Start
38
-
39
- ### Install globally
40
-
41
- ```bash
42
- npm install -g fossbook
43
- ```
44
-
45
- ### Create a new publication
46
-
47
- ```bash
48
- mkdir my-blog && cd my-blog
49
- fossbook init
50
- ```
51
-
52
- This creates a publication project with the following structure:
53
-
54
- ```
55
- my-blog/
56
- ├── content/
57
- │ ├── about.md
58
- │ └── posts/
59
- ├── static/
60
- │ └── images/
61
- ├── fossbook.config.js
62
- └── package.json
63
- ```
64
-
65
- ### Create a new work
66
-
67
- ```bash
68
- fossbook new "My First Post"
69
- ```
70
-
71
- This creates `content/posts/My First Post/index.md` with pre-filled front-matter
72
- and an `images/` directory for comic panels, illustrations, and other artwork.
73
-
74
- To create the default article and one or more configured translations together:
75
-
76
- ```bash
77
- fossbook new "My First Post" --lang ko,ja
78
- ```
79
-
80
- This also creates `index.ko.md` and `index.ja.md` in the same post directory. Running the command again preserves existing files and creates only missing translations.
81
-
82
- ### Build the site
83
-
84
- ```bash
85
- fossbook build
86
- ```
87
-
88
- ### Preview locally
89
-
90
- ```bash
91
- fossbook serve
92
- ```
93
-
94
- Open http://localhost:3000 to view your site.
95
-
96
- ## Configuration
97
-
98
- Create a `fossbook.config.js` in your project root:
99
-
100
- ```js
101
- module.exports = {
102
- blogName: "My Blog",
103
- authorName: "Your Name",
104
- authorDescription: "A short bio",
105
- authorWebsite: "https://example.com",
106
- blogDescription: "A blog about things",
107
- blogsite: "https://example.com",
108
-
109
- // Optional
110
- githubCNAME: "example.com",
111
- googleAnalyticsID: "",
112
- authorTwitter: "@you",
113
- siteTwitter: "@yourblog",
114
- githubRepository: "https://github.com/you/your-blog",
115
- image: "https://example.com/default-image.png",
116
- theme: "archie",
117
-
118
- // Optional: URL prefix for posts. Defaults to "posts" -> /posts/<slug>/.
119
- // Set to "" to serve posts at the site root, /<slug>/.
120
- postsPath: "posts",
121
-
122
- // Optional multilingual configuration
123
- defaultLanguage: "en",
124
- defaultLanguageInSubdir: false,
125
- languages: {
126
- en: { languageName: "English", locale: "en-US" },
127
- ko: { languageName: "한국어", locale: "ko-KR" },
128
- },
129
-
130
- // Optional comments (see "Comments" below)
131
- comments: {
132
- provider: "utterances",
133
- repo: "you/your-blog",
134
- issueTerm: "pathname",
135
- theme: "github-light",
136
- },
137
-
138
- // Directory overrides (defaults shown)
139
- content: "./content",
140
- postsDir: "./content/posts",
141
- outputDir: "./public",
142
- staticDir: "./static",
143
- themesDir: "./themes",
144
- };
145
- ```
146
-
147
- ## Content Format
148
-
149
- Fossbook source remains readable Markdown. A work combines YAML front-matter,
150
- prose, and standard GitHub Flavored Markdown with optional publishing
151
- extensions for visual storytelling.
152
-
153
- ### Authoring comics
154
-
155
- Keep each work and its artwork together. Standard Markdown images become comic
156
- panels; image titles can control captions, size, and alignment. A blockquote
157
- placed immediately after an image becomes its dialogue transcript:
158
-
159
- ```markdown
160
- ![Two programmers talking](images/panel-01.png "size:70% A late-night debugging session")
161
-
162
- > "Do you think anyone will notice?" \
163
- > "They always notice."
164
- ```
165
-
166
- Use `:::panel` containers to frame artwork, dialogue, and narration as one
167
- scene. Use `:::panels` to compose responsive multi-column sequences:
168
-
169
- ```markdown
170
- ::::panels columns="2" label="Two scenes"
171
- :::panel
172
- ![The first scene](images/scene-01.png)
173
-
174
- > Dialogue for the first scene.
175
-
176
- Narration following the first scene.
177
- :::
178
-
179
- :::panel divider="true"
180
- ![The second scene](images/scene-02.png)
181
-
182
- > Dialogue for the second scene.
183
-
184
- Narration following the dialogue.
185
- :::
186
- ::::
187
- ```
188
-
189
- The bundled theme adapts panel groups and sized images for mobile screens. It
190
- also adds a transcript visibility control when a work contains image-linked
191
- dialogue. Alternative text, captions, group labels, and transcript text remain
192
- part of the authored source rather than being baked into the page layout.
193
-
194
- See [Fossbook Markdown](fossbook_markdown.md) for the complete syntax for
195
- panels, responsive groups, image presentation, dialogue, aligned links,
196
- Mermaid diagrams, and syntax-highlighted code.
197
-
198
- ### Post front-matter
199
-
200
- ```markdown
201
- ---
202
- title: My Post Title
203
- date: 2026-02-17
204
- description: "A brief summary of the post"
205
- image: "feature.png"
206
- tags: "JavaScript, Node.js, Static Site"
207
- ---
208
-
209
- Your Markdown content here...
210
- ```
211
-
212
- ### Directory structure
213
-
214
- ```
215
- content/posts/My Post Title/
216
- ├── index.md
217
- └── images/
218
- └── feature.png
219
- ```
220
-
221
- ### Post URLs
222
-
223
- By default, posts are served under `/posts/`, e.g. `/posts/my-post-title/`. To
224
- change the prefix or move posts to the site root, set `postsPath` in
225
- `fossbook.config.js`:
226
-
227
- ```js
228
- postsPath: "", // serves the post above at /my-post-title/ (site root)
229
- // postsPath: "blog", // or use a different prefix: /blog/my-post-title/
230
- ```
231
-
232
- This affects the generated output directory, the post URL, post links on the
233
- home/all-posts/tag pages, and image paths. The on-disk source layout under
234
- `content/posts/` does not change.
235
-
236
- ## Multilingual Sites
237
-
238
- Fossbook generates a separate static page for each available language. The
239
- browser loads only the selected language page; changing languages follows a
240
- normal link and does not require JavaScript.
241
-
242
- When a reader uses the language switcher, Fossbook remembers that choice in
243
- the browser. A later visit to the default-language homepage redirects to the
244
- remembered language's homepage. Direct links to posts and other pages are
245
- never redirected, and language links continue to work when browser storage is
246
- unavailable.
247
-
248
- ### Configuration
249
-
250
- Add the languages supported by the site to `fossbook.config.js`:
251
-
252
- ```js
253
- module.exports = {
254
- defaultLanguage: "en",
255
- defaultLanguageInSubdir: false,
256
- languages: {
257
- en: {
258
- languageName: "English",
259
- locale: "en-US",
260
- },
261
- ko: {
262
- languageName: "한국어",
263
- locale: "ko-KR",
264
- },
265
- ja: {
266
- languageName: "日本語",
267
- locale: "ja-JP",
268
- },
269
- },
270
- };
271
- ```
272
-
273
- Put localized blog information in a sibling config file for each language. For
274
- example, `fossbook.config.en.js` contains:
275
-
276
- ```js
277
- module.exports = {
278
- blogName: "My Blog",
279
- authorName: "Jane Doe",
280
- authorDescription: "Open source developer and writer",
281
- blogDescription: "An English-language blog",
282
- };
283
- ```
284
-
285
- And `fossbook.config.ko.js` contains:
286
-
287
- ```js
288
- module.exports = {
289
- blogName: "나의 블로그",
290
- authorName: "이수현",
291
- authorDescription: "오픈 소스 개발자이자 작가",
292
- blogDescription: "한국어 블로그",
293
- homeLabel: "처음",
294
- allPostsLabel: "모든 글",
295
- aboutLabel: "이곳은",
296
- tagsLabel: "태그",
297
- allTagsLabel: "모든 태그",
298
- postedOnLabel: "올린 날:",
299
- readMoreLabel: "더 읽기",
300
- previousPageLabel: "앞으로",
301
- nextPageLabel: "뒤로",
302
- transcriptLabel: "말글 보이기",
303
- copyLinkLabel: "링크 복사",
304
- linkCopiedLabel: "링크를 복사했습니다",
305
- copyLinkErrorLabel: "링크를 복사하지 못했습니다",
306
- };
307
- ```
308
-
309
- - `defaultLanguage` identifies the language represented by `index.md` and
310
- `about.md`.
311
- - `defaultLanguageInSubdir: false` keeps the default language at the site root.
312
- Other languages are generated below their language code, such as `/ko/`.
313
- - Set `defaultLanguageInSubdir: true` to generate the default language below
314
- its code as well, such as `/en/`.
315
- - Language config files use the main config filename with the language code
316
- inserted before the extension: `fossbook.config.<language>.js`.
317
- - Values omitted from a language config fall back to `fossbook.config.js`.
318
- - `locale` controls language-specific date formatting.
319
- - Language keys should use standard language tags such as `en`, `ko`, `ja`, or
320
- `zh-Hant`.
321
-
322
- If `languages` is omitted, Fossbook keeps its existing single-language
323
- behavior and URLs.
324
-
325
- ### Create translated posts
326
-
327
- Create only the default-language article:
328
-
329
- ```bash
330
- fossbook new "Charles Babbage and Ada Lovelace"
331
- ```
332
-
333
- Create the default article and multiple translations together:
334
-
335
- ```bash
336
- fossbook new "Charles Babbage and Ada Lovelace" --lang ko,ja
337
- ```
338
-
339
- The languages passed to `--lang` must be present in the `languages`
340
- configuration. The command always ensures that the default `index.md` exists,
341
- then creates the requested translation files. Existing Markdown files and
342
- images are never overwritten, so the command can be run later to add another
343
- translation:
344
-
345
- ```bash
346
- fossbook new "Charles Babbage and Ada Lovelace" --lang ja
347
- ```
348
-
349
- ### Source structure
350
-
351
- Translations live in the same post bundle and share its `images/` directory:
352
-
353
- ```text
354
- content/
355
- ├── about.md
356
- ├── about.ko.md
357
- └── posts/
358
- └── Charles Babbage and Ada Lovelace/
359
- ├── index.md
360
- ├── index.ko.md
361
- ├── index.ja.md
362
- └── images/
363
- └── feature.png
364
- ```
365
-
366
- - `index.md` is the default-language article.
367
- - `index.<language>.md` is a translated article.
368
- - `about.md` is the default About page.
369
- - `about.<language>.md` is a translated About page.
370
- - A translation is generated only when its Markdown source exists. Fossbook
371
- does not substitute default-language content for a missing translation.
372
-
373
- ### Generated output
374
-
375
- With `defaultLanguage: "en"` and `defaultLanguageInSubdir: false`, the example
376
- above generates:
377
-
378
- ```text
379
- public/
380
- ├── index.html
381
- ├── about/index.html
382
- ├── posts/Charles Babbage and Ada Lovelace/index.html
383
- ├── ko/
384
- │ ├── index.html
385
- │ ├── about/index.html
386
- │ └── posts/Charles Babbage and Ada Lovelace/index.html
387
- └── ja/
388
- ├── index.html
389
- └── posts/Charles Babbage and Ada Lovelace/index.html
390
- ```
391
-
392
- Translated article and About pages display ISO language links such as `EN`,
393
- `KO`, and `JA`. Only translations that exist are shown. These pages also emit
394
- canonical URLs and `hreflang` alternate links for search engines.
395
-
396
- ### Mermaid diagrams
397
-
398
- Fenced code blocks tagged `mermaid` are rendered as diagrams instead of code.
399
- The [Mermaid](https://mermaid.js.org) script is loaded from a CDN only on pages
400
- that contain a diagram.
401
-
402
- ````markdown
403
- ```mermaid
404
- sequenceDiagram
405
- Alice->>Bob: Hello Bob
406
- Bob-->>Alice: Hi Alice
407
- ```
408
- ````
409
-
410
- ## CLI Reference
411
-
412
- ```
413
- Usage: fossbook <command> [options]
414
-
415
- Commands:
416
- build Build the static site
417
- serve Build and start a local dev server
418
- new <title> Create a new post
419
- init Create a new fossbook site project
420
-
421
- Options:
422
- -c, --config Path to config file (default: ./fossbook.config.js)
423
- -o, --output Output directory (default: ./public)
424
- -p, --port Dev server port (default: 3000)
425
- --lang (new) Comma-separated translation languages, e.g. ko,ja
426
- -v, --version Show version number
427
- -h, --help Show help
428
- ```
429
-
430
- ## Theming
431
-
432
- Fossbook ships with the Archie theme by default. To use a custom theme:
433
-
434
- 1. Create a `themes/<your-theme>/` directory in your project
435
- 2. Add `layouts/` (HTML templates) and `assets/` (CSS, fonts, images)
436
- 3. Set `theme: "your-theme"` in `fossbook.config.js`
437
-
438
- Theme resolution order: user project `themes/` → built-in `themes/`.
439
-
440
- ## Comments
441
-
442
- Fossbook supports [utterances](https://utteranc.es) — a commenting widget that
443
- stores comments as GitHub issues. When enabled, a comment box is rendered at the
444
- bottom of every post page.
445
-
446
- ### Setup
447
-
448
- 1. Make the repository that backs the comments **public**.
449
- 2. Install the [utterances GitHub App](https://github.com/apps/utterances) on
450
- that repository so the bot can create issues.
451
- 3. Add a `comments` block to `fossbook.config.js`:
452
-
453
- ```js
454
- comments: {
455
- provider: "utterances", // currently the only supported provider
456
- repo: "you/your-blog", // owner/repo that stores the comment issues
457
- issueTerm: "pathname", // how a post maps to an issue (see below)
458
- theme: "github-light", // any utterances theme, e.g. "github-dark"
459
- },
460
- ```
461
-
462
- Omit the `comments` block (or set it to `null`) to disable comments.
463
-
464
- ### Options
465
-
466
- | Field | Required | Default | Description |
467
- | ----------- | -------- | ---------------- | ----------------------------------------------------------------------------- |
468
- | `provider` | yes | — | Must be `"utterances"`. |
469
- | `repo` | yes | — | `owner/repo` whose issues store the comments. |
470
- | `issueTerm` | no | `"pathname"` | Mapping between a page and its issue: `pathname`, `url`, `title`, `og:title`. |
471
- | `theme` | no | `"github-light"` | Any [utterances theme](https://utteranc.es/#configuration). |
472
-
473
- ### Mapping notes
474
-
475
- With `issueTerm: "pathname"`, each post is matched to a GitHub issue whose title
476
- equals the page's pathname (with the leading slash stripped). If you change a
477
- post's URL, the existing comment thread no longer matches — rename the issue
478
- title to the new pathname to keep the old comments.
479
-
480
- ### Required layout files
481
-
482
- ```
483
- layouts/
484
- ├── home.html # Home page with pagination
485
- ├── post.html # Individual post page
486
- ├── page.html # Static pages (e.g., about)
487
- ├── all_posts.html # All posts listing
488
- ├── tag.html # Per-tag listing
489
- ├── tag_list.html # Tag index page
490
- └── partials/
491
- └── footer.html # Footer partial
492
- ```
493
-
494
- ## Deploying to GitHub Pages
495
-
496
- Fossbook can automatically deploy your blog to GitHub Pages using GitHub Actions.
497
-
498
- ### Prerequisites: Install GitHub CLI (`gh`)
499
-
500
- The `fossbook deploy` command uses the GitHub CLI to create repositories and monitor deployments.
501
-
502
- **Linux (Debian/Ubuntu):**
503
-
504
- ```bash
505
- sudo apt install gh
506
- ```
507
-
508
- **macOS:**
509
-
510
- ```bash
511
- brew install gh
512
- ```
513
-
514
- **Windows:**
515
-
516
- ```bash
517
- winget install GitHub.cli
518
- ```
519
-
520
- Then authenticate with your GitHub account:
521
-
522
- ```bash
523
- gh auth login
524
- ```
525
-
526
- Follow the prompts to log in via browser or token.
527
-
528
- ### Initialize with GitHub
529
-
530
- ```bash
531
- mkdir my-blog && cd my-blog
532
- fossbook init --github
533
- ```
534
-
535
- This will:
536
-
537
- 1. Scaffold the site project (config, content directories)
538
- 2. Create a GitHub repository for your blog
539
- 3. Generate `.github/workflows/deploy.yml` for automatic deployments
540
- 4. Push the initial commit to GitHub
541
-
542
- ### Publish a post
543
-
544
- ```bash
545
- fossbook new "My New Article"
546
- # ... edit content/posts/My New Article/index.md ...
547
- fossbook deploy
548
- ```
549
-
550
- Fossbook will build the site, commit, push to GitHub, wait for the CI/CD pipeline to finish, and display the live URL:
551
-
552
- ```
553
- Building site... done.
554
- Committing: "Publish: My New Article"
555
- Pushing to origin/main...
556
- Waiting for GitHub Pages deployment... ✓
557
-
558
- ✅ Published! View your article at:
559
- https://username.github.io/my-blog/My%20New%20Article/
560
- ```
561
-
562
- **Deploy options:**
563
-
564
- ```bash
565
- fossbook deploy --message "Update homepage" # Custom commit message
566
- fossbook deploy --no-wait # Push without waiting for CI
567
- ```
568
-
569
- ## License
570
-
571
- - Generator code: [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause)
572
- - Archie theme: [MIT License](https://github.com/athul/archie?tab=MIT-1-ov-file#readme)
573
-
574
- ## Credits
575
-
576
- Adapted from [kartiknair's blog](https://github.com/kartiknair/blog) and styled using the [Archie theme](https://github.com/athul/archie).
1
+ # Fossbook
2
+
3
+ A Markdown-based authoring and web-publishing tool for comics, illustrated
4
+ stories, and articles. Fossbook extends familiar Markdown with comic panels,
5
+ responsive layouts, image-linked dialogue, and reader-controlled transcripts,
6
+ then generates a complete static site that can be published on GitHub Pages.
7
+
8
+ Fossbook began as the publishing system for
9
+ [F/OSS Comics](https://fosscomics.com). It is now an independent Node.js
10
+ package for creators who want to keep words, artwork, translations, and site
11
+ configuration in portable source files rather than a proprietary editor.
12
+
13
+ ![A cartoonist assembles colorful blocks into a larger structure, representing Fossbook's composable publishing workflow.](docs/images/fossbook-comic-authoring.png)
14
+
15
+ _Artwork from [F/OSS Comics](https://fosscomics.com)._
16
+
17
+ ## Features
18
+
19
+ - **Comic authoring in Markdown** — Combine artwork, captions, dialogue,
20
+ narration, and links in readable text files
21
+ - **Panels and responsive layouts** — Arrange images or mixed-content comic
22
+ panels in grids that collapse for narrow screens
23
+ - **Accessible transcripts** — Associate dialogue with artwork and let readers
24
+ show or hide transcript text while keeping it available for printing
25
+ - **Image presentation controls** — Set image size, alignment, captions, panel
26
+ borders, dividers, backgrounds, and spacing with constrained Markdown syntax
27
+ - **Multilingual publishing** — Keep translations beside the original work and
28
+ generate language-specific pages, navigation, and `hreflang` metadata
29
+ - **Static web output** — Generate fast, portable HTML with pagination, tags,
30
+ SEO metadata, syntax highlighting, and Mermaid diagrams
31
+ - **Creator-owned workflow** — Store Markdown and artwork in Git, preview
32
+ locally, customize themes, and deploy to GitHub Pages
33
+
34
+ Fossbook also works as a conventional static blog generator. Its comic
35
+ extensions build on GitHub Flavored Markdown instead of replacing it.
36
+
37
+ ## Quick Start
38
+
39
+ ### Install globally
40
+
41
+ ```bash
42
+ npm install -g fossbook
43
+ ```
44
+
45
+ ### Create a new publication
46
+
47
+ ```bash
48
+ mkdir my-blog && cd my-blog
49
+ fossbook init
50
+ ```
51
+
52
+ This creates a publication project with the following structure:
53
+
54
+ ```
55
+ my-blog/
56
+ ├── content/
57
+ │ ├── about.md
58
+ │ └── posts/
59
+ ├── static/
60
+ │ └── images/
61
+ ├── fossbook.config.js
62
+ └── package.json
63
+ ```
64
+
65
+ ### Create a new work
66
+
67
+ ```bash
68
+ fossbook new "My First Post"
69
+ ```
70
+
71
+ This creates `content/posts/My First Post/index.md` with pre-filled front-matter
72
+ and an `images/` directory for comic panels, illustrations, and other artwork.
73
+
74
+ To create the default article and one or more configured translations together:
75
+
76
+ ```bash
77
+ fossbook new "My First Post" --lang ko,ja
78
+ ```
79
+
80
+ This also creates `index.ko.md` and `index.ja.md` in the same post directory. Running the command again preserves existing files and creates only missing translations.
81
+
82
+ ### Build the site
83
+
84
+ ```bash
85
+ fossbook build
86
+ ```
87
+
88
+ Posts with `draft: true` in their front matter are excluded from generated
89
+ post pages, home pages, archives, and tags. To include them in a local build:
90
+
91
+ ```bash
92
+ fossbook build --include-drafts
93
+ ```
94
+
95
+ ### Preview locally
96
+
97
+ ```bash
98
+ fossbook serve --include-drafts
99
+ ```
100
+
101
+ Open http://localhost:3000 to view your site.
102
+
103
+ ## Configuration
104
+
105
+ Create a `fossbook.config.js` in your project root:
106
+
107
+ ```js
108
+ module.exports = {
109
+ blogName: "My Blog",
110
+ authorName: "Your Name",
111
+ authorDescription: "A short bio",
112
+ authorWebsite: "https://example.com",
113
+ blogDescription: "A blog about things",
114
+ blogsite: "https://example.com",
115
+
116
+ // Optional
117
+ githubCNAME: "example.com",
118
+ googleAnalyticsID: "",
119
+ authorTwitter: "@you",
120
+ siteTwitter: "@yourblog",
121
+ githubRepository: "https://github.com/you/your-blog",
122
+ image: "https://example.com/default-image.png",
123
+ theme: "archie",
124
+
125
+ // PNG publishing optimization (defaults shown)
126
+ imageOptimization: true,
127
+ imageMaxWidth: 1400,
128
+ imageMaxWidthOverride: 1200,
129
+ imageWebP: true,
130
+ imageResponsiveWidths: [600, 800, 1000],
131
+ cacheDir: "./.fossbook-cache",
132
+
133
+ // Optional: URL prefix for posts. Defaults to "posts" -> /posts/<slug>/.
134
+ // Set to "" to serve posts at the site root, /<slug>/.
135
+ postsPath: "posts",
136
+
137
+ // Optional multilingual configuration
138
+ defaultLanguage: "en",
139
+ defaultLanguageInSubdir: false,
140
+ languages: {
141
+ en: { languageName: "English", locale: "en-US" },
142
+ ko: { languageName: "한국어", locale: "ko-KR" },
143
+ },
144
+
145
+ // Optional comments (see "Comments" below)
146
+ comments: {
147
+ provider: "utterances",
148
+ repo: "you/your-blog",
149
+ issueTerm: "pathname",
150
+ theme: "github-light",
151
+ },
152
+
153
+ // Directory overrides (defaults shown)
154
+ content: "./content",
155
+ postsDir: "./content/posts",
156
+ outputDir: "./public",
157
+ staticDir: "./static",
158
+ themesDir: "./themes",
159
+ };
160
+ ```
161
+
162
+ During a build, Fossbook resizes PNGs from post and static image directories to
163
+ `imageMaxWidth` while preserving their aspect ratio. Source images are never
164
+ changed, images are never enlarged or cropped, and images already within the
165
+ limit retain their original published bytes when lossless optimization would
166
+ increase the file size. Processed images are cached by source content and
167
+ settings in `cacheDir`, so unchanged images do not need to be processed again
168
+ after `public` is rebuilt. Set `imageOptimization` to `false` to copy PNGs
169
+ unchanged.
170
+
171
+ The default cache is `.fossbook-cache/images/`, separate from both source
172
+ artwork and `public/`. Each entry is keyed by the source bytes, transformation
173
+ settings, Fossbook cache schema, and Sharp/libvips encoder versions. Fossbook
174
+ validates cached image format and dimensions before use, regenerates missing or
175
+ corrupt entries atomically, and prints cache hits, misses, and regenerated image
176
+ counts at the end of each build. It also writes
177
+ `.fossbook-cache/image-cache-status.json` for CI save decisions.
178
+
179
+ New projects ignore `.fossbook-cache/` automatically. Existing sites should add
180
+ this rule to `.gitignore`:
181
+
182
+ ```gitignore
183
+ .fossbook-cache/
184
+ ```
185
+
186
+ For post images, Fossbook also generates lossless WebP copies at the configured
187
+ responsive widths. Generated HTML uses WebP when supported and retains the PNG
188
+ as a fallback. Candidate widths larger than the source or publication limit are
189
+ skipped, and duplicate widths produce only one file. Set `imageWebP` to `false`
190
+ to publish only PNG copies.
191
+
192
+ For small lettering or detailed diagrams, add `publish-width:1200` to the
193
+ existing Markdown image title. Overrides are capped by
194
+ `imageMaxWidthOverride`:
195
+
196
+ ```markdown
197
+ ![Detailed diagram](images/diagram.png "publish-width:1200 Detailed diagram")
198
+ ```
199
+
200
+ ### GitHub Actions image cache
201
+
202
+ `fossbook init` generates a deployment workflow with the image cache
203
+ configuration below. Existing consumer sites can use this complete workflow.
204
+ The resolve step reads the site's effective Fossbook configuration, so custom
205
+ `cacheDir`, `postsDir`, `staticDir`, and theme paths are handled automatically.
206
+ With the defaults, the exact archived path is `.fossbook-cache/images`.
207
+
208
+ ```yaml
209
+ name: Deploy to GitHub Pages
210
+
211
+ on:
212
+ push:
213
+ branches: [main]
214
+
215
+ permissions:
216
+ contents: read
217
+ pages: write
218
+ id-token: write
219
+
220
+ concurrency:
221
+ group: "pages"
222
+ cancel-in-progress: false
223
+
224
+ jobs:
225
+ build-and-deploy:
226
+ runs-on: ubuntu-latest
227
+ environment:
228
+ name: github-pages
229
+ url: ${{ steps.deployment.outputs.page_url }}
230
+ steps:
231
+ - uses: actions/checkout@v4
232
+
233
+ - name: Setup Node.js
234
+ uses: actions/setup-node@v4
235
+ with:
236
+ node-version: "20"
237
+ cache: npm
238
+
239
+ - name: Install dependencies
240
+ run: npm ci
241
+
242
+ - name: Resolve Fossbook image cache
243
+ id: image-cache-config
244
+ run: node -e "require('fossbook/lib/mod/github_cache').writeGitHubCacheOutputs()"
245
+
246
+ - name: Restore Fossbook image cache
247
+ id: image-cache-restore
248
+ uses: actions/cache/restore@v4
249
+ continue-on-error: true
250
+ with:
251
+ path: ${{ steps.image-cache-config.outputs.cache-path }}/images
252
+ key: ${{ runner.os }}-${{ runner.arch }}-fossbook-images-v4-${{ steps.image-cache-config.outputs.input-hash }}-${{ github.run_id }}-${{ github.run_attempt }}
253
+ restore-keys: |
254
+ ${{ runner.os }}-${{ runner.arch }}-fossbook-images-v4-${{ steps.image-cache-config.outputs.input-hash }}-
255
+ ${{ runner.os }}-${{ runner.arch }}-fossbook-images-v4-
256
+
257
+ - name: Build site
258
+ run: npx fossbook build
259
+
260
+ - name: Read Fossbook image cache status
261
+ id: image-cache-status
262
+ continue-on-error: true
263
+ shell: bash
264
+ env:
265
+ FOSSBOOK_CACHE_STATUS: ${{ steps.image-cache-config.outputs.status-path }}
266
+ run: |
267
+ node -e "const fs=require('fs');let writes=0;try{writes=JSON.parse(fs.readFileSync(process.env.FOSSBOOK_CACHE_STATUS,'utf8')).writes||0}catch(error){console.warn(error.message)}fs.appendFileSync(process.env.GITHUB_OUTPUT, 'new-entries='+(writes>0)+'\n')"
268
+
269
+ - name: Save Fossbook image cache
270
+ if: ${{ steps.image-cache-restore.outputs.cache-hit != 'true' && steps.image-cache-status.outputs.new-entries == 'true' }}
271
+ uses: actions/cache/save@v4
272
+ continue-on-error: true
273
+ with:
274
+ path: ${{ steps.image-cache-config.outputs.cache-path }}/images
275
+ key: ${{ runner.os }}-${{ runner.arch }}-fossbook-images-v4-${{ steps.image-cache-config.outputs.input-hash }}-${{ github.run_id }}-${{ github.run_attempt }}
276
+
277
+ - name: Setup Pages
278
+ uses: actions/configure-pages@v4
279
+ with:
280
+ enablement: true
281
+
282
+ - name: Upload artifact
283
+ uses: actions/upload-pages-artifact@v3
284
+ with:
285
+ path: "./public"
286
+
287
+ - name: Deploy to GitHub Pages
288
+ id: deployment
289
+ uses: actions/deploy-pages@v4
290
+ ```
291
+
292
+ GitHub cache archives are immutable. The resolve helper computes an input hash
293
+ from dependencies, effective image settings, Markdown image metadata, source
294
+ artwork, and custom theme PNGs. The save key adds the unique workflow run and
295
+ attempt, while restore first searches the matching input-hash prefix and then
296
+ the broader OS/architecture/schema prefix. This lets a regenerated corrupt
297
+ entry be saved as a newer archive instead of repeatedly selecting an immutable
298
+ damaged archive. Fossbook's per-image keys remain authoritative, so changing
299
+ one image or one output setting regenerates only affected results. Markdown
300
+ prose is excluded from the input hash and does not create redundant archives.
301
+
302
+ An exact cache hit is never saved again. A partial or cold restore is saved only
303
+ when Fossbook reports newly generated entries. Cache restore, status, and save
304
+ steps use `continue-on-error`, so expiration, eviction, quota failures, and
305
+ service outages fall back to a normal cold build. To reset manually, delete the
306
+ repository cache in GitHub's Actions cache settings or increment
307
+ `fossbook-images-v4` in both key locations; the next build recreates it.
308
+
309
+ The example runs only for trusted pushes. GitHub may allow pull requests and
310
+ forks to read caches accessible from their base branch. Because processed
311
+ artwork can still disclose source content, do not enable these cache steps in
312
+ untrusted workflows when the artwork is private. Do not use
313
+ `pull_request_target` for publication builds. Only
314
+ `.fossbook-cache/images` is archived, so source PDFs, secrets, `public/`, and
315
+ unrelated build files are excluded.
316
+
317
+ Fossbook's own [CI workflow](./.github/workflows/build.yml) tests the generator
318
+ and cache behavior; it is distinct from this consumer-site publication
319
+ workflow and does not archive production artwork.
320
+
321
+ ## Content Format
322
+
323
+ Fossbook source remains readable Markdown. A work combines YAML front-matter,
324
+ prose, and standard GitHub Flavored Markdown with optional publishing
325
+ extensions for visual storytelling.
326
+
327
+ ### Authoring comics
328
+
329
+ Keep each work and its artwork together. Standard Markdown images become comic
330
+ panels; image titles can control captions, size, and alignment. A blockquote
331
+ placed immediately after an image becomes its dialogue transcript:
332
+
333
+ ```markdown
334
+ ![Two programmers talking](images/panel-01.png "size:70% A late-night debugging session")
335
+
336
+ > "Do you think anyone will notice?" \
337
+ > "They always notice."
338
+ ```
339
+
340
+ Use `:::panel` containers to frame artwork, dialogue, and narration as one
341
+ scene. Use `:::panels` to compose responsive multi-column sequences:
342
+
343
+ ```markdown
344
+ ::::panels columns="2" label="Two scenes"
345
+ :::panel
346
+ ![The first scene](images/scene-01.png)
347
+
348
+ > Dialogue for the first scene.
349
+
350
+ Narration following the first scene.
351
+ :::
352
+
353
+ :::panel divider="true"
354
+ ![The second scene](images/scene-02.png)
355
+
356
+ > Dialogue for the second scene.
357
+
358
+ Narration following the dialogue.
359
+ :::
360
+ ::::
361
+ ```
362
+
363
+ The bundled theme adapts panel groups and sized images for mobile screens. It
364
+ also adds a transcript visibility control when a work contains image-linked
365
+ dialogue. Alternative text, captions, group labels, and transcript text remain
366
+ part of the authored source rather than being baked into the page layout.
367
+
368
+ See [Fossbook Markdown](fossbook_markdown.md) for the complete syntax for
369
+ panels, responsive groups, image presentation, dialogue, aligned links,
370
+ Mermaid diagrams, and syntax-highlighted code.
371
+
372
+ ### Post front-matter
373
+
374
+ ```markdown
375
+ ---
376
+ title: My Post Title
377
+ date: 2026-02-17
378
+ description: "A brief summary of the post"
379
+ image: "feature.png"
380
+ tags: "JavaScript, Node.js, Static Site"
381
+ draft: false
382
+ ---
383
+
384
+ Your Markdown content here...
385
+ ```
386
+
387
+ Set `draft: true` while a post is in progress. Normal builds and deployments
388
+ exclude it. Use `fossbook serve --include-drafts` to preview drafts locally,
389
+ then remove the field or set it to `false` before publishing.
390
+
391
+ ### Directory structure
392
+
393
+ ```
394
+ content/posts/My Post Title/
395
+ ├── index.md
396
+ └── images/
397
+ └── feature.png
398
+ ```
399
+
400
+ ### Post URLs
401
+
402
+ By default, posts are served under `/posts/`, e.g. `/posts/my-post-title/`. To
403
+ change the prefix or move posts to the site root, set `postsPath` in
404
+ `fossbook.config.js`:
405
+
406
+ ```js
407
+ postsPath: "", // serves the post above at /my-post-title/ (site root)
408
+ // postsPath: "blog", // or use a different prefix: /blog/my-post-title/
409
+ ```
410
+
411
+ This affects the generated output directory, the post URL, post links on the
412
+ home/all-posts/tag pages, and image paths. The on-disk source layout under
413
+ `content/posts/` does not change.
414
+
415
+ ## Multilingual Sites
416
+
417
+ Fossbook generates a separate static page for each available language. The
418
+ browser loads only the selected language page; changing languages follows a
419
+ normal link and does not require JavaScript.
420
+
421
+ When a reader uses the language switcher, Fossbook remembers that choice in
422
+ the browser. A later visit to the default-language homepage redirects to the
423
+ remembered language's homepage. Direct links to posts and other pages are
424
+ never redirected, and language links continue to work when browser storage is
425
+ unavailable.
426
+
427
+ ### Configuration
428
+
429
+ Add the languages supported by the site to `fossbook.config.js`:
430
+
431
+ ```js
432
+ module.exports = {
433
+ defaultLanguage: "en",
434
+ defaultLanguageInSubdir: false,
435
+ languages: {
436
+ en: {
437
+ languageName: "English",
438
+ locale: "en-US",
439
+ },
440
+ ko: {
441
+ languageName: "한국어",
442
+ locale: "ko-KR",
443
+ },
444
+ ja: {
445
+ languageName: "日本語",
446
+ locale: "ja-JP",
447
+ },
448
+ },
449
+ };
450
+ ```
451
+
452
+ Put localized blog information in a sibling config file for each language. For
453
+ example, `fossbook.config.en.js` contains:
454
+
455
+ ```js
456
+ module.exports = {
457
+ blogName: "My Blog",
458
+ authorName: "Jane Doe",
459
+ authorDescription: "Open source developer and writer",
460
+ blogDescription: "An English-language blog",
461
+ };
462
+ ```
463
+
464
+ And `fossbook.config.ko.js` contains:
465
+
466
+ ```js
467
+ module.exports = {
468
+ blogName: "나의 블로그",
469
+ authorName: "이수현",
470
+ authorDescription: "오픈 소스 개발자이자 작가",
471
+ blogDescription: "한국어 블로그",
472
+ homeLabel: "처음",
473
+ allPostsLabel: "모든 글",
474
+ aboutLabel: "이곳은",
475
+ tagsLabel: "태그",
476
+ allTagsLabel: "모든 태그",
477
+ postedOnLabel: "올린 날:",
478
+ readMoreLabel: "더 읽기",
479
+ previousPageLabel: "앞으로",
480
+ nextPageLabel: "뒤로",
481
+ transcriptLabel: "말글 보이기",
482
+ copyLinkLabel: "링크 복사",
483
+ linkCopiedLabel: "링크를 복사했습니다",
484
+ copyLinkErrorLabel: "링크를 복사하지 못했습니다",
485
+ };
486
+ ```
487
+
488
+ - `defaultLanguage` identifies the language represented by `index.md` and
489
+ `about.md`.
490
+ - `defaultLanguageInSubdir: false` keeps the default language at the site root.
491
+ Other languages are generated below their language code, such as `/ko/`.
492
+ - Set `defaultLanguageInSubdir: true` to generate the default language below
493
+ its code as well, such as `/en/`.
494
+ - Language config files use the main config filename with the language code
495
+ inserted before the extension: `fossbook.config.<language>.js`.
496
+ - Values omitted from a language config fall back to `fossbook.config.js`.
497
+ - `locale` controls language-specific date formatting.
498
+ - Language keys should use standard language tags such as `en`, `ko`, `ja`, or
499
+ `zh-Hant`.
500
+
501
+ If `languages` is omitted, Fossbook keeps its existing single-language
502
+ behavior and URLs.
503
+
504
+ ### Create translated posts
505
+
506
+ Create only the default-language article:
507
+
508
+ ```bash
509
+ fossbook new "Charles Babbage and Ada Lovelace"
510
+ ```
511
+
512
+ Create the default article and multiple translations together:
513
+
514
+ ```bash
515
+ fossbook new "Charles Babbage and Ada Lovelace" --lang ko,ja
516
+ ```
517
+
518
+ The languages passed to `--lang` must be present in the `languages`
519
+ configuration. The command always ensures that the default `index.md` exists,
520
+ then creates the requested translation files. Existing Markdown files and
521
+ images are never overwritten, so the command can be run later to add another
522
+ translation:
523
+
524
+ ```bash
525
+ fossbook new "Charles Babbage and Ada Lovelace" --lang ja
526
+ ```
527
+
528
+ ### Source structure
529
+
530
+ Translations live in the same post bundle and share its `images/` directory:
531
+
532
+ ```text
533
+ content/
534
+ ├── about.md
535
+ ├── about.ko.md
536
+ └── posts/
537
+ └── Charles Babbage and Ada Lovelace/
538
+ ├── index.md
539
+ ├── index.ko.md
540
+ ├── index.ja.md
541
+ └── images/
542
+ └── feature.png
543
+ ```
544
+
545
+ - `index.md` is the default-language article.
546
+ - `index.<language>.md` is a translated article.
547
+ - `about.md` is the default About page.
548
+ - `about.<language>.md` is a translated About page.
549
+ - A translation is generated only when its Markdown source exists. Fossbook
550
+ does not substitute default-language content for a missing translation.
551
+
552
+ ### Generated output
553
+
554
+ With `defaultLanguage: "en"` and `defaultLanguageInSubdir: false`, the example
555
+ above generates:
556
+
557
+ ```text
558
+ public/
559
+ ├── index.html
560
+ ├── about/index.html
561
+ ├── posts/Charles Babbage and Ada Lovelace/index.html
562
+ ├── ko/
563
+ │ ├── index.html
564
+ │ ├── about/index.html
565
+ │ └── posts/Charles Babbage and Ada Lovelace/index.html
566
+ └── ja/
567
+ ├── index.html
568
+ └── posts/Charles Babbage and Ada Lovelace/index.html
569
+ ```
570
+
571
+ Translated article and About pages display ISO language links such as `EN`,
572
+ `KO`, and `JA`. Only translations that exist are shown. These pages also emit
573
+ canonical URLs and `hreflang` alternate links for search engines.
574
+
575
+ ### Mermaid diagrams
576
+
577
+ Fenced code blocks tagged `mermaid` are rendered as diagrams instead of code.
578
+ The [Mermaid](https://mermaid.js.org) script is loaded from a CDN only on pages
579
+ that contain a diagram.
580
+
581
+ ````markdown
582
+ ```mermaid
583
+ sequenceDiagram
584
+ Alice->>Bob: Hello Bob
585
+ Bob-->>Alice: Hi Alice
586
+ ```
587
+ ````
588
+
589
+ ## CLI Reference
590
+
591
+ ```
592
+ Usage: fossbook <command> [options]
593
+
594
+ Commands:
595
+ build Build the static site
596
+ serve Build and start a local dev server
597
+ new <title> Create a new post
598
+ init Create a new fossbook site project
599
+
600
+ Options:
601
+ -c, --config Path to config file (default: ./fossbook.config.js)
602
+ -o, --output Output directory (default: ./public)
603
+ -p, --port Dev server port (default: 3000)
604
+ --lang (new) Comma-separated translation languages, e.g. ko,ja
605
+ -v, --version Show version number
606
+ -h, --help Show help
607
+ ```
608
+
609
+ ## Theming
610
+
611
+ Fossbook ships with the Archie theme by default. To use a custom theme:
612
+
613
+ 1. Create a `themes/<your-theme>/` directory in your project
614
+ 2. Add `layouts/` (HTML templates) and `assets/` (CSS, fonts, images)
615
+ 3. Set `theme: "your-theme"` in `fossbook.config.js`
616
+
617
+ Theme resolution order: user project `themes/` → built-in `themes/`.
618
+
619
+ ## Comments
620
+
621
+ Fossbook supports [utterances](https://utteranc.es) — a commenting widget that
622
+ stores comments as GitHub issues. When enabled, a comment box is rendered at the
623
+ bottom of every post page.
624
+
625
+ ### Setup
626
+
627
+ 1. Make the repository that backs the comments **public**.
628
+ 2. Install the [utterances GitHub App](https://github.com/apps/utterances) on
629
+ that repository so the bot can create issues.
630
+ 3. Add a `comments` block to `fossbook.config.js`:
631
+
632
+ ```js
633
+ comments: {
634
+ provider: "utterances", // currently the only supported provider
635
+ repo: "you/your-blog", // owner/repo that stores the comment issues
636
+ issueTerm: "pathname", // how a post maps to an issue (see below)
637
+ theme: "github-light", // any utterances theme, e.g. "github-dark"
638
+ },
639
+ ```
640
+
641
+ Omit the `comments` block (or set it to `null`) to disable comments.
642
+
643
+ ### Options
644
+
645
+ | Field | Required | Default | Description |
646
+ | ----------- | -------- | ---------------- | ----------------------------------------------------------------------------- |
647
+ | `provider` | yes | — | Must be `"utterances"`. |
648
+ | `repo` | yes | — | `owner/repo` whose issues store the comments. |
649
+ | `issueTerm` | no | `"pathname"` | Mapping between a page and its issue: `pathname`, `url`, `title`, `og:title`. |
650
+ | `theme` | no | `"github-light"` | Any [utterances theme](https://utteranc.es/#configuration). |
651
+
652
+ ### Mapping notes
653
+
654
+ With `issueTerm: "pathname"`, each post is matched to a GitHub issue whose title
655
+ equals the page's pathname (with the leading slash stripped). If you change a
656
+ post's URL, the existing comment thread no longer matches — rename the issue
657
+ title to the new pathname to keep the old comments.
658
+
659
+ ### Required layout files
660
+
661
+ ```
662
+ layouts/
663
+ ├── home.html # Home page with pagination
664
+ ├── post.html # Individual post page
665
+ ├── page.html # Static pages (e.g., about)
666
+ ├── all_posts.html # All posts listing
667
+ ├── tag.html # Per-tag listing
668
+ ├── tag_list.html # Tag index page
669
+ └── partials/
670
+ └── footer.html # Footer partial
671
+ ```
672
+
673
+ ## Deploying to GitHub Pages
674
+
675
+ Fossbook can automatically deploy your blog to GitHub Pages using GitHub Actions.
676
+
677
+ ### Prerequisites: Install GitHub CLI (`gh`)
678
+
679
+ The `fossbook deploy` command uses the GitHub CLI to create repositories and monitor deployments.
680
+
681
+ **Linux (Debian/Ubuntu):**
682
+
683
+ ```bash
684
+ sudo apt install gh
685
+ ```
686
+
687
+ **macOS:**
688
+
689
+ ```bash
690
+ brew install gh
691
+ ```
692
+
693
+ **Windows:**
694
+
695
+ ```bash
696
+ winget install GitHub.cli
697
+ ```
698
+
699
+ Then authenticate with your GitHub account:
700
+
701
+ ```bash
702
+ gh auth login
703
+ ```
704
+
705
+ Follow the prompts to log in via browser or token.
706
+
707
+ ### Initialize with GitHub
708
+
709
+ ```bash
710
+ mkdir my-blog && cd my-blog
711
+ fossbook init --github
712
+ ```
713
+
714
+ This will:
715
+
716
+ 1. Scaffold the site project (config, content directories)
717
+ 2. Create a GitHub repository for your blog
718
+ 3. Generate `.github/workflows/deploy.yml` for automatic deployments
719
+ 4. Push the initial commit to GitHub
720
+
721
+ ### Publish a post
722
+
723
+ ```bash
724
+ fossbook new "My New Article"
725
+ # ... edit content/posts/My New Article/index.md ...
726
+ fossbook deploy
727
+ ```
728
+
729
+ Fossbook will build the site, commit, push to GitHub, wait for the CI/CD pipeline to finish, and display the live URL:
730
+
731
+ ```
732
+ Building site... done.
733
+ Committing: "Publish: My New Article"
734
+ Pushing to origin/main...
735
+ Waiting for GitHub Pages deployment... ✓
736
+
737
+ ✅ Published! View your article at:
738
+ https://username.github.io/my-blog/My%20New%20Article/
739
+ ```
740
+
741
+ **Deploy options:**
742
+
743
+ ```bash
744
+ fossbook deploy --message "Update homepage" # Custom commit message
745
+ fossbook deploy --no-wait # Push without waiting for CI
746
+ ```
747
+
748
+ ## License
749
+
750
+ - Generator code: [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause)
751
+ - Archie theme: [MIT License](https://github.com/athul/archie?tab=MIT-1-ov-file#readme)
752
+
753
+ ## Credits
754
+
755
+ Adapted from [kartiknair's blog](https://github.com/kartiknair/blog) and styled using the [Archie theme](https://github.com/athul/archie).