svedit 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +762 -120
- package/dist/AnnotatedTextProperty.svelte +122 -75
- package/dist/AnnotatedTextProperty.svelte.d.ts.map +1 -1
- package/dist/Command.svelte.d.ts +118 -0
- package/dist/Command.svelte.d.ts.map +1 -0
- package/dist/Command.svelte.js +314 -0
- package/dist/CustomProperty.svelte +31 -42
- package/dist/CustomProperty.svelte.d.ts.map +1 -1
- package/dist/KeyMapper.svelte.d.ts +36 -0
- package/dist/KeyMapper.svelte.d.ts.map +1 -0
- package/dist/KeyMapper.svelte.js +141 -0
- package/dist/Node.svelte +33 -35
- package/dist/Node.svelte.d.ts.map +1 -1
- package/dist/NodeArrayProperty.svelte +40 -37
- package/dist/NodeArrayProperty.svelte.d.ts.map +1 -1
- package/dist/Session.svelte.d.ts +223 -0
- package/dist/Session.svelte.d.ts.map +1 -0
- package/dist/Session.svelte.js +594 -0
- package/dist/Svedit.svelte +1352 -1465
- package/dist/Svedit.svelte.d.ts.map +1 -1
- package/dist/Transaction.svelte.d.ts +122 -51
- package/dist/Transaction.svelte.d.ts.map +1 -1
- package/dist/Transaction.svelte.js +845 -551
- package/dist/UnknownNode.svelte +6 -6
- package/dist/UnknownNode.svelte.d.ts.map +1 -1
- package/dist/doc_utils.d.ts +127 -0
- package/dist/doc_utils.d.ts.map +1 -0
- package/dist/doc_utils.js +482 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +20 -4
- package/dist/{commands.svelte.d.ts → transforms.svelte.d.ts} +1 -2
- package/dist/transforms.svelte.d.ts.map +1 -0
- package/dist/transforms.svelte.js +155 -0
- package/dist/types.d.ts +256 -110
- package/dist/{util.d.ts → utils.d.ts} +23 -2
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +338 -0
- package/package.json +25 -23
- package/src/lib/AnnotatedTextProperty.svelte +122 -75
- package/src/lib/Command.svelte.js +314 -0
- package/src/lib/CustomProperty.svelte +31 -42
- package/src/lib/KeyMapper.svelte.js +141 -0
- package/src/lib/Node.svelte +33 -35
- package/src/lib/NodeArrayProperty.svelte +40 -37
- package/src/lib/Session.svelte.js +594 -0
- package/src/lib/Svedit.svelte +1352 -1465
- package/src/lib/Transaction.svelte.js +845 -551
- package/src/lib/UnknownNode.svelte +6 -6
- package/src/lib/doc_utils.js +482 -0
- package/src/lib/index.js +20 -4
- package/src/lib/transforms.svelte.js +155 -0
- package/src/lib/types.d.ts +256 -110
- package/src/lib/utils.js +338 -0
- package/dist/Document.svelte.d.ts +0 -214
- package/dist/Document.svelte.d.ts.map +0 -1
- package/dist/Document.svelte.js +0 -796
- package/dist/commands.svelte.d.ts.map +0 -1
- package/dist/commands.svelte.js +0 -271
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js +0 -235
- package/src/lib/Document.svelte.js +0 -796
- package/src/lib/commands.svelte.js +0 -271
- package/src/lib/util.js +0 -235
package/src/lib/utils.js
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { Annotation, AnnotatedText, SelectionRange, Selection } from './types.d.ts';
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Detect if the current browser is on a mobile device
|
|
7
|
+
* @returns {boolean} true if mobile browser, false otherwise
|
|
8
|
+
*/
|
|
9
|
+
export function is_mobile_browser() {
|
|
10
|
+
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const user_agent = navigator.userAgent;
|
|
15
|
+
return (
|
|
16
|
+
/iPhone|iPad|iPod|Android|Mobile/i.test(user_agent) ||
|
|
17
|
+
'ontouchstart' in window ||
|
|
18
|
+
navigator.maxTouchPoints > 0
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Detect if the current browser is Chrome on desktop
|
|
24
|
+
* @returns {boolean} true if Chrome desktop browser, false otherwise
|
|
25
|
+
*/
|
|
26
|
+
export function is_chrome_desktop_browser() {
|
|
27
|
+
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const user_agent = navigator.userAgent;
|
|
31
|
+
const is_chrome = user_agent.includes('Chrome') && !user_agent.includes('Edg');
|
|
32
|
+
const is_mobile = is_mobile_browser();
|
|
33
|
+
return is_chrome && !is_mobile;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the actual character length (accounting for multi-byte characters)
|
|
38
|
+
*
|
|
39
|
+
* Uses Intl.Segmenter to count grapheme clusters rather than UTF-16 code units,
|
|
40
|
+
* ensuring emojis and other complex Unicode sequences are counted as single characters.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} str - The string to measure
|
|
43
|
+
* @returns {number} The number of visual characters (grapheme clusters)
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* get_char_length('Hello') // Returns: 5
|
|
47
|
+
* get_char_length('a😀b') // Returns: 3 (not 4)
|
|
48
|
+
* get_char_length('👋🏽') // Returns: 1 (not 4 - skin tone modifier treated as single char)
|
|
49
|
+
*/
|
|
50
|
+
export function get_char_length(str) {
|
|
51
|
+
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
52
|
+
return [...segmenter.segment(str)].length;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get a single character at the specified position (accounting for multi-byte characters)
|
|
57
|
+
*
|
|
58
|
+
* Uses Intl.Segmenter to access grapheme clusters rather than UTF-16 code units,
|
|
59
|
+
* ensuring emojis and other complex Unicode sequences are treated as single characters.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} str - The string to access
|
|
62
|
+
* @param {number} index - Character position (0-based)
|
|
63
|
+
* @returns {string} The character at the specified position, or empty string if index is out of bounds
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* get_char_at('Hello', 1) // Returns: 'e'
|
|
67
|
+
* get_char_at('a😀b', 1) // Returns: '😀' (full emoji)
|
|
68
|
+
* get_char_at('👋🏽', 0) // Returns: '👋🏽' (skin tone modifier included)
|
|
69
|
+
*/
|
|
70
|
+
export function get_char_at(str, index) {
|
|
71
|
+
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
72
|
+
const segments = [...segmenter.segment(str)];
|
|
73
|
+
return segments[index].segment;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Slice string by character positions (accounting for multi-byte characters)
|
|
78
|
+
*
|
|
79
|
+
* Uses Intl.Segmenter to slice by grapheme clusters rather than UTF-16 code units,
|
|
80
|
+
* ensuring emojis and other complex Unicode sequences remain intact.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} str - The string to slice
|
|
83
|
+
* @param {number} start - Starting character position (inclusive)
|
|
84
|
+
* @param {number} [end] - Ending character position (exclusive). If undefined, slices to end
|
|
85
|
+
* @returns {string} The sliced string with complete characters
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* char_slice('Hello 😀 World', 6, 8) // Returns: '😀 ' (emoji stays intact)
|
|
89
|
+
* char_slice('a👋🏽b', 1, 2) // Returns: '👋🏽' (skin tone modifier included)
|
|
90
|
+
*/
|
|
91
|
+
export function char_slice(str, start, end = undefined) {
|
|
92
|
+
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
93
|
+
const segments = [...segmenter.segment(str)];
|
|
94
|
+
return segments
|
|
95
|
+
.slice(start, end)
|
|
96
|
+
.map((s) => s.segment)
|
|
97
|
+
.join('');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Convert UTF-16 code unit offset to grapheme cluster offset within a string
|
|
102
|
+
*
|
|
103
|
+
* Converts DOM selection offsets (which use UTF-16 code units) to character-based
|
|
104
|
+
* offsets (grapheme clusters) for consistent text manipulation.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} str - The string to convert offsets within
|
|
107
|
+
* @param {number} utf16_offset - The UTF-16 code unit offset from DOM
|
|
108
|
+
* @returns {number} The corresponding grapheme cluster offset
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* // For string "a😀b" where 😀 uses 2 UTF-16 code units
|
|
112
|
+
* utf16_to_char_offset("a😀b", 3) // Returns: 2 (position after emoji)
|
|
113
|
+
*/
|
|
114
|
+
export function utf16_to_char_offset(str, utf16_offset) {
|
|
115
|
+
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
116
|
+
const segments = [...segmenter.segment(str)];
|
|
117
|
+
let char_offset = 0;
|
|
118
|
+
let utf16_count = 0;
|
|
119
|
+
|
|
120
|
+
for (const segment of segments) {
|
|
121
|
+
if (utf16_count >= utf16_offset) break;
|
|
122
|
+
utf16_count += segment.segment.length;
|
|
123
|
+
if (utf16_count > utf16_offset) break;
|
|
124
|
+
char_offset++;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return char_offset;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Convert grapheme cluster offset to UTF-16 code unit offset within a string
|
|
132
|
+
*
|
|
133
|
+
* Converts character-based offsets (grapheme clusters) to DOM selection offsets
|
|
134
|
+
* (UTF-16 code units) for proper DOM selection positioning.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} str - The string to convert offsets within
|
|
137
|
+
* @param {number} char_offset - The grapheme cluster offset
|
|
138
|
+
* @returns {number} The corresponding UTF-16 code unit offset for DOM operations
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* // For string "a😀b" where 😀 uses 2 UTF-16 code units
|
|
142
|
+
* char_to_utf16_offset("a😀b", 2) // Returns: 3 (UTF-16 position after emoji)
|
|
143
|
+
*/
|
|
144
|
+
export function char_to_utf16_offset(str, char_offset) {
|
|
145
|
+
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
146
|
+
const segments = [...segmenter.segment(str)];
|
|
147
|
+
let utf16_offset = 0;
|
|
148
|
+
|
|
149
|
+
for (let i = 0; i < Math.min(char_offset, segments.length); i++) {
|
|
150
|
+
utf16_offset += segments[i].segment.length;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return utf16_offset;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Splits an annotated text at the specified character position.
|
|
158
|
+
*
|
|
159
|
+
* Annotations that span the split point will be divided appropriately,
|
|
160
|
+
* with offsets adjusted for each resulting part.
|
|
161
|
+
*
|
|
162
|
+
* @param {AnnotatedText} text_with_annotations - Annotated text object
|
|
163
|
+
* @param {number} at_position - Character position where to split (0-based)
|
|
164
|
+
* @returns {[AnnotatedText, AnnotatedText]} Tuple of [left_part, right_part]
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* split_annotated_text({text: "Hello world", annotations: [{start_offset: 6, end_offset: 11, node_id: "strong"}]}, 8)
|
|
168
|
+
* // Returns:
|
|
169
|
+
* // [
|
|
170
|
+
* // {text: "Hello wo", annotations: [{start_offset: 6, end_offset: 8, node_id: "strong"}]},
|
|
171
|
+
* // {text: "rld", annotations: [{start_offset: 0, end_offset: 3, node_id: "strong"}]}
|
|
172
|
+
* // ]
|
|
173
|
+
*/
|
|
174
|
+
export function split_annotated_text(text_with_annotations, at_position) {
|
|
175
|
+
const { text, annotations } = text_with_annotations;
|
|
176
|
+
|
|
177
|
+
// Split the text using character-aware slicing
|
|
178
|
+
const left_text = char_slice(text, 0, at_position);
|
|
179
|
+
const right_text = char_slice(text, at_position);
|
|
180
|
+
|
|
181
|
+
/** @type {Array<Annotation>} */
|
|
182
|
+
const left_annotations = [];
|
|
183
|
+
/** @type {Array<Annotation>} */
|
|
184
|
+
const right_annotations = [];
|
|
185
|
+
|
|
186
|
+
// Process each annotation
|
|
187
|
+
for (const { start_offset, end_offset, node_id } of annotations) {
|
|
188
|
+
if (end_offset <= at_position) {
|
|
189
|
+
// Annotation is entirely in the left part
|
|
190
|
+
left_annotations.push({ start_offset, end_offset, node_id });
|
|
191
|
+
} else if (start_offset >= at_position) {
|
|
192
|
+
// Annotation is entirely in the right part - shift offsets
|
|
193
|
+
right_annotations.push({
|
|
194
|
+
start_offset: start_offset - at_position,
|
|
195
|
+
end_offset: end_offset - at_position,
|
|
196
|
+
node_id
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
// Annotation spans the split point - split it
|
|
200
|
+
left_annotations.push({ start_offset, end_offset: at_position, node_id });
|
|
201
|
+
right_annotations.push({ start_offset: 0, end_offset: end_offset - at_position, node_id });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return [
|
|
206
|
+
{ text: left_text, annotations: left_annotations },
|
|
207
|
+
{ text: right_text, annotations: right_annotations }
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Joins two annotated texts into a single annotated text.
|
|
213
|
+
*
|
|
214
|
+
* Annotations from the second text will have their offsets shifted by the length
|
|
215
|
+
* of the first text. Adjacent annotations of the same type and data will be merged.
|
|
216
|
+
*
|
|
217
|
+
* @param {AnnotatedText} first_text - First annotated text object
|
|
218
|
+
* @param {AnnotatedText} second_text - Second annotated text object
|
|
219
|
+
* @returns {AnnotatedText} Combined annotated text object
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* join_annotated_text({text: "Hello wo", annotations: [{start_offset: 6, end_offset: 8, node_id: "strong"}]}, {text: "rld", annotations: [{start_offset: 0, end_offset: 3, node_id: "strong"}]})
|
|
223
|
+
* // Returns: {text: "Hello world", annotations: [{start_offset: 6, end_offset: 11, node_id: "strong"}]}
|
|
224
|
+
*/
|
|
225
|
+
export function join_annotated_text(first_text, second_text) {
|
|
226
|
+
const { text: first_text_content, annotations: first_annotations } = first_text;
|
|
227
|
+
const { text: second_text_content, annotations: second_annotations } = second_text;
|
|
228
|
+
|
|
229
|
+
// Join the text content
|
|
230
|
+
const joined_text = first_text_content + second_text_content;
|
|
231
|
+
|
|
232
|
+
// Start with all annotations from the first text (unchanged)
|
|
233
|
+
/** @type {Array<Annotation>} */
|
|
234
|
+
const joined_annotations = [...first_annotations];
|
|
235
|
+
|
|
236
|
+
// Add annotations from the second text, shifting their offsets
|
|
237
|
+
const offset = get_char_length(first_text_content);
|
|
238
|
+
for (const { start_offset, end_offset, node_id } of second_annotations) {
|
|
239
|
+
/** @type {Annotation} */
|
|
240
|
+
const shifted_annotation = {
|
|
241
|
+
start_offset: start_offset + offset,
|
|
242
|
+
end_offset: end_offset + offset,
|
|
243
|
+
node_id
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// Check if this annotation can be merged with the last annotation from first text
|
|
247
|
+
const last_annotation = joined_annotations[joined_annotations.length - 1];
|
|
248
|
+
if (
|
|
249
|
+
last_annotation &&
|
|
250
|
+
last_annotation.end_offset === shifted_annotation.start_offset && // annotations are adjacent
|
|
251
|
+
last_annotation.node_id === shifted_annotation.node_id
|
|
252
|
+
) {
|
|
253
|
+
// same node_id
|
|
254
|
+
// Merge by extending the end position of the last annotation
|
|
255
|
+
last_annotation.end_offset = shifted_annotation.end_offset;
|
|
256
|
+
} else {
|
|
257
|
+
// Add as separate annotation
|
|
258
|
+
joined_annotations.push(shifted_annotation);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return { text: joined_text, annotations: joined_annotations };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Convert snake_case string to PascalCase
|
|
267
|
+
*
|
|
268
|
+
* @param {string} str - The snake_case string to convert
|
|
269
|
+
* @returns {string} The converted PascalCase string
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* snake_to_pascal('list_item') // Returns: 'ListItem'
|
|
273
|
+
*/
|
|
274
|
+
export function snake_to_pascal(str) {
|
|
275
|
+
return str
|
|
276
|
+
.split('_')
|
|
277
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
278
|
+
.join('');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function traverse(node_id, schema, nodes) {
|
|
282
|
+
const json = [];
|
|
283
|
+
const visited = {};
|
|
284
|
+
const visit = (node) => {
|
|
285
|
+
if (!node || visited[node.id]) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
visited[node.id] = true;
|
|
289
|
+
for (const [property_name, value] of Object.entries(node)) {
|
|
290
|
+
const property_definition = schema[node.type].properties[property_name];
|
|
291
|
+
|
|
292
|
+
if (property_definition?.type === 'node_array') {
|
|
293
|
+
for (const v of value) {
|
|
294
|
+
if (typeof v === 'string') {
|
|
295
|
+
visit(nodes[v]);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
} else if (property_definition?.type === 'node') {
|
|
299
|
+
visit(nodes[value]);
|
|
300
|
+
} else if (property_definition?.type === 'annotated_text') {
|
|
301
|
+
for (const annotation of value.annotations) {
|
|
302
|
+
visit(nodes[annotation.node_id]);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Finally add the node to the result.
|
|
307
|
+
// Deep clone, to make sure nothing of the original document is referenced.
|
|
308
|
+
json.push(structuredClone(node));
|
|
309
|
+
};
|
|
310
|
+
// Start with the root node (document_id)
|
|
311
|
+
visit(nodes[node_id]);
|
|
312
|
+
return json;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Extracts the normalized range from a text or node selection.
|
|
317
|
+
* Returns start and end offsets in document order (start <= end), regardless of selection direction.
|
|
318
|
+
* @param {Selection} [selection] - The selection to extract the range from
|
|
319
|
+
* @returns {SelectionRange | null} The normalized selection range, or null if selection is null, undefined, or a property selection
|
|
320
|
+
*/
|
|
321
|
+
export function get_selection_range(selection) {
|
|
322
|
+
if (selection && selection.type !== 'property') {
|
|
323
|
+
return {
|
|
324
|
+
start_offset: Math.min(selection.anchor_offset, selection.focus_offset),
|
|
325
|
+
end_offset: Math.max(selection.anchor_offset, selection.focus_offset)
|
|
326
|
+
};
|
|
327
|
+
} else {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function is_selection_collapsed(selection) {
|
|
333
|
+
if (selection && selection.type !== 'property') {
|
|
334
|
+
return selection.anchor_offset === selection.focus_offset;
|
|
335
|
+
} else {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @import {
|
|
3
|
-
* NodeId,
|
|
4
|
-
* DocumentPath,
|
|
5
|
-
* Selection,
|
|
6
|
-
* Annotation,
|
|
7
|
-
* PrimitiveType,
|
|
8
|
-
* DocumentSchemaPrimitive,
|
|
9
|
-
* NodeProperty,
|
|
10
|
-
* NodeArrayProperty,
|
|
11
|
-
* NodeKind,
|
|
12
|
-
* NodeSchema,
|
|
13
|
-
* TextNodeSchema,
|
|
14
|
-
* NonTextNodeSchema,
|
|
15
|
-
* DocumentSchema,
|
|
16
|
-
* SerializedDocument
|
|
17
|
-
* } from './types.d.ts';
|
|
18
|
-
*/
|
|
19
|
-
/**
|
|
20
|
-
* Identity function — keeps schema at runtime & makes IDE infer types.
|
|
21
|
-
* Similar to your define_schema pattern but for document schemas.
|
|
22
|
-
*
|
|
23
|
-
* @template {Record<string, NodeSchema>} S
|
|
24
|
-
* @param {S} schema - The document schema to validate
|
|
25
|
-
* @returns {S} The same schema, but with type information preserved
|
|
26
|
-
*/
|
|
27
|
-
export function define_document_schema<S extends Record<string, NodeSchema>>(schema: S): S;
|
|
28
|
-
/**
|
|
29
|
-
* Check if a string represents a valid primitive type.
|
|
30
|
-
* @param {string} type - The type string to check
|
|
31
|
-
* @returns {type is PrimitiveType} True if it's a valid primitive type
|
|
32
|
-
*/
|
|
33
|
-
export function is_primitive_type(type: string): type is PrimitiveType;
|
|
34
|
-
/**
|
|
35
|
-
* Get the default node type for a property that references nodes.
|
|
36
|
-
* @param {NodeProperty | NodeArrayProperty} property_definition - The property definition
|
|
37
|
-
* @returns {string | null} The default node type, or null if none specified
|
|
38
|
-
*/
|
|
39
|
-
export function get_default_node_type(property_definition: NodeProperty | NodeArrayProperty): string | null;
|
|
40
|
-
/**
|
|
41
|
-
* Validate a document schema to ensure all referenced node types exist.
|
|
42
|
-
* @param {DocumentSchema} document_schema - The document schema to validate
|
|
43
|
-
* @throws {Error} Throws if the document schema is invalid
|
|
44
|
-
*/
|
|
45
|
-
export function validate_document_schema(document_schema: DocumentSchema): void;
|
|
46
|
-
export default class Document {
|
|
47
|
-
/**
|
|
48
|
-
* @param {DocumentSchema} schema - The document schema
|
|
49
|
-
* @param {SerializedDocument} serialized_doc - The serialized document array
|
|
50
|
-
* @param {object} [options] - Optional configuration
|
|
51
|
-
* @param {Selection} [options.selection] - Initial selection state
|
|
52
|
-
* @param {any} [options.config] - Document configuration
|
|
53
|
-
*/
|
|
54
|
-
constructor(schema: DocumentSchema, serialized_doc: SerializedDocument, { selection, config }?: {
|
|
55
|
-
selection?: Selection;
|
|
56
|
-
config?: any;
|
|
57
|
-
});
|
|
58
|
-
config: any;
|
|
59
|
-
document_id: any;
|
|
60
|
-
nodes: any;
|
|
61
|
-
history: any[];
|
|
62
|
-
history_index: number;
|
|
63
|
-
can_undo: boolean;
|
|
64
|
-
can_redo: boolean;
|
|
65
|
-
selected_node: any;
|
|
66
|
-
layout_node: any;
|
|
67
|
-
available_annotation_types: any;
|
|
68
|
-
/**
|
|
69
|
-
* Sets the selection with validation
|
|
70
|
-
* @param {Selection | undefined} value - The new selection
|
|
71
|
-
* @throws {Error} Throws if the selection is invalid
|
|
72
|
-
*/
|
|
73
|
-
set selection(value: Selection | undefined);
|
|
74
|
-
/**
|
|
75
|
-
* Gets the current selection
|
|
76
|
-
* @returns {Selection | undefined}
|
|
77
|
-
*/
|
|
78
|
-
get selection(): Selection | undefined;
|
|
79
|
-
/**
|
|
80
|
-
* Validates that a selection is within bounds and refers to valid paths.
|
|
81
|
-
*
|
|
82
|
-
* @param {Selection} selection - The selection to validate
|
|
83
|
-
* @throws {Error} Throws if the selection is invalid
|
|
84
|
-
* @private
|
|
85
|
-
*/
|
|
86
|
-
private _validate_selection;
|
|
87
|
-
schema: DocumentSchema;
|
|
88
|
-
generate_id(): any;
|
|
89
|
-
get_available_annotation_types(): any;
|
|
90
|
-
get_selected_node(): any;
|
|
91
|
-
get_layout_node(): any;
|
|
92
|
-
_apply_op(op: any): void;
|
|
93
|
-
/**
|
|
94
|
-
* Validate a node against the document schema.
|
|
95
|
-
* @param {any} node - The node to validate
|
|
96
|
-
* @throws {Error} Throws if the node is invalid
|
|
97
|
-
*/
|
|
98
|
-
validate_node(node: any): void;
|
|
99
|
-
/**
|
|
100
|
-
* Creates a new transaction for making atomic changes to the document.
|
|
101
|
-
*
|
|
102
|
-
* @returns {Transaction} A new transaction instance
|
|
103
|
-
*/
|
|
104
|
-
get tr(): Transaction;
|
|
105
|
-
/**
|
|
106
|
-
* Applies a transaction to the document.
|
|
107
|
-
*
|
|
108
|
-
* @param {Transaction} transaction - The transaction to apply
|
|
109
|
-
*/
|
|
110
|
-
apply(transaction: Transaction): this;
|
|
111
|
-
undo(): this;
|
|
112
|
-
redo(): this;
|
|
113
|
-
/**
|
|
114
|
-
* Gets a node instance or property value at the specified path.
|
|
115
|
-
* @param {DocumentPath|string} path - Path to the node or property
|
|
116
|
-
* @returns {any} Either a node instance object or the value of a property
|
|
117
|
-
* @example
|
|
118
|
-
* // Get a node by ID
|
|
119
|
-
* doc.get('list_1') // => { type: 'list', id: 'list_1', ... }
|
|
120
|
-
*
|
|
121
|
-
* @example
|
|
122
|
-
* // Get a node array property
|
|
123
|
-
* doc.get(['list_1', 'list_items']) // => [ 'list_item_1', 'list_item_2' ]
|
|
124
|
-
*
|
|
125
|
-
* @example
|
|
126
|
-
* // Get a specific node from an array
|
|
127
|
-
* doc.get(['page_1', 'body', 3, 'list_items', 0]) // => { type: 'list_item', id: 'list_item_1', ... }
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* // Get an annotated text property
|
|
131
|
-
* doc.get(['page_1', 'cover', 'title']) // => {text: 'Hello world', annotations: []}
|
|
132
|
-
*/
|
|
133
|
-
get(path: DocumentPath | string): any;
|
|
134
|
-
/**
|
|
135
|
-
* While .get gives you the value of a path, inspect gives you
|
|
136
|
-
* the type info of that value.
|
|
137
|
-
*
|
|
138
|
-
* @todo The layout of these should be improved and more explictly typed
|
|
139
|
-
*
|
|
140
|
-
* @example
|
|
141
|
-
* doc.inspect(['page_1', 'body']) => {
|
|
142
|
-
* kind: 'property',
|
|
143
|
-
* name: 'body',
|
|
144
|
-
* type: 'node_array',
|
|
145
|
-
* node_types: ['text', 'story', 'list'],
|
|
146
|
-
* default_node_type: 'text'
|
|
147
|
-
* }
|
|
148
|
-
*
|
|
149
|
-
* @example
|
|
150
|
-
* doc.inspect(['page_1', 'body', 1]) => {
|
|
151
|
-
* kind: 'node',
|
|
152
|
-
* id: 'paragraph_234',
|
|
153
|
-
* type: 'paragraph',
|
|
154
|
-
* properties: {...}
|
|
155
|
-
* }
|
|
156
|
-
*
|
|
157
|
-
* @param {DocumentPath} path
|
|
158
|
-
* @returns {{kind: 'property'|'node', [key: string]: any}}
|
|
159
|
-
*/
|
|
160
|
-
inspect(path: DocumentPath): {
|
|
161
|
-
kind: "property" | "node";
|
|
162
|
-
[key: string]: any;
|
|
163
|
-
};
|
|
164
|
-
/**
|
|
165
|
-
* Determines the kind of a node ('block' for structured blocks, 'text' for pure
|
|
166
|
-
* text nodes or 'annotation' for annotation nodes.
|
|
167
|
-
* @param {any} node
|
|
168
|
-
* @returns {NodeKind}
|
|
169
|
-
*/
|
|
170
|
-
kind(node: any): NodeKind;
|
|
171
|
-
/**
|
|
172
|
-
* Returns the annotation object that is currently "under the cursor".
|
|
173
|
-
* NOTE: Annotations in Svedit are exclusive, so there can only be one active_annotation
|
|
174
|
-
*
|
|
175
|
-
* @param {string} annotation_type
|
|
176
|
-
* @returns {Annotation|null}
|
|
177
|
-
*/
|
|
178
|
-
active_annotation(annotation_type: string): Annotation | null;
|
|
179
|
-
get_selected_plain_text(): string;
|
|
180
|
-
get_selected_nodes(): any;
|
|
181
|
-
select_parent(): void;
|
|
182
|
-
get_selection_range(): {
|
|
183
|
-
start: number;
|
|
184
|
-
end: number;
|
|
185
|
-
length: number;
|
|
186
|
-
};
|
|
187
|
-
traverse(node_id: any): any[];
|
|
188
|
-
/**
|
|
189
|
-
* Convert the document to serialized format.
|
|
190
|
-
* @returns {SerializedDocument} The serialized document array
|
|
191
|
-
*/
|
|
192
|
-
to_json(): SerializedDocument;
|
|
193
|
-
property_type(type: any, property: any): "string" | "number" | "boolean" | "node" | "node_array" | "annotated_text" | "integer" | "datetime" | "string_array" | "number_array" | "boolean_array" | "integer_array";
|
|
194
|
-
count_references(node_id: any): number;
|
|
195
|
-
/**
|
|
196
|
-
* @param {NodeId} node_id
|
|
197
|
-
* @returns {NodeId[]}
|
|
198
|
-
*/
|
|
199
|
-
get_referenced_nodes(node_id: NodeId): NodeId[];
|
|
200
|
-
#private;
|
|
201
|
-
}
|
|
202
|
-
import type { NodeSchema } from './types.d.ts';
|
|
203
|
-
import type { PrimitiveType } from './types.d.ts';
|
|
204
|
-
import type { NodeProperty } from './types.d.ts';
|
|
205
|
-
import type { NodeArrayProperty } from './types.d.ts';
|
|
206
|
-
import type { DocumentSchema } from './types.d.ts';
|
|
207
|
-
import type { Selection } from './types.d.ts';
|
|
208
|
-
import Transaction from './Transaction.svelte.js';
|
|
209
|
-
import type { DocumentPath } from './types.d.ts';
|
|
210
|
-
import type { NodeKind } from './types.d.ts';
|
|
211
|
-
import type { Annotation } from './types.d.ts';
|
|
212
|
-
import type { SerializedDocument } from './types.d.ts';
|
|
213
|
-
import type { NodeId } from './types.d.ts';
|
|
214
|
-
//# sourceMappingURL=Document.svelte.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Document.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Document.svelte.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;GAOG;AACH,uCAJ0C,CAAC,SAA7B,MAAM,CAAC,MAAM,EAAE,UAAU,CAAE,UAC9B,CAAC,GACC,CAAC,CAIb;AAED;;;;GAIG;AACH,wCAHW,MAAM,GACJ,IAAI,IAAI,aAAa,CAQjC;AAED;;;;GAIG;AACH,2DAHW,YAAY,GAAG,iBAAiB,GAC9B,MAAM,GAAG,IAAI,CASzB;AAED;;;;GAIG;AACH,0DAHW,cAAc,QAexB;AAuGD;IAgGE;;;;;;OAMG;IACH,oBANW,cAAc,kBACd,kBAAkB,0BAE1B;QAA4B,SAAS,GAA7B,SAAS;QACK,MAAM,GAApB,GAAG;KACb,EAqBA;IAxHD,YAAkB;IAClB,iBAAuB;IACvB,WAAiB;IACjB,eAAqB;IACrB,sBAA2B;IAG3B,kBAA6C;IAC7C,kBAAkE;IAGnE,mBAAmD;IACnD,iBAA+C;IAC/C,gCAA6E;IAU5E;;;;OAIG;IACH,qBAHW,SAAS,GAAG,SAAS,EAQ/B;IAlBD;;;OAGG;IACH,iBAFa,SAAS,GAAG,SAAS,CAIjC;IAcD;;;;;;OAMG;IACH,4BAiDC;IAaC,uBAAoB;IAkBtB,mBAMC;IAED,sCAKA;IAGD,yBAkBC;IAID,uBAoBC;IAGA,yBAWC;IAED;;;;OAIG;IACH,oBAHW,GAAG,QAKb;IAED;;;;OAIG;IACH,UAFa,WAAW,CASvB;IAED;;;;OAIG;IACH,mBAFW,WAAW,QAkBrB;IAED,aAWC;IAED,aAaC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,UAlBW,YAAY,GAAC,MAAM,GACjB,GAAG,CAoFf;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,cAHW,YAAY,GACV;QAAC,IAAI,EAAE,UAAU,GAAC,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAC,CAuBzD;IAED;;;;;OAKG;IACH,WAHW,GAAG,GACD,QAAQ,CAIpB;IAED;;;;;;OAMG;IACH,mCAHW,MAAM,GACJ,UAAU,GAAC,IAAI,CAqB3B;IAGD,kCAOC;IAED,0BAOC;IAED,sBAmCC;IAED;;;;MAaC;IAKD,8BAgCC;IAED;;;OAGG;IACH,WAFa,kBAAkB,CAI9B;IAID,mNAWC;IAGD,uCAkBC;IAGD;;;OAGG;IACH,8BAHW,MAAM,GACJ,MAAM,EAAE,CASpB;;CACF;gCAxwBS,cAAc;mCAAd,cAAc;kCAAd,cAAc;uCAAd,cAAc;oCAAd,cAAc;+BAAd,cAAc;wBAnBA,yBAAyB;kCAmBvC,cAAc;8BAAd,cAAc;gCAAd,cAAc;wCAAd,cAAc;4BAAd,cAAc"}
|