@aionbuilders/nabu 0.1.0-alpha.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 (62) hide show
  1. package/README.md +131 -0
  2. package/dist/behaviors/index.d.ts +1 -0
  3. package/dist/behaviors/index.js +1 -0
  4. package/dist/behaviors/text/RichText.svelte +33 -0
  5. package/dist/behaviors/text/RichText.svelte.d.ts +11 -0
  6. package/dist/behaviors/text/index.d.ts +3 -0
  7. package/dist/behaviors/text/index.js +3 -0
  8. package/dist/behaviors/text/rich-text.extension.d.ts +2 -0
  9. package/dist/behaviors/text/rich-text.extension.js +75 -0
  10. package/dist/behaviors/text/text.behavior.svelte.d.ts +103 -0
  11. package/dist/behaviors/text/text.behavior.svelte.js +346 -0
  12. package/dist/blocks/Block.svelte +18 -0
  13. package/dist/blocks/Block.svelte.d.ts +11 -0
  14. package/dist/blocks/Nabu.svelte +31 -0
  15. package/dist/blocks/Nabu.svelte.d.ts +12 -0
  16. package/dist/blocks/block.svelte.d.ts +143 -0
  17. package/dist/blocks/block.svelte.js +364 -0
  18. package/dist/blocks/container.utils.d.ts +28 -0
  19. package/dist/blocks/container.utils.js +114 -0
  20. package/dist/blocks/heading/Heading.svelte +42 -0
  21. package/dist/blocks/heading/Heading.svelte.d.ts +11 -0
  22. package/dist/blocks/heading/heading.svelte.d.ts +45 -0
  23. package/dist/blocks/heading/heading.svelte.js +94 -0
  24. package/dist/blocks/heading/hooks/onBeforeInput.hook.d.ts +3 -0
  25. package/dist/blocks/heading/hooks/onBeforeInput.hook.js +58 -0
  26. package/dist/blocks/heading/index.d.ts +7 -0
  27. package/dist/blocks/heading/index.js +41 -0
  28. package/dist/blocks/index.d.ts +10 -0
  29. package/dist/blocks/index.js +12 -0
  30. package/dist/blocks/list/List.svelte +25 -0
  31. package/dist/blocks/list/List.svelte.d.ts +11 -0
  32. package/dist/blocks/list/ListItem.svelte +45 -0
  33. package/dist/blocks/list/ListItem.svelte.d.ts +11 -0
  34. package/dist/blocks/list/index.d.ts +10 -0
  35. package/dist/blocks/list/index.js +41 -0
  36. package/dist/blocks/list/list-item.svelte.d.ts +50 -0
  37. package/dist/blocks/list/list-item.svelte.js +213 -0
  38. package/dist/blocks/list/list.behavior.svelte.d.ts +23 -0
  39. package/dist/blocks/list/list.behavior.svelte.js +61 -0
  40. package/dist/blocks/list/list.svelte.d.ts +39 -0
  41. package/dist/blocks/list/list.svelte.js +139 -0
  42. package/dist/blocks/megablock.svelte.d.ts +13 -0
  43. package/dist/blocks/megablock.svelte.js +64 -0
  44. package/dist/blocks/nabu.svelte.d.ts +121 -0
  45. package/dist/blocks/nabu.svelte.js +395 -0
  46. package/dist/blocks/paragraph/Paragraph.svelte +38 -0
  47. package/dist/blocks/paragraph/Paragraph.svelte.d.ts +11 -0
  48. package/dist/blocks/paragraph/index.d.ts +7 -0
  49. package/dist/blocks/paragraph/index.js +44 -0
  50. package/dist/blocks/paragraph/paragraph.svelte.d.ts +41 -0
  51. package/dist/blocks/paragraph/paragraph.svelte.js +86 -0
  52. package/dist/blocks/selection.svelte.d.ts +38 -0
  53. package/dist/blocks/selection.svelte.js +143 -0
  54. package/dist/index.d.ts +4 -0
  55. package/dist/index.js +5 -0
  56. package/dist/utils/extensions.d.ts +69 -0
  57. package/dist/utils/extensions.js +43 -0
  58. package/dist/utils/index.d.ts +2 -0
  59. package/dist/utils/index.js +2 -0
  60. package/dist/utils/selection.svelte.d.ts +219 -0
  61. package/dist/utils/selection.svelte.js +611 -0
  62. package/package.json +74 -0
