@sametalmdr/word-editor 0.2.0-alpha → 0.3.0-alpha

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 (82) hide show
  1. package/CHANGELOG.md +108 -70
  2. package/LICENSE +21 -21
  3. package/README.md +286 -247
  4. package/dist/canvas/box-clipboard.js +238 -238
  5. package/dist/canvas/box-selection.js +80 -80
  6. package/dist/canvas/canvas-editor.js +502 -502
  7. package/dist/canvas/controller.js +717 -717
  8. package/dist/canvas/geometry.js +131 -131
  9. package/dist/canvas/gesture.d.ts +0 -1
  10. package/dist/canvas/gesture.js +289 -289
  11. package/dist/canvas/handles.js +212 -212
  12. package/dist/canvas/menu.js +82 -82
  13. package/dist/canvas/mode-controller.js +81 -81
  14. package/dist/canvas/nudge.js +108 -108
  15. package/dist/canvas/overlay.js +149 -149
  16. package/dist/canvas/snap.js +132 -132
  17. package/dist/canvas/viewport.js +112 -112
  18. package/dist/canvas/z-order.js +171 -171
  19. package/dist/commands/alignment.js +150 -150
  20. package/dist/commands/box.js +272 -272
  21. package/dist/commands/clipboard.js +93 -93
  22. package/dist/commands/graphemes.js +112 -112
  23. package/dist/commands/marks.js +153 -153
  24. package/dist/commands/paragraph-props.js +176 -176
  25. package/dist/commands/size.js +368 -368
  26. package/dist/commands/text.js +291 -291
  27. package/dist/docx.d.ts +1 -0
  28. package/dist/docx.js +15 -13
  29. package/dist/editor/editing-host.js +434 -434
  30. package/dist/element.js +41 -41
  31. package/dist/fonts/OFL-Carlito.txt +94 -0
  32. package/dist/fonts/calibri-metrics.d.ts +27 -0
  33. package/dist/fonts/calibri-metrics.js +88 -0
  34. package/dist/history/history.js +433 -433
  35. package/dist/index.js +63 -63
  36. package/dist/input/beforeinput.js +224 -224
  37. package/dist/input/clipboard.js +107 -107
  38. package/dist/input/composition.js +341 -341
  39. package/dist/input/keymap.js +54 -54
  40. package/dist/io/ooxml/custgeom.js +72 -72
  41. package/dist/io/ooxml/drawingml.js +232 -232
  42. package/dist/io/ooxml/edits.js +67 -67
  43. package/dist/io/ooxml/export-splice.js +880 -880
  44. package/dist/io/ooxml/export.js +365 -365
  45. package/dist/io/ooxml/fill.d.ts +93 -8
  46. package/dist/io/ooxml/fill.js +618 -562
  47. package/dist/io/ooxml/fit.d.ts +252 -0
  48. package/dist/io/ooxml/fit.js +598 -0
  49. package/dist/io/ooxml/import-gate.js +200 -200
  50. package/dist/io/ooxml/import.js +691 -691
  51. package/dist/io/ooxml/inflate.js +126 -126
  52. package/dist/io/ooxml/media.js +87 -87
  53. package/dist/io/ooxml/ns.d.ts +2 -0
  54. package/dist/io/ooxml/ns.js +26 -21
  55. package/dist/io/ooxml/numbering.js +290 -290
  56. package/dist/io/ooxml/opc-write.js +171 -171
  57. package/dist/io/ooxml/provenance.js +93 -93
  58. package/dist/io/ooxml/recipe.js +708 -708
  59. package/dist/io/ooxml/relationships.js +59 -59
  60. package/dist/io/ooxml/run-fonts.d.ts +89 -0
  61. package/dist/io/ooxml/run-fonts.js +378 -0
  62. package/dist/io/ooxml/slots.d.ts +4 -0
  63. package/dist/io/ooxml/slots.js +582 -581
  64. package/dist/io/ooxml/styles-resolve.js +257 -257
  65. package/dist/io/ooxml/units.js +43 -43
  66. package/dist/io/ooxml/value.js +120 -120
  67. package/dist/io/ooxml/xml-scan.js +616 -616
  68. package/dist/io/ooxml/zip-container.js +228 -228
  69. package/dist/io/ooxml/zip-reject.js +106 -106
  70. package/dist/measure/placement.js +83 -83
  71. package/dist/measure/report.js +75 -75
  72. package/dist/model/ids.js +70 -70
  73. package/dist/model/normalize.js +146 -146
  74. package/dist/model/position.js +283 -283
  75. package/dist/model/transaction.js +209 -209
  76. package/dist/platform/capabilities.js +25 -25
  77. package/dist/react/index.js +21 -21
  78. package/dist/selection/controller.js +315 -315
  79. package/dist/selection/dom-map.js +334 -334
  80. package/dist/state/editor-state.js +109 -109
  81. package/dist/styles.css +66 -66
  82. package/package.json +94 -93
