@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,611 @@
1
+ /**
2
+ * @typedef {{
3
+ * anchorNode: Node | null,
4
+ * anchorOffset: number,
5
+ * focusNode: Node | null,
6
+ * focusOffset: number,
7
+ * startNode: Node | null,
8
+ * startOffset: number,
9
+ * endNode: Node | null,
10
+ * endOffset: number,
11
+ * isCollapsed: boolean,
12
+ * rangeCount: number,
13
+ * type: string,
14
+ * direction: "forward" | "backward" | "none",
15
+ * text: string,
16
+ * html: string
17
+ * }} SelectionObject
18
+ */
19
+
20
+
21
+ /**
22
+ * Utilitaire réactif pour gérer la sélection de texte avec Svelte 5 runes
23
+ * @example
24
+ * const selection = new SvelteSelection();
25
+ * // Utiliser selection.text, selection.isCollapsed, etc. dans vos composants
26
+ */
27
+ export class SvelteSelection {
28
+ constructor() {
29
+ // Utilisation d'un effect root pour éviter les fuites mémoire
30
+ this.#cleanup = $effect.root(() => {
31
+ $effect(() => {
32
+ // Vérification de la disponibilité de l'API
33
+ if (typeof window === 'undefined' || !window.getSelection) {
34
+ return;
35
+ }
36
+
37
+ document.addEventListener('selectionchange', this.#updateSelection);
38
+ this.#updateSelection();
39
+
40
+ return () => {
41
+ document.removeEventListener('selectionchange', this.#updateSelection);
42
+ this.#cleanup?.();
43
+ };
44
+ });
45
+ });
46
+ }
47
+
48
+ /** @type {Function?} */
49
+ #cleanup = null;
50
+
51
+ /** @type {Selection?} */
52
+ raw = null;
53
+
54
+ /**
55
+ * État interne de la sélection
56
+ * @type {SelectionObject?}
57
+ */
58
+ #sel = $state(null);
59
+ is = $derived(!!this.#sel);
60
+
61
+ /** @type {Range[]} */
62
+ #ranges = $state([]);
63
+
64
+ // Propriétés réactives de base
65
+ anchorNode = $derived(this.#sel?.anchorNode ?? null);
66
+ anchorOffset = $derived(this.#sel?.anchorOffset ?? 0);
67
+ focusNode = $derived(this.#sel?.focusNode ?? null);
68
+ focusOffset = $derived(this.#sel?.focusOffset ?? 0);
69
+ isCollapsed = $derived(this.#sel?.isCollapsed ?? true);
70
+ rangeCount = $derived(this.#sel?.rangeCount ?? 0);
71
+ type = $derived(this.#sel?.type ?? '');
72
+ direction = $derived(this.#sel?.direction ?? 'none');
73
+
74
+ startNode = $derived(this.#sel?.startNode ?? null);
75
+ endNode = $derived(this.#sel?.endNode ?? null);
76
+ startOffset = $derived(this.#sel?.startOffset ?? 0);
77
+ endOffset = $derived(this.#sel?.endOffset ?? 0);
78
+
79
+ // Propriétés des ranges
80
+ ranges = $derived(this.#ranges);
81
+ firstRange = $derived(this.#ranges[0] ?? null);
82
+ get range() {
83
+ // Retourne la première range si elle existe, sinon null
84
+ return this.firstRange;
85
+ }
86
+ lastRange = $derived(this.#ranges[this.#ranges.length - 1] ?? null);
87
+
88
+ // Propriétés dérivées utiles
89
+ text = $derived(this.#sel?.text ?? '');
90
+ html = $derived(this.#sel?.html ?? '');
91
+ hasSelection = $derived(!this.isCollapsed && this.rangeCount > 0);
92
+ isEmpty = $derived(this.text.length === 0);
93
+ hasMultipleRanges = $derived(this.#ranges.length > 1);
94
+
95
+ // Informations sur les éléments contenants
96
+ commonAncestor = $derived.by(() => {
97
+ if (!this.hasSelection || this.rangeCount === 0) return null;
98
+ try {
99
+ const range = this.firstRange;
100
+ return range?.commonAncestorContainer ?? null;
101
+ } catch {
102
+ return null;
103
+ }
104
+ });
105
+
106
+ // Informations dérivées des ranges
107
+ allText = $derived.by(() => {
108
+ return this.#ranges.map(range => range.toString()).join('');
109
+ });
110
+
111
+ allHtml = $derived.by(() => {
112
+ return this.#ranges.map(range => {
113
+ try {
114
+ const fragment = range.cloneContents();
115
+ const div = document.createElement('div');
116
+ div.appendChild(fragment);
117
+ return div.innerHTML;
118
+ } catch {
119
+ return '';
120
+ }
121
+ }).join('');
122
+ });
123
+
124
+ // Bounding rectangles de toutes les ranges
125
+ boundingRects = $derived.by(() => {
126
+ return this.#ranges.map(range => {
127
+ try {
128
+ return range.getBoundingClientRect();
129
+ } catch {
130
+ return null;
131
+ }
132
+ }).filter(rect => rect !== null);
133
+ });
134
+
135
+ // Méthode privée pour mettre à jour la sélection
136
+ #updateSelection = () => {
137
+ try {
138
+ this.raw = window.getSelection();
139
+
140
+ if (!this.raw) {
141
+ this.#sel = null;
142
+ this.#ranges = [];
143
+ return;
144
+ }
145
+
146
+ // Mise à jour des ranges
147
+ const newRanges = [];
148
+ for (let i = 0; i < this.raw.rangeCount; i++) {
149
+ try {
150
+ const range = this.raw.getRangeAt(i);
151
+ newRanges.push(range);
152
+ } catch (error) {
153
+ console.warn(`Erreur lors de la récupération de la range ${i}:`, error);
154
+ }
155
+ }
156
+ this.#ranges = newRanges;
157
+
158
+
159
+ let text = '';
160
+ let html = '';
161
+
162
+ if (this.raw.rangeCount > 0 && !this.raw.isCollapsed) {
163
+ try {
164
+ const range = this.raw.getRangeAt(0);
165
+ text = range.toString();
166
+
167
+ const fragment = range.cloneContents();
168
+ const div = document.createElement('div');
169
+ div.appendChild(fragment);
170
+ html = div.innerHTML;
171
+ } catch (error) {
172
+ console.warn('Erreur lors de l\'extraction du contenu de la sélection:', error);
173
+ }
174
+ }
175
+
176
+ const range = this.raw.rangeCount > 0 ? this.raw.getRangeAt(0) : null;
177
+
178
+ this.#sel = {
179
+ //SEL
180
+ anchorNode: this.raw.anchorNode,
181
+ anchorOffset: this.raw.anchorOffset,
182
+ focusNode: this.raw.focusNode,
183
+ focusOffset: this.raw.focusOffset,
184
+ //RANGE:
185
+ startNode: range?.startContainer || null,
186
+ startOffset: range?.startOffset || 0,
187
+ endNode: range?.endContainer || null,
188
+ endOffset: range?.endOffset || 0,
189
+ //MISC
190
+ isCollapsed: this.raw.isCollapsed,
191
+ rangeCount: this.raw.rangeCount,
192
+ type: this.raw.type,
193
+ direction: this.raw.direction || 'none',
194
+ text,
195
+ html
196
+ };
197
+ } catch (error) {
198
+ console.error('Erreur lors de la mise à jour de la sélection:', error);
199
+ this.#sel = null;
200
+ this.#ranges = [];
201
+ }
202
+ };
203
+
204
+ // Méthode pour rafraîchir la sélection manuellement
205
+ refresh = () => this.#updateSelection();
206
+
207
+ // Méthodes de manipulation de la sélection avec gestion d'erreurs
208
+
209
+ /**
210
+ * Ajoute une range à la sélection.
211
+ * @param {Range} range - La range à ajouter.
212
+ * @returns {boolean} True si l'opération a réussi
213
+ */
214
+ addRange = (range) => {
215
+ try {
216
+ this.raw?.addRange(range);
217
+ this.#updateSelection();
218
+ return true;
219
+ } catch (error) {
220
+ console.warn('Erreur lors de l\'ajout de la range:', error);
221
+ return false;
222
+ }
223
+ };
224
+
225
+ /**
226
+ * Supprime toutes les ranges de la sélection.
227
+ * @returns {boolean} True si l'opération a réussi
228
+ */
229
+ removeAllRanges = () => {
230
+ try {
231
+ this.raw?.removeAllRanges();
232
+ this.#updateSelection();
233
+ return true;
234
+ } catch (error) {
235
+ console.warn('Erreur lors de la suppression des ranges:', error);
236
+ return false;
237
+ }
238
+ };
239
+
240
+ /**
241
+ * Supprime une range spécifique de la sélection.
242
+ * @param {Range} range - La range à supprimer.
243
+ * @returns {boolean} True si l'opération a réussi
244
+ */
245
+ removeRange = (range) => {
246
+ try {
247
+ this.raw?.removeRange(range);
248
+ this.#updateSelection();
249
+ return true;
250
+ } catch (error) {
251
+ console.warn('Erreur lors de la suppression de la range:', error);
252
+ return false;
253
+ }
254
+ };
255
+
256
+ /**
257
+ * Récupère la range à l'index spécifié.
258
+ * @param {number} index - L'index de la range à récupérer.
259
+ * @returns {Range|null} La range à l'index spécifié, ou null si elle n'existe pas.
260
+ */
261
+ getRangeAt = (index) => {
262
+ try {
263
+ return this.raw?.getRangeAt(index) ?? null;
264
+ } catch (error) {
265
+ console.warn('Erreur lors de la récupération de la range:', error);
266
+ return null;
267
+ }
268
+ };
269
+
270
+ /**
271
+ * Réduit la sélection à un nœud et offset spécifiques.
272
+ * @param {Node} node - Le nœud vers lequel réduire la sélection.
273
+ * @param {number} offset - L'offset dans le nœud.
274
+ * @returns {boolean} True si l'opération a réussi
275
+ */
276
+ collapse = (node, offset = 0) => {
277
+ try {
278
+ this.raw?.collapse(node, offset);
279
+ this.#updateSelection();
280
+ return true;
281
+ } catch (error) {
282
+ console.warn('Erreur lors de la réduction de la sélection:', error);
283
+ return false;
284
+ }
285
+ };
286
+
287
+ /**
288
+ * Réduit la sélection au début de la range courante.
289
+ * @returns {boolean} True si l'opération a réussi
290
+ */
291
+ collapseToStart = () => {
292
+ try {
293
+ this.raw?.collapseToStart();
294
+ this.#updateSelection();
295
+ return true;
296
+ } catch (error) {
297
+ console.warn('Erreur lors de la réduction au début:', error);
298
+ return false;
299
+ }
300
+ };
301
+
302
+ /**
303
+ * Réduit la sélection à la fin de la range courante.
304
+ * @returns {boolean} True si l'opération a réussi
305
+ */
306
+ collapseToEnd = () => {
307
+ try {
308
+ this.raw?.collapseToEnd();
309
+ this.#updateSelection();
310
+ return true;
311
+ } catch (error) {
312
+ console.warn('Erreur lors de la réduction à la fin:', error);
313
+ return false;
314
+ }
315
+ };
316
+
317
+ /**
318
+ * Sélectionne le contenu d'un nœud.
319
+ * @param {Node} node - Le nœud dont le contenu doit être sélectionné.
320
+ * @returns {boolean} True si l'opération a réussi
321
+ */
322
+ selectAllChildren = (node) => {
323
+ try {
324
+ this.raw?.selectAllChildren(node);
325
+ this.#updateSelection();
326
+ return true;
327
+ } catch (error) {
328
+ console.warn('Erreur lors de la sélection des enfants:', error);
329
+ return false;
330
+ }
331
+ };
332
+
333
+ /**
334
+ * Étend la sélection vers un nœud et offset spécifiques.
335
+ * @param {Node} node - Le nœud vers lequel étendre.
336
+ * @param {number} offset - L'offset dans le nœud.
337
+ * @returns {boolean} True si l'opération a réussi
338
+ */
339
+ extend = (node, offset = 0) => {
340
+ try {
341
+ this.raw?.extend(node, offset);
342
+ this.#updateSelection();
343
+ return true;
344
+ } catch (error) {
345
+ console.warn('Erreur lors de l\'extension de la sélection:', error);
346
+ return false;
347
+ }
348
+ };
349
+
350
+ /**
351
+ * Définit la base et l'extension de la sélection.
352
+ * @param {Node} anchorNode - Le nœud d'ancrage.
353
+ * @param {number} anchorOffset - L'offset d'ancrage.
354
+ * @param {Node} focusNode - Le nœud de focus.
355
+ * @param {number} focusOffset - L'offset de focus.
356
+ * @returns {boolean} True si l'opération a réussi
357
+
358
+ */
359
+ setBaseAndExtent = (anchorNode, anchorOffset, focusNode, focusOffset) => {
360
+ try {
361
+ this.raw?.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
362
+ this.#updateSelection();
363
+ return true;
364
+ } catch (error) {
365
+ console.warn('Erreur lors de la définition de la base et extension:', error);
366
+ return false;
367
+ }
368
+ };
369
+
370
+ /**
371
+ * Vérifie si la sélection contient un nœud donné.
372
+ * @param {Node} node - Le nœud à vérifier.
373
+ * @param {boolean} allowPartialContainment - Permet la contenance partielle.
374
+ * @returns {boolean} True si le nœud est contenu dans la sélection.
375
+ */
376
+ containsNode = (node, allowPartialContainment = false) => {
377
+ try {
378
+ return this.raw?.containsNode(node, allowPartialContainment) ?? false;
379
+ } catch (error) {
380
+ console.warn('Erreur lors de la vérification de contenance:', error);
381
+ return false;
382
+ }
383
+ };
384
+
385
+ /**
386
+ * Crée une nouvelle range basée sur la sélection courante.
387
+ * @returns {Range|null} Une nouvelle range ou null.
388
+ */
389
+ createRange = () => {
390
+ try {
391
+ if (!this.hasSelection) return null;
392
+ return this.firstRange?.cloneRange() ?? null;
393
+ } catch (error) {
394
+ console.warn('Erreur lors de la création de la range:', error);
395
+ return null;
396
+ }
397
+ };
398
+
399
+ /**
400
+ * Crée des copies de toutes les ranges courantes.
401
+ * @returns {Range[]} Un array de toutes les ranges clonées.
402
+ */
403
+ createAllRanges = () => {
404
+ try {
405
+ return this.#ranges.map(range => range.cloneRange());
406
+ } catch (error) {
407
+ console.warn('Erreur lors de la création des ranges:', error);
408
+ return [];
409
+ }
410
+ };
411
+
412
+ /**
413
+ * Récupère les coordonnées de toutes les ranges.
414
+ * @returns {DOMRect[]} Un array des rectangles de toutes les ranges.
415
+ */
416
+ getAllBoundingRects = () => {
417
+ return this.boundingRects;
418
+ };
419
+
420
+ /**
421
+ * Vérifie si toutes les ranges sont dans un élément donné.
422
+ * @param {Element} element - L'élément à vérifier.
423
+ * @returns {boolean} True si toutes les ranges sont contenues.
424
+ */
425
+ allRangesInElement = (element) => {
426
+ return this.#ranges.every(range => {
427
+ try {
428
+ return element.contains(range.commonAncestorContainer);
429
+ } catch {
430
+ return false;
431
+ }
432
+ });
433
+ };
434
+
435
+ /**
436
+ * Filtre les ranges selon un prédicat.
437
+ * @param {function(Range): boolean} predicate - Fonction de filtre.
438
+ * @returns {Range[]} Les ranges qui passent le filtre.
439
+ */
440
+ filterRanges = (predicate) => {
441
+ return this.#ranges.filter(predicate);
442
+ };
443
+
444
+ /**
445
+ * Sélectionne tout le contenu du document.
446
+ * @returns {boolean} True si l'opération a réussi
447
+ */
448
+ selectAll = () => {
449
+ try {
450
+ this.raw?.selectAllChildren(document.body);
451
+ this.#updateSelection();
452
+ return true;
453
+ } catch (error) {
454
+ console.warn('Erreur lors de la sélection de tout:', error);
455
+ return false;
456
+ }
457
+ };
458
+
459
+ /**
460
+ * Efface la sélection courante.
461
+ * @returns {boolean} True si l'opération a réussi
462
+ */
463
+ clear = () => {
464
+ return this.removeAllRanges();
465
+ };
466
+
467
+ /**
468
+ * Ajout à la classe SvelteSelection pour observer les mutations DOM
469
+ * qui peuvent affecter la position du caret sans déclencher selectionchange
470
+ */
471
+
472
+ // Propriétés à ajouter à la classe
473
+ /** @type {MutationObserver?} */
474
+ #mutationObserver = null;
475
+
476
+ /** @type {Set<Element>} */
477
+ #observedElements = new Set();
478
+
479
+ /** @type {number?} */
480
+ #debounceTimer = null;
481
+
482
+ /**
483
+ * Observe un élément contenteditable pour les mutations DOM qui peuvent
484
+ * affecter la position du caret sans déclencher selectionchange
485
+ *
486
+ * @param {Element} element - L'élément contenteditable à observer
487
+ * @param {Object} options - Options d'observation
488
+ * @param {number} options.debounceMs - Délai de debounce en ms (défaut: 16ms = ~1 frame)
489
+ * @param {boolean} options.observeAttributes - Observer les changements d'attributs (défaut: false)
490
+ * @returns {boolean} True si l'observation a été configurée avec succès
491
+ */
492
+ observe = (element, options = {}) => {
493
+ const {
494
+ debounceMs = 16,
495
+ observeAttributes = false
496
+ } = options;
497
+
498
+ try {
499
+ // Vérifier que l'élément est contenteditable
500
+ if (!element.isContentEditable && element.contentEditable !== 'true') {
501
+ console.warn('L\'élément observé n\'est pas contenteditable');
502
+ }
503
+
504
+ // Créer l'observer si il n'existe pas encore
505
+ if (!this.#mutationObserver) {
506
+ this.#mutationObserver = new MutationObserver((mutations) => {
507
+ // Filtrer les mutations pertinentes
508
+ const relevantMutations = mutations.filter(mutation => {
509
+ // Ignorer les mutations sur les attributs non-pertinents
510
+ if (mutation.type === 'attributes') {
511
+ const attr = mutation.attributeName;
512
+ // Ne considérer que les attributs qui peuvent affecter le layout/contenu
513
+ return ['class', 'style', 'contenteditable'].includes(attr);
514
+ }
515
+
516
+ // Toujours considérer les changements de contenu
517
+ return mutation.type === 'childList' || mutation.type === 'characterData';
518
+ });
519
+
520
+ if (relevantMutations.length === 0) return;
521
+
522
+ // Debounce pour éviter trop d'appels
523
+ this.#updateSelection();
524
+ // clearTimeout(this.#debounceTimer);
525
+ // this.#debounceTimer = setTimeout(() => {
526
+ // this.#updateSelection();
527
+ // }, debounceMs);
528
+ });
529
+ }
530
+
531
+ // Ajouter l'élément à la liste des observés
532
+ this.#observedElements.add(element);
533
+
534
+ // Configurer l'observation
535
+ this.#mutationObserver.observe(element, {
536
+ childList: true, // Ajout/suppression d'enfants
537
+ subtree: true, // Observer tous les descendants
538
+ characterData: true, // Changements de texte dans les nœuds text
539
+ attributes: observeAttributes, // Changements d'attributs (optionnel)
540
+ attributeFilter: observeAttributes ? ['class', 'style', 'contenteditable'] : undefined
541
+ });
542
+
543
+ return true;
544
+ } catch (error) {
545
+ console.warn('Erreur lors de la configuration de l\'observation:', error);
546
+ return false;
547
+ }
548
+ };
549
+
550
+ /**
551
+ * Arrête l'observation d'un élément spécifique
552
+ * @param {Element} element - L'élément à ne plus observer
553
+ * @returns {boolean} True si l'élément était observé et a été retiré
554
+ */
555
+ unobserve = (element) => {
556
+ const wasObserved = this.#observedElements.delete(element);
557
+
558
+ // Si plus aucun élément n'est observé, déconnecter l'observer
559
+ if (this.#observedElements.size === 0 && this.#mutationObserver) {
560
+ this.#mutationObserver.disconnect();
561
+ this.#mutationObserver = null;
562
+ clearTimeout(this.#debounceTimer);
563
+ this.#debounceTimer = null;
564
+ }
565
+
566
+ return wasObserved;
567
+ };
568
+
569
+ /**
570
+ * Arrête l'observation de tous les éléments
571
+ */
572
+ unobserveAll = () => {
573
+ if (this.#mutationObserver) {
574
+ this.#mutationObserver.disconnect();
575
+ this.#mutationObserver = null;
576
+ }
577
+
578
+ this.#observedElements.clear();
579
+ clearTimeout(this.#debounceTimer);
580
+ this.#debounceTimer = null;
581
+ };
582
+
583
+ /**
584
+ * Getter pour savoir quels éléments sont observés
585
+ * @returns {Element[]} Liste des éléments actuellement observés
586
+ */
587
+ get observedElements() {
588
+ return Array.from(this.#observedElements);
589
+ }
590
+
591
+ /**
592
+ * Vérifie si un élément est actuellement observé
593
+ * @param {Element} element - L'élément à vérifier
594
+ * @returns {boolean} True si l'élément est observé
595
+ */
596
+ isObserving = (element) => {
597
+ return this.#observedElements.has(element);
598
+ };
599
+
600
+ /**
601
+ * Nettoie les ressources utilisées par l'instance.
602
+ * À appeler lors de la destruction du composant.
603
+ */
604
+ destroy = () => {
605
+ this.unobserveAll();
606
+ this.#cleanup?.();
607
+ this.raw = null;
608
+ this.#sel = null;
609
+ this.#ranges = [];
610
+ };
611
+ }
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@aionbuilders/nabu",
3
+ "version": "0.1.0-alpha.0",
4
+ "scripts": {
5
+ "dev": "vite dev",
6
+ "build": "vite build && npm run prepack",
7
+ "preview": "vite preview",
8
+ "prepare": "svelte-kit sync || echo ''",
9
+ "prepack": "bunx svelte-kit sync && bunx svelte-package && bunx publint",
10
+ "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
11
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "!dist/**/*.test.*",
16
+ "!dist/**/*.spec.*"
17
+ ],
18
+ "sideEffects": [
19
+ "**/*.css"
20
+ ],
21
+ "svelte": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "type": "module",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "svelte": "./dist/index.js"
28
+ }
29
+ },
30
+ "peerDependencies": {
31
+ "svelte": "^5.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@sveltejs/adapter-auto": "^7.0.0",
35
+ "@sveltejs/kit": "^2.50.2",
36
+ "@sveltejs/package": "^2.5.7",
37
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
38
+ "publint": "^0.3.17",
39
+ "svelte": "^5.49.2",
40
+ "svelte-check": "^4.3.6",
41
+ "typescript": "^5.9.3",
42
+ "vite": "^7.3.1",
43
+ "vite-plugin-top-level-await": "^1.6.0",
44
+ "vite-plugin-wasm": "^3.5.0"
45
+ },
46
+ "description": "Modular, local-first block editor engine for Svelte 5, built on a single contenteditable architecture and Loro-CRDT.",
47
+ "author": {
48
+ "name": "Killian Di Vincenzo",
49
+ "url": "https://github.com/killiandvcz"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "license": "MIT",
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/aionbuilders/nabu"
58
+ },
59
+ "homepage": "https://github.com/aionbuilders/nabu#readme",
60
+ "keywords": [
61
+ "svelte",
62
+ "svelte5",
63
+ "editor",
64
+ "block-editor",
65
+ "rich-text",
66
+ "crdt",
67
+ "loro",
68
+ "local-first",
69
+ "collaborative"
70
+ ],
71
+ "dependencies": {
72
+ "loro-crdt": "^1.10.6"
73
+ }
74
+ }