@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,143 @@
1
+
2
+ import { SvelteSelection } from "../utils/selection.svelte";
3
+
4
+ /**
5
+ * @import { Nabu } from "./nabu.svelte";
6
+ * @import { Block } from "./block.svelte";
7
+ * @import { LoroTree, LoroTreeNode } from "loro-crdt";
8
+ */
9
+
10
+ export class NabuSelection extends SvelteSelection {
11
+ /** @param {Nabu} nabu */
12
+ constructor(nabu) {
13
+ super();
14
+ this.nabu = nabu;
15
+
16
+ $effect(() => {
17
+ if (this.blocks) {
18
+ this.previous.forEach(block => block.clearSelection());
19
+ let i = 0;
20
+ const lastIndex = this.blocks.size - 1;
21
+ this.blocks.forEach((block) => {
22
+ block.selected = true;
23
+ block.isSelectionStart = (i === 0);
24
+ block.isSelectionEnd = i === lastIndex;
25
+ i++;
26
+ });
27
+ this.previous = new Set(this.blocks);
28
+ }
29
+ })
30
+ }
31
+
32
+ anchorBlock = $derived.by(() => {
33
+ this.anchorNode;
34
+ const id = this.anchorNode?.parentElement?.closest("[data-block-id]")?.getAttribute("data-block-id");
35
+ return id ? this.nabu.blocks.get(id) : null;
36
+ });
37
+
38
+ focusBlock = $derived.by(() => {
39
+ this.focusNode;
40
+ const id = this.focusNode?.parentElement?.closest("[data-block-id]")?.getAttribute("data-block-id");
41
+ return id ? this.nabu.blocks.get(id) : null;
42
+ });
43
+
44
+ startBlock = $derived.by(() => {
45
+ this.range;
46
+ const id = this.range?.startContainer?.parentElement?.closest("[data-block-id]")?.getAttribute("data-block-id");
47
+ return id ? this.nabu.blocks.get(id) : null;
48
+ });
49
+
50
+ endBlock = $derived.by(() => {
51
+ this.range;
52
+ const id = this.range?.endContainer?.parentElement?.closest("[data-block-id]")?.getAttribute("data-block-id");
53
+ return id ? this.nabu.blocks.get(id) : null;
54
+ });
55
+
56
+ start = $derived(this.startBlock?.selection && {
57
+ block: this.startBlock,
58
+ ...this.startBlock.selection
59
+ });
60
+
61
+ end = $derived(this.endBlock?.selection && {
62
+ block: this.endBlock,
63
+ ...this.endBlock.selection
64
+ });
65
+
66
+ /**
67
+ * Définit le curseur à un endroit précis du document (Modèle -> DOM)
68
+ * @param {Block} block
69
+ * @param {number} offset
70
+ */
71
+ setCursor(block, offset) {
72
+ if (!block.element) return;
73
+
74
+ // On délègue au bloc le soin de trouver le bon point DOM
75
+ // (Chaque type de bloc sait comment il affiche son texte)
76
+ //@ts-ignore
77
+ const point = block.getDOMPoint?.(offset);
78
+ if (!point) return;
79
+
80
+ this.setBaseAndExtent(point.node, point.offset, point.node, point.offset);
81
+ }
82
+
83
+ /** @type {Set<Block>} */
84
+ previous = new Set();
85
+ blocks = $derived(new Set(getNodesBetween(this.nabu.tree, this.startBlock?.node.id.toString() || "", this.endBlock?.node.id.toString() || "").map(node => {
86
+ return this.nabu.blocks.get(node.id.toString());
87
+ }).filter(b => !!b)));
88
+
89
+ }
90
+
91
+
92
+
93
+ /**
94
+ * Récupère tous les nœuds situés entre deux nœuds dans un arbre Loro (version aplatie).
95
+ * L'ordre dans lequel on passe A et B n'a pas d'importance.
96
+ * @param {LoroTree} tree - L'arbre Loro à parcourir
97
+ * @param {string} nodeIdA - ID du premier nœud de la sélection
98
+ * @param {string} nodeIdB - ID du second nœud de la sélection
99
+ */
100
+ function getNodesBetween(tree, nodeIdA, nodeIdB) {
101
+ const roots = tree.roots();
102
+
103
+ let isRecording = false;
104
+ /** @type {LoroTreeNode[]} */
105
+ const nodesInRange = [];
106
+
107
+
108
+ // if (nodeIdA && nodeIdA === nodeIdB) {
109
+ //
110
+ // }
111
+
112
+ /** @param {LoroTreeNode[]} nodes */
113
+ function traverse(nodes) {
114
+ for (const node of nodes) {
115
+ const currentId = node.id.toString();
116
+
117
+ if (currentId === nodeIdA) {
118
+ isRecording = true;
119
+ nodesInRange.push(node);
120
+
121
+ if (currentId === nodeIdB) {
122
+ return true;
123
+ }
124
+ } else if (isRecording) {
125
+ nodesInRange.push(node);
126
+ if (currentId === nodeIdB) {
127
+ return true;
128
+ }
129
+ }
130
+
131
+ const children = node.children();
132
+ if (children && children.length > 0) {
133
+ if (traverse(children)) {
134
+ return true;
135
+ }
136
+ }
137
+ }
138
+ return false; // La fin n'a pas été trouvée dans cette branche
139
+ }
140
+
141
+ traverse(roots);
142
+ return nodesInRange;
143
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./blocks";
2
+ export * from "./utils";
3
+ export * from "./behaviors";
4
+ export { RichTextExtension, RichText } from "./behaviors/text";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // Reexport your entry components here
2
+ export { RichTextExtension, RichText } from './behaviors/text';
3
+ export * from './blocks';
4
+ export * from "./utils";
5
+ export * from './behaviors';
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @import { Nabu, Block } from '../blocks'
3
+ */
4
+ /**
5
+ * @typedef {{
6
+ * onInit: function(Nabu): void,
7
+ * onBlockCreate: function(Nabu, Block): void,
8
+ * onBlockDelete: function(Nabu, Block): void,
9
+ * onBlockUpdate: function(Nabu, Block): void,
10
+ * onBeforeTransaction: function(Nabu, Array<{type: string, block: Block}>): void,
11
+ * onAfterTransaction: function(Nabu, Array<{type: string, block: Block}>): void,
12
+ * onSplit: function(Nabu, Block, Event, {offset: number, delta: import('loro-crdt').Delta<string>}): {block: Block},
13
+ * onBeforeInput: function(Nabu, InputEvent, Block): void,
14
+ * onInput: function(Nabu, InputEvent, Block): void,
15
+ * } & Object<string, function>} ExtensionHooks
16
+ */
17
+ /**
18
+ * @typedef {Object} ExtensionInit
19
+ * @property {string} [name]
20
+ * @property {import('svelte').Component} [component]
21
+ * @property {typeof Block} [block]
22
+ * @property {Partial<ExtensionHooks>} [hooks]
23
+ * @property {Record<string, (nabu: Nabu) => any>} [serializers]
24
+ */
25
+ export class Extension {
26
+ /** @param {string} name @param {ExtensionInit} init */
27
+ constructor(name: string, init?: ExtensionInit);
28
+ name: string;
29
+ component: import("svelte").Component<any, any, string> | undefined;
30
+ block: typeof Block | undefined;
31
+ /** @type {Partial<ExtensionHooks>} */
32
+ hooks: Partial<ExtensionHooks>;
33
+ /** @type {Record<string, (nabu: Nabu) => any>} */
34
+ serializers: Record<string, (nabu: Nabu) => any>;
35
+ }
36
+ export function extension(name: string, init: ExtensionInit): Extension;
37
+ export type ExtensionHooks = {
38
+ onInit: (arg0: Nabu) => void;
39
+ onBlockCreate: (arg0: Nabu, arg1: Block) => void;
40
+ onBlockDelete: (arg0: Nabu, arg1: Block) => void;
41
+ onBlockUpdate: (arg0: Nabu, arg1: Block) => void;
42
+ onBeforeTransaction: (arg0: Nabu, arg1: Array<{
43
+ type: string;
44
+ block: Block;
45
+ }>) => void;
46
+ onAfterTransaction: (arg0: Nabu, arg1: Array<{
47
+ type: string;
48
+ block: Block;
49
+ }>) => void;
50
+ onSplit: (arg0: Nabu, arg1: Block, arg2: Event, arg3: {
51
+ offset: number;
52
+ delta: import("loro-crdt").Delta<string>;
53
+ }) => {
54
+ block: Block;
55
+ };
56
+ onBeforeInput: (arg0: Nabu, arg1: InputEvent, arg2: Block) => void;
57
+ onInput: (arg0: Nabu, arg1: InputEvent, arg2: Block) => void;
58
+ } & {
59
+ [x: string]: Function;
60
+ };
61
+ export type ExtensionInit = {
62
+ name?: string | undefined;
63
+ component?: import("svelte").Component<any, any, string> | undefined;
64
+ block?: typeof Block | undefined;
65
+ hooks?: Partial<ExtensionHooks> | undefined;
66
+ serializers?: Record<string, (nabu: Nabu) => any> | undefined;
67
+ };
68
+ import type { Block } from '../blocks';
69
+ import type { Nabu } from '../blocks';
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @import { Nabu, Block } from '../blocks'
3
+ */
4
+
5
+ /**
6
+ * @typedef {{
7
+ * onInit: function(Nabu): void,
8
+ * onBlockCreate: function(Nabu, Block): void,
9
+ * onBlockDelete: function(Nabu, Block): void,
10
+ * onBlockUpdate: function(Nabu, Block): void,
11
+ * onBeforeTransaction: function(Nabu, Array<{type: string, block: Block}>): void,
12
+ * onAfterTransaction: function(Nabu, Array<{type: string, block: Block}>): void,
13
+ * onSplit: function(Nabu, Block, Event, {offset: number, delta: import('loro-crdt').Delta<string>}): {block: Block},
14
+ * onBeforeInput: function(Nabu, InputEvent, Block): void,
15
+ * onInput: function(Nabu, InputEvent, Block): void,
16
+ * } & Object<string, function>} ExtensionHooks
17
+ */
18
+
19
+ /**
20
+ * @typedef {Object} ExtensionInit
21
+ * @property {string} [name]
22
+ * @property {import('svelte').Component} [component]
23
+ * @property {typeof Block} [block]
24
+ * @property {Partial<ExtensionHooks>} [hooks]
25
+ * @property {Record<string, (nabu: Nabu) => any>} [serializers]
26
+ */
27
+
28
+ export class Extension {
29
+ /** @param {string} name @param {ExtensionInit} init */
30
+ constructor(name, init = {}) {
31
+ this.name = name;
32
+ this.component = init.component;
33
+ this.block = init.block;
34
+ /** @type {Partial<ExtensionHooks>} */
35
+ this.hooks = init.hooks || {};
36
+ /** @type {Record<string, (nabu: Nabu) => any>} */
37
+ this.serializers = init.serializers || {};
38
+ }
39
+ }
40
+
41
+
42
+ /** @param {string} name @param {ExtensionInit} init */
43
+ export const extension = (name, init) => new Extension(name, init);
@@ -0,0 +1,2 @@
1
+ export * from "./extensions.js";
2
+ export * from "./selection.svelte.js";
@@ -0,0 +1,2 @@
1
+ export * from "./extensions.js";
2
+ export * from "./selection.svelte.js";
@@ -0,0 +1,219 @@
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
+ * Utilitaire réactif pour gérer la sélection de texte avec Svelte 5 runes
21
+ * @example
22
+ * const selection = new SvelteSelection();
23
+ * // Utiliser selection.text, selection.isCollapsed, etc. dans vos composants
24
+ */
25
+ export class SvelteSelection {
26
+ /** @type {Selection?} */
27
+ raw: Selection | null;
28
+ is: boolean;
29
+ anchorNode: Node | null;
30
+ anchorOffset: number;
31
+ focusNode: Node | null;
32
+ focusOffset: number;
33
+ isCollapsed: boolean;
34
+ rangeCount: number;
35
+ type: string;
36
+ direction: "forward" | "backward" | "none";
37
+ startNode: Node | null;
38
+ endNode: Node | null;
39
+ startOffset: number;
40
+ endOffset: number;
41
+ ranges: Range[];
42
+ firstRange: Range;
43
+ get range(): Range;
44
+ lastRange: Range;
45
+ text: string;
46
+ html: string;
47
+ hasSelection: boolean;
48
+ isEmpty: boolean;
49
+ hasMultipleRanges: boolean;
50
+ commonAncestor: Node | null;
51
+ allText: string;
52
+ allHtml: string;
53
+ boundingRects: DOMRect[];
54
+ refresh: () => void;
55
+ /**
56
+ * Ajoute une range à la sélection.
57
+ * @param {Range} range - La range à ajouter.
58
+ * @returns {boolean} True si l'opération a réussi
59
+ */
60
+ addRange: (range: Range) => boolean;
61
+ /**
62
+ * Supprime toutes les ranges de la sélection.
63
+ * @returns {boolean} True si l'opération a réussi
64
+ */
65
+ removeAllRanges: () => boolean;
66
+ /**
67
+ * Supprime une range spécifique de la sélection.
68
+ * @param {Range} range - La range à supprimer.
69
+ * @returns {boolean} True si l'opération a réussi
70
+ */
71
+ removeRange: (range: Range) => boolean;
72
+ /**
73
+ * Récupère la range à l'index spécifié.
74
+ * @param {number} index - L'index de la range à récupérer.
75
+ * @returns {Range|null} La range à l'index spécifié, ou null si elle n'existe pas.
76
+ */
77
+ getRangeAt: (index: number) => Range | null;
78
+ /**
79
+ * Réduit la sélection à un nœud et offset spécifiques.
80
+ * @param {Node} node - Le nœud vers lequel réduire la sélection.
81
+ * @param {number} offset - L'offset dans le nœud.
82
+ * @returns {boolean} True si l'opération a réussi
83
+ */
84
+ collapse: (node: Node, offset?: number) => boolean;
85
+ /**
86
+ * Réduit la sélection au début de la range courante.
87
+ * @returns {boolean} True si l'opération a réussi
88
+ */
89
+ collapseToStart: () => boolean;
90
+ /**
91
+ * Réduit la sélection à la fin de la range courante.
92
+ * @returns {boolean} True si l'opération a réussi
93
+ */
94
+ collapseToEnd: () => boolean;
95
+ /**
96
+ * Sélectionne le contenu d'un nœud.
97
+ * @param {Node} node - Le nœud dont le contenu doit être sélectionné.
98
+ * @returns {boolean} True si l'opération a réussi
99
+ */
100
+ selectAllChildren: (node: Node) => boolean;
101
+ /**
102
+ * Étend la sélection vers un nœud et offset spécifiques.
103
+ * @param {Node} node - Le nœud vers lequel étendre.
104
+ * @param {number} offset - L'offset dans le nœud.
105
+ * @returns {boolean} True si l'opération a réussi
106
+ */
107
+ extend: (node: Node, offset?: number) => boolean;
108
+ /**
109
+ * Définit la base et l'extension de la sélection.
110
+ * @param {Node} anchorNode - Le nœud d'ancrage.
111
+ * @param {number} anchorOffset - L'offset d'ancrage.
112
+ * @param {Node} focusNode - Le nœud de focus.
113
+ * @param {number} focusOffset - L'offset de focus.
114
+ * @returns {boolean} True si l'opération a réussi
115
+
116
+ */
117
+ setBaseAndExtent: (anchorNode: Node, anchorOffset: number, focusNode: Node, focusOffset: number) => boolean;
118
+ /**
119
+ * Vérifie si la sélection contient un nœud donné.
120
+ * @param {Node} node - Le nœud à vérifier.
121
+ * @param {boolean} allowPartialContainment - Permet la contenance partielle.
122
+ * @returns {boolean} True si le nœud est contenu dans la sélection.
123
+ */
124
+ containsNode: (node: Node, allowPartialContainment?: boolean) => boolean;
125
+ /**
126
+ * Crée une nouvelle range basée sur la sélection courante.
127
+ * @returns {Range|null} Une nouvelle range ou null.
128
+ */
129
+ createRange: () => Range | null;
130
+ /**
131
+ * Crée des copies de toutes les ranges courantes.
132
+ * @returns {Range[]} Un array de toutes les ranges clonées.
133
+ */
134
+ createAllRanges: () => Range[];
135
+ /**
136
+ * Récupère les coordonnées de toutes les ranges.
137
+ * @returns {DOMRect[]} Un array des rectangles de toutes les ranges.
138
+ */
139
+ getAllBoundingRects: () => DOMRect[];
140
+ /**
141
+ * Vérifie si toutes les ranges sont dans un élément donné.
142
+ * @param {Element} element - L'élément à vérifier.
143
+ * @returns {boolean} True si toutes les ranges sont contenues.
144
+ */
145
+ allRangesInElement: (element: Element) => boolean;
146
+ /**
147
+ * Filtre les ranges selon un prédicat.
148
+ * @param {function(Range): boolean} predicate - Fonction de filtre.
149
+ * @returns {Range[]} Les ranges qui passent le filtre.
150
+ */
151
+ filterRanges: (predicate: (arg0: Range) => boolean) => Range[];
152
+ /**
153
+ * Sélectionne tout le contenu du document.
154
+ * @returns {boolean} True si l'opération a réussi
155
+ */
156
+ selectAll: () => boolean;
157
+ /**
158
+ * Efface la sélection courante.
159
+ * @returns {boolean} True si l'opération a réussi
160
+ */
161
+ clear: () => boolean;
162
+ /**
163
+ * Observe un élément contenteditable pour les mutations DOM qui peuvent
164
+ * affecter la position du caret sans déclencher selectionchange
165
+ *
166
+ * @param {Element} element - L'élément contenteditable à observer
167
+ * @param {Object} options - Options d'observation
168
+ * @param {number} options.debounceMs - Délai de debounce en ms (défaut: 16ms = ~1 frame)
169
+ * @param {boolean} options.observeAttributes - Observer les changements d'attributs (défaut: false)
170
+ * @returns {boolean} True si l'observation a été configurée avec succès
171
+ */
172
+ observe: (element: Element, options?: {
173
+ debounceMs: number;
174
+ observeAttributes: boolean;
175
+ }) => boolean;
176
+ /**
177
+ * Arrête l'observation d'un élément spécifique
178
+ * @param {Element} element - L'élément à ne plus observer
179
+ * @returns {boolean} True si l'élément était observé et a été retiré
180
+ */
181
+ unobserve: (element: Element) => boolean;
182
+ /**
183
+ * Arrête l'observation de tous les éléments
184
+ */
185
+ unobserveAll: () => void;
186
+ /**
187
+ * Getter pour savoir quels éléments sont observés
188
+ * @returns {Element[]} Liste des éléments actuellement observés
189
+ */
190
+ get observedElements(): Element[];
191
+ /**
192
+ * Vérifie si un élément est actuellement observé
193
+ * @param {Element} element - L'élément à vérifier
194
+ * @returns {boolean} True si l'élément est observé
195
+ */
196
+ isObserving: (element: Element) => boolean;
197
+ /**
198
+ * Nettoie les ressources utilisées par l'instance.
199
+ * À appeler lors de la destruction du composant.
200
+ */
201
+ destroy: () => void;
202
+ #private;
203
+ }
204
+ export type SelectionObject = {
205
+ anchorNode: Node | null;
206
+ anchorOffset: number;
207
+ focusNode: Node | null;
208
+ focusOffset: number;
209
+ startNode: Node | null;
210
+ startOffset: number;
211
+ endNode: Node | null;
212
+ endOffset: number;
213
+ isCollapsed: boolean;
214
+ rangeCount: number;
215
+ type: string;
216
+ direction: "forward" | "backward" | "none";
217
+ text: string;
218
+ html: string;
219
+ };