fossbook 0.2.0 → 0.2.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.
package/lib/mod/marked.js
CHANGED
|
@@ -1,6 +1,71 @@
|
|
|
1
1
|
const marked = require("marked");
|
|
2
2
|
const hljs = require("highlight.js");
|
|
3
3
|
|
|
4
|
+
function escapeAttribute(value) {
|
|
5
|
+
return value
|
|
6
|
+
.replace(/&/g, "&")
|
|
7
|
+
.replace(/"/g, """)
|
|
8
|
+
.replace(/</g, "<")
|
|
9
|
+
.replace(/>/g, ">");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function parsePanelAttributes(source) {
|
|
13
|
+
const attributes = {};
|
|
14
|
+
const attributePattern = /([a-z][a-z-]*)="([^"]*)"/gy;
|
|
15
|
+
let offset = 0;
|
|
16
|
+
|
|
17
|
+
while (offset < source.length) {
|
|
18
|
+
while (source[offset] === " " || source[offset] === "\t") offset += 1;
|
|
19
|
+
if (offset === source.length) break;
|
|
20
|
+
|
|
21
|
+
attributePattern.lastIndex = offset;
|
|
22
|
+
const match = attributePattern.exec(source);
|
|
23
|
+
if (!match) throw new Error(`Invalid panels attribute near: ${source.slice(offset)}`);
|
|
24
|
+
if (match[1] !== "columns" && match[1] !== "label") {
|
|
25
|
+
throw new Error(`Unsupported panels attribute: ${match[1]}`);
|
|
26
|
+
}
|
|
27
|
+
if (Object.hasOwn(attributes, match[1])) {
|
|
28
|
+
throw new Error(`Duplicate panels attribute: ${match[1]}`);
|
|
29
|
+
}
|
|
30
|
+
attributes[match[1]] = match[2];
|
|
31
|
+
offset = attributePattern.lastIndex;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const columns = attributes.columns === undefined ? 2 : Number(attributes.columns);
|
|
35
|
+
if (!Number.isInteger(columns) || columns < 1 || columns > 6) {
|
|
36
|
+
throw new Error("Panel columns must be an integer from 1 to 6");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { columns, label: attributes.label };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const panelsExtension = {
|
|
43
|
+
name: "panels",
|
|
44
|
+
level: "block",
|
|
45
|
+
start(source) {
|
|
46
|
+
return source.match(/^:::panels(?:[ \t]|$)/m)?.index;
|
|
47
|
+
},
|
|
48
|
+
tokenizer(source) {
|
|
49
|
+
const match = /^:::panels([^\n]*)\n([\s\S]*?)\n:::(?:\n|$)/.exec(source);
|
|
50
|
+
if (!match) return undefined;
|
|
51
|
+
|
|
52
|
+
const attributes = parsePanelAttributes(match[1].trim());
|
|
53
|
+
return {
|
|
54
|
+
type: "panels",
|
|
55
|
+
raw: match[0],
|
|
56
|
+
columns: attributes.columns,
|
|
57
|
+
label: attributes.label,
|
|
58
|
+
tokens: this.lexer.blockTokens(match[2]),
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
renderer(token) {
|
|
62
|
+
const label = token.label
|
|
63
|
+
? ` role="group" aria-label="${escapeAttribute(token.label)}"`
|
|
64
|
+
: "";
|
|
65
|
+
return `<div class="panel-group" style="--panel-columns: ${token.columns};"${label}>\n${this.parser.parse(token.tokens)}</div>\n`;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
4
69
|
marked.setOptions({
|
|
5
70
|
renderer: new marked.Renderer(),
|
|
6
71
|
pedantic: false,
|
|
@@ -66,7 +131,7 @@ const renderer = {
|
|
|
66
131
|
// Construct the image tag with optional size and title
|
|
67
132
|
let imageTag = `<img src="${href}" alt="${text}"`;
|
|
68
133
|
if (size) {
|
|
69
|
-
imageTag += ` style="width: ${size};"`;
|
|
134
|
+
imageTag += ` class="sized-image" style="width: ${size};"`;
|
|
70
135
|
}
|
|
71
136
|
imageTag += ">";
|
|
72
137
|
|
|
@@ -114,6 +179,7 @@ const renderer = {
|
|
|
114
179
|
};
|
|
115
180
|
|
|
116
181
|
marked.use({
|
|
182
|
+
extensions: [panelsExtension],
|
|
117
183
|
renderer,
|
|
118
184
|
hooks: {
|
|
119
185
|
postprocess(html) {
|
package/package.json
CHANGED
|
@@ -223,6 +223,46 @@
|
|
|
223
223
|
figure img {
|
|
224
224
|
max-height: 500px;
|
|
225
225
|
}
|
|
226
|
+
|
|
227
|
+
.panel-group {
|
|
228
|
+
display: grid;
|
|
229
|
+
grid-template-columns: repeat(var(--panel-columns), minmax(0, 1fr));
|
|
230
|
+
gap: 1.5rem;
|
|
231
|
+
align-items: start;
|
|
232
|
+
margin: 1.5rem 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.panel-group .image-container,
|
|
236
|
+
.panel-group figure {
|
|
237
|
+
min-width: 0;
|
|
238
|
+
width: 100%;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.panel-group figure {
|
|
242
|
+
display: block;
|
|
243
|
+
padding: 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.panel-group img {
|
|
247
|
+
display: block;
|
|
248
|
+
height: auto;
|
|
249
|
+
width: 100%;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.panel-group figcaption {
|
|
253
|
+
margin-top: 0.75rem;
|
|
254
|
+
text-align: center;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@media screen and (max-width: 599px) {
|
|
258
|
+
.panel-group {
|
|
259
|
+
grid-template-columns: 1fr;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.sized-image {
|
|
263
|
+
width: 100% !important;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
226
266
|
|
|
227
267
|
@media screen and (min-width: 600px) {
|
|
228
268
|
figure {
|