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.
@@ -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[] : never;
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 { AsType, Type } from "../schema/types/Type.ts";
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: AsType<T>;
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
- export type InlineMarkdownNode = BoldMarkdownNode | ItalicMarkdownNode | CodeMarkdownNode | LinkMarkdownNode | AttributedStringMarkdownNode | TextNode;
28
- type ParagraphBlockNode = {
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: "listitem";
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: InlineMarkdownNode[][];
50
- rows: InlineMarkdownNode[][][];
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 BlockMarkdownNode = ParagraphBlockNode | HeadingBlockNode | ListBlockNode | TableBlockNode;
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: "listitemmarker";
93
+ kind: "listItemMarker";
55
94
  content: string;
56
95
  };
57
96
  type TableMarkerSyntaxNode = {
58
- kind: "tablemarker";
97
+ kind: "tableMarker";
59
98
  content: string;
60
99
  };
61
100
  type HeadingMarkerSyntaxNode = {
62
- kind: "headingmarker";
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: "listitem",
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: "listitemmarker",
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: "headingmarker", content: (result[1] ?? "") + (result[2] ?? "") },
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: "tablemarker",
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\|?.+?(?:(?<!\\)\|.+?)*(?<!\\)\|?)+)(\n{2,}|$)/,
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?.split("|").map(th => parseInlineMarkdown(th.trim(), false)) ?? [],
301
+ header: headers ? parseContentRow(headers) : [],
262
302
  rows: body
263
303
  ?.split("\n")
264
- .slice(1)
265
- .map(tr => removeSurroundingPipes(tr)
266
- .split("|")
267
- .map(tc => parseInlineMarkdown(tc.trim(), false))) ?? [],
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((headerMarkerEnd ?? "") + "\n" + (bodySeparators ?? "")),
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
- .split("|")
292
- .flatMap((tc, i) => i === 0
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 blockRules = [headingRule, tableRule, listRule, paragraphRule];
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 "listitemmarker":
345
- case "tablemarker":
346
- case "headingmarker":
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
- listitemmarker: (a, b) => ({ ...a, content: a.content + b.content }),
358
- tablemarker: (a, b) => ({ ...a, content: a.content + b.content }),
359
- headingmarker: (a, b) => ({ ...a, content: a.content + b.content }),
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 (deepEqual(instanceContent, savedInstanceContent)) {
42
- window.removeEventListener("beforeunload", onBeforeUnload);
42
+ if (hasUnsavedChanges) {
43
+ window.addEventListener("beforeunload", onBeforeUnload);
43
44
  }
44
45
  else {
45
- window.addEventListener("beforeunload", onBeforeUnload);
46
+ window.removeEventListener("beforeunload", onBeforeUnload);
46
47
  }
47
48
  return () => {
48
49
  window.removeEventListener("beforeunload", onBeforeUnload);
49
50
  };
50
- }, [instanceContent, savedInstanceContent]);
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 { BlockMarkdownNode } from "../../shared/utils/markdown.ts";
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 (_jsx("p", { children: node.content.map((inline, ii) => (_jsx(InlineMarkdown, { node: inline }, ii))) }));
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 (_jsx(Tag, { children: node.content.map((inline, ii) => (_jsx(InlineMarkdown, { node: inline }, ii))) }));
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("tr", { children: node.header.map((th, hi) => (_jsx("th", { children: th.map((inline, hii) => (_jsx(InlineMarkdown, { node: inline }, hii))) }, hi))) }) }), _jsx("tbody", { children: node.rows.map((tr, ri) => (_jsx("tr", { children: tr.map((tc, ci) => (_jsx("td", { children: tc.map((inline, cii) => (_jsx(InlineMarkdown, { node: inline }, cii))) }, ci))) }, ri))) })] }));
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 null;
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 "listitemmarker":
5
+ case "listItemMarker":
6
6
  return _jsx("span", { class: "list-item-marker", children: node.content });
7
- case "tablemarker":
7
+ case "tableMarker":
8
8
  return _jsx("span", { class: "table-marker", children: node.content });
9
- case "headingmarker":
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsondb",
3
- "version": "0.7.12",
3
+ "version": "0.8.1",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "Lukas Obermann",
@@ -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;