tsondb 0.7.12 → 0.8.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/dist/src/node/schema/types/Type.d.ts +18 -3
- package/dist/src/node/schema/types/Type.js +1 -1
- package/dist/src/node/utils/displayName.d.ts +2 -2
- package/dist/src/shared/utils/array.d.ts +7 -0
- package/dist/src/shared/utils/array.js +16 -0
- package/dist/src/shared/utils/markdown.d.ts +65 -14
- package/dist/src/shared/utils/markdown.js +280 -28
- package/dist/src/web/components/InstanceRouteSkeleton.js +7 -6
- package/dist/src/web/routes/CreateInstance.js +1 -0
- package/dist/src/web/routes/Instance.js +2 -1
- package/dist/src/web/utils/BlockMarkdown.d.ts +2 -1
- package/dist/src/web/utils/BlockMarkdown.js +22 -8
- package/dist/src/web/utils/BlockMarkdownHighlighting.js +11 -3
- package/dist/src/web/utils/InlineMarkdown.js +5 -0
- package/package.json +1 -1
- package/public/css/styles.css +14 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type Decl } from "../index.ts";
|
|
1
|
+
import { type Decl, type EnumDecl, type TypeAliasDecl, type TypeParameter } from "../index.ts";
|
|
2
2
|
import type { BaseNode } from "../Node.ts";
|
|
3
3
|
import type { ArrayType } from "./generic/ArrayType.ts";
|
|
4
|
-
import type { EnumType } from "./generic/EnumType.ts";
|
|
4
|
+
import type { EnumCaseDecl, EnumType } from "./generic/EnumType.ts";
|
|
5
5
|
import type { MemberDecl, ObjectType } from "./generic/ObjectType.ts";
|
|
6
6
|
import type { BooleanType } from "./primitives/BooleanType.ts";
|
|
7
7
|
import type { DateType } from "./primitives/DateType.ts";
|
|
@@ -17,9 +17,23 @@ export interface BaseType extends BaseNode {
|
|
|
17
17
|
}
|
|
18
18
|
export type Type = BooleanType | DateType | FloatType | IntegerType | StringType | ArrayType | ObjectType | TypeArgumentType | ReferenceIdentifierType | IncludeIdentifierType | NestedEntityMapType | EnumType | ChildEntitiesType;
|
|
19
19
|
export declare function walkTypeNodeTree(callbackFn: (type: Type, parentTypes: Type[], parentDecl: Decl) => void, type: Type, parentTypes: Type[], parentDecl: Decl): void;
|
|
20
|
+
type EnumCaseTypeAsType<Case extends string, T extends Type | null> = T extends object ? {
|
|
21
|
+
kind: Case;
|
|
22
|
+
} & {
|
|
23
|
+
[AV in Case]: AsType<T>;
|
|
24
|
+
} : {
|
|
25
|
+
kind: Case;
|
|
26
|
+
};
|
|
27
|
+
export type AsDeepType<T extends Type> = T extends ArrayType<infer I> ? AsType<I>[] : T extends ObjectType<infer P> ? {
|
|
28
|
+
[K in keyof P]: P[K] extends MemberDecl<Type, true> ? AsType<P[K]["type"]> : AsType<P[K]["type"]> | undefined;
|
|
29
|
+
} : T extends BooleanType ? boolean : T extends DateType ? Date : T extends FloatType ? number : T extends IntegerType ? number : T extends StringType ? string : T extends TypeArgumentType ? unknown : T extends IncludeIdentifierType<TypeParameter[], infer Decl> ? Decl extends TypeAliasDecl<string, infer TA> ? AsType<TA> : Decl extends EnumDecl<string, infer EC> ? AsType<EnumType<EC>> : unknown : T extends NestedEntityMapType ? unknown : T extends ReferenceIdentifierType ? string : T extends ChildEntitiesType ? string[] : T extends EnumType<infer EC> ? EC extends Record<string, EnumCaseDecl> ? {
|
|
30
|
+
[Case in keyof EC]: EnumCaseTypeAsType<Case & string, EC[Case]["type"]>;
|
|
31
|
+
}[keyof EC] : never : never;
|
|
20
32
|
export type AsType<T extends Type> = T extends ArrayType<infer I> ? AsType<I>[] : T extends ObjectType<infer P> ? {
|
|
21
33
|
[K in keyof P]: P[K] extends MemberDecl<Type, true> ? AsType<P[K]["type"]> : AsType<P[K]["type"]> | undefined;
|
|
22
|
-
} : T extends BooleanType ? boolean : T extends DateType ? Date : T extends FloatType ? number : T extends IntegerType ? number : T extends StringType ? string : T extends TypeArgumentType ? unknown : T extends IncludeIdentifierType ? unknown : T extends NestedEntityMapType ? unknown : T extends ReferenceIdentifierType ? string : T extends ChildEntitiesType ? string[] :
|
|
34
|
+
} : T extends BooleanType ? boolean : T extends DateType ? Date : T extends FloatType ? number : T extends IntegerType ? number : T extends StringType ? string : T extends TypeArgumentType ? unknown : T extends IncludeIdentifierType ? unknown : T extends NestedEntityMapType ? unknown : T extends ReferenceIdentifierType ? string : T extends ChildEntitiesType ? string[] : T extends EnumType<infer EC> ? EC extends Record<string, EnumCaseDecl> ? {
|
|
35
|
+
[Case in keyof EC]: EnumCaseTypeAsType<Case & string, EC[Case]["type"]>;
|
|
36
|
+
}[keyof EC] : never : never;
|
|
23
37
|
export type AsNode<T> = T extends (infer I)[] ? ArrayType<AsNode<I>> : T extends Record<string, unknown> ? ObjectType<{
|
|
24
38
|
[K in keyof T]: T[K] extends MemberDecl ? T[K] : T extends null | undefined ? MemberDecl<AsNode<NonNullable<T[K]>>, false> : MemberDecl<AsNode<T[K]>, true>;
|
|
25
39
|
}> : T extends string ? StringType : T extends number ? FloatType : T extends boolean ? BooleanType : T extends Date ? DateType : never;
|
|
@@ -31,3 +45,4 @@ export declare const findTypeAtPath: (type: Type, path: string[], options?: {
|
|
|
31
45
|
*/
|
|
32
46
|
export type StructureFormatter<T extends Type> = (type: T, value: unknown) => unknown;
|
|
33
47
|
export declare const formatValue: StructureFormatter<Type>;
|
|
48
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertExhaustive } from "../../../shared/utils/typeSafety.js";
|
|
2
|
-
import { isTypeAliasDecl } from "../index.js";
|
|
2
|
+
import { isTypeAliasDecl, } from "../index.js";
|
|
3
3
|
import { NodeKind } from "../Node.js";
|
|
4
4
|
import { formatArrayValue, isArrayType } from "./generic/ArrayType.js";
|
|
5
5
|
import { formatEnumType } from "./generic/EnumType.js";
|
|
@@ -2,13 +2,13 @@ import type { GetInstanceById } from "../../node/server/index.ts";
|
|
|
2
2
|
import { type DisplayNameResult } from "../../shared/utils/displayName.ts";
|
|
3
3
|
import type { InstanceContainer } from "../../shared/utils/instances.ts";
|
|
4
4
|
import { type EntityDecl } from "../schema/declarations/EntityDecl.ts";
|
|
5
|
-
import type {
|
|
5
|
+
import type { AsDeepType, Type } from "../schema/types/Type.ts";
|
|
6
6
|
export type GetChildInstancesForInstanceId = (parentEntityName: string, parentId: string, childEntityName: string) => {
|
|
7
7
|
id: string;
|
|
8
8
|
content: unknown;
|
|
9
9
|
}[];
|
|
10
10
|
export type DisplayNameCustomizer<T extends Type> = (params: {
|
|
11
|
-
instance:
|
|
11
|
+
instance: AsDeepType<T>;
|
|
12
12
|
instanceId: string;
|
|
13
13
|
instanceDisplayName: string;
|
|
14
14
|
instanceDisplayNameLocaleId: string | undefined;
|
|
@@ -22,3 +22,10 @@ export declare const unique: <T>(arr: T[], equalityCheck?: (a: T, b: T) => boole
|
|
|
22
22
|
* Moves an element from one position to another within the array.
|
|
23
23
|
*/
|
|
24
24
|
export declare const reorder: <T>(arr: T[], sourceIndex: number, targetIndex: number) => T[];
|
|
25
|
+
/**
|
|
26
|
+
* Splits an array into chunks of a specified size.
|
|
27
|
+
* @param arr The array to be chunked.
|
|
28
|
+
* @param size The size of each chunk.
|
|
29
|
+
* @returns An array of chunks, where each chunk is an array of elements. The last chunk may be smaller than the specified size if there are not enough elements left.
|
|
30
|
+
*/
|
|
31
|
+
export declare const chunk: <T>(arr: T[], size: number) => T[][];
|
|
@@ -59,3 +59,19 @@ export const reorder = (arr, sourceIndex, targetIndex) => {
|
|
|
59
59
|
...arr.slice(sourceIndex + 1),
|
|
60
60
|
];
|
|
61
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* Splits an array into chunks of a specified size.
|
|
64
|
+
* @param arr The array to be chunked.
|
|
65
|
+
* @param size The size of each chunk.
|
|
66
|
+
* @returns An array of chunks, where each chunk is an array of elements. The last chunk may be smaller than the specified size if there are not enough elements left.
|
|
67
|
+
*/
|
|
68
|
+
export const chunk = (arr, size) => {
|
|
69
|
+
if (size <= 0) {
|
|
70
|
+
throw new RangeError(`size must be a positive integer, got ${size.toString()}`);
|
|
71
|
+
}
|
|
72
|
+
return arr.reduce((chunks, item, index) => {
|
|
73
|
+
const chunkIndex = Math.floor(index / size);
|
|
74
|
+
(chunks[chunkIndex] ??= []).push(item);
|
|
75
|
+
return chunks;
|
|
76
|
+
}, []);
|
|
77
|
+
};
|
|
@@ -24,45 +24,96 @@ 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: "container";
|
|
74
|
+
name?: string;
|
|
75
|
+
content: BlockMarkdownNode[];
|
|
76
|
+
};
|
|
77
|
+
export type FootnoteBlockNode = {
|
|
78
|
+
kind: "footnote";
|
|
79
|
+
label: string;
|
|
80
|
+
content: BlockMarkdownNode[];
|
|
81
|
+
};
|
|
82
|
+
export type DefinitionListBlockNode = {
|
|
83
|
+
kind: "definitionList";
|
|
84
|
+
content: DefinitionListItemBlockNode[];
|
|
51
85
|
};
|
|
52
|
-
export type
|
|
86
|
+
export type DefinitionListItemBlockNode = {
|
|
87
|
+
kind: "definitionListItem";
|
|
88
|
+
terms: InlineMarkdownNode[][];
|
|
89
|
+
definitions: BlockMarkdownNode[][];
|
|
90
|
+
};
|
|
91
|
+
export type BlockMarkdownNode = ParagraphBlockNode | HeadingBlockNode | ListBlockNode | TableBlockNode | SectionBlockNode | FootnoteBlockNode | DefinitionListBlockNode;
|
|
53
92
|
type ListItemMarkerSyntaxNode = {
|
|
54
|
-
kind: "
|
|
93
|
+
kind: "listItemMarker";
|
|
55
94
|
content: string;
|
|
56
95
|
};
|
|
57
96
|
type TableMarkerSyntaxNode = {
|
|
58
|
-
kind: "
|
|
97
|
+
kind: "tableMarker";
|
|
59
98
|
content: string;
|
|
60
99
|
};
|
|
61
100
|
type HeadingMarkerSyntaxNode = {
|
|
62
|
-
kind: "
|
|
101
|
+
kind: "headingMarker";
|
|
102
|
+
content: string;
|
|
103
|
+
};
|
|
104
|
+
type SectionMarkerSyntaxNode = {
|
|
105
|
+
kind: "sectionMarker";
|
|
106
|
+
content: string;
|
|
107
|
+
};
|
|
108
|
+
type FootnoteMarkerSyntaxNode = {
|
|
109
|
+
kind: "footnoteMarker";
|
|
110
|
+
content: string;
|
|
111
|
+
};
|
|
112
|
+
type DefinitionMarkerSyntaxNode = {
|
|
113
|
+
kind: "definitionMarker";
|
|
63
114
|
content: string;
|
|
64
115
|
};
|
|
65
|
-
type SyntaxNode = ListItemMarkerSyntaxNode | TableMarkerSyntaxNode | HeadingMarkerSyntaxNode;
|
|
116
|
+
type SyntaxNode = ListItemMarkerSyntaxNode | TableMarkerSyntaxNode | HeadingMarkerSyntaxNode | SectionMarkerSyntaxNode | FootnoteMarkerSyntaxNode | DefinitionMarkerSyntaxNode;
|
|
66
117
|
export type BlockSyntaxMarkdownNode = InlineMarkdownNode | SyntaxNode;
|
|
67
118
|
export declare const parseBlockMarkdown: (text: string) => BlockMarkdownNode[];
|
|
68
119
|
export declare const parseBlockMarkdownForSyntaxHighlighting: (text: string) => BlockSyntaxMarkdownNode[];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { chunk } from "./array.js";
|
|
1
2
|
import { omitUndefinedKeys } from "./object.js";
|
|
2
3
|
import { assertExhaustive } from "./typeSafety.js";
|
|
3
4
|
const codeRule = {
|
|
@@ -12,7 +13,7 @@ const codeRule = {
|
|
|
12
13
|
}),
|
|
13
14
|
};
|
|
14
15
|
const boldWithItalicRule = {
|
|
15
|
-
pattern: /(
|
|
16
|
+
pattern: /(?<!\\|\*\*.*)\*\*(([^\\*]*)?\*(?!\*).*?[^\\*]\*.*?)(?<!\\)\*\*/,
|
|
16
17
|
map: (result, parseInside) => ({
|
|
17
18
|
kind: "bold",
|
|
18
19
|
content: parseInside(result[1] ?? ""),
|
|
@@ -23,7 +24,7 @@ const boldWithItalicRule = {
|
|
|
23
24
|
}),
|
|
24
25
|
};
|
|
25
26
|
const italicWithBoldRule = {
|
|
26
|
-
pattern: /(?<![\\*])\*(?=\*\*|[^*])(
|
|
27
|
+
pattern: /(?<![\\*]|[\\*]\*.*)\*(?=\*\*|[^*])([^*]*?\*\*[^*]*?\*\*[^*]*?)(?<=[^\\*]|[^\\]\*\*)\*(?!\*)/,
|
|
27
28
|
map: (result, parseInside) => ({
|
|
28
29
|
kind: "italic",
|
|
29
30
|
content: parseInside(result[1] ?? ""),
|
|
@@ -149,6 +150,17 @@ const attributedRule = {
|
|
|
149
150
|
};
|
|
150
151
|
},
|
|
151
152
|
};
|
|
153
|
+
const footnoteRefRule = {
|
|
154
|
+
pattern: /(?<!\\)\[\^([a-zA-Z0-9*]+?)\]/,
|
|
155
|
+
map: ([_match, label = ""]) => ({
|
|
156
|
+
kind: "footnoteRef",
|
|
157
|
+
label,
|
|
158
|
+
}),
|
|
159
|
+
mapHighlighting: ([match]) => ({
|
|
160
|
+
kind: "footnoteRef",
|
|
161
|
+
label: match,
|
|
162
|
+
}),
|
|
163
|
+
};
|
|
152
164
|
const textNode = (content) => ({
|
|
153
165
|
kind: "text",
|
|
154
166
|
content: content,
|
|
@@ -173,6 +185,7 @@ const inlineRules = [
|
|
|
173
185
|
italicWithBoldRule,
|
|
174
186
|
boldRule,
|
|
175
187
|
italicRule,
|
|
188
|
+
footnoteRefRule,
|
|
176
189
|
textRule,
|
|
177
190
|
];
|
|
178
191
|
const parseForInlineRules = (rules, text, forSyntaxHighlighting) => {
|
|
@@ -203,19 +216,19 @@ const nodesForTrailingWhitespace = (text) => {
|
|
|
203
216
|
return trailingWhitespace.length === 0 ? [] : [textNode(trailingWhitespace)];
|
|
204
217
|
};
|
|
205
218
|
const listRule = {
|
|
206
|
-
pattern: /^((?:(?:\d+\.|[-*]) [^\n]+?)(?:\n(?:\d+\.|[-*]) [^\n]+?)*)(\n{2,}
|
|
219
|
+
pattern: /^((?:(?:\d+\.|[-*]) [^\n]+?)(?:\n(?:\d+\.|[-*]) [^\n]+?)*)(\n{2,}|\s*$)/,
|
|
207
220
|
map: result => ({
|
|
208
221
|
kind: "list",
|
|
209
222
|
ordered: /^\d+\. /.test(result[0]),
|
|
210
223
|
content: (result[1] ?? "").split("\n").map(item => ({
|
|
211
|
-
kind: "
|
|
224
|
+
kind: "listItem",
|
|
212
225
|
content: parseInlineMarkdown(item.replace(/^\d+\. |[-*] /, ""), false),
|
|
213
226
|
})),
|
|
214
227
|
}),
|
|
215
228
|
mapHighlighting: result => [
|
|
216
229
|
...(result[1] ?? "").split("\n").flatMap((item, index, array) => [
|
|
217
230
|
{
|
|
218
|
-
kind: "
|
|
231
|
+
kind: "listItemMarker",
|
|
219
232
|
content: /^(\d+\. |[-*] )/.exec(item)?.[1] ?? "",
|
|
220
233
|
},
|
|
221
234
|
...parseInlineMarkdown(item.replace(/^\d+\. |[-*] /, ""), true),
|
|
@@ -243,28 +256,111 @@ const headingRule = {
|
|
|
243
256
|
content: parseInlineMarkdown(result[3] ?? "", false),
|
|
244
257
|
}),
|
|
245
258
|
mapHighlighting: result => [
|
|
246
|
-
{ kind: "
|
|
259
|
+
{ kind: "headingMarker", content: (result[1] ?? "") + (result[2] ?? "") },
|
|
247
260
|
...parseInlineMarkdown(result[3] ?? "", true),
|
|
248
261
|
...nodesForTrailingWhitespace(result[4]),
|
|
249
262
|
],
|
|
250
263
|
};
|
|
251
|
-
const removeSurroundingPipes = (text) => text.replace(/^\|/, "").replace(/\|$/, "");
|
|
252
264
|
const tableMarker = (text) => ({
|
|
253
|
-
kind: "
|
|
265
|
+
kind: "tableMarker",
|
|
254
266
|
content: text,
|
|
255
267
|
});
|
|
268
|
+
const sectionSeparatorPattern = /^\|? *-{3,} *(?:\| *-{3,} *)+\|?$|^\|? *={3,} *(?:\| *={3,} *)+\|?$/;
|
|
269
|
+
const sectionWithHeaderSeparatorPattern = /^\|? *={3,} *(?:\| *={3,} *)+\|?$/;
|
|
270
|
+
export const checkTableRowsAreSections = (rows) => rows.every(row => row.kind === "tableSection");
|
|
271
|
+
const parseContentRow = (row) => row
|
|
272
|
+
.replace(/^\|/, "")
|
|
273
|
+
.split(/(\|+)/)
|
|
274
|
+
.reduce((acc, segment, index, arr) => {
|
|
275
|
+
if (index % 2 === 0 && segment.trim() !== "") {
|
|
276
|
+
const colSpan = arr[index + 1]?.length;
|
|
277
|
+
return [
|
|
278
|
+
...acc,
|
|
279
|
+
omitUndefinedKeys({
|
|
280
|
+
kind: "tableCell",
|
|
281
|
+
colSpan: colSpan !== undefined && colSpan > 1 ? colSpan : undefined,
|
|
282
|
+
content: parseInlineMarkdown(segment.trim(), false),
|
|
283
|
+
}),
|
|
284
|
+
];
|
|
285
|
+
}
|
|
286
|
+
return acc;
|
|
287
|
+
}, []);
|
|
288
|
+
const parseContentRowForSyntaxHighlighting = (row) => row.split(/(\|+)/).reduce((acc, segment, index) => {
|
|
289
|
+
if (index % 2 === 0) {
|
|
290
|
+
return [...acc, ...parseInlineMarkdown(segment, true)];
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
return [...acc, tableMarker(segment)];
|
|
294
|
+
}
|
|
295
|
+
}, []);
|
|
256
296
|
const tableRule = {
|
|
257
|
-
pattern: /^(?:(\|#)(.+?)(#\|)\n)?(\|)?(.+?(?:(?<!\\)\|.+?)+)((?<!\\)\|)?\n((?:\| *)?(?:-{3,}|:-{2,}|-{2,}:|:-+:)(?: *\| *(?:-{3,}|:-{2,}|-{2,}:|:-+:))*(?: *\|)?)((?:\n\|?.+?(?:(?<!\\)
|
|
297
|
+
pattern: /^(?:(\|#)(.+?)(#\|)\n)?(\|)?(.+?(?:(?<!\\)\|.+?)+)((?<!\\)\|)?\n((?:\| *)?(?:-{3,}|:-{2,}|-{2,}:|:-+:)(?: *\| *(?:-{3,}|:-{2,}|-{2,}:|:-+:))*(?: *\|)?)((?:\n\|?.+?(?:(?<!\\)\|+.+?)*(?:(?<!\\)\|+)?)+)(\n{2,}|$)/,
|
|
258
298
|
map: ([_res, _captionMarkerStart, caption, _captionMarkerEnd, _headerMarkerStart, headers, _headerMarkerEnd, _bodySeparators, body, _trailingWhitespace,]) => omitUndefinedKeys({
|
|
259
299
|
kind: "table",
|
|
260
300
|
caption: caption !== undefined ? parseInlineMarkdown(caption.trim(), false) : undefined,
|
|
261
|
-
header: headers
|
|
301
|
+
header: headers ? parseContentRow(headers) : [],
|
|
262
302
|
rows: body
|
|
263
303
|
?.split("\n")
|
|
264
|
-
.slice(1)
|
|
265
|
-
.
|
|
266
|
-
.
|
|
267
|
-
|
|
304
|
+
.slice(1) // leading newline due to regex
|
|
305
|
+
.reduce((accRows, row) => {
|
|
306
|
+
if (sectionSeparatorPattern.test(row)) {
|
|
307
|
+
const hasHeader = sectionWithHeaderSeparatorPattern.test(row);
|
|
308
|
+
const newSection = omitUndefinedKeys({
|
|
309
|
+
kind: "tableSection",
|
|
310
|
+
header: hasHeader ? [] : undefined,
|
|
311
|
+
rows: [],
|
|
312
|
+
});
|
|
313
|
+
if (accRows[0] === undefined) {
|
|
314
|
+
return [newSection];
|
|
315
|
+
}
|
|
316
|
+
if (checkTableRowsAreSections(accRows)) {
|
|
317
|
+
return [...accRows, newSection];
|
|
318
|
+
}
|
|
319
|
+
return [{ kind: "tableSection", rows: accRows }, newSection];
|
|
320
|
+
}
|
|
321
|
+
const lastRow = accRows[accRows.length - 1];
|
|
322
|
+
const rowContent = parseContentRow(row);
|
|
323
|
+
if (lastRow === undefined) {
|
|
324
|
+
return [
|
|
325
|
+
{
|
|
326
|
+
kind: "tableRow",
|
|
327
|
+
cells: rowContent,
|
|
328
|
+
},
|
|
329
|
+
];
|
|
330
|
+
}
|
|
331
|
+
if (checkTableRowsAreSections(accRows)) {
|
|
332
|
+
const lastSection = lastRow;
|
|
333
|
+
if (lastSection.header !== undefined && lastSection.header.length === 0) {
|
|
334
|
+
return [
|
|
335
|
+
...accRows.slice(0, -1),
|
|
336
|
+
{
|
|
337
|
+
...lastSection,
|
|
338
|
+
header: rowContent,
|
|
339
|
+
},
|
|
340
|
+
];
|
|
341
|
+
}
|
|
342
|
+
return [
|
|
343
|
+
...accRows.slice(0, -1),
|
|
344
|
+
{
|
|
345
|
+
...lastSection,
|
|
346
|
+
rows: [
|
|
347
|
+
...lastSection.rows,
|
|
348
|
+
{
|
|
349
|
+
kind: "tableRow",
|
|
350
|
+
cells: rowContent,
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
},
|
|
354
|
+
];
|
|
355
|
+
}
|
|
356
|
+
return [
|
|
357
|
+
...accRows,
|
|
358
|
+
{
|
|
359
|
+
kind: "tableRow",
|
|
360
|
+
cells: rowContent,
|
|
361
|
+
},
|
|
362
|
+
];
|
|
363
|
+
}, []) ?? [],
|
|
268
364
|
}),
|
|
269
365
|
mapHighlighting: ([_res, captionMarkerStart, caption, captionMarkerEnd, headerMarkerStart, headers, headerMarkerEnd, bodySeparators, body, trailingWhitespace,]) => [
|
|
270
366
|
...(caption !== undefined
|
|
@@ -281,22 +377,170 @@ const tableRule = {
|
|
|
281
377
|
.flatMap((th, i) => i === 0
|
|
282
378
|
? parseInlineMarkdown(th, true)
|
|
283
379
|
: [tableMarker("|"), ...parseInlineMarkdown(th, true)]) ?? []),
|
|
284
|
-
tableMarker(
|
|
380
|
+
tableMarker(headerMarkerEnd ?? ""),
|
|
381
|
+
textNode("\n"),
|
|
382
|
+
tableMarker(bodySeparators ?? ""),
|
|
285
383
|
...(body
|
|
286
384
|
?.split("\n")
|
|
287
385
|
.slice(1)
|
|
288
386
|
.flatMap((tr) => [
|
|
289
387
|
textNode("\n"),
|
|
290
|
-
...tr
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
? parseInlineMarkdown(tc, true)
|
|
294
|
-
: [tableMarker("|"), ...parseInlineMarkdown(tc, true)]),
|
|
388
|
+
...(sectionSeparatorPattern.test(tr)
|
|
389
|
+
? [tableMarker(tr)]
|
|
390
|
+
: parseContentRowForSyntaxHighlighting(tr)),
|
|
295
391
|
]) ?? []),
|
|
296
392
|
...nodesForTrailingWhitespace(trailingWhitespace),
|
|
297
393
|
],
|
|
298
394
|
};
|
|
299
|
-
const
|
|
395
|
+
const containerRule = {
|
|
396
|
+
pattern: /^::: ([\w-_]+)?(\n+)(.+?)\n:::(\n{2,}|\s*$)/s,
|
|
397
|
+
map: ([_match, name, _leadingContentWhitespace, content, _trailingWhitespace,]) => ({
|
|
398
|
+
kind: "container",
|
|
399
|
+
name: name ?? undefined,
|
|
400
|
+
content: parseBlockMarkdown(content ?? ""),
|
|
401
|
+
}),
|
|
402
|
+
mapHighlighting: ([_match, name, leadingContentWhitespace = "", content, trailingWhitespace,]) => [
|
|
403
|
+
{ kind: "sectionMarker", content: `::: ${name ?? ""}` },
|
|
404
|
+
textNode(leadingContentWhitespace),
|
|
405
|
+
...parseBlockMarkdownForSyntaxHighlighting(content ?? ""),
|
|
406
|
+
textNode("\n"),
|
|
407
|
+
{ kind: "sectionMarker", content: ":::" },
|
|
408
|
+
...nodesForTrailingWhitespace(trailingWhitespace),
|
|
409
|
+
],
|
|
410
|
+
};
|
|
411
|
+
const removeIndentation = (text) => text
|
|
412
|
+
.split("\n")
|
|
413
|
+
.map(line => line.replace(/^ {2}/, ""))
|
|
414
|
+
.join("\n");
|
|
415
|
+
export const syntaxNodeToString = (node) => {
|
|
416
|
+
switch (node.kind) {
|
|
417
|
+
case "bold":
|
|
418
|
+
case "italic":
|
|
419
|
+
case "link":
|
|
420
|
+
case "attributed":
|
|
421
|
+
return node.content.map(syntaxNodeToString).join("");
|
|
422
|
+
case "text":
|
|
423
|
+
case "code":
|
|
424
|
+
case "listItemMarker":
|
|
425
|
+
case "tableMarker":
|
|
426
|
+
case "headingMarker":
|
|
427
|
+
case "sectionMarker":
|
|
428
|
+
case "footnoteMarker":
|
|
429
|
+
case "definitionMarker":
|
|
430
|
+
return node.content;
|
|
431
|
+
case "footnoteRef":
|
|
432
|
+
return node.label;
|
|
433
|
+
default:
|
|
434
|
+
return assertExhaustive(node);
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
const addIndentationToSyntax = (nodes, nextUpperNode) => nodes.reduce((accNodes, currentNode, index) => {
|
|
438
|
+
switch (currentNode.kind) {
|
|
439
|
+
case "bold":
|
|
440
|
+
case "italic":
|
|
441
|
+
case "link":
|
|
442
|
+
case "attributed":
|
|
443
|
+
return [
|
|
444
|
+
...accNodes,
|
|
445
|
+
{
|
|
446
|
+
...currentNode,
|
|
447
|
+
content: addIndentationToSyntax(currentNode.content, nodes[index + 1] ?? nextUpperNode),
|
|
448
|
+
},
|
|
449
|
+
];
|
|
450
|
+
case "text":
|
|
451
|
+
case "code":
|
|
452
|
+
case "listItemMarker":
|
|
453
|
+
case "tableMarker":
|
|
454
|
+
case "headingMarker":
|
|
455
|
+
case "sectionMarker":
|
|
456
|
+
case "footnoteMarker":
|
|
457
|
+
case "definitionMarker": {
|
|
458
|
+
const nextNode = nodes[index + 1] ?? nextUpperNode;
|
|
459
|
+
const currentContent = currentNode.content.endsWith("\n") &&
|
|
460
|
+
nextNode &&
|
|
461
|
+
/^[^\n]*?\S+?/.test(syntaxNodeToString(nextNode))
|
|
462
|
+
? currentNode.content + " "
|
|
463
|
+
: currentNode.content;
|
|
464
|
+
return [
|
|
465
|
+
...accNodes,
|
|
466
|
+
{ ...currentNode, content: currentContent.replace(/\n([^\n]*?\S+?)/g, "\n $1") },
|
|
467
|
+
];
|
|
468
|
+
}
|
|
469
|
+
case "footnoteRef":
|
|
470
|
+
return [...accNodes, currentNode];
|
|
471
|
+
default:
|
|
472
|
+
return assertExhaustive(currentNode);
|
|
473
|
+
}
|
|
474
|
+
}, []);
|
|
475
|
+
const footnoteRule = {
|
|
476
|
+
pattern: /^\[\^([a-zA-Z0-9*]+?)\]: (.+?(?:\n(?: {2}.+)?)*)(\n{2,}|\s*$)/,
|
|
477
|
+
map: ([_match, label = "", content = "", _trailingWhitespace]) => ({
|
|
478
|
+
kind: "footnote",
|
|
479
|
+
label: label,
|
|
480
|
+
content: parseBlockMarkdown(removeIndentation(content)),
|
|
481
|
+
}),
|
|
482
|
+
mapHighlighting: ([_match, label = "", content = "", trailingWhitespace,]) => [
|
|
483
|
+
{ kind: "footnoteMarker", content: `[^${label}]:` },
|
|
484
|
+
textNode(" "),
|
|
485
|
+
...addIndentationToSyntax(parseBlockMarkdownForSyntaxHighlighting(removeIndentation(content))),
|
|
486
|
+
...nodesForTrailingWhitespace(trailingWhitespace),
|
|
487
|
+
],
|
|
488
|
+
};
|
|
489
|
+
const definitionListItemSeparatorPattern = /(^.+?(?=\n:)|\n\n[^: ].*?(?=\n:))/s;
|
|
490
|
+
const definitionListRule = {
|
|
491
|
+
pattern: /((?:[^\n]+?(?:\n[^\n]+?)*)(?:\n: .+?(?:\n {2}.+?)*)+(?:\n\n(?:[^\n]+?(?:\n[^\n]+?)*)(?:\n: .+?(?:\n(?=\n)|\n {2}.+?)*))*)(\n{2,}|\s*$)/,
|
|
492
|
+
map: ([_res, content = "", _trailingWhitespace]) => {
|
|
493
|
+
const definitionItemPairs = chunk(content.split(definitionListItemSeparatorPattern).slice(1), 2);
|
|
494
|
+
const items = definitionItemPairs.map(([termsText = "", definitionsText = ""]) => {
|
|
495
|
+
const terms = termsText
|
|
496
|
+
.trim()
|
|
497
|
+
.split("\n")
|
|
498
|
+
.map(term => parseInlineMarkdown(term.trim(), false));
|
|
499
|
+
const definitions = definitionsText
|
|
500
|
+
.split("\n:")
|
|
501
|
+
.slice(1)
|
|
502
|
+
.map(definition => parseBlockMarkdown(removeIndentation(definition.trim())));
|
|
503
|
+
return {
|
|
504
|
+
kind: "definitionListItem",
|
|
505
|
+
terms,
|
|
506
|
+
definitions,
|
|
507
|
+
};
|
|
508
|
+
});
|
|
509
|
+
return {
|
|
510
|
+
kind: "definitionList",
|
|
511
|
+
content: items,
|
|
512
|
+
};
|
|
513
|
+
},
|
|
514
|
+
mapHighlighting: ([_res, content = "", trailingWhitespace]) => {
|
|
515
|
+
const items = chunk(content.split(definitionListItemSeparatorPattern).slice(1), 2).flatMap(([termsText = "", definitionsText = ""]) => {
|
|
516
|
+
const terms = termsText
|
|
517
|
+
.split("\n")
|
|
518
|
+
.flatMap((term, index, termArray) => [
|
|
519
|
+
...parseInlineMarkdown(term, true),
|
|
520
|
+
...(index < termArray.length - 1 ? [textNode("\n")] : []),
|
|
521
|
+
]);
|
|
522
|
+
const definitions = definitionsText
|
|
523
|
+
.split("\n:")
|
|
524
|
+
.slice(1)
|
|
525
|
+
.flatMap((definition, defIndex, defArray) => [
|
|
526
|
+
{ kind: "definitionMarker", content: ":" },
|
|
527
|
+
...addIndentationToSyntax(parseBlockMarkdownForSyntaxHighlighting(removeIndentation(definition))),
|
|
528
|
+
...(defIndex < defArray.length - 1 ? [textNode("\n")] : []),
|
|
529
|
+
]);
|
|
530
|
+
return [...terms, textNode("\n"), ...definitions];
|
|
531
|
+
});
|
|
532
|
+
return [...items, ...nodesForTrailingWhitespace(trailingWhitespace)];
|
|
533
|
+
},
|
|
534
|
+
};
|
|
535
|
+
const blockRules = [
|
|
536
|
+
containerRule,
|
|
537
|
+
headingRule,
|
|
538
|
+
footnoteRule,
|
|
539
|
+
tableRule,
|
|
540
|
+
listRule,
|
|
541
|
+
definitionListRule,
|
|
542
|
+
paragraphRule,
|
|
543
|
+
];
|
|
300
544
|
const parseActiveBlockRule = (rule, res) => [
|
|
301
545
|
rule.map(res),
|
|
302
546
|
];
|
|
@@ -336,14 +580,18 @@ const reduceSyntaxNode = (node) => {
|
|
|
336
580
|
switch (node.kind) {
|
|
337
581
|
case "bold":
|
|
338
582
|
case "italic":
|
|
583
|
+
case "link":
|
|
339
584
|
return { ...node, content: reduceSyntaxNodes(node.content) };
|
|
340
585
|
case "code":
|
|
341
|
-
case "link":
|
|
342
586
|
case "attributed":
|
|
343
587
|
case "text":
|
|
344
|
-
case "
|
|
345
|
-
case "
|
|
346
|
-
case "
|
|
588
|
+
case "listItemMarker":
|
|
589
|
+
case "tableMarker":
|
|
590
|
+
case "headingMarker":
|
|
591
|
+
case "sectionMarker":
|
|
592
|
+
case "footnoteMarker":
|
|
593
|
+
case "footnoteRef":
|
|
594
|
+
case "definitionMarker":
|
|
347
595
|
return node;
|
|
348
596
|
default:
|
|
349
597
|
return assertExhaustive(node);
|
|
@@ -354,9 +602,13 @@ const syntaxNodeMergeRules = {
|
|
|
354
602
|
italic: (a, b) => ({ ...a, content: [...a.content, ...b.content] }),
|
|
355
603
|
code: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
356
604
|
text: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
605
|
+
listItemMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
606
|
+
tableMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
607
|
+
headingMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
608
|
+
sectionMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
609
|
+
footnoteMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
610
|
+
definitionMarker: (a, b) => ({ ...a, content: a.content + b.content }),
|
|
611
|
+
footnoteRef: null,
|
|
360
612
|
link: null,
|
|
361
613
|
attributed: null,
|
|
362
614
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useLocation, useRoute } from "preact-iso";
|
|
3
|
-
import { useCallback, useContext, useEffect, useState } from "preact/hooks";
|
|
3
|
+
import { useCallback, useContext, useEffect, useMemo, useState } from "preact/hooks";
|
|
4
4
|
import { removeAt } from "../../shared/utils/array.js";
|
|
5
5
|
import { deepEqual } from "../../shared/utils/compare.js";
|
|
6
6
|
import { getSerializedDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
|
|
@@ -37,17 +37,18 @@ export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSub
|
|
|
37
37
|
const [customId, setCustomId] = useState("");
|
|
38
38
|
const client = useContext(GitClientContext);
|
|
39
39
|
const { route } = useLocation();
|
|
40
|
+
const hasUnsavedChanges = useMemo(() => !deepEqual(instanceContent, savedInstanceContent), [instanceContent, savedInstanceContent]);
|
|
40
41
|
useEffect(() => {
|
|
41
|
-
if (
|
|
42
|
-
window.
|
|
42
|
+
if (hasUnsavedChanges) {
|
|
43
|
+
window.addEventListener("beforeunload", onBeforeUnload);
|
|
43
44
|
}
|
|
44
45
|
else {
|
|
45
|
-
window.
|
|
46
|
+
window.removeEventListener("beforeunload", onBeforeUnload);
|
|
46
47
|
}
|
|
47
48
|
return () => {
|
|
48
49
|
window.removeEventListener("beforeunload", onBeforeUnload);
|
|
49
50
|
};
|
|
50
|
-
}, [
|
|
51
|
+
}, [hasUnsavedChanges]);
|
|
51
52
|
useEffect(() => {
|
|
52
53
|
document.title =
|
|
53
54
|
(entity && titleBuilder({ locales, entity, instanceContent, instanceId: id })) ??
|
|
@@ -147,5 +148,5 @@ export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSub
|
|
|
147
148
|
}
|
|
148
149
|
}, children: "Delete" }))] }), !id && isLocaleEntity && (_jsxs("div", { class: "field field--id", children: [_jsx("label", { htmlFor: "id", children: "ID" }), _jsx("p", { className: "comment", children: "The instance\u2019s identifier. An IETF language tag (BCP47)." }), _jsx("input", { type: "text", id: "id", value: customId, required: true, pattern: "[a-z]{2,3}(-[A-Z]{2,3})?", placeholder: "en-US, de-DE, \u2026", onInput: event => {
|
|
149
150
|
setCustomId(event.currentTarget.value);
|
|
150
|
-
}, "aria-invalid": idErrors.length > 0 }), _jsx(ValidationErrors, { errors: idErrors })] })), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entity.type, value: instanceContent, path: undefined, instanceNamesByEntity: instanceNamesByEntity, childInstances: childInstances, getDeclFromDeclName: getDeclFromDeclName, onChange: setInstanceContent, onChildChange: handleOnChildChange, onChildAdd: handleOnChildAdd, onChildRemove: handleOnChildRemove, checkIsLocaleEntity: checkIsLocaleEntity }), _jsx("div", { class: "form-footer btns", children: buttons.map(button => (_jsx("button", { type: "submit", name: button.name, class: button.primary ? "primary" : undefined, children: button.label }, button.name))) })] })] }));
|
|
151
|
+
}, "aria-invalid": idErrors.length > 0 }), _jsx(ValidationErrors, { errors: idErrors })] })), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entity.type, value: instanceContent, path: undefined, instanceNamesByEntity: instanceNamesByEntity, childInstances: childInstances, getDeclFromDeclName: getDeclFromDeclName, onChange: setInstanceContent, onChildChange: handleOnChildChange, onChildAdd: handleOnChildAdd, onChildRemove: handleOnChildRemove, checkIsLocaleEntity: checkIsLocaleEntity }), _jsx("div", { class: "form-footer btns", children: buttons.map(button => (_jsx("button", { type: "submit", name: button.name, class: button.primary ? "primary" : undefined, disabled: !hasUnsavedChanges, children: button.label }, button.name))) })] })] }));
|
|
151
152
|
};
|
|
@@ -24,6 +24,7 @@ const onSubmit = async ({ locales, entity, buttonName, instanceContent, isLocale
|
|
|
24
24
|
switch (buttonName) {
|
|
25
25
|
case "saveandcontinue": {
|
|
26
26
|
route(`/entities/${entity.name}/instances/${createdInstance.instance.id}`);
|
|
27
|
+
setInstanceContent(createdInstance.instance.content);
|
|
27
28
|
break;
|
|
28
29
|
}
|
|
29
30
|
case "saveandaddanother": {
|
|
@@ -21,7 +21,7 @@ const titleBuilder = ({ locales, entity, instanceId, instanceContent, }) => {
|
|
|
21
21
|
}
|
|
22
22
|
return undefined;
|
|
23
23
|
};
|
|
24
|
-
const onSubmit = async ({ locales, entity, instanceId, instanceContent, buttonName, childInstances, route, updateLocalGitState, }) => {
|
|
24
|
+
const onSubmit = async ({ locales, entity, instanceId, instanceContent, buttonName, childInstances, route, updateLocalGitState, setInstanceContent, }) => {
|
|
25
25
|
try {
|
|
26
26
|
if (instanceId && buttonName) {
|
|
27
27
|
await updateInstanceByEntityNameAndId(locales, entity.name, instanceId, {
|
|
@@ -33,6 +33,7 @@ const onSubmit = async ({ locales, entity, instanceId, instanceContent, buttonNa
|
|
|
33
33
|
await updateLocalGitState?.();
|
|
34
34
|
switch (buttonName) {
|
|
35
35
|
case "saveandcontinue": {
|
|
36
|
+
setInstanceContent(instanceContent);
|
|
36
37
|
break;
|
|
37
38
|
}
|
|
38
39
|
case "save": {
|
|
@@ -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,36 @@
|
|
|
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 "container":
|
|
22
|
+
return (_jsxs("div", { class: node.name, children: [insertBefore, node.content.map((childNode, i) => (_jsx(BlockMarkdown, { node: childNode, outerHeadingLevel: outerHeadingLevel }, 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, outerHeadingLevel: outerHeadingLevel, insertBefore: label }, i)))] }));
|
|
26
|
+
}
|
|
27
|
+
case "definitionList":
|
|
28
|
+
return (_jsxs(_Fragment, { children: [insertBefore, _jsx("dl", { children: node.content.map((item, ii) => (_jsxs("div", { children: [item.terms.map((term, ti) => (_jsx("dt", { children: term.map((inline, iii) => (_jsx(InlineMarkdown, { node: inline }, iii))) }, ti))), item.definitions.map((definition, di) => (_jsx("dd", { children: definition.map((n, i) => (_jsx(BlockMarkdown, { node: n, outerHeadingLevel: outerHeadingLevel }, i))) }, di)))] }, ii))) })] }));
|
|
19
29
|
default:
|
|
20
|
-
return
|
|
30
|
+
return assertExhaustive(node);
|
|
21
31
|
}
|
|
22
32
|
};
|
|
33
|
+
const TableRow = ({ cells, cellType = "td", }) => {
|
|
34
|
+
const CellTag = cellType;
|
|
35
|
+
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))) }));
|
|
36
|
+
};
|
|
@@ -2,12 +2,20 @@ 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 });
|
|
17
|
+
case "definitionMarker":
|
|
18
|
+
return _jsx("span", { class: "definition-description-marker", children: node.content });
|
|
11
19
|
default:
|
|
12
20
|
return _jsx(InlineMarkdown, { node: node });
|
|
13
21
|
}
|
|
@@ -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 {
|
|
@@ -720,7 +725,7 @@ form > .field--container {
|
|
|
720
725
|
color: var(--color-syntax-italic);
|
|
721
726
|
}
|
|
722
727
|
|
|
723
|
-
.editor-highlighting .list-item-marker {
|
|
728
|
+
.editor-highlighting :is(.list-item-marker, .definition-description-marker) {
|
|
724
729
|
color: var(--color-syntax-list-item-marker);
|
|
725
730
|
}
|
|
726
731
|
|
|
@@ -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;
|