fossbook 0.2.3 → 0.2.5
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 +63 -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,71 +272,95 @@
|
|
|
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;
|
|
271
|
-
border:
|
|
283
|
+
border: 1px solid #575752;
|
|
272
284
|
border-radius: var(--panel-border-radius, 0);
|
|
273
285
|
background: var(--panel-background, #fff);
|
|
274
286
|
box-shadow: var(--panel-box-shadow, none);
|
|
275
287
|
}
|
|
276
288
|
|
|
277
|
-
.panel-group-boxed[style*="--panel-border
|
|
289
|
+
.panel-group-boxed[style*="--panel-border:"] > .image-container > figure {
|
|
290
|
+
border: var(--panel-border);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.panel-group-boxed[style*="--panel-border-color:"] > .image-container > figure {
|
|
278
294
|
border-color: var(--panel-border-color);
|
|
279
295
|
}
|
|
280
296
|
|
|
281
|
-
.panel-group-boxed[style*="--panel-border-style:"] figure {
|
|
297
|
+
.panel-group-boxed[style*="--panel-border-style:"] > .image-container > figure {
|
|
282
298
|
border-style: var(--panel-border-style);
|
|
283
299
|
}
|
|
284
300
|
|
|
285
|
-
.panel-group-boxed[style*="--panel-border-width:"] figure {
|
|
301
|
+
.panel-group-boxed[style*="--panel-border-width:"] > .image-container > figure {
|
|
286
302
|
border-width: var(--panel-border-width);
|
|
287
303
|
}
|
|
288
304
|
|
|
289
|
-
.panel-group-boxed[style*="--panel-background-color:"] figure {
|
|
305
|
+
.panel-group-boxed[style*="--panel-background-color:"] > .image-container > figure {
|
|
290
306
|
background-color: var(--panel-background-color);
|
|
291
307
|
}
|
|
292
308
|
|
|
293
|
-
.panel-group-boxed figcaption {
|
|
309
|
+
.panel-group-boxed > .image-container > figure > figcaption {
|
|
294
310
|
flex: 1;
|
|
295
311
|
margin-top: 0;
|
|
296
312
|
padding: 0.75rem;
|
|
297
313
|
font-size: 0.9rem;
|
|
298
314
|
}
|
|
299
315
|
|
|
300
|
-
.comic-
|
|
316
|
+
.comic-panel {
|
|
301
317
|
margin: 2rem 0;
|
|
302
318
|
padding: 1rem;
|
|
303
319
|
border: 2px solid #575752;
|
|
304
|
-
|
|
305
|
-
|
|
320
|
+
border-radius: var(--comic-panel-border-radius, 0);
|
|
321
|
+
background: var(--comic-panel-background, #fff);
|
|
322
|
+
box-shadow: var(--comic-panel-shadow, 4px 4px 0 rgba(70, 70, 66, 0.14));
|
|
306
323
|
}
|
|
307
324
|
|
|
308
|
-
.comic-
|
|
325
|
+
.comic-panel[style*="--comic-panel-border-color:"] {
|
|
326
|
+
border-color: var(--comic-panel-border-color);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.comic-panel[style*="--comic-panel-border-style:"] {
|
|
330
|
+
border-style: var(--comic-panel-border-style);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.comic-panel[style*="--comic-panel-border-width:"] {
|
|
334
|
+
border-width: var(--comic-panel-border-width);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.comic-panel[style*="--comic-panel-background-color:"] {
|
|
338
|
+
background-color: var(--comic-panel-background-color);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.comic-panel > :first-child {
|
|
309
342
|
margin-top: 0;
|
|
310
343
|
}
|
|
311
344
|
|
|
312
|
-
.comic-
|
|
345
|
+
.comic-panel > :last-child {
|
|
313
346
|
margin-bottom: 0;
|
|
314
347
|
}
|
|
315
348
|
|
|
316
|
-
.comic-
|
|
317
|
-
.comic-
|
|
349
|
+
.comic-panel .image-container,
|
|
350
|
+
.comic-panel figure {
|
|
318
351
|
display: block;
|
|
319
352
|
width: 100%;
|
|
320
353
|
}
|
|
321
354
|
|
|
322
|
-
.comic-
|
|
355
|
+
.comic-panel figure {
|
|
323
356
|
padding: 0;
|
|
324
357
|
}
|
|
325
358
|
|
|
326
|
-
.comic-
|
|
359
|
+
.comic-panel .image-dialogue {
|
|
327
360
|
margin-bottom: 1rem;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.comic-panel-divider .image-dialogue {
|
|
328
364
|
padding-bottom: 1rem;
|
|
329
365
|
border-bottom: 1px solid #575752;
|
|
330
366
|
}
|
|
@@ -338,13 +374,21 @@
|
|
|
338
374
|
width: 100% !important;
|
|
339
375
|
}
|
|
340
376
|
|
|
341
|
-
.comic-
|
|
377
|
+
.comic-panel {
|
|
342
378
|
margin: 1.5rem 0;
|
|
343
379
|
padding: 0.75rem;
|
|
344
380
|
border-width: 1px;
|
|
345
381
|
box-shadow: 2px 2px 0 rgba(70, 70, 66, 0.14);
|
|
346
382
|
}
|
|
347
383
|
}
|
|
384
|
+
|
|
385
|
+
.comic-panel[style*="--comic-panel-border:"] {
|
|
386
|
+
border: var(--comic-panel-border);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.comic-panel[style*="--comic-panel-shadow:"] {
|
|
390
|
+
box-shadow: var(--comic-panel-shadow);
|
|
391
|
+
}
|
|
348
392
|
|
|
349
393
|
@media screen and (min-width: 600px) {
|
|
350
394
|
figure {
|