fossbook 0.2.13 → 0.2.15
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 +16 -1
- package/bin/fossbook.js +3 -0
- package/lib/deploy.js +1 -0
- package/lib/index.js +9 -1
- package/lib/init.js +1 -1
- package/lib/mod/page.js +1 -0
- package/lib/posts.js +1 -0
- package/package.json +1 -1
- package/themes/archie/assets/styles/main.css +46 -3
- package/themes/archie/layouts/post.html +45 -7
package/README.md
CHANGED
|
@@ -85,10 +85,17 @@ This also creates `index.ko.md` and `index.ja.md` in the same post directory. Ru
|
|
|
85
85
|
fossbook build
|
|
86
86
|
```
|
|
87
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
|
+
|
|
88
95
|
### Preview locally
|
|
89
96
|
|
|
90
97
|
```bash
|
|
91
|
-
fossbook serve
|
|
98
|
+
fossbook serve --include-drafts
|
|
92
99
|
```
|
|
93
100
|
|
|
94
101
|
Open http://localhost:3000 to view your site.
|
|
@@ -204,11 +211,16 @@ date: 2026-02-17
|
|
|
204
211
|
description: "A brief summary of the post"
|
|
205
212
|
image: "feature.png"
|
|
206
213
|
tags: "JavaScript, Node.js, Static Site"
|
|
214
|
+
draft: false
|
|
207
215
|
---
|
|
208
216
|
|
|
209
217
|
Your Markdown content here...
|
|
210
218
|
```
|
|
211
219
|
|
|
220
|
+
Set `draft: true` while a post is in progress. Normal builds and deployments
|
|
221
|
+
exclude it. Use `fossbook serve --include-drafts` to preview drafts locally,
|
|
222
|
+
then remove the field or set it to `false` before publishing.
|
|
223
|
+
|
|
212
224
|
### Directory structure
|
|
213
225
|
|
|
214
226
|
```
|
|
@@ -300,6 +312,9 @@ module.exports = {
|
|
|
300
312
|
previousPageLabel: "앞으로",
|
|
301
313
|
nextPageLabel: "뒤로",
|
|
302
314
|
transcriptLabel: "말글 보이기",
|
|
315
|
+
copyLinkLabel: "링크 복사",
|
|
316
|
+
linkCopiedLabel: "링크를 복사했습니다",
|
|
317
|
+
copyLinkErrorLabel: "링크를 복사하지 못했습니다",
|
|
303
318
|
};
|
|
304
319
|
```
|
|
305
320
|
|
package/bin/fossbook.js
CHANGED
|
@@ -24,6 +24,7 @@ Options:
|
|
|
24
24
|
--clean Remove output directory before build (default: true)
|
|
25
25
|
--github (init) Also create a GitHub repo and push
|
|
26
26
|
--lang (new) Comma-separated translation languages, e.g. ko,ja
|
|
27
|
+
--include-drafts (build, serve) Include posts with draft: true
|
|
27
28
|
-m, --message (deploy) Custom commit message
|
|
28
29
|
--no-wait (deploy) Push without waiting for CI status
|
|
29
30
|
--draft (deploy) Commit locally without pushing
|
|
@@ -64,6 +65,7 @@ switch (command) {
|
|
|
64
65
|
const config = loadConfig(configPath);
|
|
65
66
|
const outputOverride = getOption("-o", "--output");
|
|
66
67
|
if (outputOverride) config.dev.outdir = outputOverride;
|
|
68
|
+
config.includeDrafts = hasFlag("--include-drafts");
|
|
67
69
|
|
|
68
70
|
const { build } = require("../lib/index");
|
|
69
71
|
build(config);
|
|
@@ -74,6 +76,7 @@ switch (command) {
|
|
|
74
76
|
const config = loadConfig(configPath);
|
|
75
77
|
const outputOverride = getOption("-o", "--output");
|
|
76
78
|
if (outputOverride) config.dev.outdir = outputOverride;
|
|
79
|
+
config.includeDrafts = hasFlag("--include-drafts");
|
|
77
80
|
const port = getOption("-p", "--port") || 3000;
|
|
78
81
|
|
|
79
82
|
const { build } = require("../lib/index");
|
package/lib/deploy.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -38,7 +38,12 @@ function createPostTranslationIndex(languageConfigs) {
|
|
|
38
38
|
const sourceFile = config.language === config.defaultLanguage
|
|
39
39
|
? "index.md"
|
|
40
40
|
: `index.${config.language}.md`;
|
|
41
|
-
|
|
41
|
+
const sourcePath = path.join(postsDir, entry.name, sourceFile);
|
|
42
|
+
if (!fs.existsSync(sourcePath)) continue;
|
|
43
|
+
|
|
44
|
+
const post = new Page(config);
|
|
45
|
+
post.readSource(sourcePath, entry.name);
|
|
46
|
+
if (post.draft && !config.includeDrafts) continue;
|
|
42
47
|
|
|
43
48
|
const prefix = config.postsPath ? `${config.postsPath}/` : "";
|
|
44
49
|
translations.push({
|
|
@@ -163,6 +168,9 @@ function build(config) {
|
|
|
163
168
|
fs.mkdirSync(config.dev.outdir, { recursive: true });
|
|
164
169
|
|
|
165
170
|
const languageConfigs = config.languageConfigs || [config];
|
|
171
|
+
languageConfigs.forEach((languageConfig) => {
|
|
172
|
+
languageConfig.includeDrafts = config.includeDrafts === true;
|
|
173
|
+
});
|
|
166
174
|
const postTranslationIndex = createPostTranslationIndex(languageConfigs);
|
|
167
175
|
const aboutTranslations = createAboutTranslations(languageConfigs);
|
|
168
176
|
const homeTranslations = createHomeTranslations(languageConfigs);
|
package/lib/init.js
CHANGED
|
@@ -130,7 +130,7 @@ function initProject(options = {}) {
|
|
|
130
130
|
// fossbook.config.ko.js, and so on. Menu labels can also be localized with
|
|
131
131
|
// homeLabel, allPostsLabel, aboutLabel, tagsLabel, allTagsLabel,
|
|
132
132
|
// postedOnLabel, readMoreLabel, previousPageLabel, nextPageLabel,
|
|
133
|
-
// and
|
|
133
|
+
// transcriptLabel, copyLinkLabel, linkCopiedLabel, and copyLinkErrorLabel.
|
|
134
134
|
|
|
135
135
|
// Comment system (optional)
|
|
136
136
|
// comments: { provider: "utterances", repo: "user/repo", issueTerm: "pathname", theme: "github-light" },
|
package/lib/mod/page.js
CHANGED
|
@@ -39,6 +39,7 @@ module.exports = class Page extends PageBase {
|
|
|
39
39
|
this.url = `${this.config.blogsite}/${this.path}/`;
|
|
40
40
|
this.image = content.attributes.image;
|
|
41
41
|
this.description = content.attributes.description;
|
|
42
|
+
this.draft = content.attributes.draft === true;
|
|
42
43
|
// default image if no imageURL is specified
|
|
43
44
|
this.imageURL = this.config.image;
|
|
44
45
|
|
package/lib/posts.js
CHANGED
|
@@ -26,6 +26,7 @@ module.exports = class Posts {
|
|
|
26
26
|
}
|
|
27
27
|
const post = new Page(this.config);
|
|
28
28
|
post.readSource(indexPath, postPath);
|
|
29
|
+
if (post.draft && !this.config.includeDrafts) return;
|
|
29
30
|
// Optionally nest posts under a URL prefix (e.g. "posts" -> /posts/<slug>/).
|
|
30
31
|
// post.slug stays the source folder name so image lookups remain correct.
|
|
31
32
|
const prefix = this.config.postsPath ? `${this.config.postsPath}/` : "";
|
package/package.json
CHANGED
|
@@ -69,12 +69,38 @@
|
|
|
69
69
|
width: var(--dialogue-width, 100%);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
.
|
|
72
|
+
.post-controls {
|
|
73
|
+
align-items: center;
|
|
73
74
|
display: flex;
|
|
75
|
+
flex-wrap: wrap;
|
|
76
|
+
gap: 1rem;
|
|
74
77
|
justify-content: flex-end;
|
|
75
78
|
margin: .75rem 0 1.5rem;
|
|
76
79
|
}
|
|
77
80
|
|
|
81
|
+
.copy-link-button {
|
|
82
|
+
background: transparent;
|
|
83
|
+
border: 1px solid var(--bordercl);
|
|
84
|
+
border-radius: 4px;
|
|
85
|
+
color: inherit;
|
|
86
|
+
cursor: pointer;
|
|
87
|
+
font: inherit;
|
|
88
|
+
font-size: .78rem;
|
|
89
|
+
line-height: 1.2;
|
|
90
|
+
min-height: 1.75rem;
|
|
91
|
+
padding: .15rem .4rem;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.copy-link-button:hover {
|
|
95
|
+
background: var(--hovercolor);
|
|
96
|
+
color: #fff;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.copy-link-button:focus-visible {
|
|
100
|
+
outline: 2px solid var(--hovercolor);
|
|
101
|
+
outline-offset: 2px;
|
|
102
|
+
}
|
|
103
|
+
|
|
78
104
|
.transcript-toggle {
|
|
79
105
|
align-items: center;
|
|
80
106
|
display: inline-flex;
|
|
@@ -124,7 +150,16 @@
|
|
|
124
150
|
}
|
|
125
151
|
|
|
126
152
|
.transcripts-hidden .image-dialogue {
|
|
127
|
-
|
|
153
|
+
border: 0;
|
|
154
|
+
clip: rect(0 0 0 0);
|
|
155
|
+
clip-path: inset(50%);
|
|
156
|
+
height: 1px;
|
|
157
|
+
margin: -1px;
|
|
158
|
+
overflow: hidden;
|
|
159
|
+
padding: 0;
|
|
160
|
+
position: absolute !important;
|
|
161
|
+
white-space: nowrap;
|
|
162
|
+
width: 1px;
|
|
128
163
|
}
|
|
129
164
|
|
|
130
165
|
|
|
@@ -152,12 +187,20 @@
|
|
|
152
187
|
}
|
|
153
188
|
|
|
154
189
|
@media print {
|
|
155
|
-
.
|
|
190
|
+
.post-controls {
|
|
156
191
|
display: none;
|
|
157
192
|
}
|
|
158
193
|
|
|
159
194
|
.transcripts-hidden .image-dialogue {
|
|
160
195
|
display: flex;
|
|
196
|
+
clip: auto;
|
|
197
|
+
clip-path: none;
|
|
198
|
+
height: auto;
|
|
199
|
+
margin: 0 auto;
|
|
200
|
+
overflow: visible;
|
|
201
|
+
position: static !important;
|
|
202
|
+
white-space: normal;
|
|
203
|
+
width: var(--dialogue-width, 100%);
|
|
161
204
|
}
|
|
162
205
|
}
|
|
163
206
|
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
|
|
30
30
|
${page.twitterCard("summary")}
|
|
31
31
|
${page.body.includes('class="blockquote-container image-dialogue"') ? `<script>
|
|
32
|
+
document.documentElement.classList.add("transcripts-hidden");
|
|
32
33
|
try {
|
|
33
|
-
if (localStorage.getItem("fossbook-comic-transcript") === "
|
|
34
|
-
document.documentElement.classList.
|
|
34
|
+
if (localStorage.getItem("fossbook-comic-transcript") === "visible") {
|
|
35
|
+
document.documentElement.classList.remove("transcripts-hidden");
|
|
35
36
|
}
|
|
36
37
|
} catch (error) {}
|
|
37
38
|
</script>` : ""}
|
|
@@ -56,12 +57,15 @@
|
|
|
56
57
|
<div class="meta">${page.config.postedOnLabel || "Posted on"} ${page.formatDate(new Date(page.date))}</div>
|
|
57
58
|
${page.languageSwitcher()}
|
|
58
59
|
</div>
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
<div class="post-controls">
|
|
61
|
+
${page.body.includes('class="blockquote-container image-dialogue"') ? `<div class="transcript-toggle">
|
|
61
62
|
<label for="comic-transcript-toggle">${page.config.transcriptLabel || "Display dialogue text"}</label>
|
|
62
|
-
<input id="comic-transcript-toggle" type="checkbox" role="switch" aria-controls="comic-transcript"
|
|
63
|
-
</div
|
|
64
|
-
|
|
63
|
+
<input id="comic-transcript-toggle" type="checkbox" role="switch" aria-controls="comic-transcript">
|
|
64
|
+
</div>` : ""}
|
|
65
|
+
<button class="copy-link-button" type="button">
|
|
66
|
+
<span aria-live="polite">${page.config.copyLinkLabel || "Copy link"}</span>
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
65
69
|
<section class="body" id="comic-transcript">
|
|
66
70
|
${page.body}
|
|
67
71
|
</section>
|
|
@@ -100,6 +104,40 @@
|
|
|
100
104
|
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
|
101
105
|
mermaid.initialize({ startOnLoad: true });
|
|
102
106
|
</script>` : ""}
|
|
107
|
+
<script>
|
|
108
|
+
const copyLinkButton = document.querySelector(".copy-link-button");
|
|
109
|
+
const copyLinkLabel = copyLinkButton.querySelector("span");
|
|
110
|
+
const defaultCopyLinkLabel = copyLinkLabel.textContent;
|
|
111
|
+
let copyLinkResetTimer;
|
|
112
|
+
const copyEpisodeLink = async () => {
|
|
113
|
+
const canonicalLink = document.querySelector('link[rel="canonical"]');
|
|
114
|
+
const episodeUrl = canonicalLink ? canonicalLink.href : window.location.href;
|
|
115
|
+
try {
|
|
116
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
117
|
+
await navigator.clipboard.writeText(episodeUrl);
|
|
118
|
+
} else {
|
|
119
|
+
const textArea = document.createElement("textarea");
|
|
120
|
+
textArea.value = episodeUrl;
|
|
121
|
+
textArea.setAttribute("readonly", "");
|
|
122
|
+
textArea.style.position = "fixed";
|
|
123
|
+
textArea.style.opacity = "0";
|
|
124
|
+
document.body.appendChild(textArea);
|
|
125
|
+
textArea.select();
|
|
126
|
+
const copied = document.execCommand("copy");
|
|
127
|
+
textArea.remove();
|
|
128
|
+
if (!copied) throw new Error("Copy command failed");
|
|
129
|
+
}
|
|
130
|
+
copyLinkLabel.textContent = ${JSON.stringify(page.config.linkCopiedLabel || "Link copied")};
|
|
131
|
+
} catch (error) {
|
|
132
|
+
copyLinkLabel.textContent = ${JSON.stringify(page.config.copyLinkErrorLabel || "Could not copy link")};
|
|
133
|
+
}
|
|
134
|
+
clearTimeout(copyLinkResetTimer);
|
|
135
|
+
copyLinkResetTimer = setTimeout(() => {
|
|
136
|
+
copyLinkLabel.textContent = defaultCopyLinkLabel;
|
|
137
|
+
}, 2000);
|
|
138
|
+
};
|
|
139
|
+
copyLinkButton.addEventListener("click", copyEpisodeLink);
|
|
140
|
+
</script>
|
|
103
141
|
${page.body.includes('class="blockquote-container image-dialogue"') ? `<script>
|
|
104
142
|
const transcriptToggle = document.querySelector("#comic-transcript-toggle");
|
|
105
143
|
const transcriptStorageKey = "fossbook-comic-transcript";
|