svedit 0.12.0 → 0.14.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.
Files changed (88) hide show
  1. package/README.md +35 -22
  2. package/dist/Command.svelte.d.ts +38 -30
  3. package/dist/Command.svelte.d.ts.map +1 -1
  4. package/dist/Command.svelte.js +271 -326
  5. package/dist/CustomProperty.svelte +3 -5
  6. package/dist/CustomProperty.svelte.d.ts +3 -6
  7. package/dist/CustomProperty.svelte.d.ts.map +1 -1
  8. package/dist/KeyMapper.svelte.d.ts +19 -6
  9. package/dist/KeyMapper.svelte.d.ts.map +1 -1
  10. package/dist/KeyMapper.svelte.js +95 -112
  11. package/dist/Node.svelte +16 -13
  12. package/dist/Node.svelte.d.ts +3 -5
  13. package/dist/Node.svelte.d.ts.map +1 -1
  14. package/dist/NodeArrayProperty.svelte +39 -22
  15. package/dist/NodeArrayProperty.svelte.d.ts +3 -5
  16. package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
  17. package/dist/NodeCaret.svelte +2 -2
  18. package/dist/NodeCaret.svelte.d.ts +6 -14
  19. package/dist/NodeCaret.svelte.d.ts.map +1 -1
  20. package/dist/NodeGap.svelte +12 -6
  21. package/dist/NodeGap.svelte.d.ts +7 -14
  22. package/dist/NodeGap.svelte.d.ts.map +1 -1
  23. package/dist/NodeGapMarkers.svelte +36 -14
  24. package/dist/NodeGapMarkers.svelte.d.ts +6 -11
  25. package/dist/NodeGapMarkers.svelte.d.ts.map +1 -1
  26. package/dist/NodeSelectionMarkers.svelte +21 -7
  27. package/dist/NodeSelectionMarkers.svelte.d.ts +2 -5
  28. package/dist/NodeSelectionMarkers.svelte.d.ts.map +1 -1
  29. package/dist/Session.svelte.d.ts +81 -129
  30. package/dist/Session.svelte.d.ts.map +1 -1
  31. package/dist/Session.svelte.js +542 -648
  32. package/dist/Svedit.svelte +146 -140
  33. package/dist/Svedit.svelte.d.ts +30 -10
  34. package/dist/Svedit.svelte.d.ts.map +1 -1
  35. package/dist/TextProperty.svelte +37 -23
  36. package/dist/TextProperty.svelte.d.ts +3 -5
  37. package/dist/TextProperty.svelte.d.ts.map +1 -1
  38. package/dist/Transaction.svelte.d.ts +64 -127
  39. package/dist/Transaction.svelte.d.ts.map +1 -1
  40. package/dist/Transaction.svelte.js +839 -1001
  41. package/dist/UnknownNode.svelte +4 -3
  42. package/dist/UnknownNode.svelte.d.ts +5 -9
  43. package/dist/UnknownNode.svelte.d.ts.map +1 -1
  44. package/dist/doc_utils.d.ts +67 -155
  45. package/dist/doc_utils.d.ts.map +1 -1
  46. package/dist/doc_utils.js +619 -863
  47. package/dist/index.d.ts +20 -17
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +1 -17
  50. package/dist/node_visibility.svelte.d.ts +110 -9
  51. package/dist/node_visibility.svelte.d.ts.map +1 -1
  52. package/dist/node_visibility.svelte.js +473 -544
  53. package/dist/paste_utils.d.ts +11 -44
  54. package/dist/paste_utils.d.ts.map +1 -1
  55. package/dist/paste_utils.js +111 -152
  56. package/dist/styles/svedit-colors.css +8 -5
  57. package/dist/transforms.svelte.d.ts +9 -7
  58. package/dist/transforms.svelte.d.ts.map +1 -1
  59. package/dist/transforms.svelte.js +125 -150
  60. package/dist/types.d.ts +294 -309
  61. package/dist/types.d.ts.map +1 -0
  62. package/dist/types.js +1 -0
  63. package/dist/utils.d.ts +40 -119
  64. package/dist/utils.d.ts.map +1 -1
  65. package/dist/utils.js +346 -473
  66. package/package.json +70 -70
  67. package/src/lib/{Command.svelte.js → Command.svelte.ts} +29 -17
  68. package/src/lib/CustomProperty.svelte +3 -5
  69. package/src/lib/{KeyMapper.svelte.js → KeyMapper.svelte.ts} +28 -11
  70. package/src/lib/Node.svelte +16 -13
  71. package/src/lib/NodeArrayProperty.svelte +39 -22
  72. package/src/lib/NodeCaret.svelte +2 -2
  73. package/src/lib/NodeGap.svelte +12 -6
  74. package/src/lib/NodeGapMarkers.svelte +36 -14
  75. package/src/lib/NodeSelectionMarkers.svelte +21 -7
  76. package/src/lib/{Session.svelte.js → Session.svelte.ts} +130 -127
  77. package/src/lib/Svedit.svelte +146 -140
  78. package/src/lib/TextProperty.svelte +37 -23
  79. package/src/lib/{Transaction.svelte.js → Transaction.svelte.ts} +139 -143
  80. package/src/lib/UnknownNode.svelte +4 -3
  81. package/src/lib/{doc_utils.js → doc_utils.ts} +247 -206
  82. package/src/lib/{index.js → index.ts} +48 -0
  83. package/src/lib/{node_visibility.svelte.js → node_visibility.svelte.ts} +68 -92
  84. package/src/lib/{paste_utils.js → paste_utils.ts} +47 -58
  85. package/src/lib/styles/svedit-colors.css +8 -5
  86. package/src/lib/{transforms.svelte.js → transforms.svelte.ts} +39 -21
  87. package/src/lib/{types.d.ts → types.ts} +161 -66
  88. package/src/lib/{utils.js → utils.ts} +110 -129
