fossbook 0.0.7 → 0.0.9

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
@@ -13,6 +13,7 @@ It was originally part of the [F/OSS Comics blog](https://fosscomics.com) and is
13
13
  - **GitHub Pages** — Built-in CNAME support for custom domains
14
14
  - **Dev server** — Local preview server with Express
15
15
  - **Syntax highlighting** — Code block highlighting via highlight.js
16
+ - **Mermaid diagrams** — ` ```mermaid ` code blocks render as diagrams
16
17
 
17
18
  ## Quick Start
18
19
 
@@ -86,6 +87,18 @@ module.exports = {
86
87
  image: "https://example.com/default-image.png",
87
88
  theme: "archie",
88
89
 
90
+ // Optional: URL prefix for posts. Defaults to "posts" -> /posts/<slug>/.
91
+ // Set to "" to serve posts at the site root, /<slug>/.
92
+ postsPath: "posts",
93
+
94
+ // Optional comments (see "Comments" below)
95
+ comments: {
96
+ provider: "utterances",
97
+ repo: "you/your-blog",
98
+ issueTerm: "pathname",
99
+ theme: "github-light",
100
+ },
101
+
89
102
  // Directory overrides (defaults shown)
90
103
  content: "./content",
91
104
  postsDir: "./content/posts",
@@ -120,6 +133,35 @@ content/posts/My Post Title/
120
133
  └── feature.png
121
134
  ```
122
135
 
136
+ ### Post URLs
137
+
138
+ By default, posts are served under `/posts/`, e.g. `/posts/my-post-title/`. To
139
+ change the prefix or move posts to the site root, set `postsPath` in
140
+ `fossbook.config.js`:
141
+
142
+ ```js
143
+ postsPath: "", // serves the post above at /my-post-title/ (site root)
144
+ // postsPath: "blog", // or use a different prefix: /blog/my-post-title/
145
+ ```
146
+
147
+ This affects the generated output directory, the post URL, post links on the
148
+ home/all-posts/tag pages, and image paths. The on-disk source layout under
149
+ `content/posts/` does not change.
150
+
151
+ ### Mermaid diagrams
152
+
153
+ Fenced code blocks tagged `mermaid` are rendered as diagrams instead of code.
154
+ The [Mermaid](https://mermaid.js.org) script is loaded from a CDN only on pages
155
+ that contain a diagram.
156
+
157
+ ````markdown
158
+ ```mermaid
159
+ sequenceDiagram
160
+ Alice->>Bob: Hello Bob
161
+ Bob-->>Alice: Hi Alice
162
+ ```
163
+ ````
164
+
123
165
  ## CLI Reference
124
166
 
125
167
  ```
@@ -149,6 +191,46 @@ Fossbook ships with the Archie theme by default. To use a custom theme:
149
191
 
150
192
  Theme resolution order: user project `themes/` → built-in `themes/`.
151
193
 
194
+ ## Comments
195
+
196
+ Fossbook supports [utterances](https://utteranc.es) — a commenting widget that
197
+ stores comments as GitHub issues. When enabled, a comment box is rendered at the
198
+ bottom of every post page.
199
+
200
+ ### Setup
201
+
202
+ 1. Make the repository that backs the comments **public**.
203
+ 2. Install the [utterances GitHub App](https://github.com/apps/utterances) on
204
+ that repository so the bot can create issues.
205
+ 3. Add a `comments` block to `fossbook.config.js`:
206
+
207
+ ```js
208
+ comments: {
209
+ provider: "utterances", // currently the only supported provider
210
+ repo: "you/your-blog", // owner/repo that stores the comment issues
211
+ issueTerm: "pathname", // how a post maps to an issue (see below)
212
+ theme: "github-light", // any utterances theme, e.g. "github-dark"
213
+ },
214
+ ```
215
+
216
+ Omit the `comments` block (or set it to `null`) to disable comments.
217
+
218
+ ### Options
219
+
220
+ | Field | Required | Default | Description |
221
+ | ----------- | -------- | ---------------- | ---------------------------------------------------------------- |
222
+ | `provider` | yes | — | Must be `"utterances"`. |
223
+ | `repo` | yes | — | `owner/repo` whose issues store the comments. |
224
+ | `issueTerm` | no | `"pathname"` | Mapping between a page and its issue: `pathname`, `url`, `title`, `og:title`. |
225
+ | `theme` | no | `"github-light"` | Any [utterances theme](https://utteranc.es/#configuration). |
226
+
227
+ ### Mapping notes
228
+
229
+ With `issueTerm: "pathname"`, each post is matched to a GitHub issue whose title
230
+ equals the page's pathname (with the leading slash stripped). If you change a
231
+ post's URL, the existing comment thread no longer matches — rename the issue
232
+ title to the new pathname to keep the old comments.
233
+
152
234
  ### Required layout files
153
235
 
154
236
  ```
package/lib/mod/config.js CHANGED
@@ -17,6 +17,7 @@ const defaults = {
17
17
  image: "",
18
18
  theme: "archie",
19
19
  basePath: "", // auto-detected for GitHub Pages project sites, e.g. "/repo-name/"
20
+ postsPath: "posts", // URL prefix for posts: "posts" serves articles at /posts/<slug>/; set "" for root
20
21
  comments: null, // { provider: "utterances", repo: "user/repo", issueTerm: "pathname", theme: "github-light" }
21
22
  content: "./content",
22
23
  postsDir: "./content/posts",
@@ -91,6 +92,9 @@ function loadConfig(configPath) {
91
92
  merged.basePath += "/";
92
93
  }
93
94
 
95
+ // Normalize postsPath into a bare segment without surrounding slashes
96
+ merged.postsPath = (merged.postsPath || "").replace(/^\/+|\/+$/g, "");
97
+
94
98
  return merged;
95
99
  }
96
100
 
package/lib/mod/marked.js CHANGED
@@ -18,6 +18,27 @@ marked.setOptions({
18
18
 
19
19
  // Override function
20
20
  const renderer = {
21
+ code(code, infostring) {
22
+ // marked >=15 passes a token object; older versions pass positional args.
23
+ let text = code;
24
+ let lang = infostring;
25
+ if (code && typeof code === "object") {
26
+ text = code.text;
27
+ lang = code.lang;
28
+ }
29
+ const language = (lang || "").trim().split(/\s+/)[0];
30
+ // Emit mermaid fenced blocks as <pre class="mermaid"> so the mermaid
31
+ // client script can render them as diagrams instead of code.
32
+ if (language === "mermaid") {
33
+ const escaped = text
34
+ .replace(/&/g, "&amp;")
35
+ .replace(/</g, "&lt;")
36
+ .replace(/>/g, "&gt;");
37
+ return `<pre class="mermaid">${escaped}</pre>`;
38
+ }
39
+ // Fall back to marked's default code rendering for everything else.
40
+ return false;
41
+ },
21
42
  image(href, title, text) {
22
43
  let size = null;
23
44
  // Check if the title contains a size specification
package/lib/mod/page.js CHANGED
@@ -26,6 +26,9 @@ module.exports = class Page extends PageBase {
26
26
  // If there's no .md extension, just get the last part of the path
27
27
  this.path = path.basename(filePath);
28
28
  }
29
+ // The source folder name, used to locate the post's images on disk.
30
+ // this.path may later be prefixed (e.g. "posts/<slug>") for output/URLs.
31
+ this.slug = this.path;
29
32
  // parsed content by fields and body
30
33
  const content = fm(mdContent);
31
34
 
@@ -75,7 +78,7 @@ module.exports = class Page extends PageBase {
75
78
  recursive: true,
76
79
  });
77
80
 
78
- fs.mkdirSync(outPath);
81
+ fs.mkdirSync(outPath, { recursive: true });
79
82
  } else {
80
83
  // remove the outputPath file if it exists
81
84
  const outPath = path.join(this.config.dev.outdir, this.path);
@@ -96,7 +99,7 @@ module.exports = class Page extends PageBase {
96
99
  );
97
100
 
98
101
  // if there is the images folder in the output directory.
99
- const srcImagesDir = path.join(this.config.dev.postsdir, this.path, "images");
102
+ const srcImagesDir = path.join(this.config.dev.postsdir, this.slug, "images");
100
103
  if (
101
104
  fs.existsSync(srcImagesDir) &&
102
105
  this.path !== ""
@@ -104,7 +107,7 @@ module.exports = class Page extends PageBase {
104
107
  // Copy images folder from postsdir to outdir
105
108
  const destImagesDir = path.join(this.config.dev.outdir, this.path, "images");
106
109
  if (!fs.existsSync(destImagesDir))
107
- fs.mkdirSync(destImagesDir);
110
+ fs.mkdirSync(destImagesDir, { recursive: true });
108
111
 
109
112
  fs.readdirSync(srcImagesDir).forEach(
110
113
  (image) => {
package/lib/posts.js CHANGED
@@ -23,6 +23,10 @@ module.exports = class Posts {
23
23
  }
24
24
  const post = new Page(this.config);
25
25
  post.readSource(path.join(this.config.dev.postsdir, postPath));
26
+ // Optionally nest posts under a URL prefix (e.g. "posts" -> /posts/<slug>/).
27
+ // post.slug stays the source folder name so image lookups remain correct.
28
+ const prefix = this.config.postsPath ? `${this.config.postsPath}/` : "";
29
+ post.path = `${prefix}${post.slug}`;
26
30
  post.url = `${this.config.blogsite}/${post.path}/`;
27
31
  post.imageURL = `${this.config.blogsite}/${post.path}/images/${post.image}`;
28
32
  this.posts.push(post);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fossbook",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "A lightweight static blog site generator for GitHub Pages",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -78,5 +78,9 @@
78
78
  ${page.footer()}
79
79
  </footer>
80
80
  </div>
81
+ ${page.body.includes('class="mermaid"') ? `<script type="module">
82
+ import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
83
+ mermaid.initialize({ startOnLoad: true });
84
+ </script>` : ""}
81
85
  </body>
82
86
  </html>