@@ -0,0 +1,346 @@
1
+ /**
2
+ * @import {NabuNode, Block, NabuSelection} from "../../blocks";
3
+ */
4
+
5
+ import { LoroText } from "loro-crdt";
6
+ import { tick } from "svelte";
7
+
8
+ /**
9
+ * Converts a Loro text delta to inline Markdown.
10
+ * @param {import('loro-crdt').Delta<string>[]} delta
11
+ * @returns {string}
12
+ */
13
+ export function deltaToMarkdown(delta) {
14
+ return delta.map(op => {
15
+ if (typeof op.insert !== 'string') return '';
16
+ const text = op.insert;
17
+ const attrs = op.attributes || {};
18
+ if (attrs.code) return `\`${text}\``;
19
+ if (attrs.bold && attrs.italic) return `***${text}***`;
20
+ if (attrs.bold) return `**${text}**`;
21
+ if (attrs.italic) return `*${text}*`;
22
+ if (attrs.strikethrough) return `~~${text}~~`;
23
+ if (attrs.underline) return `<u>${text}</u>`;
24
+ return text;
25
+ }).join('');
26
+ }
27
+
28
+ /**
29
+ * @typedef {NabuNode<{type: "paragraph", text: LoroText}>} TextNode
30
+ */
31
+
32
+ export class TextBehavior {
33
+ /** @param {Block} block @param {LoroText} [container] */
34
+ constructor(block, container) {
35
+ this.block = block;
36
+ this.nabu = block.nabu;
37
+ const node = /** @type {TextNode} */ (block.node);
38
+ /** @type {LoroText} */
39
+ this.container = container ?? node.data.get("text") ?? node.data.setContainer("text", new LoroText());
40
+ this.text = $state(this.container.toString());
41
+ this.delta = $state(this.container.toDelta());
42
+ this.container.subscribe(() => {
43
+ this.text = this.container.toString();
44
+ this.delta = this.container.toDelta();
45
+ });
46
+ }
47
+
48
+ selection = $derived(this.block.element && this.block.nabu.selection && this.getSelection(this.block.element, this.block.nabu.selection));
49
+
50
+ /**
51
+ * @param {HTMLElement} element
52
+ * @param {NabuSelection} globalSelection
53
+ */
54
+ getSelection(element, globalSelection) {
55
+ if (!this.block.selected || !element || !globalSelection) return null;
56
+
57
+ const globalRange = globalSelection.firstRange;
58
+ if (!globalRange) return null;
59
+
60
+ const containsStart = element.contains(globalRange.startContainer);
61
+ const containsEnd = element.contains(globalRange.endContainer);
62
+
63
+ let from = 0;
64
+ let to = this.text.length;
65
+
66
+ if (containsStart) {
67
+ from = this.calculateOffset(globalRange.startContainer, globalRange.startOffset);
68
+ }
69
+
70
+ if (containsEnd) {
71
+ to = this.calculateOffset(globalRange.endContainer, globalRange.endOffset);
72
+ }
73
+
74
+ return {
75
+ from,
76
+ to,
77
+ isCollapsed: from === to,
78
+ direction: globalSelection.direction,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Calcule l'offset textuel d'un point DOM par rapport au début de ce bloc
84
+ * @param {Node} node
85
+ * @param {number} offset
86
+ * @param {HTMLElement?} [element]
87
+ */
88
+ calculateOffset(node, offset, element = this.block.element) {
89
+ if (!element) return 0;
90
+ const range = document.createRange();
91
+ range.setStart(element, 0);
92
+ range.setEnd(node, offset);
93
+ return range.toString().length;
94
+ }
95
+
96
+ /** @param {InputEvent} event @param {ReturnType<TextBehavior["getSelection"]>} [selection] */
97
+ handleBeforeInput(event, selection = this.selection) {
98
+ // Hook system (Markdown shortcuts, de-transformation, etc.)
99
+ const hooks = this.nabu.hooks.get("onBeforeInput");
100
+ if (hooks) {
101
+ for (const hook of hooks) {
102
+ const result = hook(this.nabu, event, this.block);
103
+ if (result === this.nabu.BREAK) {
104
+ event.preventDefault();
105
+ return true;
106
+ }
107
+ }
108
+ }
109
+
110
+ switch (event.inputType) {
111
+ case "insertText":
112
+ return this.handleInsertText(event, selection);
113
+ case "insertLineBreak":
114
+ case "insertSoftLineBreak":
115
+ return this.handleInsertLineBreak(event, selection);
116
+ case "deleteContentBackward":
117
+ return this.handleDeleteContentBackward(event, selection);
118
+ case "deleteContentForward":
119
+ return this.handleDeleteContentForward(event, selection);
120
+ case "insertParagraph":
121
+ return this.handleInsertParagraph(event, selection);
122
+ default:
123
+ console.warn("Unhandled input type:", event.inputType);
124
+ return;
125
+ }
126
+ }
127
+
128
+ /** @param {InputEvent} event @param {ReturnType<TextBehavior["getSelection"]>} [selection] */
129
+ handleInsertText(event, selection = this.selection) {
130
+ if (!selection) return;
131
+
132
+ const textToInsert = event.data || "";
133
+ if (!selection.isCollapsed) this.delete({index: selection.from, length: selection.to - selection.from}, selection);
134
+ this.insert(selection.from, textToInsert);
135
+ this.block.commit();
136
+ tick().then(() => this.nabu.selection.setCursor(this.block, selection.from + textToInsert.length));
137
+ }
138
+
139
+ /** @param {InputEvent} event @param {ReturnType<TextBehavior["getSelection"]>} [sel] */
140
+ handleInsertLineBreak(event, sel = this.selection) {
141
+ if (!sel) return;
142
+ if (!sel.isCollapsed) {
143
+ this.delete({index: sel.from, length: sel.to - sel.from}, sel);
144
+ }
145
+ this.insert(sel.from, "\n");
146
+ this.block.commit();
147
+
148
+ tick().then(() => this.nabu.selection.setCursor(this.block, sel.from + 1));
149
+ }
150
+
151
+ /** @param {InputEvent} event @param {ReturnType<TextBehavior["getSelection"]>} [sel] */
152
+ handleDeleteContentBackward(event, sel = this.selection) {
153
+ if (!sel) return;
154
+ if (sel.isCollapsed && sel.from === 0) {
155
+ const previousBlock = this.block.findBackward(b => b.behaviors.has("text") && b.behaviors.get("text") instanceof TextBehavior);
156
+ if (!previousBlock) return;
157
+ const previousBehavior = previousBlock?.behaviors.get("text");
158
+ if (previousBehavior && previousBehavior instanceof TextBehavior) {
159
+ event.preventDefault();
160
+ const previousLength = previousBehavior.text.length;
161
+ // previousBehavior.delta([
162
+ // { retain: previousLength },
163
+ // ...this.container.toDelta()
164
+ // ])
165
+ previousBlock.consume(this.block);
166
+ this.block.destroy();
167
+ this.block.commit();
168
+
169
+ tick().then(() => this.nabu.selection.setCursor(previousBehavior.block, previousLength));
170
+ }
171
+ } else {
172
+ event.preventDefault();
173
+ const length = sel.isCollapsed ? 1 : sel.to - sel.from;
174
+ const index = sel.isCollapsed ? sel.from - 1 : sel.from;
175
+ this.delete({index, length});
176
+ this.block.commit();
177
+
178
+ tick().then(() => this.nabu.selection.setCursor(this.block, index));
179
+ }
180
+ }
181
+
182
+
183
+ /** @param {InputEvent} event @param {ReturnType<TextBehavior["getSelection"]>} [sel] */
184
+ handleDeleteContentForward(event, sel = this.selection) {
185
+ if (!sel) return;
186
+ if (sel.isCollapsed && sel.from === this.text.length) {
187
+ const nextBlock = this.block.findForward(b => b.behaviors.has("text") && b.behaviors.get("text") instanceof TextBehavior);
188
+ if (!nextBlock) return;
189
+ const nextBehavior = nextBlock?.behaviors.get("text");
190
+ if (nextBehavior && nextBehavior instanceof TextBehavior) {
191
+ event.preventDefault();
192
+ // this.delta([
193
+ // { retain: this.container.length },
194
+ // ...nextBehavior.container.toDelta()
195
+ // ]);
196
+ this.block.consume(nextBlock);
197
+
198
+
199
+ nextBehavior.block.destroy();
200
+ this.block.commit();
201
+
202
+ tick().then(() => this.nabu.selection.setCursor(this.block, sel.from));
203
+ }
204
+ }
205
+ }
206
+
207
+ /** @param {InputEvent} event @param {ReturnType<TextBehavior["getSelection"]>} [sel] */
208
+ handleInsertParagraph(event, sel = this.selection) {
209
+ if (!sel) return;
210
+ const from = sel.from;
211
+ const to = sel.isCollapsed ? this.text.length : sel.to ;
212
+ const newBlock = this.block.ascend("onSplit", event, { offset: from, delta: this.container.sliceDelta(from, to)});
213
+ return newBlock;
214
+ }
215
+
216
+ /**
217
+ * Retrouve le nœud texte et l'offset DOM pour un offset Modèle donné
218
+ * @param {number} targetOffset
219
+ * @param {HTMLElement?} [element]
220
+ */
221
+ getDOMPoint(targetOffset, element = this.block.element) {
222
+ if (!element) return null;
223
+
224
+ const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
225
+ let currentOffset = 0;
226
+ let node = walker.nextNode();
227
+
228
+ while (node) {
229
+ const length = node.textContent?.length || 0;
230
+ if (currentOffset + length >= targetOffset) {
231
+ return { node, offset: targetOffset - currentOffset };
232
+ }
233
+ currentOffset += length;
234
+ node = walker.nextNode();
235
+ }
236
+
237
+ // Fallback : fin du bloc ou élément lui-même si vide
238
+ return { node: element, offset: element.childNodes.length };
239
+ }
240
+
241
+ /** @param {number} index @param {string} text */
242
+ insert(index, text) {
243
+ this.container.insert(index, text);
244
+ }
245
+
246
+ /** @param {Parameters<Block["delete"]>[0]} [deletion] @param {ReturnType<TextBehavior["getSelection"]>} [selection] */
247
+ delete(deletion, selection = this.selection) {
248
+ const l = this.container.length;
249
+ let from = deletion?.from ?? selection?.from ?? 0;
250
+ if (from < 0) from = l + from + 1;
251
+ let to = deletion?.to ?? selection?.to ?? l;
252
+ if (to < 0) to = l + (to + 1) ;
253
+ const index = deletion?.index ?? from;
254
+ const length = deletion?.length ?? (to - from);
255
+ this.container.delete(index, length);
256
+ }
257
+
258
+ /** @param {import('loro-crdt').Delta<string>[]} data */
259
+ applyDelta(data = []) {
260
+ this.container.applyDelta(data);
261
+ }
262
+
263
+ /**
264
+ * @param {Parameters<Block["split"]>[0]} [options]
265
+ * @param {ReturnType<TextBehavior["getSelection"]>} [selection]
266
+ * @returns {ReturnType<Block["split"]>}
267
+ */
268
+ split(options, selection = this.selection) {
269
+ const sel = selection;
270
+ if (!sel) return null;
271
+ const offset = options?.index ?? options?.offset ?? options?.from;
272
+ const from = options?.from ?? offset ?? sel.from;
273
+ const to = options?.to ?? (options?.length ? from + options.length : (offset ?? sel.to));
274
+ return this.block.ascend("onSplit", null, { offset: from, delta: this.container.sliceDelta(from, to)});
275
+ }
276
+
277
+ /** @param {string} markName @param {any} value @param {ReturnType<TextBehavior["getSelection"]>} sel */
278
+ applyMark(markName, value, sel) {
279
+ if (!sel || sel.isCollapsed) return;
280
+ this.container.mark({ start: sel.from, end: sel.to }, markName, value);
281
+ }
282
+
283
+ /** @param {string} markName @param {ReturnType<TextBehavior["getSelection"]>} sel */
284
+ removeMark(markName, sel) {
285
+ if (!sel || sel.isCollapsed) return;
286
+ this.container.unmark({ start: sel.from, end: sel.to }, markName);
287
+ }
288
+
289
+ /** @param {string} markName @param {ReturnType<TextBehavior["getSelection"]>} sel */
290
+ isMarkActive(markName, sel) {
291
+ if (!sel || sel.isCollapsed) return false;
292
+ const delta = this.container.toDelta();
293
+ let offset = 0;
294
+ for (const op of delta) {
295
+ if (typeof op.insert !== 'string') continue;
296
+ const opEnd = offset + op.insert.length;
297
+ if (opEnd <= sel.from) { offset = opEnd; continue; }
298
+ if (offset >= sel.to) break;
299
+ if (!op.attributes?.[markName]) return false;
300
+ offset = opEnd;
301
+ }
302
+ return true;
303
+ }
304
+
305
+ /** @param {string} markName @param {any} value @param {ReturnType<TextBehavior["getSelection"]>} sel */
306
+ toggleMark(markName, value, sel) {
307
+ if (!sel || sel.isCollapsed) return;
308
+ if (this.isMarkActive(markName, sel)) {
309
+ this.removeMark(markName, sel);
310
+ } else {
311
+ this.applyMark(markName, value, sel);
312
+ }
313
+ }
314
+
315
+ /** @returns {string} */
316
+ toMarkdown() {
317
+ return deltaToMarkdown(this.delta);
318
+ }
319
+
320
+ /**
321
+ * Converts the text delta to a Slate-like JSON format.
322
+ * @returns {{text: string, [mark: string]: any}[]}
323
+ */
324
+ toJSON() {
325
+ return this.delta
326
+ .filter(op => typeof op.insert === 'string')
327
+ .map(op => {
328
+ const run = { text: /** @type {string} */ (op.insert) };
329
+ if (op.attributes) Object.assign(run, op.attributes);
330
+ return run;
331
+ });
332
+ }
333
+
334
+ /** @param {Block} other */
335
+ absorbs(other) {
336
+ const otherBehavior = other.behaviors.get("text");
337
+ if (!otherBehavior || !(otherBehavior instanceof TextBehavior)) return false;
338
+ this.applyDelta([
339
+ { retain: this.container.length },
340
+ ...otherBehavior.container.toDelta()
341
+ ]);
342
+ // other.destroy();
343
+ return true;
344
+ }
345
+
346
+ }
@@ -0,0 +1,18 @@
1
+ <script>
2
+ /** @type {{ block: import('./block.svelte.js').Block }} */
3
+ let { block } = $props();
4
+
5
+ // Récupération dynamique du composant via le Registry de Nabu
6
+ // block.nabu.components est la Map<string, Component>
7
+ let Component = $derived(block.component || block.nabu.components.get(block.type));
8
+ </script>
9
+
10
+ {#if Component}
11
+ <!-- On passe l'instance du bloc au composant spécifique -->
12
+ <Component {block} />
13
+ {:else}
14
+ <!-- Fallback pour les types inconnus (utile en dev) -->
15
+ <div style="color: red; border: 1px dashed red; padding: 0.5rem; margin: 0.5rem 0;" contenteditable="false">
16
+ ⚠️ Unknown block type: <strong>{block.type}</strong>
17
+ </div>
18
+ {/if}
@@ -0,0 +1,11 @@
1
+ export default Block;
2
+ type Block = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const Block: import("svelte").Component<{
7
+ block: import("./block.svelte.js").Block;
8
+ }, {}, "">;
9
+ type $$ComponentProps = {
10
+ block: import("./block.svelte.js").Block;
11
+ };
@@ -0,0 +1,31 @@
1
+ <script>
2
+ import { Nabu } from "./nabu.svelte.js";
3
+ import Block from "./Block.svelte";
4
+
5
+ /** @type {{ engine: Nabu }} */
6
+ let { engine = new Nabu() } = $props();
7
+ </script>
8
+
9
+ <div
10
+ contenteditable="true"
11
+ class="nabu-editor"
12
+ spellcheck="false"
13
+ translate="no"
14
+ data-nabu-root="true"
15
+ onbeforeinput={(e) => engine.handleBeforeinput(e)}
16
+ onkeydown={(e) => engine.handleKeydown(e)}
17
+ >
18
+ {#each engine.children as block (block.id)}
19
+ <Block {block} />
20
+ {/each}
21
+ </div>
22
+
23
+ <style>
24
+ .nabu-editor {
25
+ outline: none;
26
+ min-height: 100px;
27
+ padding: 1rem;
28
+ white-space: pre-wrap; /* Important pour le comportement éditeur */
29
+ word-break: break-word;
30
+ }
31
+ </style>
@@ -0,0 +1,12 @@
1
+ export default Nabu;
2
+ type Nabu = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const Nabu: import("svelte").Component<{
7
+ engine: Nabu;
8
+ }, {}, "">;
9
+ import { Nabu } from "./nabu.svelte.js";
10
+ type $$ComponentProps = {
11
+ engine: Nabu;
12
+ };
@@ -0,0 +1,143 @@
1
+ export class Block {
2
+ /** @param {Nabu} nabu @param {NabuNode} node */
3
+ static load(nabu: Nabu, node: NabuNode): Block;
4
+ /** @param {Nabu} nabu @param {string} type @param {Object} [props={}] @param {string|null} [parentId=null] @param {number|null} [index=null] */
5
+ static create(nabu: Nabu, type: string, props?: Object, parentId?: string | null, index?: number | null): Block;
6
+ /** @param {Nabu} nabu @param {NabuNode} node */
7
+ constructor(nabu: Nabu, node: NabuNode);
8
+ nabu: Nabu;
9
+ node: NabuNode;
10
+ id: string;
11
+ type: string;
12
+ /** @type {MegaBlock | null} */
13
+ parent: MegaBlock | null;
14
+ serializers: SvelteMap<any, any>;
15
+ behaviors: SvelteMap<any, any>;
16
+ selected: boolean;
17
+ isSelectionStart: boolean;
18
+ isSelectionEnd: boolean;
19
+ isIntermediate: boolean;
20
+ clearSelection(): void;
21
+ /** @type {{from: number, to: number, direction: "forward" | "backward" | "none"} | null} */
22
+ selection: {
23
+ from: number;
24
+ to: number;
25
+ direction: "forward" | "backward" | "none";
26
+ } | null;
27
+ index: number;
28
+ /** @type {Block?} */
29
+ previous: Block | null;
30
+ /** @type {Block?} */
31
+ next: Block | null;
32
+ parents: MegaBlock[];
33
+ component: import("svelte").Component<any, any, string> | null;
34
+ /** @type {HTMLElement | null} */
35
+ element: HTMLElement | null;
36
+ /** @param {(block: Block) => boolean} predicate @returns {Block | null} */
37
+ findForward(predicate: (block: Block) => boolean): Block | null;
38
+ /** @param {(block: Block) => boolean} predicate @returns {Block | null} */
39
+ findBackward(predicate: (block: Block) => boolean): Block | null;
40
+ /**
41
+ * Returns real, uncommitted siblings, which is useful for extensions that want to check the document structure before the transaction is committed.
42
+ */
43
+ getAdjacentSiblings(): {
44
+ previous: null;
45
+ next: null;
46
+ previousNode?: undefined;
47
+ nextNode?: undefined;
48
+ } | {
49
+ previous: Block | null | undefined;
50
+ next: Block | null | undefined;
51
+ previousNode: import("loro-crdt").LoroTreeNode<Record<string, unknown>> | import("loro-crdt").LoroTreeNode<{
52
+ type: string;
53
+ }> | null;
54
+ nextNode: import("loro-crdt").LoroTreeNode<Record<string, unknown>> | import("loro-crdt").LoroTreeNode<{
55
+ type: string;
56
+ }> | null;
57
+ };
58
+ /**
59
+ * Helper pour trouver le dernier descendant profond qui matche
60
+ * @param {(block: Block) => boolean} predicate
61
+ * @returns {Block | null}
62
+ */
63
+ findLastDescendant(predicate: (block: Block) => boolean): Block | null;
64
+ destroy(): void;
65
+ /**
66
+ * Transforme ce bloc en un autre type de bloc.
67
+ * @param {string} newType
68
+ * @param {Object} [props={}]
69
+ */
70
+ transformTo(newType: string, props?: Object): void;
71
+ /** @param {number} index @param {string} text */
72
+ insert(index: number, text: string): void;
73
+ /** @param {{from?: number, to?: number, index?: number, length?: number}} [deletion] */
74
+ delete(deletion?: {
75
+ from?: number;
76
+ to?: number;
77
+ index?: number;
78
+ length?: number;
79
+ }): void;
80
+ /** @param {Block} block @returns {any} */
81
+ absorbs(block: Block): any;
82
+ /** @param {Block} block @returns {any} */
83
+ mergeWith(block: Block): any;
84
+ /**
85
+ * Consumes another block, handling children relocation intelligently.
86
+ * @param {Block} otherBlock - The block to consume or be consumed by
87
+ * @param {'into' | 'from'} direction - 'into' = this merges into other, 'from' = other merges into this
88
+ * @returns {Block} The surviving block
89
+ */
90
+ consume(otherBlock: Block, direction?: "into" | "from"): Block;
91
+ /** @param {Block[]} children @param {number | null} [index] */
92
+ adoptChildren(children: Block[], index?: number | null): void;
93
+ /** @param {{from?: number, to?: number, index?: number, length?: number, offset?: number}} options @returns {{block: Block} | null} */
94
+ split(options: {
95
+ from?: number;
96
+ to?: number;
97
+ index?: number;
98
+ length?: number;
99
+ offset?: number;
100
+ }): {
101
+ block: Block;
102
+ } | null;
103
+ /** @param {{start?: number, end?: number, offset?: number} | null } options @param {boolean} [passive=false] */
104
+ focus(options?: {
105
+ start?: number;
106
+ end?: number;
107
+ offset?: number;
108
+ } | null, passive?: boolean): {
109
+ start: {
110
+ node: Node;
111
+ offset: number;
112
+ } | null;
113
+ end: {
114
+ node: Node;
115
+ offset: number;
116
+ } | null;
117
+ options: {
118
+ startOffset: number;
119
+ endOffset: number;
120
+ };
121
+ };
122
+ /**
123
+ * @param {number} offset
124
+ * @returns {{node: Node, offset: number} | null}
125
+ */
126
+ getDOMPoint(offset: number): {
127
+ node: Node;
128
+ offset: number;
129
+ } | null;
130
+ /** @param {string} eventName @param {Event} event @param {Object} [data={}] */
131
+ ascend(eventName: string, event: Event, data?: Object): any;
132
+ /** @param {InputEvent} event @returns {any} */
133
+ beforeinput(event: InputEvent): any;
134
+ /** @param {KeyboardEvent} event @returns {any} */
135
+ keydown(event: KeyboardEvent): any;
136
+ commit(): void;
137
+ /** @param {string} format */
138
+ serialize(format: string): any;
139
+ }
140
+ import type { Nabu } from "./nabu.svelte";
141
+ import type { NabuNode } from "./nabu.svelte";
142
+ import type { MegaBlock } from "./megablock.svelte";
143
+ import { SvelteMap } from "svelte/reactivity";