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 +82 -0
- package/lib/mod/config.js +4 -0
- package/lib/mod/marked.js +21 -0
- package/lib/mod/page.js +6 -3
- package/lib/posts.js +4 -0
- package/package.json +1 -1
- package/themes/archie/layouts/post.html +4 -0
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, "&")
|
|
35
|
+
.replace(/</g, "<")
|
|
36
|
+
.replace(/>/g, ">");
|
|
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.
|
|
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
|
@@ -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>
|