@@ -1,209 +1,209 @@
1
- import { normalizeDoc } from "./normalize.js";
2
- import { apply, dirtyParagraphIds } from "./step-apply.js";
3
- import { mapThroughSteps } from "./step-map.js";
4
-
5
- /**
6
- * One user-visible change. A transaction is the unit of undo, so everything that
7
- * should disappear on a single Ctrl+Z belongs in one of these — typing over a
8
- * selection is a delete *and* an insert, but one transaction.
9
- *
10
- * @typedef {object} Transaction
11
- * @property {import("./step.js").Step[]} steps
12
- * @property {import("./node.js").Doc} docBefore
13
- * @property {import("./node.js").Doc} doc normalized result
14
- * @property {import("./position.js").Selection} selectionBefore
15
- * @property {import("./position.js").Selection} selectionAfter
16
- * @property {TransactionOrigin} origin
17
- * @property {number} timestamp
18
- * @property {import("./node.js").Marks | null | undefined} storedMarks
19
- * `undefined` means "derive from the default rules"; an explicit value (or
20
- * `null`) overrides them. See `state/editor-state.js`.
21
- * @property {number} [gestureId]
22
- * ADR-0016 §1 / ADR-0018 Karar 3's pointer-gesture bracket id. Set only by
23
- * a pointer drag or a keyboard nudge (both `origin: "pointer"`) via
24
- * `setGesture`; `undefined` for every other origin. `HistoryEngine`
25
- * compares this by equality across consecutive "pointer" transactions to
26
- * decide whether they belong to the SAME user gesture — it never enters
27
- * `Doc`'s own schema, never reaches `toJSON`, never reaches the clipboard
28
- * (invariant #13): it lives on `Transaction`/`HistoryEntry` only.
29
- * @property {readonly string[]} [unverifiedAssumptions]
30
- * The command-layer counterpart of `ImportReport.unverifiedAssumptions`
31
- * (`io/ooxml/import.js`) — but DYNAMIC, not static: import's list is
32
- * disclosed unconditionally on every document, this one is populated only
33
- * when a command actually exercised a constant it could not verify from a
34
- * primary source (ADR-0028: `commands/size.js`'s clamp ceiling, sourced
35
- * only from a secondary reference). `undefined` when nothing was noted —
36
- * same absent-vs-empty convention as `gestureId`. Same scope restriction:
37
- * never model schema, never `toJSON`, never the clipboard.
38
- */
39
-
40
- /**
41
- * What produced the transaction. Drives undo coalescing, so it has to be set
42
- * honestly — `"input"` is only for ordinary typing, `"pointer"` is only for a
43
- * box drag (`commands/box.js`'s `moveBox`/`resizeBox`), each origin coalescing
44
- * under its own rule in `HistoryEngine.canCoalesce`.
45
- * @typedef {"input" | "paste" | "format" | "history" | "ime" | "pointer"} TransactionOrigin
46
- */
47
-
48
- /**
49
- * Accumulates steps against an evolving document.
50
- *
51
- * Commands naturally compute their ranges against the document they were handed,
52
- * but each step after the first must be expressed in coordinates *after* the
53
- * preceding steps. `map()` bridges that: hand it a position in the original
54
- * document and it returns the equivalent position now.
55
- */
56
- export class TransactionBuilder {
57
- /**
58
- * @param {import("./node.js").Doc} doc
59
- * @param {import("./position.js").Selection} selection
60
- * @param {TransactionOrigin} [origin]
61
- */
62
- constructor(doc, selection, origin = "input") {
63
- /** @type {import("./node.js").Doc} */
64
- this.docBefore = doc;
65
- /** @type {import("./node.js").Doc} */
66
- this.doc = doc;
67
- /** @type {import("./step.js").Step[]} */
68
- this.steps = [];
69
- /** @type {import("./position.js").Selection} */
70
- this.selectionBefore = selection;
71
- /** @type {import("./position.js").Selection} */
72
- this.selectionAfter = selection;
73
- /** @type {TransactionOrigin} */
74
- this.origin = origin;
75
- /** @type {Set<import("./node.js").NodeId>} */
76
- this.dirtyIds = new Set();
77
- /** @type {import("./node.js").Marks | null | undefined} */
78
- this.storedMarks = undefined;
79
- /** @type {number | undefined} */
80
- this.gestureId = undefined;
81
- /** @type {string[] | undefined} */
82
- this.unverifiedAssumptions = undefined;
83
- }
84
-
85
- /**
86
- * Applies a step, expressed in *current* coordinates.
87
- * @param {import("./step.js").Step} step
88
- * @returns {this}
89
- */
90
- step(step) {
91
- for (const id of dirtyParagraphIds(this.doc, step)) {
92
- this.dirtyIds.add(id);
93
- }
94
- this.doc = apply(this.doc, step);
95
- this.steps.push(step);
96
- return this;
97
- }
98
-
99
- /**
100
- * Translates a position from the original document into current coordinates.
101
- * @param {import("./position.js").Position} pos
102
- * @param {-1 | 1} [bias]
103
- * @returns {import("./position.js").Position}
104
- */
105
- map(pos, bias = 1) {
106
- return mapThroughSteps(this.docBefore, this.steps, pos, bias).pos;
107
- }
108
-
109
- /**
110
- * @param {import("./position.js").Selection} selection
111
- * @returns {this}
112
- */
113
- setSelection(selection) {
114
- this.selectionAfter = selection;
115
- return this;
116
- }
117
-
118
- /**
119
- * Overrides the default `storedMarks` handling. Used by `toggleMark` on a
120
- * collapsed selection, which changes no document content at all.
121
- * @param {import("./node.js").Marks | null} marks
122
- * @returns {this}
123
- */
124
- setStoredMarks(marks) {
125
- this.storedMarks = marks;
126
- return this;
127
- }
128
-
129
- /**
130
- * Tags this transaction with the pointer-gesture bracket it belongs to
131
- * (ADR-0016 §1, ADR-0018 Karar 3) — `HistoryEngine.canCoalesce` compares
132
- * this by equality across consecutive `"pointer"` transactions to decide
133
- * whether they are the SAME user gesture. Never model schema, never
134
- * `toJSON`, never the clipboard (invariant #13) — `id` is minted by the
135
- * caller's `IdFactory` before this is called; this method never mints one.
136
- * @param {number} id
137
- * @returns {this}
138
- */
139
- setGesture(id) {
140
- this.gestureId = id;
141
- return this;
142
- }
143
-
144
- /**
145
- * Records that this transaction exercised a constant not verified from a
146
- * primary source — the dynamic, command-layer counterpart of
147
- * `ImportReport.unverifiedAssumptions`. Deduplicates identical messages
148
- * (a range edit can clamp several segments against the same ceiling in one
149
- * transaction; the caller should not have to track "already noted" itself).
150
- * @param {string} message
151
- * @returns {this}
152
- */
153
- noteUnverifiedAssumption(message) {
154
- if (this.unverifiedAssumptions === undefined) {
155
- this.unverifiedAssumptions = [];
156
- }
157
- if (!this.unverifiedAssumptions.includes(message)) {
158
- this.unverifiedAssumptions.push(message);
159
- }
160
- return this;
161
- }
162
-
163
- /** @returns {boolean} */
164
- get isEmpty() {
165
- return this.steps.length === 0;
166
- }
167
-
168
- /**
169
- * Whether the steps actually changed anything. A step can be a no-op — marking
170
- * text that already carries the mark, deleting an empty range — and such a
171
- * transaction must not reach the history stack, or Ctrl+Z would appear to do
172
- * nothing.
173
- * @returns {boolean}
174
- */
175
- get changesDoc() {
176
- return this.doc !== this.docBefore;
177
- }
178
-
179
- /**
180
- * @param {number} [timestamp]
181
- * @returns {Transaction}
182
- */
183
- build(timestamp = Date.now()) {
184
- return {
185
- steps: this.steps,
186
- docBefore: this.docBefore,
187
- // Normalisation happens once, here — never between steps, so a step is
188
- // free to leave empty or mergeable runs behind.
189
- doc: normalizeDoc(this.doc, this.dirtyIds),
190
- selectionBefore: this.selectionBefore,
191
- selectionAfter: this.selectionAfter,
192
- origin: this.origin,
193
- timestamp,
194
- storedMarks: this.storedMarks,
195
- gestureId: this.gestureId,
196
- unverifiedAssumptions: this.unverifiedAssumptions,
197
- };
198
- }
199
- }
200
-
201
- /**
202
- * @param {import("./node.js").Doc} doc
203
- * @param {import("./position.js").Selection} selection
204
- * @param {TransactionOrigin} [origin]
205
- * @returns {TransactionBuilder}
206
- */
207
- export function createTransaction(doc, selection, origin = "input") {
208
- return new TransactionBuilder(doc, selection, origin);
209
- }
1
+ import { normalizeDoc } from "./normalize.js";
2
+ import { apply, dirtyParagraphIds } from "./step-apply.js";
3
+ import { mapThroughSteps } from "./step-map.js";
4
+
5
+ /**
6
+ * One user-visible change. A transaction is the unit of undo, so everything that
7
+ * should disappear on a single Ctrl+Z belongs in one of these — typing over a
8
+ * selection is a delete *and* an insert, but one transaction.
9
+ *
10
+ * @typedef {object} Transaction
11
+ * @property {import("./step.js").Step[]} steps
12
+ * @property {import("./node.js").Doc} docBefore
13
+ * @property {import("./node.js").Doc} doc normalized result
14
+ * @property {import("./position.js").Selection} selectionBefore
15
+ * @property {import("./position.js").Selection} selectionAfter
16
+ * @property {TransactionOrigin} origin
17
+ * @property {number} timestamp
18
+ * @property {import("./node.js").Marks | null | undefined} storedMarks
19
+ * `undefined` means "derive from the default rules"; an explicit value (or
20
+ * `null`) overrides them. See `state/editor-state.js`.
21
+ * @property {number} [gestureId]
22
+ * ADR-0016 §1 / ADR-0018 Karar 3's pointer-gesture bracket id. Set only by
23
+ * a pointer drag or a keyboard nudge (both `origin: "pointer"`) via
24
+ * `setGesture`; `undefined` for every other origin. `HistoryEngine`
25
+ * compares this by equality across consecutive "pointer" transactions to
26
+ * decide whether they belong to the SAME user gesture — it never enters
27
+ * `Doc`'s own schema, never reaches `toJSON`, never reaches the clipboard
28
+ * (invariant #13): it lives on `Transaction`/`HistoryEntry` only.
29
+ * @property {readonly string[]} [unverifiedAssumptions]
30
+ * The command-layer counterpart of `ImportReport.unverifiedAssumptions`
31
+ * (`io/ooxml/import.js`) — but DYNAMIC, not static: import's list is
32
+ * disclosed unconditionally on every document, this one is populated only
33
+ * when a command actually exercised a constant it could not verify from a
34
+ * primary source (ADR-0028: `commands/size.js`'s clamp ceiling, sourced
35
+ * only from a secondary reference). `undefined` when nothing was noted —
36
+ * same absent-vs-empty convention as `gestureId`. Same scope restriction:
37
+ * never model schema, never `toJSON`, never the clipboard.
38
+ */
39
+
40
+ /**
41
+ * What produced the transaction. Drives undo coalescing, so it has to be set
42
+ * honestly — `"input"` is only for ordinary typing, `"pointer"` is only for a
43
+ * box drag (`commands/box.js`'s `moveBox`/`resizeBox`), each origin coalescing
44
+ * under its own rule in `HistoryEngine.canCoalesce`.
45
+ * @typedef {"input" | "paste" | "format" | "history" | "ime" | "pointer"} TransactionOrigin
46
+ */
47
+
48
+ /**
49
+ * Accumulates steps against an evolving document.
50
+ *
51
+ * Commands naturally compute their ranges against the document they were handed,
52
+ * but each step after the first must be expressed in coordinates *after* the
53
+ * preceding steps. `map()` bridges that: hand it a position in the original
54
+ * document and it returns the equivalent position now.
55
+ */
56
+ export class TransactionBuilder {
57
+ /**
58
+ * @param {import("./node.js").Doc} doc
59
+ * @param {import("./position.js").Selection} selection
60
+ * @param {TransactionOrigin} [origin]
61
+ */
62
+ constructor(doc, selection, origin = "input") {
63
+ /** @type {import("./node.js").Doc} */
64
+ this.docBefore = doc;
65
+ /** @type {import("./node.js").Doc} */
66
+ this.doc = doc;
67
+ /** @type {import("./step.js").Step[]} */
68
+ this.steps = [];
69
+ /** @type {import("./position.js").Selection} */
70
+ this.selectionBefore = selection;
71
+ /** @type {import("./position.js").Selection} */
72
+ this.selectionAfter = selection;
73
+ /** @type {TransactionOrigin} */
74
+ this.origin = origin;
75
+ /** @type {Set<import("./node.js").NodeId>} */
76
+ this.dirtyIds = new Set();
77
+ /** @type {import("./node.js").Marks | null | undefined} */
78
+ this.storedMarks = undefined;
79
+ /** @type {number | undefined} */
80
+ this.gestureId = undefined;
81
+ /** @type {string[] | undefined} */
82
+ this.unverifiedAssumptions = undefined;
83
+ }
84
+
85
+ /**
86
+ * Applies a step, expressed in *current* coordinates.
87
+ * @param {import("./step.js").Step} step
88
+ * @returns {this}
89
+ */
90
+ step(step) {
91
+ for (const id of dirtyParagraphIds(this.doc, step)) {
92
+ this.dirtyIds.add(id);
93
+ }
94
+ this.doc = apply(this.doc, step);
95
+ this.steps.push(step);
96
+ return this;
97
+ }
98
+
99
+ /**
100
+ * Translates a position from the original document into current coordinates.
101
+ * @param {import("./position.js").Position} pos
102
+ * @param {-1 | 1} [bias]
103
+ * @returns {import("./position.js").Position}
104
+ */
105
+ map(pos, bias = 1) {
106
+ return mapThroughSteps(this.docBefore, this.steps, pos, bias).pos;
107
+ }
108
+
109
+ /**
110
+ * @param {import("./position.js").Selection} selection
111
+ * @returns {this}
112
+ */
113
+ setSelection(selection) {
114
+ this.selectionAfter = selection;
115
+ return this;
116
+ }
117
+
118
+ /**
119
+ * Overrides the default `storedMarks` handling. Used by `toggleMark` on a
120
+ * collapsed selection, which changes no document content at all.
121
+ * @param {import("./node.js").Marks | null} marks
122
+ * @returns {this}
123
+ */
124
+ setStoredMarks(marks) {
125
+ this.storedMarks = marks;
126
+ return this;
127
+ }
128
+
129
+ /**
130
+ * Tags this transaction with the pointer-gesture bracket it belongs to
131
+ * (ADR-0016 §1, ADR-0018 Karar 3) — `HistoryEngine.canCoalesce` compares
132
+ * this by equality across consecutive `"pointer"` transactions to decide
133
+ * whether they are the SAME user gesture. Never model schema, never
134
+ * `toJSON`, never the clipboard (invariant #13) — `id` is minted by the
135
+ * caller's `IdFactory` before this is called; this method never mints one.
136
+ * @param {number} id
137
+ * @returns {this}
138
+ */
139
+ setGesture(id) {
140
+ this.gestureId = id;
141
+ return this;
142
+ }
143
+
144
+ /**
145
+ * Records that this transaction exercised a constant not verified from a
146
+ * primary source — the dynamic, command-layer counterpart of
147
+ * `ImportReport.unverifiedAssumptions`. Deduplicates identical messages
148
+ * (a range edit can clamp several segments against the same ceiling in one
149
+ * transaction; the caller should not have to track "already noted" itself).
150
+ * @param {string} message
151
+ * @returns {this}
152
+ */
153
+ noteUnverifiedAssumption(message) {
154
+ if (this.unverifiedAssumptions === undefined) {
155
+ this.unverifiedAssumptions = [];
156
+ }
157
+ if (!this.unverifiedAssumptions.includes(message)) {
158
+ this.unverifiedAssumptions.push(message);
159
+ }
160
+ return this;
161
+ }
162
+
163
+ /** @returns {boolean} */
164
+ get isEmpty() {
165
+ return this.steps.length === 0;
166
+ }
167
+
168
+ /**
169
+ * Whether the steps actually changed anything. A step can be a no-op — marking
170
+ * text that already carries the mark, deleting an empty range — and such a
171
+ * transaction must not reach the history stack, or Ctrl+Z would appear to do
172
+ * nothing.
173
+ * @returns {boolean}
174
+ */
175
+ get changesDoc() {
176
+ return this.doc !== this.docBefore;
177
+ }
178
+
179
+ /**
180
+ * @param {number} [timestamp]
181
+ * @returns {Transaction}
182
+ */
183
+ build(timestamp = Date.now()) {
184
+ return {
185
+ steps: this.steps,
186
+ docBefore: this.docBefore,
187
+ // Normalisation happens once, here — never between steps, so a step is
188
+ // free to leave empty or mergeable runs behind.
189
+ doc: normalizeDoc(this.doc, this.dirtyIds),
190
+ selectionBefore: this.selectionBefore,
191
+ selectionAfter: this.selectionAfter,
192
+ origin: this.origin,
193
+ timestamp,
194
+ storedMarks: this.storedMarks,
195
+ gestureId: this.gestureId,
196
+ unverifiedAssumptions: this.unverifiedAssumptions,
197
+ };
198
+ }
199
+ }
200
+
201
+ /**
202
+ * @param {import("./node.js").Doc} doc
203
+ * @param {import("./position.js").Selection} selection
204
+ * @param {TransactionOrigin} [origin]
205
+ * @returns {TransactionBuilder}
206
+ */
207
+ export function createTransaction(doc, selection, origin = "input") {
208
+ return new TransactionBuilder(doc, selection, origin);
209
+ }
@@ -1,25 +1,25 @@
1
- /**
2
- * @typedef {object} Capabilities
3
- * @property {boolean} indexedDB
4
- * @property {boolean} compressionStream
5
- * @property {boolean} decompressionStream
6
- * @property {boolean} fileSystemAccess
7
- * @property {boolean} dom
8
- */
9
-
10
- /**
11
- * Detects platform capabilities without touching any global at import time.
12
- * Every check is lazy — it only runs when this function is called.
13
- * @returns {Capabilities}
14
- */
15
- export function detectCapabilities() {
16
- return {
17
- indexedDB: typeof indexedDB !== "undefined",
18
- compressionStream: typeof CompressionStream !== "undefined",
19
- decompressionStream: typeof DecompressionStream !== "undefined",
20
- fileSystemAccess:
21
- typeof window !== "undefined" &&
22
- typeof (/** @type {any} */ (window).showOpenFilePicker) === "function",
23
- dom: typeof document !== "undefined",
24
- };
25
- }
1
+ /**
2
+ * @typedef {object} Capabilities
3
+ * @property {boolean} indexedDB
4
+ * @property {boolean} compressionStream
5
+ * @property {boolean} decompressionStream
6
+ * @property {boolean} fileSystemAccess
7
+ * @property {boolean} dom
8
+ */
9
+
10
+ /**
11
+ * Detects platform capabilities without touching any global at import time.
12
+ * Every check is lazy — it only runs when this function is called.
13
+ * @returns {Capabilities}
14
+ */
15
+ export function detectCapabilities() {
16
+ return {
17
+ indexedDB: typeof indexedDB !== "undefined",
18
+ compressionStream: typeof CompressionStream !== "undefined",
19
+ decompressionStream: typeof DecompressionStream !== "undefined",
20
+ fileSystemAccess:
21
+ typeof window !== "undefined" &&
22
+ typeof (/** @type {any} */ (window).showOpenFilePicker) === "function",
23
+ dom: typeof document !== "undefined",
24
+ };
25
+ }
@@ -1,21 +1,21 @@
1
- import { createElement, useEffect, useRef } from "react";
2
- import { createEditor } from "../index.js";
3
-
4
- /**
5
- * Thin React wrapper around createEditor(). Holds no internal state of its
6
- * own — the editor instance is the single source of truth.
7
- * @returns {import("react").ReactElement}
8
- */
9
- export function WordEditor() {
10
- const containerRef = useRef(/** @type {HTMLDivElement | null} */ (null));
11
-
12
- useEffect(() => {
13
- if (!containerRef.current) {
14
- return undefined;
15
- }
16
- const editor = createEditor({ container: containerRef.current });
17
- return () => editor.destroy();
18
- }, []);
19
-
20
- return createElement("div", { ref: containerRef });
21
- }
1
+ import { createElement, useEffect, useRef } from "react";
2
+ import { createEditor } from "../index.js";
3
+
4
+ /**
5
+ * Thin React wrapper around createEditor(). Holds no internal state of its
6
+ * own — the editor instance is the single source of truth.
7
+ * @returns {import("react").ReactElement}
8
+ */
9
+ export function WordEditor() {
10
+ const containerRef = useRef(/** @type {HTMLDivElement | null} */ (null));
11
+
12
+ useEffect(() => {
13
+ if (!containerRef.current) {
14
+ return undefined;
15
+ }
16
+ const editor = createEditor({ container: containerRef.current });
17
+ return () => editor.destroy();
18
+ }, []);
19
+
20
+ return createElement("div", { ref: containerRef });
21
+ }