fossbook 0.2.3 → 0.2.4
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 +123 -40
- package/package.json +1 -1
- package/themes/archie/assets/styles/main.css +59 -19
package/lib/mod/marked.js
CHANGED
|
@@ -9,6 +9,24 @@ function escapeAttribute(value) {
|
|
|
9
9
|
.replace(/>/g, ">");
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
function matchFencedContainer(source, name) {
|
|
13
|
+
const opening = new RegExp(`^(:{3,})${name}(?=[ \\t]|\\n)([^\\n]*)\\n`).exec(source);
|
|
14
|
+
if (!opening) return undefined;
|
|
15
|
+
|
|
16
|
+
const contentStart = opening[0].length;
|
|
17
|
+
const closing = new RegExp(`^${opening[1]}[ \\t]*(?:\\n|$)`, "m")
|
|
18
|
+
.exec(source.slice(contentStart));
|
|
19
|
+
if (!closing) return undefined;
|
|
20
|
+
|
|
21
|
+
const contentEnd = contentStart + closing.index;
|
|
22
|
+
const rawEnd = contentEnd + closing[0].length;
|
|
23
|
+
return {
|
|
24
|
+
attributes: opening[2].trim(),
|
|
25
|
+
content: source.slice(contentStart, contentEnd),
|
|
26
|
+
raw: source.slice(0, rawEnd),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
12
30
|
const panelStyleProperties = new Map([
|
|
13
31
|
["background", "--panel-background"],
|
|
14
32
|
["background-color", "--panel-background-color"],
|
|
@@ -21,7 +39,18 @@ const panelStyleProperties = new Map([
|
|
|
21
39
|
["gap", "--panel-gap"],
|
|
22
40
|
]);
|
|
23
41
|
|
|
24
|
-
|
|
42
|
+
const comicPanelStyleProperties = new Map([
|
|
43
|
+
["background", "--comic-panel-background"],
|
|
44
|
+
["background-color", "--comic-panel-background-color"],
|
|
45
|
+
["border", "--comic-panel-border"],
|
|
46
|
+
["border-color", "--comic-panel-border-color"],
|
|
47
|
+
["border-radius", "--comic-panel-border-radius"],
|
|
48
|
+
["border-style", "--comic-panel-border-style"],
|
|
49
|
+
["border-width", "--comic-panel-border-width"],
|
|
50
|
+
["box-shadow", "--comic-panel-shadow"],
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
function parseStyle(source, styleProperties, context) {
|
|
25
54
|
const declarations = [];
|
|
26
55
|
const properties = new Set();
|
|
27
56
|
const declarationPattern = /([a-z][a-z-]*)\s*:\s*([^;]+)(?:;|$)/gy;
|
|
@@ -33,22 +62,22 @@ function parsePanelStyle(source) {
|
|
|
33
62
|
|
|
34
63
|
declarationPattern.lastIndex = offset;
|
|
35
64
|
const match = declarationPattern.exec(source);
|
|
36
|
-
if (!match) throw new Error(`Invalid
|
|
65
|
+
if (!match) throw new Error(`Invalid ${context} style near: ${source.slice(offset)}`);
|
|
37
66
|
|
|
38
67
|
const property = match[1];
|
|
39
68
|
const value = match[2].trim();
|
|
40
|
-
if (!
|
|
41
|
-
throw new Error(`Unsupported
|
|
69
|
+
if (!styleProperties.has(property)) {
|
|
70
|
+
throw new Error(`Unsupported ${context} style property: ${property}`);
|
|
42
71
|
}
|
|
43
72
|
if (properties.has(property)) {
|
|
44
|
-
throw new Error(`Duplicate
|
|
73
|
+
throw new Error(`Duplicate ${context} style property: ${property}`);
|
|
45
74
|
}
|
|
46
75
|
if (!value || /[{}]|url\s*\(|expression\s*\(|@import|javascript\s*:/i.test(value)) {
|
|
47
|
-
throw new Error(`Invalid value for
|
|
76
|
+
throw new Error(`Invalid value for ${context} style property: ${property}`);
|
|
48
77
|
}
|
|
49
78
|
|
|
50
79
|
properties.add(property);
|
|
51
|
-
declarations.push(`${
|
|
80
|
+
declarations.push(`${styleProperties.get(property)}: ${value};`);
|
|
52
81
|
offset = declarationPattern.lastIndex;
|
|
53
82
|
}
|
|
54
83
|
|
|
@@ -86,15 +115,49 @@ function parsePanelAttributes(source) {
|
|
|
86
115
|
throw new Error('Panel boxed must be "true"');
|
|
87
116
|
}
|
|
88
117
|
|
|
89
|
-
|
|
90
|
-
|
|
118
|
+
const style = parseStyle(attributes.style || "", panelStyleProperties, "panel");
|
|
119
|
+
if (attributes.boxed !== "true" && style.some((declaration) => !declaration.startsWith("--panel-gap:"))) {
|
|
120
|
+
throw new Error('Panel card styles require boxed="true"');
|
|
91
121
|
}
|
|
92
122
|
|
|
93
123
|
return {
|
|
94
124
|
columns,
|
|
95
125
|
label: attributes.label,
|
|
96
126
|
boxed: attributes.boxed === "true",
|
|
97
|
-
style
|
|
127
|
+
style,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseComicPanelAttributes(source) {
|
|
132
|
+
const attributes = {};
|
|
133
|
+
const attributePattern = /([a-z][a-z-]*)="([^"]*)"/gy;
|
|
134
|
+
let offset = 0;
|
|
135
|
+
|
|
136
|
+
while (offset < source.length) {
|
|
137
|
+
while (source[offset] === " " || source[offset] === "\t") offset += 1;
|
|
138
|
+
if (offset === source.length) break;
|
|
139
|
+
|
|
140
|
+
attributePattern.lastIndex = offset;
|
|
141
|
+
const match = attributePattern.exec(source);
|
|
142
|
+
if (!match) throw new Error(`Invalid panel attribute near: ${source.slice(offset)}`);
|
|
143
|
+
if (!["label", "divider", "style"].includes(match[1])) {
|
|
144
|
+
throw new Error(`Unsupported panel attribute: ${match[1]}`);
|
|
145
|
+
}
|
|
146
|
+
if (Object.hasOwn(attributes, match[1])) {
|
|
147
|
+
throw new Error(`Duplicate panel attribute: ${match[1]}`);
|
|
148
|
+
}
|
|
149
|
+
attributes[match[1]] = match[2];
|
|
150
|
+
offset = attributePattern.lastIndex;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (attributes.divider !== undefined && attributes.divider !== "true") {
|
|
154
|
+
throw new Error('Panel divider must be "true"');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
label: attributes.label,
|
|
159
|
+
divider: attributes.divider === "true",
|
|
160
|
+
style: parseStyle(attributes.style || "", comicPanelStyleProperties, "panel"),
|
|
98
161
|
};
|
|
99
162
|
}
|
|
100
163
|
|
|
@@ -102,57 +165,77 @@ const panelsExtension = {
|
|
|
102
165
|
name: "panels",
|
|
103
166
|
level: "block",
|
|
104
167
|
start(source) {
|
|
105
|
-
return source.match(
|
|
168
|
+
return source.match(/^:{3,}panels(?:[ \t]|$)/m)?.index;
|
|
106
169
|
},
|
|
107
170
|
tokenizer(source) {
|
|
108
|
-
const
|
|
109
|
-
if (!
|
|
171
|
+
const container = matchFencedContainer(source, "panels");
|
|
172
|
+
if (!container) return undefined;
|
|
110
173
|
|
|
111
|
-
const attributes = parsePanelAttributes(
|
|
174
|
+
const attributes = parsePanelAttributes(container.attributes);
|
|
175
|
+
const tokens = this.lexer.blockTokens(container.content);
|
|
112
176
|
return {
|
|
113
177
|
type: "panels",
|
|
114
|
-
raw:
|
|
178
|
+
raw: container.raw,
|
|
115
179
|
columns: attributes.columns,
|
|
116
180
|
label: attributes.label,
|
|
117
181
|
boxed: attributes.boxed,
|
|
118
182
|
style: attributes.style,
|
|
119
|
-
|
|
183
|
+
nested: tokens.some((token) => token.type === "panel"),
|
|
184
|
+
tokens,
|
|
120
185
|
};
|
|
121
186
|
},
|
|
122
187
|
renderer(token) {
|
|
123
188
|
const label = token.label
|
|
124
189
|
? ` role="group" aria-label="${escapeAttribute(token.label)}"`
|
|
125
190
|
: "";
|
|
126
|
-
const className =
|
|
191
|
+
const className = [
|
|
192
|
+
"panel-group",
|
|
193
|
+
token.boxed ? "panel-group-boxed" : "",
|
|
194
|
+
token.nested ? "panel-group-nested" : "",
|
|
195
|
+
].filter(Boolean).join(" ");
|
|
127
196
|
const style = [`--panel-columns: ${token.columns};`, ...token.style].join(" ");
|
|
128
197
|
return `<div class="${className}" style="${escapeAttribute(style)}"${label}>\n${this.parser.parse(token.tokens)}</div>\n`;
|
|
129
198
|
},
|
|
130
199
|
};
|
|
131
200
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
201
|
+
function createComicPanelExtension(name, type) {
|
|
202
|
+
return {
|
|
203
|
+
name: type,
|
|
204
|
+
level: "block",
|
|
205
|
+
start(source) {
|
|
206
|
+
return source.match(new RegExp(`^:{3,}${name}(?:[ \\t]|$)`, "m"))?.index;
|
|
207
|
+
},
|
|
138
208
|
tokenizer(source) {
|
|
139
|
-
|
|
140
|
-
|
|
209
|
+
const container = matchFencedContainer(source, name);
|
|
210
|
+
if (!container) return undefined;
|
|
141
211
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
212
|
+
const attributes = parseComicPanelAttributes(container.attributes);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
type,
|
|
216
|
+
raw: container.raw,
|
|
217
|
+
label: attributes.label,
|
|
218
|
+
divider: attributes.divider,
|
|
219
|
+
style: attributes.style,
|
|
220
|
+
tokens: this.lexer.blockTokens(container.content),
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
renderer(token) {
|
|
224
|
+
const label = token.label
|
|
225
|
+
? ` aria-label="${escapeAttribute(token.label)}"`
|
|
226
|
+
: "";
|
|
227
|
+
const style = token.style.length
|
|
228
|
+
? ` style="${escapeAttribute(token.style.join(" "))}"`
|
|
229
|
+
: "";
|
|
230
|
+
const className = token.divider
|
|
231
|
+
? "comic-panel comic-panel-divider"
|
|
232
|
+
: "comic-panel";
|
|
233
|
+
return `<section class="${className}"${style}${label}>\n${this.parser.parse(token.tokens)}</section>\n`;
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const panelExtension = createComicPanelExtension("panel", "panel");
|
|
156
239
|
|
|
157
240
|
marked.setOptions({
|
|
158
241
|
renderer: new marked.Renderer(),
|
|
@@ -267,7 +350,7 @@ const renderer = {
|
|
|
267
350
|
};
|
|
268
351
|
|
|
269
352
|
marked.use({
|
|
270
|
-
extensions: [panelsExtension,
|
|
353
|
+
extensions: [panelsExtension, panelExtension],
|
|
271
354
|
renderer,
|
|
272
355
|
hooks: {
|
|
273
356
|
postprocess(html) {
|
package/package.json
CHANGED
|
@@ -227,11 +227,23 @@
|
|
|
227
227
|
.panel-group {
|
|
228
228
|
display: grid;
|
|
229
229
|
grid-template-columns: repeat(var(--panel-columns), minmax(0, 1fr));
|
|
230
|
-
gap: 1.5rem;
|
|
230
|
+
gap: var(--panel-gap, 1.5rem);
|
|
231
231
|
align-items: start;
|
|
232
232
|
margin: 1.5rem 0;
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
.panel-group-nested {
|
|
236
|
+
align-items: stretch;
|
|
237
|
+
grid-auto-rows: 1fr;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.panel-group-nested > .comic-panel {
|
|
241
|
+
box-sizing: border-box;
|
|
242
|
+
min-width: 0;
|
|
243
|
+
height: 100%;
|
|
244
|
+
margin: 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
235
247
|
.panel-group .image-container,
|
|
236
248
|
.panel-group figure {
|
|
237
249
|
min-width: 0;
|
|
@@ -260,11 +272,11 @@
|
|
|
260
272
|
gap: var(--panel-gap, 0.75rem);
|
|
261
273
|
}
|
|
262
274
|
|
|
263
|
-
.panel-group-boxed .image-container {
|
|
275
|
+
.panel-group-boxed > .image-container {
|
|
264
276
|
height: 100%;
|
|
265
277
|
}
|
|
266
278
|
|
|
267
|
-
.panel-group-boxed figure {
|
|
279
|
+
.panel-group-boxed > .image-container > figure {
|
|
268
280
|
height: 100%;
|
|
269
281
|
display: flex;
|
|
270
282
|
flex-direction: column;
|
|
@@ -274,57 +286,77 @@
|
|
|
274
286
|
box-shadow: var(--panel-box-shadow, none);
|
|
275
287
|
}
|
|
276
288
|
|
|
277
|
-
.panel-group-boxed[style*="--panel-border-color:"] figure {
|
|
289
|
+
.panel-group-boxed[style*="--panel-border-color:"] > .image-container > figure {
|
|
278
290
|
border-color: var(--panel-border-color);
|
|
279
291
|
}
|
|
280
292
|
|
|
281
|
-
.panel-group-boxed[style*="--panel-border-style:"] figure {
|
|
293
|
+
.panel-group-boxed[style*="--panel-border-style:"] > .image-container > figure {
|
|
282
294
|
border-style: var(--panel-border-style);
|
|
283
295
|
}
|
|
284
296
|
|
|
285
|
-
.panel-group-boxed[style*="--panel-border-width:"] figure {
|
|
297
|
+
.panel-group-boxed[style*="--panel-border-width:"] > .image-container > figure {
|
|
286
298
|
border-width: var(--panel-border-width);
|
|
287
299
|
}
|
|
288
300
|
|
|
289
|
-
.panel-group-boxed[style*="--panel-background-color:"] figure {
|
|
301
|
+
.panel-group-boxed[style*="--panel-background-color:"] > .image-container > figure {
|
|
290
302
|
background-color: var(--panel-background-color);
|
|
291
303
|
}
|
|
292
304
|
|
|
293
|
-
.panel-group-boxed figcaption {
|
|
305
|
+
.panel-group-boxed > .image-container > figure > figcaption {
|
|
294
306
|
flex: 1;
|
|
295
307
|
margin-top: 0;
|
|
296
308
|
padding: 0.75rem;
|
|
297
309
|
font-size: 0.9rem;
|
|
298
310
|
}
|
|
299
311
|
|
|
300
|
-
.comic-
|
|
312
|
+
.comic-panel {
|
|
301
313
|
margin: 2rem 0;
|
|
302
314
|
padding: 1rem;
|
|
303
|
-
border: 2px solid #575752;
|
|
304
|
-
|
|
305
|
-
|
|
315
|
+
border: var(--comic-panel-border, 2px solid #575752);
|
|
316
|
+
border-radius: var(--comic-panel-border-radius, 0);
|
|
317
|
+
background: var(--comic-panel-background, #fff);
|
|
318
|
+
box-shadow: var(--comic-panel-shadow, 4px 4px 0 rgba(70, 70, 66, 0.14));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.comic-panel[style*="--comic-panel-border-color:"] {
|
|
322
|
+
border-color: var(--comic-panel-border-color);
|
|
306
323
|
}
|
|
307
324
|
|
|
308
|
-
.comic-
|
|
325
|
+
.comic-panel[style*="--comic-panel-border-style:"] {
|
|
326
|
+
border-style: var(--comic-panel-border-style);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.comic-panel[style*="--comic-panel-border-width:"] {
|
|
330
|
+
border-width: var(--comic-panel-border-width);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.comic-panel[style*="--comic-panel-background-color:"] {
|
|
334
|
+
background-color: var(--comic-panel-background-color);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.comic-panel > :first-child {
|
|
309
338
|
margin-top: 0;
|
|
310
339
|
}
|
|
311
340
|
|
|
312
|
-
.comic-
|
|
341
|
+
.comic-panel > :last-child {
|
|
313
342
|
margin-bottom: 0;
|
|
314
343
|
}
|
|
315
344
|
|
|
316
|
-
.comic-
|
|
317
|
-
.comic-
|
|
345
|
+
.comic-panel .image-container,
|
|
346
|
+
.comic-panel figure {
|
|
318
347
|
display: block;
|
|
319
348
|
width: 100%;
|
|
320
349
|
}
|
|
321
350
|
|
|
322
|
-
.comic-
|
|
351
|
+
.comic-panel figure {
|
|
323
352
|
padding: 0;
|
|
324
353
|
}
|
|
325
354
|
|
|
326
|
-
.comic-
|
|
355
|
+
.comic-panel .image-dialogue {
|
|
327
356
|
margin-bottom: 1rem;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.comic-panel-divider .image-dialogue {
|
|
328
360
|
padding-bottom: 1rem;
|
|
329
361
|
border-bottom: 1px solid #575752;
|
|
330
362
|
}
|
|
@@ -338,13 +370,21 @@
|
|
|
338
370
|
width: 100% !important;
|
|
339
371
|
}
|
|
340
372
|
|
|
341
|
-
.comic-
|
|
373
|
+
.comic-panel {
|
|
342
374
|
margin: 1.5rem 0;
|
|
343
375
|
padding: 0.75rem;
|
|
344
376
|
border-width: 1px;
|
|
345
377
|
box-shadow: 2px 2px 0 rgba(70, 70, 66, 0.14);
|
|
346
378
|
}
|
|
347
379
|
}
|
|
380
|
+
|
|
381
|
+
.comic-panel[style*="--comic-panel-border:"] {
|
|
382
|
+
border: var(--comic-panel-border);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.comic-panel[style*="--comic-panel-shadow:"] {
|
|
386
|
+
box-shadow: var(--comic-panel-shadow);
|
|
387
|
+
}
|
|
348
388
|
|
|
349
389
|
@media screen and (min-width: 600px) {
|
|
350
390
|
figure {
|