tsondb 0.7.12 → 0.8.0
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/dist/src/shared/utils/markdown.d.ts +52 -14
- package/dist/src/shared/utils/markdown.js +225 -25
- package/dist/src/web/utils/BlockMarkdown.d.ts +2 -1
- package/dist/src/web/utils/BlockMarkdown.js +20 -8
- package/dist/src/web/utils/BlockMarkdownHighlighting.js +9 -3
- package/dist/src/web/utils/InlineMarkdown.js +5 -0
- package/package.json +1 -1
- package/public/css/styles.css +13 -0
|
@@ -24,45 +24,83 @@ type AttributedStringMarkdownNode = {
|
|
|
24
24
|
attributes: Record<string, string | number | boolean>;
|
|
25
25
|
content: InlineMarkdownNode[];
|
|
26
26
|
};
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
type FootnoteRefInlineNode = {
|
|
28
|
+
kind: "footnoteRef";
|
|
29
|
+
label: string;
|
|
30
|
+
};
|
|
31
|
+
export type InlineMarkdownNode = BoldMarkdownNode | ItalicMarkdownNode | CodeMarkdownNode | LinkMarkdownNode | AttributedStringMarkdownNode | TextNode | FootnoteRefInlineNode;
|
|
32
|
+
export declare const checkTableRowsAreSections: (rows: TableRowBlockNode[] | TableSectionBlockNode[]) => rows is TableSectionBlockNode[];
|
|
33
|
+
export declare const syntaxNodeToString: (node: BlockSyntaxMarkdownNode) => string;
|
|
34
|
+
export type ParagraphBlockNode = {
|
|
29
35
|
kind: "paragraph";
|
|
30
36
|
content: InlineMarkdownNode[];
|
|
31
37
|
};
|
|
32
|
-
type HeadingBlockNode = {
|
|
38
|
+
export type HeadingBlockNode = {
|
|
33
39
|
kind: "heading";
|
|
34
40
|
level: number;
|
|
35
41
|
content: InlineMarkdownNode[];
|
|
36
42
|
};
|
|
37
|
-
type ListBlockNode = {
|
|
43
|
+
export type ListBlockNode = {
|
|
38
44
|
kind: "list";
|
|
39
45
|
ordered: boolean;
|
|
40
46
|
content: ListItemNode[];
|
|
41
47
|
};
|
|
42
|
-
type ListItemNode = {
|
|
43
|
-
kind: "
|
|
48
|
+
export type ListItemNode = {
|
|
49
|
+
kind: "listItem";
|
|
44
50
|
content: InlineMarkdownNode[];
|
|
45
51
|
};
|
|
46
|
-
type TableBlockNode = {
|
|
52
|
+
export type TableBlockNode = {
|
|
47
53
|
kind: "table";
|
|
48
54
|
caption?: InlineMarkdownNode[];
|
|
49
|
-
header:
|
|
50
|
-
rows:
|
|
55
|
+
header: TableCellBlockNode[];
|
|
56
|
+
rows: TableRowBlockNode[] | TableSectionBlockNode[];
|
|
57
|
+
};
|
|
58
|
+
export type TableSectionBlockNode = {
|
|
59
|
+
kind: "tableSection";
|
|
60
|
+
header?: TableCellBlockNode[];
|
|
61
|
+
rows: TableRowBlockNode[];
|
|
62
|
+
};
|
|
63
|
+
export type TableRowBlockNode = {
|
|
64
|
+
kind: "tableRow";
|
|
65
|
+
cells: TableCellBlockNode[];
|
|
66
|
+
};
|
|
67
|
+
export type TableCellBlockNode = {
|
|
68
|
+
kind: "tableCell";
|
|
69
|
+
colSpan?: number;
|
|
70
|
+
content: InlineMarkdownNode[];
|
|
71
|
+
};
|
|
72
|
+
export type SectionBlockNode = {
|
|
73
|
+
kind: "section";
|
|
74
|
+
name?: string;
|
|
75
|
+
content: BlockMarkdownNode[];
|
|
51
76
|
};
|
|
52
|
-
export type
|
|
77
|
+
export type FootnoteBlockNode = {
|
|
78
|
+
kind: "footnote";
|
|
79
|
+
label: string;
|
|
80
|
+
content: BlockMarkdownNode[];
|
|
81
|
+
};
|
|
82
|
+
export type BlockMarkdownNode = ParagraphBlockNode | HeadingBlockNode | ListBlockNode | TableBlockNode | SectionBlockNode | FootnoteBlockNode;
|
|
53
83
|
type ListItemMarkerSyntaxNode = {
|
|
54
|
-
kind: "
|
|
84
|
+
kind: "listItemMarker";
|
|
55
85
|
content: string;
|
|
56
86
|
};
|
|
57
87
|
type TableMarkerSyntaxNode = {
|
|
58
|
-
kind: "
|
|
88
|
+
kind: "tableMarker";
|
|
59
89
|
content: string;
|
|
60
90
|
};
|
|
61
91
|
type HeadingMarkerSyntaxNode = {
|
|
62
|
-
kind: "
|
|
92
|
+
kind: "headingMarker";
|
|
93
|
+
content: string;
|
|
94
|
+
};
|
|
95
|
+
type SectionMarkerSyntaxNode = {
|
|
96
|
+
kind: "sectionMarker";
|
|
97
|
+
content: string;
|
|
98
|
+
};
|
|
99
|
+
type FootnoteMarkerSyntaxNode = {
|
|
100
|
+
kind: "footnoteMarker";
|
|
63
101
|
content: string;
|
|
64
102
|
};
|
|
65
|
-
type SyntaxNode = ListItemMarkerSyntaxNode | TableMarkerSyntaxNode | HeadingMarkerSyntaxNode;
|
|
103
|
+
type SyntaxNode = ListItemMarkerSyntaxNode | TableMarkerSyntaxNode | HeadingMarkerSyntaxNode | SectionMarkerSyntaxNode | FootnoteMarkerSyntaxNode;
|
|
66
104
|
export type BlockSyntaxMarkdownNode = InlineMarkdownNode | SyntaxNode;
|
|
67
105
|
export declare const parseBlockMarkdown: (text: string) => BlockMarkdownNode[];
|
|
68
106
|
export declare const parseBlockMarkdownForSyntaxHighlighting: (text: string) => BlockSyntaxMarkdownNode[];
|
|
@@ -149,6 +149,17 @@ const attributedRule = {
|
|
|
149
149
|
};
|
|
150
150
|
},
|
|
151
151
|
};
|
|
152
|
+
const footnoteRefRule = {
|
|
153
|
+
pattern: /(?<!\\)\[\^([a-zA-Z0-9*]+?)\]/,
|
|
154
|
+
map: ([_match, label = ""]) => ({
|
|
155
|
+
kind: "footnoteRef",
|
|
156
|
+
label,
|
|
157
|
+
}),
|
|
158
|
+
mapHighlighting: ([match]) => ({
|
|
159
|
+
kind: "footnoteRef",
|
|
160
|
+
label: match,
|
|
161
|
+
}),
|
|
162
|
+
};
|
|
152
163
|
const textNode = (content) => ({
|
|
153
164
|
kind: "text",
|
|
154
165
|
content: content,
|
|
@@ -173,6 +184,7 @@ const inlineRules = [
|
|
|
173
184
|
italicWithBoldRule,
|
|
174
185
|
boldRule,
|
|
175
186
|
italicRule,
|
|
187
|
+
footnoteRefRule,
|
|
176
188
|
textRule,
|
|
177
189
|
];
|
|
178
190
|
const parseForInlineRules = (rules, text, forSyntaxHighlighting) => {
|
|
@@ -203,19 +215,19 @@ const nodesForTrailingWhitespace = (text) => {
|
|
|
203
215
|
return trailingWhitespace.length === 0 ? [] : [textNode(trailingWhitespace)];
|
|
204
216
|
};
|
|
205
217
|
const listRule = {
|
|
206
|
-
pattern: /^((?:(?:\d+\.|[-*]) [^\n]+?)(?:\n(?:\d+\.|[-*]) [^\n]+?)*)(\n{2,}
|
|
218
|
+
pattern: /^((?:(?:\d+\.|[-*]) [^\n]+?)(?:\n(?:\d+\.|[-*]) [^\n]+?)*)(\n{2,}|\s*$)/,
|
|
207
219
|
map: result => ({
|
|
208
220
|
kind: "list",
|
|
209
221
|
ordered: /^\d+\. /.test(result[0]),
|
|
210
222
|
content: (result[1] ?? "").split("\n").map(item => ({
|
|
211
|
-
kind: "
|
|
223
|
+
kind: "listItem",
|
|
212
224
|
content: parseInlineMarkdown(item.replace(/^\d+\. |[-*] /, ""), false),
|
|
213
225
|
})),
|
|
214
226
|
}),
|
|
215
227
|
mapHighlighting: result => [
|
|
216
228
|
...(result[1] ?? "").split("\n").flatMap((item, index, array) => [
|
|
217
229
|
{
|
|
218
|
-
kind: "
|
|
230
|
+
kind: "listItemMarker",
|
|
219
231
|
content: /^(\d+\. |[-*] )/.exec(item)?.[1] ?? "",
|
|
220
232
|
},
|
|
221
233
|
...parseInlineMarkdown(item.replace(/^\d+\. |[-*] /, ""), true),
|
|
@@ -243,28 +255,111 @@ const headingRule = {
|
|
|
243
255
|
content: parseInlineMarkdown(result[3] ?? "", false),
|
|
244
256
|
}),
|
|
245
257
|
mapHighlighting: result => [
|
|
246
|
-
{ kind: "
|
|
258
|
+
{ kind: "headingMarker", content: (result[1] ?? "") + (result[2] ?? "") },
|
|
247
259
|
...parseInlineMarkdown(result[3] ?? "", true),
|
|
248
260
|
...nodesForTrailingWhitespace(result[4]),
|
|
249
261
|
],
|
|
250
262
|
};
|
|
251
|
-
const removeSurroundingPipes = (text) => text.replace(/^\|/, "").replace(/\|$/, "");
|
|
252
263
|
const tableMarker = (text) => ({
|
|
253
|
-
kind: "
|
|
264
|
+
kind: "tableMarker",
|
|
254
265
|
content: text,
|
|
255
266
|
});
|
|
267
|
+
const sectionSeparatorPattern = /^\|? *-{3,} *(?:\| *-{3,} *)+\|?$|^\|? *={3,} *(?:\| *={3,} *)+\|?$/;
|
|
268
|
+
const sectionWithHeaderSeparatorPattern = /^\|? *={3,} *(?:\| *={3,} *)+\|?$/;
|
|
269
|
+
export const checkTableRowsAreSections = (rows) => rows.every(row => row.kind === "tableSection");
|
|
270
|
+
const parseContentRow = (row) => row
|
|
271
|
+
.replace(/^\|/, "")
|
|
272
|
+
.split(/(\|+)/)
|
|
273
|
+
.reduce((acc, segment, index, arr) => {
|
|
274
|
+
if (index % 2 === 0 && segment.trim() !== "") {
|
|
275
|
+
const colSpan = arr[index + 1]?.length;
|
|
276
|
+
return [
|
|
277
|
+
...acc,
|
|
278
|
+
omitUndefinedKeys({
|
|
279
|
+
kind: "tableCell",
|
|
280
|
+
colSpan: colSpan !== undefined && colSpan > 1 ? colSpan : undefined,
|
|
281
|
+
content: parseInlineMarkdown(segment.trim(), false),
|
|
282
|
+
}),
|
|
283
|
+
];
|
|
284
|
+
}
|
|
285
|
+
return acc;
|
|
286
|
+
}, []);
|
|
287
|
+
const parseContentRowForSyntaxHighlighting = (row) => row.split(/(\|+)/).reduce((acc, segment, index) => {
|
|
288
|
+
if (index % 2 === 0) {
|
|
289
|
+
return [...acc, ...parseInlineMarkdown(segment, true)];
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
return [...acc, tableMarker(segment)];
|
|
293
|
+
}
|
|
294
|
+
}, []);
|
|
256
295
|
const tableRule = {
|
|
257
|
-
pattern: /^(?:(\|#)(.+?)(#\|)\n)?(\|)?(.+?(?:(?<!\\)\|.+?)+)((?<!\\)\|)?\n((?:\| *)?(?:-{3,}|:-{2,}|-{2,}:|:-+:)(?: *\| *(?:-{3,}|:-{2,}|-{2,}:|:-+:))*(?: *\|)?)((?:\n\|?.+?(?:(?<!\\)
|
|
296
|
+
pattern: /^(?:(\|#)(.+?)(#\|)\n)?(\|)?(.+?(?:(?<!\\)\|.+?)+)((?<!\\)\|)?\n((?:\| *)?(?:-{3,}|:-{2,}|-{2,}:|:-+:)(?: *\| *(?:-{3,}|:-{2,}|-{2,}:|:-+:))*(?: *\|)?)((?:\n\|?.+?(?:(?<!\\)\|+.+?)*(?:(?<!\\)\|+)?)+)(\n{2,}|$)/,
|
|
258
297
|
map: ([_res, _captionMarkerStart, caption, _captionMarkerEnd, _headerMarkerStart, headers, _headerMarkerEnd, _bodySeparators, body, _trailingWhitespace,]) => omitUndefinedKeys({
|
|
259
298
|
kind: "table",
|
|
260
299
|
caption: caption !== undefined ? parseInlineMarkdown(caption.trim(), false) : undefined,
|
|
261
|
-
header: headers
|
|
300
|
+
header: headers ? parseContentRow(headers) : [],
|
|
262
301
|
rows: body
|
|
263
302
|
?.split("\n")
|
|
264
|
-
.slice(1)
|
|
265
|
-
.
|
|
266
|
-
.
|
|
267
|
-
|
|
303
|
+
.slice(1) // leading newline due to regex
|
|
304
|
+
.reduce((accRows, row) => {
|
|
305
|
+
if (sectionSeparatorPattern.test(row)) {
|
|
306
|
+
const hasHeader = sectionWithHeaderSeparatorPattern.test(row);
|
|
307
|
+
const newSection = omitUndefinedKeys({
|
|
308
|
+
kind: "tableSection",
|
|
309
|
+
header: hasHeader ? [] : undefined,
|
|
310
|
+
rows: [],
|
|
311
|
+
});
|
|
312
|
+
if (accRows[0] === undefined) {
|
|
313
|
+
return [newSection];
|
|
314
|
+
}
|
|
315
|
+
if (checkTableRowsAreSections(accRows)) {
|
|
316
|
+
return [...accRows, newSection];
|
|
317
|
+
}
|
|
318
|
+
return [{ kind: "tableSection", rows: accRows }, newSection];
|
|
319
|
+
}
|
|
320
|
+
const lastRow = accRows[accRows.length - 1];
|
|
321
|
+
const rowContent = parseContentRow(row);
|
|
322
|
+
if (lastRow === undefined) {
|
|
323
|
+
return [
|
|
324
|
+
{
|
|
325
|
+
kind: "tableRow",
|
|
326
|
+
cells: rowContent,
|
|
327
|
+
},
|
|
328
|
+
];
|
|
329
|
+
}
|
|
330
|
+
if (checkTableRowsAreSections(accRows)) {
|
|
331
|
+
const lastSection = lastRow;
|
|
332
|
+
if (lastSection.header !== undefined && lastSection.header.length === 0) {
|
|
333
|
+
return [
|
|
334
|
+
...accRows.slice(0, -1),
|
|
335
|
+
{
|
|
336
|
+
...lastSection,
|
|
337
|
+
header: rowContent,
|
|
338
|
+
},
|
|
339
|
+
];
|
|
340
|
+
}
|
|
341
|
+
return [
|
|
342
|
+
...accRows.slice(0, -1),
|
|
343
|
+
{
|
|
344
|
+
...lastSection,
|
|
345
|
+
rows: [
|
|
346
|
+
...lastSection.rows,
|
|
347
|
+
{
|
|
348
|
+
kind: "tableRow",
|
|
349
|
+
cells: rowContent,
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
},
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
return [
|
|
356
|
+
...accRows,
|
|
357
|
+
{
|
|
358
|
+
kind: "tableRow",
|
|
359
|
+
cells: rowContent,
|
|
360
|
+
},
|
|
361
|
+
];
|
|
362
|
+
}, []) ?? [],
|
|
268
363
|
}),
|
|
269
364
|
mapHighlighting: ([_res, captionMarkerStart, caption, captionMarkerEnd, headerMarkerStart, headers, headerMarkerEnd, bodySeparators, body, trailingWhitespace,]) => [
|
|
270
365
|
...(caption !== undefined
|
|
@@ -281,22 +376,121 @@ const tableRule = {
|
|
|
281
376
|
.flatMap((th, i) => i === 0
|
|
282
377
|
? parseInlineMarkdown(th, true)
|
|
283
378
|
: [tableMarker("|"), ...parseInlineMarkdown(th, true)]) ?? []),
|
|
284
|
-
tableMarker(
|
|
379
|
+
tableMarker(headerMarkerEnd ?? ""),
|
|
380
|
+
textNode("\n"),
|
|
381
|
+
tableMarker(bodySeparators ?? ""),
|
|
285
382
|
...(body
|
|
286
383
|
?.split("\n")
|
|
287
384
|
.slice(1)
|
|
288
385
|
.flatMap((tr) => [
|
|
289
386
|
textNode("\n"),
|
|
290
|
-
...tr
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
? parseInlineMarkdown(tc, true)
|
|
294
|
-
: [tableMarker("|"), ...parseInlineMarkdown(tc, true)]),
|
|
387
|
+
...(sectionSeparatorPattern.test(tr)
|
|
388
|
+
? [tableMarker(tr)]
|
|
389
|
+
: parseContentRowForSyntaxHighlighting(tr)),
|
|
295
390
|
]) ?? []),
|
|
296
391
|
...nodesForTrailingWhitespace(trailingWhitespace),
|
|
297
392
|
],
|
|
298
393
|
};
|
|
299
|
-
const
|
|
394
|
+
const sectionRule = {
|
|
395
|
+
pattern: /^::: (\w+)?(\n+)(.+?)\n:::(\n{2,}|\s*$)/s,
|
|
396
|
+
map: ([_match, name, _leadingContentWhitespace, content, _trailingWhitespace,]) => ({
|
|
397
|
+
kind: "section",
|
|
398
|
+
name: name ?? undefined,
|
|
399
|
+
content: parseBlockMarkdown(content ?? ""),
|
|
400
|
+
}),
|
|
401
|
+
mapHighlighting: ([_match, name, leadingContentWhitespace = "", content, trailingWhitespace,]) => [
|
|
402
|
+
{ kind: "sectionMarker", content: `::: ${name ?? ""}` },
|
|
403
|
+
textNode(leadingContentWhitespace),
|
|
404
|
+
...parseBlockMarkdownForSyntaxHighlighting(content ?? ""),
|
|
405
|
+
textNode("\n"),
|
|
406
|
+
{ kind: "sectionMarker", content: ":::" },
|
|
407
|
+
...nodesForTrailingWhitespace(trailingWhitespace),
|
|
408
|
+
],
|
|
409
|
+
};
|
|
410
|
+
const removeIndentation = (text) => text
|
|
411
|
+
.split("\n")
|
|
412
|
+
.map(line => line.replace(/^ {2}/, ""))
|
|
413
|
+
.join("\n");
|
|
414
|
+
export const syntaxNodeToString = (node) => {
|
|
415
|
+
switch (node.kind) {
|
|
416
|
+
case "bold":
|
|
417
|
+
case "italic":
|
|
418
|
+
case "link":
|
|
419
|
+
case "attributed":
|
|
420
|
+
return node.content.map(syntaxNodeToString).join("");
|
|
421
|
+
case "text":
|
|
422
|
+
case "code":
|
|
423
|
+
case "listItemMarker":
|
|
424
|
+
case "tableMarker":
|
|
425
|
+
case "headingMarker":
|
|
426
|
+
case "sectionMarker":
|
|
427
|
+
case "footnoteMarker":
|
|
428
|
+
return node.content;
|
|
429
|
+
case "footnoteRef":
|
|
430
|
+
return node.label;
|
|
431
|
+
default:
|
|
432
|
+
return assertExhaustive(node);
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
const addIndentationToSyntax = (nodes, nextUpperNode) => nodes.reduce((accNodes, currentNode, index) => {
|
|
436
|
+
switch (currentNode.kind) {
|
|
437
|
+
case "bold":
|
|
438
|
+
case "italic":
|
|
439
|
+
case "link":
|
|
440
|
+
case "attributed":
|
|
441
|
+
return [
|
|
442
|
+
...accNodes,
|
|
443
|
+
{
|
|
444
|
+
...currentNode,
|
|
445
|
+
content: addIndentationToSyntax(currentNode.content, nodes[index + 1] ?? nextUpperNode),
|
|
446
|
+
},
|
|
447
|
+
];
|
|
448
|
+
case "text":
|
|
449
|
+
case "code":
|
|
450
|
+
case "listItemMarker":
|
|
451
|
+
case "tableMarker":
|
|
452
|
+
case "headingMarker":
|
|
453
|
+
case "sectionMarker":
|
|
454
|
+
case "footnoteMarker": {
|
|
455
|
+
const nextNode = nodes[index + 1] ?? nextUpperNode;
|
|
456
|
+
const currentContent = currentNode.content.endsWith("\n") &&
|
|
457
|
+
nextNode &&
|
|
458
|
+
/^[^\n]*?\S+?/.test(syntaxNodeToString(nextNode))
|
|
459
|
+
? currentNode.content + " "
|
|
460
|
+
: currentNode.content;
|
|
461
|
+
return [
|
|
462
|
+
...accNodes,
|
|
463
|
+
{ ...currentNode, content: currentContent.replace(/\n([^\n]*?\S+?)/g, "\n $1") },
|
|
464
|
+
];
|
|
465
|
+
}
|
|
466
|
+
case "footnoteRef":
|
|
467
|
+
return [...accNodes, currentNode];
|
|
468
|
+
default:
|
|
469
|
+
return assertExhaustive(currentNode);
|
|
470
|
+
}
|
|
471
|
+
}, []);
|
|
472
|
+
const footnoteRule = {
|
|
473
|
+
pattern: /^\[\^([a-zA-Z0-9*]+?)\]: (.+?(?:\n(?: {2}.+)?)*)(\n{2,}|\s*$)/,
|
|
474
|
+
map: ([_match, label = "", content = "", _trailingWhitespace]) => ({
|
|
475
|
+
kind: "footnote",
|
|
476
|
+
label: label,
|
|
477
|
+
content: parseBlockMarkdown(removeIndentation(content)),
|
|
478
|
+
}),
|
|
479
|
+
mapHighlighting: ([_match, label = "", content = "", trailingWhitespace,]) => [
|
|
480
|
+
{ kind: "footnoteMarker", content: `[^${label}]:` },
|
|
481
|
+
textNode(" "),
|
|
482
|
+
...addIndentationToSyntax(parseBlockMarkdownForSyntaxHighlighting(removeIndentation(content))),
|
|
483
|
+
...nodesForTrailingWhitespace(trailingWhitespace),
|
|
484
|
+
],
|
|
485
|
+
};
|
|
486
|
+
const blockRules = [
|
|
487
|
+
sectionRule,
|
|
488
|
+
headingRule,
|
|
489
|
+
footnoteRule,
|
|
490
|
+
tableRule,
|
|
491
|
+
listRule,
|
|
492
|
+
paragraphRule,
|
|
493
|
+
];
|
|
300
494
|
const parseActiveBlockRule = (rule, res) => [
|
|
301
495
|
rule.map(res),
|
|
302
496
|
];
|
|
@@ -341,9 +535,12 @@ const reduceSyntaxNode = (node) => {
|
|
|
341
535
|
case "link":
|
|
342
536
|
case "attributed":
|
|
343
537
|
case "text":
|
|
344
|
-
case "
|
|
345
|
-
case "
|
|
346
|
-
case "
|
|
538
|
+
case "listItemMarker":
|
|
539
|
+
case "tableMarker":
|
|
540
|
+
case "headingMarker":
|
|
541
|
+
case "sectionMarker":
|
|
542
|
+
case "footnoteMarker":
|
|
543
|
+
case "footnoteRef":
|
|
347
544
|
return node;
|
|
348
545
|
default:
|
|
349
546
|
return assertExhaustive(node);
|
|
@@ -354,9 +551,12 @@ const syntaxNodeMergeRules = {
|
|
|
354
551
|
italic: (a, b) => ({ ...a, content: [...a.content, ...b.content] }),
|
|
355
552
|
code: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
356
553
|
text: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
554
|
+
listItemMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
555
|
+
tableMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
556
|
+
headingMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
557
|
+
sectionMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
558
|
+
footnoteMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
559
|
+
footnoteRef: null,
|
|
360
560
|
link: null,
|
|
361
561
|
attributed: null,
|
|
362
562
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { FunctionalComponent } from "preact";
|
|
2
|
-
import type
|
|
2
|
+
import { type BlockMarkdownNode } from "../../shared/utils/markdown.ts";
|
|
3
3
|
type Props = {
|
|
4
4
|
node: BlockMarkdownNode;
|
|
5
5
|
outerHeadingLevel?: number;
|
|
6
|
+
insertBefore?: preact.ComponentChildren;
|
|
6
7
|
};
|
|
7
8
|
export declare const BlockMarkdown: FunctionalComponent<Props>;
|
|
8
9
|
export {};
|
|
@@ -1,22 +1,34 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
|
+
import { checkTableRowsAreSections, } from "../../shared/utils/markdown.js";
|
|
3
|
+
import { assertExhaustive } from "../../shared/utils/typeSafety.js";
|
|
2
4
|
import { InlineMarkdown } from "./InlineMarkdown.js";
|
|
3
|
-
export const BlockMarkdown = ({ node, outerHeadingLevel = 0 }) => {
|
|
5
|
+
export const BlockMarkdown = ({ node, outerHeadingLevel = 0, insertBefore, }) => {
|
|
4
6
|
switch (node.kind) {
|
|
5
7
|
case "paragraph":
|
|
6
|
-
return (
|
|
8
|
+
return (_jsxs("p", { children: [insertBefore, node.content.map((inline, ii) => (_jsx(InlineMarkdown, { node: inline }, ii)))] }));
|
|
7
9
|
case "heading":
|
|
8
10
|
const Tag = `h${(node.level + outerHeadingLevel).toString()}`;
|
|
9
|
-
return (
|
|
11
|
+
return (_jsxs(Tag, { children: [insertBefore, node.content.map((inline, ii) => (_jsx(InlineMarkdown, { node: inline }, ii)))] }));
|
|
10
12
|
case "list":
|
|
11
13
|
if (node.ordered) {
|
|
12
|
-
return (_jsx("ol", { children: node.content.map((item, ii) => (_jsx("li", { children: item.content.map((inline, iii) => (_jsx(InlineMarkdown, { node: inline }, iii))) }, ii))) }));
|
|
14
|
+
return (_jsxs(_Fragment, { children: [insertBefore, _jsx("ol", { children: node.content.map((item, ii) => (_jsx("li", { children: item.content.map((inline, iii) => (_jsx(InlineMarkdown, { node: inline }, iii))) }, ii))) })] }));
|
|
13
15
|
}
|
|
14
16
|
else {
|
|
15
|
-
return (_jsx("ul", { children: node.content.map((item, ii) => (_jsx("li", { children: item.content.map((inline, iii) => (_jsx(InlineMarkdown, { node: inline }, iii))) }, ii))) }));
|
|
17
|
+
return (_jsxs(_Fragment, { children: [insertBefore, _jsx("ul", { children: node.content.map((item, ii) => (_jsx("li", { children: item.content.map((inline, iii) => (_jsx(InlineMarkdown, { node: inline }, iii))) }, ii))) })] }));
|
|
16
18
|
}
|
|
17
19
|
case "table":
|
|
18
|
-
return (_jsxs("table", { children: [node.caption !== undefined && (_jsx("caption", { children: node.caption.map((inline, ci) => (_jsx(InlineMarkdown, { node: inline }, ci))) })), _jsx("thead", { children: _jsx(
|
|
20
|
+
return (_jsxs(_Fragment, { children: [insertBefore, _jsxs("table", { children: [node.caption !== undefined && (_jsx("caption", { children: node.caption.map((inline, ci) => (_jsx(InlineMarkdown, { node: inline }, ci))) })), _jsx("thead", { children: _jsx(TableRow, { cells: node.header, cellType: "th" }) }), checkTableRowsAreSections(node.rows) ? (node.rows.map((section, si) => (_jsxs("tbody", { children: [section.header && _jsx(TableRow, { cells: section.header, cellType: "th" }), section.rows.map((row, ri) => (_jsx(TableRow, { cells: row.cells }, ri)))] }, si)))) : (_jsx("tbody", { children: node.rows.map((row, ri) => (_jsx(TableRow, { cells: row.cells }, ri))) }))] })] }));
|
|
21
|
+
case "section":
|
|
22
|
+
return (_jsxs("div", { class: node.name, children: [insertBefore, node.content.map((childNode, i) => (_jsx(BlockMarkdown, { node: childNode }, i)))] }));
|
|
23
|
+
case "footnote": {
|
|
24
|
+
const label = (_jsxs(_Fragment, { children: [_jsxs("span", { class: "footnote-label", children: [node.label, /^\*+$/.test(node.label) ? ")" : ":"] }), " "] }));
|
|
25
|
+
return (_jsxs("div", { role: "note", children: [insertBefore, node.content.map((n, i) => (_jsx(BlockMarkdown, { node: n, insertBefore: label }, i)))] }));
|
|
26
|
+
}
|
|
19
27
|
default:
|
|
20
|
-
return
|
|
28
|
+
return assertExhaustive(node);
|
|
21
29
|
}
|
|
22
30
|
};
|
|
31
|
+
const TableRow = ({ cells, cellType = "td", }) => {
|
|
32
|
+
const CellTag = cellType;
|
|
33
|
+
return (_jsx("tr", { children: cells.map((tc, ci) => (_jsx(CellTag, { scope: cellType === "th" && cells.length === 1 ? "colgroup" : undefined, colSpan: tc.colSpan, children: tc.content.map((inline, cii) => (_jsx(InlineMarkdown, { node: inline }, cii))) }, ci))) }));
|
|
34
|
+
};
|
|
@@ -2,12 +2,18 @@ import { jsx as _jsx } from "preact/jsx-runtime";
|
|
|
2
2
|
import { InlineMarkdown } from "./InlineMarkdown.js";
|
|
3
3
|
export const BlockMarkdownHighlighting = ({ node }) => {
|
|
4
4
|
switch (node.kind) {
|
|
5
|
-
case "
|
|
5
|
+
case "listItemMarker":
|
|
6
6
|
return _jsx("span", { class: "list-item-marker", children: node.content });
|
|
7
|
-
case "
|
|
7
|
+
case "tableMarker":
|
|
8
8
|
return _jsx("span", { class: "table-marker", children: node.content });
|
|
9
|
-
case "
|
|
9
|
+
case "headingMarker":
|
|
10
10
|
return _jsx("span", { class: "heading-marker", children: node.content });
|
|
11
|
+
case "sectionMarker":
|
|
12
|
+
return _jsx("span", { class: "section-marker", children: node.content });
|
|
13
|
+
case "footnoteMarker":
|
|
14
|
+
return _jsx("span", { class: "footnote-marker", children: node.content });
|
|
15
|
+
case "footnoteRef":
|
|
16
|
+
return _jsx("span", { class: "footnote-marker", children: node.label });
|
|
11
17
|
default:
|
|
12
18
|
return _jsx(InlineMarkdown, { node: node });
|
|
13
19
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { Fragment } from "preact";
|
|
3
|
+
import { assertExhaustive } from "../../shared/utils/typeSafety.js";
|
|
3
4
|
const emptyNode = { kind: "text", content: "" };
|
|
4
5
|
export const InlineMarkdown = ({ node }) => {
|
|
5
6
|
switch (node.kind) {
|
|
@@ -21,7 +22,11 @@ export const InlineMarkdown = ({ node }) => {
|
|
|
21
22
|
const trailingNodes = node.content.slice(attributesEnd);
|
|
22
23
|
return (_jsxs("span", { class: "attributed", ...Object.fromEntries(Object.entries(node.attributes).map(([k, v]) => [`data-${k}`, v.toString()])), children: [leadingNodes.map((inline, i) => (_jsx(InlineMarkdown, { node: inline }, i))), Array.from({ length: count }, (_, i) => (_jsxs(Fragment, { children: [_jsx("span", { class: "attributed__name", children: _jsx(InlineMarkdown, { node: attributes[i * 4] ?? emptyNode }) }), _jsx("span", { class: "attributed__separator", children: _jsx(InlineMarkdown, { node: attributes[i * 4 + 1] ?? emptyNode }) }), _jsx("span", { class: "attributed__value", children: _jsx(InlineMarkdown, { node: attributes[i * 4 + 2] ?? emptyNode }) }), i < count - 1 && (_jsx("span", { class: "attributed__separator", children: _jsx(InlineMarkdown, { node: attributes[i * 4 + 3] ?? emptyNode }) }))] }, `attr-${(i + 1).toString()}`))), trailingNodes.map((inline, i) => (_jsx(InlineMarkdown, { node: inline }, i)))] }));
|
|
23
24
|
}
|
|
25
|
+
case "footnoteRef":
|
|
26
|
+
return /^\*+$/.test(node.label) ? (_jsx("span", { class: "footnote-ref", children: node.label })) : (_jsx("sup", { class: "footnote-ref", children: node.label }));
|
|
24
27
|
case "text":
|
|
25
28
|
return node.content;
|
|
29
|
+
default:
|
|
30
|
+
return assertExhaustive(node);
|
|
26
31
|
}
|
|
27
32
|
};
|
package/package.json
CHANGED
package/public/css/styles.css
CHANGED
|
@@ -63,6 +63,8 @@
|
|
|
63
63
|
--color-syntax-table-marker: rgb(237, 58, 118);
|
|
64
64
|
--color-syntax-attributed: rgb(48, 81, 228);
|
|
65
65
|
--color-syntax-heading-marker: rgb(62, 189, 191);
|
|
66
|
+
--color-syntax-footnote: rgb(48, 126, 228);
|
|
67
|
+
--color-syntax-section: rgb(172, 147, 3);
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
@media (prefers-color-scheme: dark) {
|
|
@@ -105,6 +107,8 @@
|
|
|
105
107
|
--color-syntax-table-marker: rgb(244, 135, 171);
|
|
106
108
|
--color-syntax-attributed: rgb(137, 157, 244);
|
|
107
109
|
--color-syntax-heading-marker: rgb(140, 229, 231);
|
|
110
|
+
--color-syntax-footnote: rgb(157, 199, 255);
|
|
111
|
+
--color-syntax-section: rgb(247, 231, 135);
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
114
|
|
|
@@ -704,6 +708,7 @@ form > .field--container {
|
|
|
704
708
|
.editor--markdown input,
|
|
705
709
|
.editor--markdown .textarea-grow-wrap__mirror {
|
|
706
710
|
font-family: var(--font-mono);
|
|
711
|
+
line-height: 1.5;
|
|
707
712
|
}
|
|
708
713
|
|
|
709
714
|
.editor--markdown textarea {
|
|
@@ -732,6 +737,14 @@ form > .field--container {
|
|
|
732
737
|
color: var(--color-syntax-heading-marker);
|
|
733
738
|
}
|
|
734
739
|
|
|
740
|
+
.editor-highlighting .footnote-marker {
|
|
741
|
+
color: var(--color-syntax-footnote);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
.editor-highlighting .section-marker {
|
|
745
|
+
color: var(--color-syntax-section);
|
|
746
|
+
}
|
|
747
|
+
|
|
735
748
|
.preview .attributed {
|
|
736
749
|
text-decoration-line: underline;
|
|
737
750
|
text-decoration-style: dashed;
|