@@ -1,20 +1,33 @@
1
1
  import { split_text, join_text, get_char_length } from './utils.js';
2
2
  import { get_default_node_type } from './doc_utils.js';
3
+ import type Transaction from './Transaction.svelte.js';
4
+ import type {
5
+ DocumentNode,
6
+ DocumentPath,
7
+ NodeArrayProperty,
8
+ NodeProperty,
9
+ Selection,
10
+ TextSelection
11
+ } from './types.js';
3
12
 
4
13
  /**
5
14
  * Set multiple properties on a node via a transaction.
6
15
  *
7
- * @param {object} tr - The transaction
8
- * @param {Array} path - Path to the node
9
- * @param {Record<string, any>} properties - Key/value pairs to set
16
+ * @param tr - The transaction
17
+ * @param path - Path to the node
18
+ * @param properties - Key/value pairs to set
10
19
  */
11
- export function set_properties(tr, path, properties) {
20
+ export function set_properties(
21
+ tr: Transaction,
22
+ path: DocumentPath,
23
+ properties: Record<string, unknown>
24
+ ): void {
12
25
  for (const [key, value] of Object.entries(properties)) {
13
26
  tr.set([...path, key], value);
14
27
  }
15
28
  }
16
29
 
17
- export function break_text_node(tr) {
30
+ export function break_text_node(tr: Transaction): boolean {
18
31
  // Keep a reference of the original selection (before any transforms are applied)
19
32
  const selection = tr.selection;
20
33
  // First we need to ensure we have a text selection
@@ -28,7 +41,7 @@ export function break_text_node(tr) {
28
41
  if (tr.kind(node) !== 'text') return false;
29
42
  const is_inside_node_array = tr.inspect(selection.path.slice(0, -2))?.type === 'node_array';
30
43
  if (!is_inside_node_array) return false; // Do nothing if we're not inside a node_array
31
- const node_array_prop = selection.path.at(-3);
44
+ const node_array_prop = String(selection.path.at(-3));
32
45
  // Get the node that owns the node_array property (e.g. a page.body)
33
46
  const node_array_node = tr.get(selection.path.slice(0, -3));
34
47
 
@@ -37,23 +50,26 @@ export function break_text_node(tr) {
37
50
  tr.delete_selection();
38
51
  }
39
52
 
40
- const split_at_position = tr.selection.anchor_offset;
53
+ // After delete_selection the selection is still a (collapsed) text selection
54
+ const split_at_position = (tr.selection as TextSelection).anchor_offset;
41
55
  const content = tr.get(selection.path);
42
56
  const [left_text, right_text] = split_text(content, split_at_position);
43
57
 
44
58
  tr.set([node.id, 'content'], left_text);
45
59
 
46
- const node_insert_position = {
60
+ const node_insert_position: Selection = {
47
61
  type: 'node',
48
62
  path: tr.selection.path.slice(0, -2),
49
- anchor_offset: parseInt(tr.selection.path.at(-2), 10) + 1,
50
- focus_offset: parseInt(tr.selection.path.at(-2), 10) + 1
63
+ anchor_offset: (tr.selection.path.at(-2) as number) + 1,
64
+ focus_offset: (tr.selection.path.at(-2) as number) + 1
51
65
  };
52
66
 
53
67
  // TODO: Only use default_node_type when caret is at the end of
54
68
  const node_array_property_definition =
55
69
  tr.schema[node_array_node.type].properties[node_array_prop];
56
- const target_node_type = get_default_node_type(node_array_property_definition);
70
+ const target_node_type = get_default_node_type(
71
+ node_array_property_definition as NodeProperty | NodeArrayProperty
72
+ );
57
73
 
58
74
  if (!target_node_type) {
59
75
  console.warn(
@@ -68,7 +84,7 @@ export function break_text_node(tr) {
68
84
  return true;
69
85
  }
70
86
 
71
- export function join_text_node(tr) {
87
+ export function join_text_node(tr: Transaction): boolean {
72
88
  // Keep a reference of the original selection (before any transforms are applied)
73
89
  const selection = tr.selection;
74
90
  // First we need to ensure we have a text selection
@@ -79,16 +95,16 @@ export function join_text_node(tr) {
79
95
  const is_inside_node_array = tr.inspect(selection.path.slice(0, -2))?.type === 'node_array';
80
96
  if (!is_inside_node_array) return false; // Do nothing if we're not inside a node_array
81
97
 
82
- const node_index = parseInt(tr.selection.path.at(-2), 10);
98
+ const node_index = tr.selection.path.at(-2) as number;
83
99
 
84
100
  // Determine if we can join with the previous node
85
101
  let can_join = false;
86
- let predecessor_node = null;
102
+ let predecessor_node: DocumentNode | null = null;
87
103
 
88
104
  if (node_index > 0) {
89
105
  const previous_text_path = [...tr.selection.path.slice(0, -2), node_index - 1];
90
106
  predecessor_node = tr.get(previous_text_path);
91
- can_join = tr.kind(predecessor_node) === 'text';
107
+ can_join = predecessor_node !== null && tr.kind(predecessor_node) === 'text';
92
108
  }
93
109
 
94
110
  // Special behavior: if we can't join and current node is empty, delete it
@@ -104,7 +120,7 @@ export function join_text_node(tr) {
104
120
  }
105
121
 
106
122
  // If we can't join for any reason, return false
107
- if (!can_join) {
123
+ if (!can_join || !predecessor_node) {
108
124
  return false;
109
125
  }
110
126
 
@@ -138,7 +154,7 @@ export function join_text_node(tr) {
138
154
  return true;
139
155
  }
140
156
 
141
- export function insert_default_node(tr) {
157
+ export function insert_default_node(tr: Transaction): boolean {
142
158
  const selection = tr.selection;
143
159
 
144
160
  // Only work with collapsed node selections
@@ -148,15 +164,17 @@ export function insert_default_node(tr) {
148
164
 
149
165
  const path = selection.path;
150
166
  const node_array_node = tr.get(path.slice(0, -1));
151
- const property_name = path.at(-1);
167
+ const property_name = String(path.at(-1));
152
168
 
153
169
  // Get the definition for this property
154
170
  const property_definition = tr.schema[node_array_node.type].properties[property_name];
155
- const default_type = get_default_node_type(property_definition);
171
+ const default_type = get_default_node_type(
172
+ property_definition as NodeProperty | NodeArrayProperty
173
+ );
156
174
 
157
175
  // Use the inserter function if available
158
- if (tr.config?.inserters?.[default_type]) {
159
- tr.config.inserters[default_type](tr);
176
+ if (tr.config?.inserters?.[default_type as string]) {
177
+ tr.config.inserters[default_type as string](tr);
160
178
  return true;
161
179
  } else {
162
180
  throw new Error(`No inserter function available for default node type '${default_type}'`);
@@ -1,12 +1,12 @@
1
1
  import type Session from './Session.svelte.js';
2
- import type Svedit from './Svedit.svelte';
2
+ import type { VisibilityRegistryApi } from './node_visibility.svelte.js';
3
3
 
4
4
  // ===== SVELTE TYPE IMPORTS =====
5
5
 
6
6
  /**
7
7
  * Import Svelte's Snippet type for properly typing children in components
8
8
  */
9
- import type { Component, Snippet } from 'svelte';
9
+ import type { Snippet } from 'svelte';
10
10
 
11
11
  // ===== SELECTION TYPE DEFINITIONS =====
12
12
 
@@ -19,6 +19,13 @@ import type { Component, Snippet } from 'svelte';
19
19
  */
20
20
  export type NodeId = string;
21
21
 
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ export type DynamicRecord = Record<string, any>;
24
+ export type SessionConfig = DynamicRecord;
25
+ export type CommandRegistry = DynamicRecord;
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ export type DocumentOperation = [string, ...any[]];
28
+
22
29
  /**
23
30
  * Array of IDs, property names (strings), or indexes (integers) that identify a node or property in the document.
24
31
  * String segments must follow the same path segment rules as NodeId.
@@ -98,54 +105,82 @@ export type PrimitiveType = ScalarType | ArrayType | RichType;
98
105
  */
99
106
  export type PropertyType = PrimitiveType | ReferenceType;
100
107
 
101
- // TODO: We may later want more sophisticated type inference, so that typing
102
- // e.g. `session.get('image')` auto-completes the node's properties based on
103
- // the schema definition. The types below are a starting point for that.
104
- //
105
- // export type DocumentSchemaPrimitive =
106
- // | 'string'
107
- // | 'number'
108
- // | 'boolean'
109
- // | 'integer'
110
- // | 'datetime'
111
- // | 'string_array'
112
- // | 'number_array'
113
- // | 'boolean_array'
114
- // | 'integer_array'
115
- // | 'text'
116
- // | 'node'
117
- // | 'node_array';
118
- //
119
- // export type DocumentSchemaValueToJs<T> = T extends 'string'
120
- // ? string
121
- // : T extends 'number'
122
- // ? number
123
- // : T extends 'boolean'
124
- // ? boolean
125
- // : T extends 'integer'
126
- // ? number
127
- // : T extends 'datetime'
128
- // ? string
129
- // : T extends 'string_array'
130
- // ? Array<string>
131
- // : T extends 'number_array'
132
- // ? Array<number>
133
- // : T extends 'boolean_array'
134
- // ? Array<boolean>
135
- // : T extends 'integer_array'
136
- // ? Array<number>
137
- // : T extends 'text'
138
- // ? AnnotatedText
139
- // : T extends 'node'
140
- // ? string
141
- // : T extends 'node_array'
142
- // ? Array<string>
143
- // : never;
144
- //
145
- // export type DocumentNodeToJs<S extends NodeSchema> = { id: string; type: string } & {
146
- // [K in keyof S['properties']]: DocumentSchemaValueToJs<S['properties'][K]['type']>;
147
- // };
108
+ // ===== SCHEMA-DERIVED NODE TYPES =====
148
109
 
110
+ /**
111
+ * Maps a schema property definition to the runtime value type it stores.
112
+ */
113
+ export type PropertyValue<P extends PropertyDefinition> = P extends {
114
+ type: 'string';
115
+ values: readonly (infer Value extends string)[];
116
+ }
117
+ ? Value
118
+ : P extends { type: 'string' }
119
+ ? string
120
+ : P extends { type: 'number' }
121
+ ? number
122
+ : P extends { type: 'integer' }
123
+ ? number
124
+ : P extends { type: 'boolean' }
125
+ ? boolean
126
+ : P extends { type: 'datetime' }
127
+ ? string
128
+ : P extends { type: 'string_array' }
129
+ ? string[]
130
+ : P extends { type: 'number_array' }
131
+ ? number[]
132
+ : P extends { type: 'integer_array' }
133
+ ? number[]
134
+ : P extends { type: 'boolean_array' }
135
+ ? boolean[]
136
+ : P extends { type: 'text' }
137
+ ? Text
138
+ : P extends { type: 'node' }
139
+ ? NodeId
140
+ : P extends { type: 'node_array' }
141
+ ? NodeArray
142
+ : never;
143
+
144
+ /**
145
+ * The runtime shape of a node of a specific type, derived from the schema.
146
+ *
147
+ * Falls back to the untyped DocumentNode when the schema is not a concrete
148
+ * schema literal (e.g. the DocumentSchema default of Session).
149
+ */
150
+ export type NodeOfType<S extends DocumentSchema, T extends keyof S> = string extends keyof S
151
+ ? DocumentNode
152
+ : {
153
+ id: NodeId;
154
+ type: T;
155
+ } & {
156
+ [K in keyof S[T]['properties']]: PropertyValue<S[T]['properties'][K]>;
157
+ };
158
+
159
+ /**
160
+ * Discriminated union of all node types in a schema. Narrow with
161
+ * `node.type === '...'` to get exact property types.
162
+ *
163
+ * Falls back to the untyped DocumentNode when the schema is not a concrete
164
+ * schema literal (e.g. the DocumentSchema default of Session).
165
+ */
166
+ export type AnyNode<S extends DocumentSchema> = string extends keyof S
167
+ ? DocumentNode
168
+ : {
169
+ [T in keyof S]: NodeOfType<S, T>;
170
+ }[keyof S];
171
+
172
+ /**
173
+ * Convenience map from node type name to its runtime node shape,
174
+ * e.g. `NodeMap<typeof document_schema>['story']`.
175
+ */
176
+ export type NodeMap<S extends DocumentSchema> = {
177
+ [T in keyof S]: NodeOfType<S, T>;
178
+ };
179
+
180
+ /**
181
+ * Assert that a node is of the given type. Narrows the static type and
182
+ * checks at runtime — for call sites that know what they expect.
183
+ */
149
184
  /**
150
185
  * A property that stores text with optional marks and annotations and required allow_newlines setting.
151
186
  */
@@ -162,6 +197,7 @@ export type TextProperty = {
162
197
  export type StringProperty = {
163
198
  type: 'string';
164
199
  default?: string;
200
+ values?: readonly string[];
165
201
  };
166
202
 
167
203
  /**
@@ -186,6 +222,8 @@ export type BooleanProperty = {
186
222
  export type IntegerProperty = {
187
223
  type: 'integer';
188
224
  default?: number;
225
+ min?: number;
226
+ max?: number;
189
227
  };
190
228
 
191
229
  /**
@@ -346,6 +384,13 @@ export type DocumentSchema = Record<string, NodeSchema>;
346
384
  export type DocumentNode = {
347
385
  id: string;
348
386
  type: string;
387
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
388
+ [key: string]: any;
389
+ };
390
+
391
+ export type Inspection = {
392
+ kind: 'property' | 'node';
393
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
349
394
  [key: string]: any;
350
395
  };
351
396
 
@@ -360,6 +405,13 @@ export type Document = {
360
405
  };
361
406
  };
362
407
 
408
+ /** Converts a document node to HTML, with access to recursive exporters. */
409
+ export type NodeHtmlExporter<S extends DocumentSchema = DocumentSchema> = (
410
+ node: DocumentNode,
411
+ session: Session<S>,
412
+ html_exporters: Record<string, NodeHtmlExporter<S>>
413
+ ) => string;
414
+
363
415
  /**
364
416
  * Props for the TextProperty component
365
417
  */
@@ -373,7 +425,7 @@ export type TextPropertyProps = {
373
425
  /** A placeholder to be rendered for empty content */
374
426
  placeholder?: string;
375
427
  /** Rest props to spread onto the rendered element (e.g. href, target, etc.) */
376
- [key: string]: any;
428
+ [key: string]: unknown;
377
429
  };
378
430
 
379
431
  /**
@@ -389,7 +441,7 @@ export type CustomPropertyProps = {
389
441
  /** The content of the custom property (e.g. an image) */
390
442
  children: Snippet;
391
443
  /** Rest props to spread onto the rendered element */
392
- [key: string]: any;
444
+ [key: string]: unknown;
393
445
  };
394
446
 
395
447
  /**
@@ -403,7 +455,25 @@ export type NodeArrayPropertyProps = {
403
455
  /** The `class` attribute on the container element */
404
456
  class?: string;
405
457
  /** Rest props to spread onto the rendered element (e.g. href, target, etc.) */
406
- [key: string]: any;
458
+ [key: string]: unknown;
459
+ };
460
+
461
+ /**
462
+ * Props passed to the consumer's `system_components.node_gap_tools`
463
+ * component. It renders inside the active node gap marker (the one carrying
464
+ * the caret), so it inherits the `--row` orientation variable and
465
+ * appears/disappears together with the caret. The marker has
466
+ * pointer-events: none — interactive tools must set pointer-events: auto.
467
+ */
468
+ export type NodeGapToolsProps = {
469
+ /** The full path to the node_array the gap belongs to */
470
+ path: DocumentPath;
471
+ /** The insertion offset of the gap (0..count) */
472
+ offset: number;
473
+ /** True for the gap before the first node */
474
+ is_first: boolean;
475
+ /** True for the gap after the last node */
476
+ is_last: boolean;
407
477
  };
408
478
 
409
479
  /**
@@ -423,15 +493,15 @@ export type NodeProps = {
423
493
  /** The type-specific content of the node */
424
494
  children: Snippet;
425
495
  /** Rest props to spread onto the rendered element (e.g. href, target, etc.) */
426
- [key: string]: any;
496
+ [key: string]: unknown;
427
497
  };
428
498
 
429
499
  /**
430
500
  * Props for the Svedit component
431
501
  */
432
- export type SveditProps = {
433
- /** The session instance */
434
- session: Session;
502
+ export type SveditProps<S extends DocumentSchema = DocumentSchema> = {
503
+ /** The schema-typed session instance */
504
+ session: Session<S>;
435
505
  /** Determines wether the document should be editable or read-only. */
436
506
  editable?: boolean;
437
507
  /** The path to the root element (e.g. ['page_1']) */
@@ -444,6 +514,24 @@ export type SveditProps = {
444
514
  spellcheck?: 'true' | 'false';
445
515
  };
446
516
 
517
+ /**
518
+ * Context provided by the Svedit component to commands and descendant components.
519
+ */
520
+ export type SveditContext<S extends DocumentSchema = DocumentSchema> = {
521
+ session: Session<S>;
522
+ editable: boolean;
523
+ is_composing: boolean;
524
+ canvas_el: HTMLElement | undefined;
525
+ canvas_focused: boolean;
526
+ focus_canvas: () => void;
527
+ visibility_registry?: VisibilityRegistryApi;
528
+ };
529
+
530
+ /** Internal descendant context after Svedit's visibility registry is installed. */
531
+ export type SveditRenderContext<S extends DocumentSchema = DocumentSchema> = SveditContext<S> & {
532
+ visibility_registry: VisibilityRegistryApi;
533
+ };
534
+
447
535
  /**
448
536
  * A range with an attached payload node, used for both marks and annotations.
449
537
  */
@@ -468,23 +556,23 @@ export type Annotation = Attachment;
468
556
  /**
469
557
  * Represents text content with marks and annotations.
470
558
  */
471
- export type AnnotatedText = {
559
+ export type Text = {
472
560
  content: string;
473
561
  marks: Array<Mark>;
474
562
  annotations: Array<Annotation>;
475
563
  };
476
564
 
477
565
  /**
478
- * Represents an annotated node array with nodes, marks and annotations
566
+ * Represents a node array with nodes, marks and annotations.
479
567
  */
480
- export type AnnotatedNodeArray = {
568
+ export type NodeArray = {
481
569
  nodes: Array<NodeId>;
482
570
  marks: Array<Mark>;
483
571
  annotations: Array<Annotation>;
484
572
  };
485
573
 
486
574
  /**
487
- * Attachment context passed to a node rendered inside an annotated node array.
575
+ * Attachment context passed to a node rendered inside a node array.
488
576
  * It is the flattened mark or annotation attachment, enriched with the resolved
489
577
  * payload node, its index in the parent attachment array, and this child node's
490
578
  * position in the attachment range.
@@ -500,8 +588,15 @@ export type NodeArrayAttachmentContext = {
500
588
  is_end: boolean;
501
589
  };
502
590
 
591
+ /** Internal context shared by a node array and its rendered child nodes. */
592
+ export type NodeArrayRenderContext = {
593
+ length: number;
594
+ mark_for: (node_index: number) => NodeArrayAttachmentContext | null;
595
+ annotations_for: (node_index: number) => NodeArrayAttachmentContext[];
596
+ };
597
+
503
598
  /**
504
- * Represents a selection highlight fragment for non-annotated text selections
599
+ * Represents a selection highlight fragment for unmarked text selections
505
600
  */
506
601
  export type SelectionHighlightFragment = {
507
602
  type: 'selection_highlight';
@@ -509,12 +604,12 @@ export type SelectionHighlightFragment = {
509
604
  };
510
605
 
511
606
  /**
512
- * Represents a mark fragment in annotated text content
607
+ * Represents a mark fragment in text content
513
608
  */
514
609
  export type MarkFragment = {
515
610
  type: 'mark';
516
611
  /** NodeId that has mark type and details */
517
- node: any;
612
+ node: DocumentNode;
518
613
  /** The text content of the mark */
519
614
  content: string;
520
615
  /** Index of the mark in the original array */
@@ -522,7 +617,7 @@ export type MarkFragment = {
522
617
  };
523
618
 
524
619
  /**
525
- * Represents a fragment of annotated text content
620
+ * Represents a fragment of text content
526
621
  */
527
622
  export type Fragment = string | MarkFragment | SelectionHighlightFragment;
528
623
 
@@ -541,7 +636,7 @@ export type NodeArrayPlainFragment = {
541
636
  export type NodeArrayMarkFragment = {
542
637
  type: 'mark';
543
638
  /** NodeId that has mark type and details */
544
- node: any;
639
+ node: DocumentNode;
545
640
  /** The nodes wrapped by the mark */
546
641
  nodes: Array<NodeId>;
547
642
  /** Start index in the original nodes array */
@@ -551,6 +646,6 @@ export type NodeArrayMarkFragment = {
551
646
  };
552
647
 
553
648
  /**
554
- * Represents a fragment of annotated node array content
649
+ * Represents a fragment of node array content
555
650
  */
556
651
  export type NodeArrayFragment = NodeArrayPlainFragment | NodeArrayMarkFragment;