quoin-editor 0.4.2 → 1.0.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 (231) hide show
  1. package/CHANGELOG.md +205 -0
  2. package/MIGRATION.md +233 -0
  3. package/README.md +78 -716
  4. package/dist/ai.cjs +1107 -0
  5. package/dist/ai.cjs.map +1 -0
  6. package/dist/ai.d.cts +68 -0
  7. package/dist/ai.d.ts +68 -0
  8. package/dist/ai.js +1107 -0
  9. package/dist/ai.js.map +1 -0
  10. package/dist/catalogue.cjs +59 -0
  11. package/dist/catalogue.cjs.map +1 -0
  12. package/dist/catalogue.d.cts +39 -0
  13. package/dist/catalogue.d.ts +39 -0
  14. package/dist/catalogue.js +59 -0
  15. package/dist/catalogue.js.map +1 -0
  16. package/dist/chunk-2QCRZ7OL.cjs +319 -0
  17. package/dist/chunk-2QCRZ7OL.cjs.map +1 -0
  18. package/dist/chunk-2VDK27IF.cjs +6666 -0
  19. package/dist/chunk-2VDK27IF.cjs.map +1 -0
  20. package/dist/chunk-47MLKSI2.js +326 -0
  21. package/dist/chunk-47MLKSI2.js.map +1 -0
  22. package/dist/chunk-5DNCCN6X.cjs +326 -0
  23. package/dist/chunk-5DNCCN6X.cjs.map +1 -0
  24. package/dist/chunk-AEJXZBVR.cjs +1378 -0
  25. package/dist/chunk-AEJXZBVR.cjs.map +1 -0
  26. package/dist/chunk-CDELOHDY.cjs +239 -0
  27. package/dist/chunk-CDELOHDY.cjs.map +1 -0
  28. package/dist/chunk-E75EHRPA.cjs +175 -0
  29. package/dist/chunk-E75EHRPA.cjs.map +1 -0
  30. package/dist/chunk-EL2OEJQC.cjs +562 -0
  31. package/dist/chunk-EL2OEJQC.cjs.map +1 -0
  32. package/dist/chunk-GLLXHMH2.cjs +25 -0
  33. package/dist/chunk-GLLXHMH2.cjs.map +1 -0
  34. package/dist/chunk-GTTPVJSD.cjs +3286 -0
  35. package/dist/chunk-GTTPVJSD.cjs.map +1 -0
  36. package/dist/chunk-HELJTLNC.cjs +100 -0
  37. package/dist/chunk-HELJTLNC.cjs.map +1 -0
  38. package/dist/chunk-HJIQT3S5.js +4047 -0
  39. package/dist/chunk-HJIQT3S5.js.map +1 -0
  40. package/dist/chunk-HZSSEYJS.cjs +504 -0
  41. package/dist/chunk-HZSSEYJS.cjs.map +1 -0
  42. package/dist/chunk-JDPTZUBW.js +184 -0
  43. package/dist/chunk-JDPTZUBW.js.map +1 -0
  44. package/dist/chunk-JEIFJHEI.js +14 -0
  45. package/dist/chunk-JEIFJHEI.js.map +1 -0
  46. package/dist/chunk-K574XHMM.js +25 -0
  47. package/dist/chunk-K574XHMM.js.map +1 -0
  48. package/dist/chunk-KAVU6T6J.js +249 -0
  49. package/dist/chunk-KAVU6T6J.js.map +1 -0
  50. package/dist/chunk-KXOBZGQO.js +1378 -0
  51. package/dist/chunk-KXOBZGQO.js.map +1 -0
  52. package/dist/chunk-LCQSG4M6.js +3335 -0
  53. package/dist/chunk-LCQSG4M6.js.map +1 -0
  54. package/dist/chunk-MQ2XQ4MX.js +100 -0
  55. package/dist/chunk-MQ2XQ4MX.js.map +1 -0
  56. package/dist/chunk-ND5XJVK2.cjs +3335 -0
  57. package/dist/chunk-ND5XJVK2.cjs.map +1 -0
  58. package/dist/chunk-NNDYYEW5.js +504 -0
  59. package/dist/chunk-NNDYYEW5.js.map +1 -0
  60. package/dist/chunk-NOKWNK2O.js +319 -0
  61. package/dist/chunk-NOKWNK2O.js.map +1 -0
  62. package/dist/chunk-OMOBMXD5.cjs +57 -0
  63. package/dist/chunk-OMOBMXD5.cjs.map +1 -0
  64. package/dist/chunk-PMWCQSQX.js +239 -0
  65. package/dist/chunk-PMWCQSQX.js.map +1 -0
  66. package/dist/chunk-POBM7MGR.js +30 -0
  67. package/dist/chunk-POBM7MGR.js.map +1 -0
  68. package/dist/chunk-PWUEPDA3.cjs +598 -0
  69. package/dist/chunk-PWUEPDA3.cjs.map +1 -0
  70. package/dist/chunk-PZ4GT5JO.cjs +4047 -0
  71. package/dist/chunk-PZ4GT5JO.cjs.map +1 -0
  72. package/dist/chunk-QAW5NH6S.cjs +14 -0
  73. package/dist/chunk-QAW5NH6S.cjs.map +1 -0
  74. package/dist/chunk-RDG4VI6E.cjs +706 -0
  75. package/dist/chunk-RDG4VI6E.cjs.map +1 -0
  76. package/dist/chunk-RUDWMKYX.js +562 -0
  77. package/dist/chunk-RUDWMKYX.js.map +1 -0
  78. package/dist/chunk-SG37UBDO.js +706 -0
  79. package/dist/chunk-SG37UBDO.js.map +1 -0
  80. package/dist/chunk-SLVDAHHK.cjs +30 -0
  81. package/dist/chunk-SLVDAHHK.cjs.map +1 -0
  82. package/dist/chunk-T7SW6OWV.js +598 -0
  83. package/dist/chunk-T7SW6OWV.js.map +1 -0
  84. package/dist/chunk-TC4DARU2.js +175 -0
  85. package/dist/chunk-TC4DARU2.js.map +1 -0
  86. package/dist/chunk-WQHV3MRE.cjs +184 -0
  87. package/dist/chunk-WQHV3MRE.cjs.map +1 -0
  88. package/dist/chunk-WXMYDXPN.cjs +33 -0
  89. package/dist/chunk-WXMYDXPN.cjs.map +1 -0
  90. package/dist/chunk-XEBWC7ZL.js +57 -0
  91. package/dist/chunk-XEBWC7ZL.js.map +1 -0
  92. package/dist/chunk-ZC24WB7Y.cjs +249 -0
  93. package/dist/chunk-ZC24WB7Y.cjs.map +1 -0
  94. package/dist/chunk-ZGZEJCRV.js +6666 -0
  95. package/dist/chunk-ZGZEJCRV.js.map +1 -0
  96. package/dist/chunk-ZHOSSWUM.js +33 -0
  97. package/dist/chunk-ZHOSSWUM.js.map +1 -0
  98. package/dist/chunk-ZS2WA357.js +3286 -0
  99. package/dist/chunk-ZS2WA357.js.map +1 -0
  100. package/dist/comments.cjs +993 -0
  101. package/dist/comments.cjs.map +1 -0
  102. package/dist/comments.d.cts +88 -0
  103. package/dist/comments.d.ts +88 -0
  104. package/dist/comments.js +993 -0
  105. package/dist/comments.js.map +1 -0
  106. package/dist/diff.cjs +343 -0
  107. package/dist/diff.cjs.map +1 -0
  108. package/dist/diff.d.cts +67 -0
  109. package/dist/diff.d.ts +67 -0
  110. package/dist/diff.js +343 -0
  111. package/dist/diff.js.map +1 -0
  112. package/dist/docx.cjs +854 -0
  113. package/dist/docx.cjs.map +1 -0
  114. package/dist/docx.d.cts +27 -0
  115. package/dist/docx.d.ts +27 -0
  116. package/dist/docx.js +854 -0
  117. package/dist/docx.js.map +1 -0
  118. package/dist/embed-provider-jXwlRmnp.d.cts +19 -0
  119. package/dist/embed-provider-jXwlRmnp.d.ts +19 -0
  120. package/dist/embed.cjs +17 -0
  121. package/dist/embed.cjs.map +1 -0
  122. package/dist/embed.d.cts +56 -0
  123. package/dist/embed.d.ts +56 -0
  124. package/dist/embed.js +17 -0
  125. package/dist/embed.js.map +1 -0
  126. package/dist/emoji-data.cjs +1968 -0
  127. package/dist/emoji-data.cjs.map +1 -0
  128. package/dist/emoji-data.js +1968 -0
  129. package/dist/emoji-data.js.map +1 -0
  130. package/dist/emoji-extension-C12gDPm6.d.cts +46 -0
  131. package/dist/emoji-extension-DpQ43HjE.d.ts +46 -0
  132. package/dist/emoji.cjs +18 -0
  133. package/dist/emoji.cjs.map +1 -0
  134. package/dist/emoji.d.cts +31 -0
  135. package/dist/emoji.d.ts +31 -0
  136. package/dist/emoji.js +18 -0
  137. package/dist/emoji.js.map +1 -0
  138. package/dist/engine.cjs +119 -0
  139. package/dist/engine.cjs.map +1 -0
  140. package/dist/engine.d.cts +144 -0
  141. package/dist/engine.d.ts +144 -0
  142. package/dist/engine.js +119 -0
  143. package/dist/engine.js.map +1 -0
  144. package/dist/find-extension-DKqLLSuP.d.ts +41 -0
  145. package/dist/find-extension-_sbZEt3R.d.cts +41 -0
  146. package/dist/find.cjs +20 -0
  147. package/dist/find.cjs.map +1 -0
  148. package/dist/find.d.cts +44 -0
  149. package/dist/find.d.ts +44 -0
  150. package/dist/find.js +20 -0
  151. package/dist/find.js.map +1 -0
  152. package/dist/headless.cjs +151 -518
  153. package/dist/headless.cjs.map +1 -1
  154. package/dist/headless.d.cts +102 -69
  155. package/dist/headless.d.ts +102 -69
  156. package/dist/headless.js +149 -467
  157. package/dist/headless.js.map +1 -1
  158. package/dist/index-CgD72kHB.d.cts +65 -0
  159. package/dist/index-DNYbTK3w.d.ts +65 -0
  160. package/dist/index.cjs +183 -2753
  161. package/dist/index.cjs.map +1 -1
  162. package/dist/index.d.cts +345 -253
  163. package/dist/index.d.ts +345 -253
  164. package/dist/index.js +168 -2696
  165. package/dist/index.js.map +1 -1
  166. package/dist/math.cjs +18 -0
  167. package/dist/math.cjs.map +1 -0
  168. package/dist/math.d.cts +49 -0
  169. package/dist/math.d.ts +49 -0
  170. package/dist/math.js +18 -0
  171. package/dist/math.js.map +1 -0
  172. package/dist/mention.cjs +18 -0
  173. package/dist/mention.cjs.map +1 -0
  174. package/dist/mention.d.cts +50 -0
  175. package/dist/mention.d.ts +50 -0
  176. package/dist/mention.js +18 -0
  177. package/dist/mention.js.map +1 -0
  178. package/dist/presence.cjs +565 -0
  179. package/dist/presence.cjs.map +1 -0
  180. package/dist/presence.d.cts +30 -0
  181. package/dist/presence.d.ts +30 -0
  182. package/dist/presence.js +565 -0
  183. package/dist/presence.js.map +1 -0
  184. package/dist/registry-CWBOSAHm.d.cts +121 -0
  185. package/dist/registry-CWBOSAHm.d.ts +121 -0
  186. package/dist/stats-extension-Bly88uio.d.ts +40 -0
  187. package/dist/stats-extension-CJPijvUb.d.cts +40 -0
  188. package/dist/stats.cjs +19 -0
  189. package/dist/stats.cjs.map +1 -0
  190. package/dist/stats.d.cts +19 -0
  191. package/dist/stats.d.ts +19 -0
  192. package/dist/stats.js +19 -0
  193. package/dist/stats.js.map +1 -0
  194. package/dist/styles.css +1 -1
  195. package/dist/table-extension-BTLxFy2S.d.cts +57 -0
  196. package/dist/table-extension-Dzf4k6hI.d.ts +57 -0
  197. package/dist/table.cjs +50 -0
  198. package/dist/table.cjs.map +1 -0
  199. package/dist/table.d.cts +57 -0
  200. package/dist/table.d.ts +57 -0
  201. package/dist/table.js +50 -0
  202. package/dist/table.js.map +1 -0
  203. package/dist/to-html-C5t7xyaM.d.ts +21 -0
  204. package/dist/to-html-CNCITCln.d.cts +21 -0
  205. package/dist/toc-extension-B3-s6h2x.d.cts +25 -0
  206. package/dist/toc-extension-DWm5alVj.d.ts +25 -0
  207. package/dist/toc.cjs +18 -0
  208. package/dist/toc.cjs.map +1 -0
  209. package/dist/toc.d.cts +24 -0
  210. package/dist/toc.d.ts +24 -0
  211. package/dist/toc.js +18 -0
  212. package/dist/toc.js.map +1 -0
  213. package/dist/types-Cz7XJLaK.d.cts +366 -0
  214. package/dist/types-DULc8wwg.d.cts +120 -0
  215. package/dist/types-DULc8wwg.d.ts +120 -0
  216. package/dist/types-a1CRBlEh.d.ts +366 -0
  217. package/dist/view-controller-DSHw0ltQ.d.cts +118 -0
  218. package/dist/view-controller-UbpzblGl.d.ts +118 -0
  219. package/dist/view.cjs +23 -0
  220. package/dist/view.cjs.map +1 -0
  221. package/dist/view.d.cts +40 -0
  222. package/dist/view.d.ts +40 -0
  223. package/dist/view.js +23 -0
  224. package/dist/view.js.map +1 -0
  225. package/dist/yjs.cjs +1026 -0
  226. package/dist/yjs.cjs.map +1 -0
  227. package/dist/yjs.d.cts +22 -0
  228. package/dist/yjs.d.ts +22 -0
  229. package/dist/yjs.js +1026 -0
  230. package/dist/yjs.js.map +1 -0
  231. package/package.json +217 -18
@@ -0,0 +1,3286 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+ var _chunkPZ4GT5JOcjs = require('./chunk-PZ4GT5JO.cjs');
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+ var _chunkND5XJVK2cjs = require('./chunk-ND5XJVK2.cjs');
65
+
66
+ // src/view/react/use-editor-state.ts
67
+ var _react = require('react');
68
+ var wholeState = (state) => state;
69
+ function useEditorState(controller, selector, isEqual = Object.is) {
70
+ const last = _react.useRef.call(void 0, null);
71
+ const subscribe = _react.useCallback.call(void 0,
72
+ (notify) => controller.editor.subscribe(() => notify()),
73
+ [controller]
74
+ );
75
+ const getSnapshot = _react.useCallback.call(void 0, () => {
76
+ const { state } = controller.editor;
77
+ const previous = last.current;
78
+ if (previous !== null && previous.state === state && previous.selector === selector) return previous.value;
79
+ const next = selector(state);
80
+ const value = previous !== null && isEqual(previous.value, next) ? previous.value : next;
81
+ last.current = { state, selector, value };
82
+ return value;
83
+ }, [controller, selector, isEqual]);
84
+ return _react.useSyncExternalStore.call(void 0, subscribe, getSnapshot, getSnapshot);
85
+ }
86
+
87
+ // src/view/selection/selection-decorations.ts
88
+ var SELECTED = { class: "quoin-selected" };
89
+ function isCrossSelection(selection) {
90
+ if (_optionalChain([selection, 'optionalAccess', _2 => _2.kind]) !== "text") return false;
91
+ const { anchor, head } = selection;
92
+ return anchor.block !== head.block || anchor.region !== head.region;
93
+ }
94
+ function selectionDecorations(state, mode) {
95
+ if (mode !== "decorations" || !isCrossSelection(state.selection)) return [];
96
+ return _chunkPZ4GT5JOcjs.markRanges.call(void 0, state).map(({ block, region, from, to }) => ({
97
+ kind: "inline",
98
+ block,
99
+ region,
100
+ from,
101
+ to,
102
+ attrs: SELECTED
103
+ }));
104
+ }
105
+
106
+ // src/view/dom/attributes.ts
107
+ var sortedEntries = (attrs) => _chunkND5XJVK2cjs.dataAttributes.call(void 0, attrs, "");
108
+ function setAttributes(element, attrs, prefix = "") {
109
+ for (const [name, value] of _chunkND5XJVK2cjs.writtenAttrs.call(void 0, _chunkND5XJVK2cjs.dataAttributes.call(void 0, attrs, prefix))) element.setAttribute(name, value);
110
+ }
111
+
112
+ // src/view/dom/segments.ts
113
+ function decorationRanges(decorations, length) {
114
+ const ranges = [];
115
+ for (const decoration of decorations) {
116
+ if (decoration.kind !== "inline") continue;
117
+ const from = Math.max(0, decoration.from);
118
+ const to = Math.min(length, decoration.to);
119
+ if (from < to) ranges.push({ from, to, attrs: decoration.attrs });
120
+ }
121
+ return ranges;
122
+ }
123
+ function toSegments(inline, decorations) {
124
+ const length = _chunkND5XJVK2cjs.inlineLength.call(void 0, inline);
125
+ const ranges = decorationRanges(decorations, length);
126
+ const widgets = decorations.filter((decoration) => decoration.kind === "widget").map((decoration) => ({ decoration, at: Math.min(Math.max(decoration.offset, 0), length) })).sort((a, b) => a.at - b.at || (_nullishCoalesce(a.decoration.side, () => ( 1))) - (_nullishCoalesce(b.decoration.side, () => ( 1))));
127
+ const edges = [...ranges.flatMap((range) => [range.from, range.to]), ...widgets.map((widget) => widget.at)];
128
+ const cuts = [...new Set(edges)].filter((point) => point > 0 && point < length).sort((a, b) => a - b);
129
+ const covering = (from, to) => ranges.filter((range) => range.from <= from && range.to >= to).map((range) => range.attrs);
130
+ const segments = [];
131
+ let nextWidget = 0;
132
+ let nextCut = 0;
133
+ const placeWidgets = (upTo) => {
134
+ for (; nextWidget < widgets.length && widgets[nextWidget].at <= upTo; nextWidget++) {
135
+ segments.push({ kind: "widget", decoration: widgets[nextWidget].decoration, from: widgets[nextWidget].at });
136
+ }
137
+ };
138
+ let start = 0;
139
+ for (const node of inline) {
140
+ const end = start + _chunkND5XJVK2cjs.nodeLength.call(void 0, node);
141
+ if (node.kind === "atom") {
142
+ placeWidgets(start);
143
+ segments.push({ kind: "atom", node, decorations: covering(start, end), from: start });
144
+ } else if (end > start) {
145
+ let from = start;
146
+ while (from < end) {
147
+ while (nextCut < cuts.length && cuts[nextCut] <= from) nextCut++;
148
+ const to = nextCut < cuts.length && cuts[nextCut] < end ? cuts[nextCut] : end;
149
+ placeWidgets(from);
150
+ segments.push({
151
+ kind: "text",
152
+ text: node.text.slice(from - start, to - start),
153
+ marks: node.marks,
154
+ decorations: covering(from, to),
155
+ from
156
+ });
157
+ from = to;
158
+ }
159
+ }
160
+ start = end;
161
+ }
162
+ placeWidgets(length);
163
+ return segments;
164
+ }
165
+ var marksKey = (marks) => marks.map((mark2) => [mark2.type, sortedEntries(mark2.attrs)]);
166
+ var decorationsKey = (decorations) => decorations.map(sortedEntries);
167
+ function segmentSignature(segment) {
168
+ switch (segment.kind) {
169
+ case "text":
170
+ return JSON.stringify(["text", marksKey(segment.marks), decorationsKey(segment.decorations)]);
171
+ case "atom": {
172
+ const { node } = segment;
173
+ const attrs = sortedEntries(node.attrs);
174
+ return JSON.stringify(["atom", node.type, attrs, marksKey(node.marks), decorationsKey(segment.decorations)]);
175
+ }
176
+ case "widget": {
177
+ const { decoration } = segment;
178
+ return JSON.stringify(["widget", decoration.widget, decoration.key, sortedEntries(decoration.attrs)]);
179
+ }
180
+ }
181
+ }
182
+
183
+ // src/view/dom/mark-elements.ts
184
+ function create(doc, tag, attributes) {
185
+ const element = doc.createElement(tag);
186
+ for (const [name, value] of attributes) element.setAttribute(name, value);
187
+ return element;
188
+ }
189
+ function linkElement({ href = "", title } = {}, doc) {
190
+ const safe = _chunkND5XJVK2cjs.sanitizeHref.call(void 0, href);
191
+ if (safe === null) return create(doc, "span", [["data-quoin-link", ""]]);
192
+ const anchor = create(doc, "a", [["href", safe], ["data-quoin-link", ""]]);
193
+ if (title) anchor.title = title;
194
+ return anchor;
195
+ }
196
+ function markElement2(mark2, doc) {
197
+ if (mark2.type === "link") return linkElement(mark2.attrs, doc);
198
+ const { tag, attrs } = _chunkND5XJVK2cjs.markElement.call(void 0, mark2);
199
+ return create(doc, tag, _chunkND5XJVK2cjs.writtenAttrs.call(void 0, attrs));
200
+ }
201
+
202
+ // src/view/dom/wrappers.ts
203
+ var created = /* @__PURE__ */ new WeakSet();
204
+ function decorationElement(attrs, doc) {
205
+ const element = doc.createElement("span");
206
+ element.setAttribute("data-quoin-deco", "");
207
+ setAttributes(element, attrs);
208
+ return element;
209
+ }
210
+ function wrapperDepth(marks, decorations) {
211
+ return marks.length + decorations.length;
212
+ }
213
+ function createWrappers(marks, decorations, doc) {
214
+ const wrappers = [
215
+ ...marks.map((mark2) => markElement2(mark2, doc)),
216
+ ...decorations.map((attrs) => decorationElement(attrs, doc))
217
+ ];
218
+ wrappers.forEach((wrapper, index) => {
219
+ created.add(wrapper);
220
+ if (index > 0) wrappers[index - 1].appendChild(wrapper);
221
+ });
222
+ return wrappers;
223
+ }
224
+ function unwrap(node, depth) {
225
+ const wrappers = [];
226
+ let content = node;
227
+ for (let level = 0; level < depth; level++) {
228
+ if (!content || !created.has(content)) return null;
229
+ if (level < depth - 1 && content.childNodes.length !== 1) return null;
230
+ wrappers.push(content);
231
+ content = content.firstChild;
232
+ }
233
+ return { wrappers, content };
234
+ }
235
+ function wrapperEntries(wrappers, from, to) {
236
+ return wrappers.map((node) => ({ node, from, to, kind: "wrapper" }));
237
+ }
238
+
239
+ // src/view/dom/atom-specs.ts
240
+ function defaultAtomText(node) {
241
+ if (node.type === "mention") return `@${_nullishCoalesce(node.attrs.label, () => ( ""))}`;
242
+ return node.type === "math" ? _nullishCoalesce(node.attrs.latex, () => ( "")) : node.type;
243
+ }
244
+ function mark(element, attribute, name) {
245
+ element.setAttribute("contenteditable", "false");
246
+ element.setAttribute(attribute, name);
247
+ return element;
248
+ }
249
+ function defaultAtom(node, doc, atomText) {
250
+ const element = mark(doc.createElement("span"), "data-quoin-atom", node.type);
251
+ setAttributes(element, node.attrs, "data-");
252
+ element.textContent = atomText(node);
253
+ return element;
254
+ }
255
+ var defaultWidget = (_decoration, doc) => doc.createElement("span");
256
+ function own(record, key) {
257
+ return record && Object.hasOwn(record, key) ? record[key] : void 0;
258
+ }
259
+ function atomSpec(segment, options, doc) {
260
+ const { node, decorations, from } = segment;
261
+ const depth = wrapperDepth(node.marks, decorations);
262
+ const soleContent = (wrappers) => wrappers.length === 0 || wrappers[wrappers.length - 1].childNodes.length === 1;
263
+ return {
264
+ sig: segmentSignature(segment),
265
+ create() {
266
+ const render = own(options.atoms, node.type);
267
+ const content = render ? mark(render(node, doc), "data-quoin-atom", node.type) : defaultAtom(node, doc, _nullishCoalesce(options.atomText, () => ( defaultAtomText)));
268
+ const wrappers = createWrappers(node.marks, decorations, doc);
269
+ _optionalChain([wrappers, 'access', _3 => _3[wrappers.length - 1], 'optionalAccess', _4 => _4.appendChild, 'call', _5 => _5(content)]);
270
+ return _nullishCoalesce(wrappers[0], () => ( content));
271
+ },
272
+ refresh(top) {
273
+ const chain = unwrap(top, depth);
274
+ return chain !== null && soleContent(chain.wrappers);
275
+ },
276
+ describe(top, out) {
277
+ const { wrappers, content } = unwrap(top, depth);
278
+ out.push(...wrapperEntries(wrappers, from, from + 1), { node: content, from, to: from + 1, kind: "atom" });
279
+ }
280
+ };
281
+ }
282
+ function widgetSpec(segment, options, doc) {
283
+ const { decoration, from } = segment;
284
+ return {
285
+ sig: segmentSignature(segment),
286
+ create() {
287
+ const render = _nullishCoalesce(own(options.widgets, decoration.widget), () => ( defaultWidget));
288
+ return mark(render(decoration, doc), "data-quoin-widget", decoration.widget);
289
+ },
290
+ describe: (node, out) => void out.push({ node, from, to: from, kind: "widget" })
291
+ };
292
+ }
293
+
294
+ // src/view/dom/dom-map.ts
295
+ var TEXT_NODE = 3;
296
+ var textLength = (node) => (_nullishCoalesce(node.textContent, () => ( ""))).length;
297
+ var indexIn = (node) => Array.prototype.indexOf.call(node.parentNode.childNodes, node);
298
+ var pointAt = (entry, shift) => ({
299
+ node: entry.node.parentNode,
300
+ offset: indexIn(entry.node) + shift
301
+ });
302
+ function textBefore(root, node, offset) {
303
+ let length = 0;
304
+ if (node.nodeType === TEXT_NODE) {
305
+ length = Math.min(offset, textLength(node));
306
+ } else {
307
+ let child = node.firstChild;
308
+ for (let index = 0; child && index < offset; index++, child = child.nextSibling) {
309
+ length += textLength(child);
310
+ }
311
+ }
312
+ for (let current = node; current !== root; current = current.parentNode) {
313
+ for (let sibling = current.previousSibling; sibling; sibling = sibling.previousSibling) {
314
+ length += textLength(sibling);
315
+ }
316
+ }
317
+ return length;
318
+ }
319
+ function createDomMap(host, length, entries) {
320
+ const byNode = new Map(entries.map((entry) => [entry.node, entry]));
321
+ const texts = entries.filter((entry) => entry.kind === "text");
322
+ const breaksAndAtoms = entries.filter((entry) => entry.kind === "break" || entry.kind === "atom");
323
+ const stops = entries.filter((entry) => entry.kind === "break" || entry.kind === "atom" || entry.kind === "filler");
324
+ function toDom(offset, assoc = 1) {
325
+ const at = Math.min(Math.max(offset, 0), length);
326
+ const after = texts.find((entry) => entry.from <= at && at < entry.to);
327
+ const before = texts.find((entry) => entry.from < at && at <= entry.to);
328
+ const text = assoc === 1 ? _nullishCoalesce(after, () => ( before)) : _nullishCoalesce(before, () => ( after));
329
+ if (text) return { node: text.node, offset: at - text.from };
330
+ const next = stops.find((entry) => entry.from >= at);
331
+ const previous = breaksAndAtoms.filter((entry) => entry.to <= at).pop();
332
+ const beforeNext = next && pointAt(next, 0);
333
+ const afterPrevious = previous && pointAt(previous, 1);
334
+ return _nullishCoalesce((assoc === 1 ? _nullishCoalesce(beforeNext, () => ( afterPrevious)) : _nullishCoalesce(afterPrevious, () => ( beforeNext))), () => ( { node: host, offset: 0 }));
335
+ }
336
+ function atChild(parent, offset, end) {
337
+ const children = parent.childNodes;
338
+ for (let index = offset; index < children.length; index++) {
339
+ const entry = byNode.get(children[index]);
340
+ if (entry) return entry.from;
341
+ }
342
+ return end;
343
+ }
344
+ function enclosingAtom(node) {
345
+ for (let current = node; current && current !== host; current = current.parentNode) {
346
+ const entry = byNode.get(current);
347
+ if (_optionalChain([entry, 'optionalAccess', _6 => _6.kind]) === "atom" || _optionalChain([entry, 'optionalAccess', _7 => _7.kind]) === "widget") return entry;
348
+ }
349
+ return void 0;
350
+ }
351
+ function fromDom(node, offset) {
352
+ if (!host.contains(node)) return null;
353
+ if (node === host) return atChild(host, offset, length);
354
+ const atom = enclosingAtom(node);
355
+ if (_optionalChain([atom, 'optionalAccess', _8 => _8.kind]) === "widget") return atom.from;
356
+ if (atom) {
357
+ const inside = textBefore(atom.node, node, offset);
358
+ return inside > 0 && inside * 2 >= textLength(atom.node) ? atom.to : atom.from;
359
+ }
360
+ const entry = byNode.get(node);
361
+ if (!entry) return fromDom(node.parentNode, indexIn(node));
362
+ if (node.nodeType === TEXT_NODE) return entry.from + Math.min(offset, entry.to - entry.from);
363
+ if (entry.kind === "break") return offset > 0 ? entry.to : entry.from;
364
+ return atChild(node, offset, entry.to);
365
+ }
366
+ return { length, toDom, fromDom };
367
+ }
368
+
369
+ // src/view/dom/reconcile.ts
370
+ var signatures = /* @__PURE__ */ new WeakMap();
371
+ function keeps(node, spec) {
372
+ return signatures.get(node) === spec.sig && (_nullishCoalesce(_optionalChain([spec, 'access', _9 => _9.refresh, 'optionalCall', _10 => _10(node)]), () => ( true)));
373
+ }
374
+ function reconcileChildren(parent, specs) {
375
+ const old = Array.from(parent.childNodes);
376
+ let head = 0;
377
+ while (head < specs.length && head < old.length && keeps(old[head], specs[head])) head++;
378
+ let oldEnd = old.length;
379
+ let newEnd = specs.length;
380
+ while (newEnd > head && oldEnd > head && keeps(old[oldEnd - 1], specs[newEnd - 1])) {
381
+ oldEnd--;
382
+ newEnd--;
383
+ }
384
+ const anchor = _nullishCoalesce(old[oldEnd], () => ( null));
385
+ for (let i = head; i < oldEnd; i++) parent.removeChild(old[i]);
386
+ for (let i = head; i < newEnd; i++) {
387
+ const node = specs[i].create();
388
+ signatures.set(node, specs[i].sig);
389
+ parent.insertBefore(node, anchor);
390
+ }
391
+ }
392
+
393
+ // src/view/dom/text-diff.ts
394
+ function textDiff(before, after, caretHint) {
395
+ if (before === after) return null;
396
+ const prefix = _chunkPZ4GT5JOcjs.commonPrefix.call(void 0, before, after);
397
+ const suffix = _chunkPZ4GT5JOcjs.commonSuffix.call(void 0, before, after);
398
+ const shared = Math.min(suffix, Math.min(before.length, after.length) - prefix);
399
+ const removed = before.length - prefix - shared;
400
+ const inserted = after.length - prefix - shared;
401
+ const earliest = prefix - (suffix - shared);
402
+ const from = caretHint === void 0 ? prefix : Math.min(Math.max(caretHint - inserted, earliest), prefix);
403
+ return { from, to: from + removed, insert: after.slice(from, from + inserted) };
404
+ }
405
+
406
+ // src/view/dom/text-specs.ts
407
+ var TEXT = "text";
408
+ var BREAK = "break";
409
+ function textPiece(doc, value, from) {
410
+ return {
411
+ sig: TEXT,
412
+ create: () => doc.createTextNode(value),
413
+ // Assigning data collapses a range inside the node to its start; an edit moves only the points after it.
414
+ refresh(node) {
415
+ const target = node;
416
+ const edit = textDiff(target.data, value);
417
+ if (edit !== null) target.replaceData(edit.from, edit.to - edit.from, edit.insert);
418
+ return true;
419
+ },
420
+ describe: (node, out) => void out.push({ node, from, to: from + value.length, kind: "text" })
421
+ };
422
+ }
423
+ function breakPiece(doc, from) {
424
+ return {
425
+ sig: BREAK,
426
+ create() {
427
+ const element = doc.createElement("br");
428
+ element.setAttribute("data-quoin-break", "");
429
+ return element;
430
+ },
431
+ describe: (node, out) => void out.push({ node, from, to: from + 1, kind: "break" })
432
+ };
433
+ }
434
+ function fillerSpec(doc, at) {
435
+ return {
436
+ sig: "filler",
437
+ create() {
438
+ const element = doc.createElement("br");
439
+ element.setAttribute("data-quoin-filler", "");
440
+ return element;
441
+ },
442
+ describe: (node, out) => void out.push({ node, from: at, to: at, kind: "filler" })
443
+ };
444
+ }
445
+ function textPieces(value, from, doc) {
446
+ const pieces = [];
447
+ let at = from;
448
+ value.split("\n").forEach((part, index) => {
449
+ if (index > 0) pieces.push(breakPiece(doc, at++));
450
+ if (part) {
451
+ pieces.push(textPiece(doc, part, at));
452
+ at += part.length;
453
+ }
454
+ });
455
+ return pieces;
456
+ }
457
+ function wrappedText(segment, doc) {
458
+ const { marks, decorations, text, from } = segment;
459
+ const depth = wrapperDepth(marks, decorations);
460
+ const pieces = textPieces(text, from, doc);
461
+ return {
462
+ sig: segmentSignature(segment),
463
+ create() {
464
+ const wrappers = createWrappers(marks, decorations, doc);
465
+ reconcileChildren(wrappers[depth - 1], pieces);
466
+ return wrappers[0];
467
+ },
468
+ refresh(node) {
469
+ const chain = unwrap(node, depth);
470
+ if (!chain) return false;
471
+ reconcileChildren(chain.wrappers[depth - 1], pieces);
472
+ return true;
473
+ },
474
+ describe(node, out) {
475
+ const { wrappers } = unwrap(node, depth);
476
+ out.push(...wrapperEntries(wrappers, from, from + text.length));
477
+ const children = Array.from(wrappers[depth - 1].childNodes);
478
+ pieces.forEach((piece, index) => piece.describe(children[index], out));
479
+ }
480
+ };
481
+ }
482
+ function textSpecs(segment, doc) {
483
+ return wrapperDepth(segment.marks, segment.decorations) === 0 ? textPieces(segment.text, segment.from, doc) : [wrappedText(segment, doc)];
484
+ }
485
+
486
+ // src/view/dom/render-inline.ts
487
+ var maps = /* @__PURE__ */ new WeakMap();
488
+ function domMapOf(host) {
489
+ return maps.get(host);
490
+ }
491
+ function specsFor(segment, options, doc) {
492
+ switch (segment.kind) {
493
+ case "text":
494
+ return textSpecs(segment, doc);
495
+ case "atom":
496
+ return [atomSpec(segment, options, doc)];
497
+ case "widget":
498
+ return [widgetSpec(segment, options, doc)];
499
+ }
500
+ }
501
+ function needsFiller(inline) {
502
+ const content = _chunkND5XJVK2cjs.inlineText.call(void 0, inline);
503
+ return content === "" || content.endsWith("\n");
504
+ }
505
+ function renderInline(host, inline, decorations, options = {}) {
506
+ const doc = host.ownerDocument;
507
+ const length = _chunkND5XJVK2cjs.inlineLength.call(void 0, inline);
508
+ const specs = toSegments(inline, decorations).flatMap((segment) => specsFor(segment, options, doc));
509
+ if (needsFiller(inline)) specs.push(fillerSpec(doc, length));
510
+ reconcileChildren(host, specs);
511
+ const entries = [];
512
+ const nodes = Array.from(host.childNodes);
513
+ specs.forEach((spec, index) => spec.describe(nodes[index], entries));
514
+ const map = createDomMap(host, length, entries);
515
+ maps.set(host, map);
516
+ return map;
517
+ }
518
+
519
+ // src/view/navigation/caret-from-point.ts
520
+ function caretFromPoint(doc, x, y) {
521
+ if (typeof doc.caretPositionFromPoint === "function") {
522
+ const position = doc.caretPositionFromPoint(x, y);
523
+ return position ? { node: position.offsetNode, offset: position.offset } : null;
524
+ }
525
+ if (typeof doc.caretRangeFromPoint === "function") {
526
+ const range = doc.caretRangeFromPoint(x, y);
527
+ return range ? { node: range.startContainer, offset: range.startOffset } : null;
528
+ }
529
+ return null;
530
+ }
531
+
532
+ // src/view/controller/host-registry.ts
533
+ function hostKey(block, region) {
534
+ return `${block}\0${region}`;
535
+ }
536
+ function createHostRegistry(onRegister) {
537
+ const byKey = /* @__PURE__ */ new Map();
538
+ const byElement = /* @__PURE__ */ new Map();
539
+ const releases = /* @__PURE__ */ new Map();
540
+ function forget(entry) {
541
+ const key = hostKey(entry.block, entry.region);
542
+ if (byKey.get(key) === entry) byKey.delete(key);
543
+ if (byElement.get(entry.element) === entry) byElement.delete(entry.element);
544
+ _optionalChain([releases, 'access', _11 => _11.get, 'call', _12 => _12(entry), 'optionalCall', _13 => _13()]);
545
+ releases.delete(entry);
546
+ }
547
+ return {
548
+ register(block, region, element) {
549
+ const key = hostKey(block, region);
550
+ const entry = { block, region, element };
551
+ for (const previous of [byKey.get(key), byElement.get(element)]) if (previous) forget(previous);
552
+ byKey.set(key, entry);
553
+ byElement.set(element, entry);
554
+ if (onRegister) releases.set(entry, onRegister(element));
555
+ return () => forget(entry);
556
+ },
557
+ get: (block, region) => _optionalChain([byKey, 'access', _14 => _14.get, 'call', _15 => _15(hostKey(block, region)), 'optionalAccess', _16 => _16.element]),
558
+ locate(node) {
559
+ for (let current = node; current; current = current.parentNode) {
560
+ const entry = byElement.get(current);
561
+ if (entry) return entry;
562
+ }
563
+ return null;
564
+ },
565
+ all: () => byKey.values()
566
+ };
567
+ }
568
+
569
+ // src/view/controller/dom-selection.ts
570
+ function domToPos(registry, node, offset) {
571
+ const host = registry.locate(node);
572
+ const mapped = host && _optionalChain([domMapOf, 'call', _17 => _17(host.element), 'optionalAccess', _18 => _18.fromDom, 'call', _19 => _19(node, offset)]);
573
+ return host && typeof mapped === "number" ? { block: host.block, region: host.region, offset: mapped } : null;
574
+ }
575
+ function posToDom(registry, pos, assoc) {
576
+ const host = registry.get(pos.block, pos.region);
577
+ return _nullishCoalesce((host && _optionalChain([domMapOf, 'call', _20 => _20(host), 'optionalAccess', _21 => _21.toDom, 'call', _22 => _22(pos.offset, assoc)])), () => ( null));
578
+ }
579
+ function readSelection(registry, domSelection, blockAt2 = () => void 0) {
580
+ const anchorNode = _optionalChain([domSelection, 'optionalAccess', _23 => _23.anchorNode]);
581
+ const focusNode = _optionalChain([domSelection, 'optionalAccess', _24 => _24.focusNode]);
582
+ if (!domSelection || !anchorNode || !focusNode) return null;
583
+ const anchor = domToPos(registry, anchorNode, domSelection.anchorOffset);
584
+ const head = domToPos(registry, focusNode, domSelection.focusOffset);
585
+ if (anchor && head) return _chunkPZ4GT5JOcjs.textSelection.call(void 0, anchor, head);
586
+ if (anchor || head || registry.locate(anchorNode) || registry.locate(focusNode)) return null;
587
+ const block = blockAt2(anchorNode);
588
+ return block !== void 0 && block === blockAt2(focusNode) ? _chunkPZ4GT5JOcjs.nodeSelection.call(void 0, block) : null;
589
+ }
590
+ function selectBlock(domSelection, element) {
591
+ if (!element) return false;
592
+ if (!element.contains(element.ownerDocument.activeElement)) element.focus();
593
+ const { anchorNode, focusNode } = domSelection;
594
+ if (!anchorNode || !focusNode || !element.contains(anchorNode) || !element.contains(focusNode)) {
595
+ domSelection.collapse(element, 0);
596
+ }
597
+ return true;
598
+ }
599
+ function writeSelection(registry, selection, domSelection, blockElement) {
600
+ if (!selection || !domSelection) return false;
601
+ if (selection.kind === "node") return selectBlock(domSelection, blockElement(selection.block));
602
+ if (selection.kind !== "text") return false;
603
+ const { anchor, head } = selection;
604
+ if (anchor.block !== head.block || anchor.region !== head.region) return false;
605
+ if (_chunkPZ4GT5JOcjs.sameSelection.call(void 0, readSelection(registry, domSelection), selection)) return true;
606
+ const base = posToDom(registry, anchor, anchor.offset < head.offset ? 1 : -1);
607
+ const extent = posToDom(registry, head, head.offset < anchor.offset ? 1 : -1);
608
+ if (!base || !extent) return false;
609
+ domSelection.setBaseAndExtent(base.node, base.offset, extent.node, extent.offset);
610
+ return true;
611
+ }
612
+
613
+ // src/view/navigation/node-entry.ts
614
+ var NODE_ENTRY = "data-quoin-node-entry";
615
+ var entersAsNode = (controller, block) => _optionalChain([controller, 'access', _25 => _25.blockElement, 'call', _26 => _26(block), 'optionalAccess', _27 => _27.querySelector, 'call', _28 => _28(`[${NODE_ENTRY}]`)]) != null;
616
+
617
+ // src/view/controller/reader-toggles.ts
618
+ var NO_CHOICES = /* @__PURE__ */ new Map();
619
+ var stores = /* @__PURE__ */ new WeakMap();
620
+ var shownOpen = (block, choices) => _nullishCoalesce(choices.get(block.id), () => ( block.attrs.open === true));
621
+ function createReaderToggles(controller) {
622
+ let choices = NO_CHOICES;
623
+ const listeners = /* @__PURE__ */ new Set();
624
+ const set = (next) => {
625
+ choices = next;
626
+ for (const listener of [...listeners]) listener();
627
+ };
628
+ controller.subscribeReadOnly(() => {
629
+ if (!controller.readOnly && choices.size > 0) set(NO_CHOICES);
630
+ });
631
+ return {
632
+ get choices() {
633
+ return choices;
634
+ },
635
+ flip(block) {
636
+ set(new Map(choices).set(block.id, !shownOpen(block, choices)));
637
+ },
638
+ subscribe(listener) {
639
+ listeners.add(listener);
640
+ return () => void listeners.delete(listener);
641
+ }
642
+ };
643
+ }
644
+ function readerToggles(controller) {
645
+ let store = stores.get(controller);
646
+ if (store === void 0) {
647
+ store = createReaderToggles(controller);
648
+ stores.set(controller, store);
649
+ }
650
+ return store;
651
+ }
652
+
653
+ // src/view/navigation/visible-blocks.ts
654
+ var known = /* @__PURE__ */ new WeakMap();
655
+ function hidersOf(doc) {
656
+ let hiders = known.get(doc);
657
+ if (hiders === void 0) {
658
+ const first = doc.blocks.findIndex(_chunkPZ4GT5JOcjs.isClosedToggle);
659
+ hiders = doc.blocks.map((_, index) => first < 0 || index <= first ? null : _chunkPZ4GT5JOcjs.hidingToggle.call(void 0, doc, index + 1));
660
+ known.set(doc, hiders);
661
+ }
662
+ return hiders;
663
+ }
664
+ var hidingToggleAt = (doc, index) => _nullishCoalesce(hidersOf(doc)[index], () => ( null));
665
+ function isHiddenAt(doc, index, choices) {
666
+ if (choices.size === 0) return hidingToggleAt(doc, index) !== null;
667
+ const closed = (block) => block.type === "TOGGLE" && !shownOpen(block, choices);
668
+ return _chunkPZ4GT5JOcjs.hidingToggle.call(void 0, doc, index + 1, closed) !== null;
669
+ }
670
+ var visibleBlockIndexes = (doc) => hidersOf(doc).flatMap((hider, index) => hider === null ? [index] : []);
671
+ function drawnBlockCount(doc, start = 0, end = doc.blocks.length) {
672
+ const hiders = hidersOf(doc);
673
+ let drawn = 0;
674
+ for (let index = Math.max(0, start); index < Math.min(end, hiders.length); index++) if (hiders[index] === null) drawn++;
675
+ return drawn;
676
+ }
677
+
678
+ // src/view/controller/dispatch-selection.ts
679
+ var dispatchSelection = (controller, selection) => controller.dispatch(controller.editor.createTransaction().setSelection(selection));
680
+
681
+ // src/view/navigation/neighbour-block.ts
682
+ var anyBlock = () => true;
683
+ function neighbourBlock(doc, from, direction, accept = anyBlock) {
684
+ const index = doc.index.get(from);
685
+ if (index === void 0) return null;
686
+ const visible = visibleBlockIndexes(doc);
687
+ const ahead = direction < 0 ? visible.filter((at) => at < index).reverse() : visible.filter((at) => at > index);
688
+ const found = ahead.find((at) => accept(doc.blocks[at]));
689
+ return found === void 0 ? null : doc.blocks[found];
690
+ }
691
+ function entryEdge(controller, block, direction) {
692
+ const { doc, schema } = controller.editor.state;
693
+ const edge = direction < 0 ? _chunkPZ4GT5JOcjs.endOf.call(void 0, doc, schema, block) : _chunkPZ4GT5JOcjs.startOf.call(void 0, doc, schema, block);
694
+ if (edge.kind !== "text") return edge;
695
+ const drawn = controller.registry.get(edge.head.block, edge.head.region) !== void 0;
696
+ return drawn && !entersAsNode(controller, block) ? edge : _chunkPZ4GT5JOcjs.nodeSelection.call(void 0, block);
697
+ }
698
+ var ownNode = (controller, block, direction) => direction < 0 && entersAsNode(controller, block) ? _chunkPZ4GT5JOcjs.nodeSelection.call(void 0, block) : null;
699
+ var holdsTextHead = (controller) => (block) => _chunkPZ4GT5JOcjs.isTextFlow.call(void 0, controller.editor.state.schema, block) && entryEdge(controller, block.id, 1).kind === "text";
700
+ function neighbourHead(controller, from, direction) {
701
+ const target = neighbourBlock(controller.editor.state.doc, from, direction, holdsTextHead(controller));
702
+ const edge = target && entryEdge(controller, target.id, direction);
703
+ return _optionalChain([edge, 'optionalAccess', _29 => _29.kind]) === "text" ? edge.head : null;
704
+ }
705
+ function moveToNeighbour(controller, from, direction) {
706
+ const target = neighbourBlock(controller.editor.state.doc, from, direction);
707
+ if (target === null) return false;
708
+ dispatchSelection(controller, entryEdge(controller, target.id, direction));
709
+ return true;
710
+ }
711
+
712
+ // src/view/selection/scroll-container.ts
713
+ var SCROLLS = /* @__PURE__ */ new Set(["auto", "scroll"]);
714
+ var scrollsVertically = (element) => SCROLLS.has(getComputedStyle(element).overflowY) && element.scrollHeight > element.clientHeight;
715
+ function scrollContainerOf(element) {
716
+ const { body, documentElement } = element.ownerDocument;
717
+ for (let node = element; node !== null; node = node.parentElement) {
718
+ if (node === body || node === documentElement) return null;
719
+ if (scrollsVertically(node)) return node;
720
+ }
721
+ return null;
722
+ }
723
+
724
+ // src/view/selection/auto-scroll.ts
725
+ var EDGE = 40;
726
+ var MAX_STEP = 20;
727
+ function edgesOf(container, win) {
728
+ if (container === null) return { top: 0, bottom: win.innerHeight };
729
+ const top = container.getBoundingClientRect().top + container.clientTop;
730
+ return { top: Math.max(top, 0), bottom: Math.min(top + container.clientHeight, win.innerHeight) };
731
+ }
732
+ function createAutoScroll(element, onScroll, edge = EDGE) {
733
+ const win = element.ownerDocument.defaultView;
734
+ let container;
735
+ let delta = 0;
736
+ let frame = null;
737
+ const step = (depth) => Math.ceil(Math.min(depth, edge) / edge * MAX_STEP);
738
+ function tick() {
739
+ frame = null;
740
+ if (delta === 0) return;
741
+ (_nullishCoalesce(container, () => ( win))).scrollBy(0, delta);
742
+ onScroll();
743
+ frame = win.requestAnimationFrame(tick);
744
+ }
745
+ return {
746
+ update(clientY) {
747
+ if (container === void 0) container = scrollContainerOf(element);
748
+ const { top, bottom } = edgesOf(container, win);
749
+ const [upper, lower] = [top + edge, bottom - edge];
750
+ delta = clientY < upper ? -step(upper - clientY) : clientY > lower ? step(clientY - lower) : 0;
751
+ if (delta !== 0 && frame === null) frame = win.requestAnimationFrame(tick);
752
+ },
753
+ stop() {
754
+ delta = 0;
755
+ container = void 0;
756
+ if (frame !== null) win.cancelAnimationFrame(frame);
757
+ frame = null;
758
+ }
759
+ };
760
+ }
761
+
762
+ // src/view/navigation/caret-in-host.ts
763
+ function caretInHost(registry, element, box, at, x, y) {
764
+ const point = caretFromPoint(element.ownerDocument, Math.min(Math.max(x, box.left), box.right - 1), y);
765
+ const pos = point && domToPos(registry, point.node, point.offset);
766
+ return pos && pos.block === at.block && pos.region === at.region ? pos : null;
767
+ }
768
+
769
+ // src/view/selection/pointer-targets.ts
770
+ function distance(rect, x, y) {
771
+ const dy = y < rect.top ? rect.top - y : Math.max(0, y - rect.bottom);
772
+ const dx = x < rect.left ? rect.left - x : Math.max(0, x - rect.right);
773
+ return [dy, dx];
774
+ }
775
+ function nearest(items, element, x, y) {
776
+ let best = null;
777
+ let bestY = Infinity;
778
+ let bestX = Infinity;
779
+ for (const item of items) {
780
+ const [dy, dx] = distance(element(item).getBoundingClientRect(), x, y);
781
+ if (dy < bestY || dy === bestY && dx < bestX) [best, bestY, bestX] = [item, dy, dx];
782
+ }
783
+ return best;
784
+ }
785
+ var elementAt = (controller, x, y) => _nullishCoalesce(_optionalChain([controller, 'access', _30 => _30.root, 'optionalAccess', _31 => _31.ownerDocument, 'access', _32 => _32.elementFromPoint, 'call', _33 => _33(x, y)]), () => ( null));
786
+ function hostAt(controller, x, y) {
787
+ const { registry } = controller;
788
+ return _nullishCoalesce(registry.locate(elementAt(controller, x, y)), () => ( nearest(registry.all(), (entry) => entry.element, x, y)));
789
+ }
790
+ function blockAt(controller, x, y) {
791
+ const element = elementAt(controller, x, y);
792
+ const under = element && controller.blockAt(element);
793
+ if (under !== void 0 && under !== null) return under;
794
+ const { doc } = controller.editor.state;
795
+ const drawn = visibleBlockIndexes(doc).flatMap((index) => {
796
+ const shell = controller.blockElement(doc.blocks[index].id);
797
+ return shell ? [{ id: doc.blocks[index].id, shell }] : [];
798
+ });
799
+ return _nullishCoalesce(_optionalChain([nearest, 'call', _34 => _34(drawn, (entry) => entry.shell, x, y), 'optionalAccess', _35 => _35.id]), () => ( null));
800
+ }
801
+ function caretPosAt(controller, host, x, y) {
802
+ const { element, block, region } = host;
803
+ const at = (offset) => ({ block, region, offset });
804
+ const end = _nullishCoalesce(_optionalChain([domMapOf, 'call', _36 => _36(element), 'optionalAccess', _37 => _37.length]), () => ( 0));
805
+ const box = element.getBoundingClientRect();
806
+ if (y < box.top) return at(0);
807
+ if (y >= box.bottom) return at(end);
808
+ return _nullishCoalesce(caretInHost(controller.registry, element, box, host, x, y), () => ( at(x < box.left ? 0 : end)));
809
+ }
810
+
811
+ // src/view/selection/native-interaction.ts
812
+ var NATIVE_INTERACTION = "data-quoin-native";
813
+
814
+ // src/view/selection/cross-block-pointer.ts
815
+ var DRAGGING = "data-quoin-dragging";
816
+ var DRAG_DISTANCE = 4;
817
+ var CONTROLS = `button, a, input, select, textarea, [${NATIVE_INTERACTION}]`;
818
+ var sameHost = (pos, host) => pos.block === host.block && pos.region === host.region;
819
+ function anchorAtCrossing(controller) {
820
+ const native = controller.domSelection();
821
+ const anchor = _optionalChain([native, 'optionalAccess', _38 => _38.anchorNode]) && domToPos(controller.registry, native.anchorNode, native.anchorOffset);
822
+ if (anchor) return anchor;
823
+ const { selection } = controller.editor.state;
824
+ return _optionalChain([selection, 'optionalAccess', _39 => _39.kind]) === "text" ? selection.anchor : null;
825
+ }
826
+ function createCrossBlockPointer(controller) {
827
+ return {
828
+ attach(root) {
829
+ const doc = root.ownerDocument;
830
+ const view = doc.defaultView;
831
+ let press = null;
832
+ let last = { x: 0, y: 0 };
833
+ let preventMouseDown = false;
834
+ const scroll = createAutoScroll(root, () => follow(last.x, last.y));
835
+ function select(selection) {
836
+ if (_chunkPZ4GT5JOcjs.sameSelection.call(void 0, selection, controller.editor.state.selection)) return;
837
+ dispatchSelection(controller, selection);
838
+ }
839
+ function startDragging() {
840
+ controller.dragging = true;
841
+ root.setAttribute(DRAGGING, "");
842
+ }
843
+ function followText(current, x, y) {
844
+ const target = hostAt(controller, x, y);
845
+ if (target === null) return;
846
+ if (current.anchor === null) {
847
+ if (target.element === current.host.element) return;
848
+ current.anchor = anchorAtCrossing(controller);
849
+ if (current.anchor === null) return;
850
+ startDragging();
851
+ }
852
+ select(_chunkPZ4GT5JOcjs.textSelection.call(void 0, current.anchor, caretPosAt(controller, target, x, y)));
853
+ }
854
+ function followBlocks(current, x, y) {
855
+ if (!controller.dragging && Math.hypot(x - current.x, y - current.y) < DRAG_DISTANCE) return;
856
+ const block = blockAt(controller, x, y);
857
+ if (block === null) return;
858
+ startDragging();
859
+ select(_chunkPZ4GT5JOcjs.blockRangeSelection.call(void 0, current.start, block));
860
+ }
861
+ function follow(x, y) {
862
+ if (_optionalChain([press, 'optionalAccess', _40 => _40.kind]) === "text") followText(press, x, y);
863
+ else if (_optionalChain([press, 'optionalAccess', _41 => _41.kind]) === "blocks") followBlocks(press, x, y);
864
+ }
865
+ function release() {
866
+ doc.removeEventListener("pointermove", onPointerMove);
867
+ doc.removeEventListener("pointerup", end);
868
+ doc.removeEventListener("pointercancel", end);
869
+ view.removeEventListener("blur", end);
870
+ scroll.stop();
871
+ press = null;
872
+ const dragged = controller.dragging;
873
+ controller.dragging = false;
874
+ root.removeAttribute(DRAGGING);
875
+ return dragged;
876
+ }
877
+ function end() {
878
+ if (release()) controller.focus();
879
+ }
880
+ function onPointerMove(event) {
881
+ if ((event.buttons & 1) === 0) return end();
882
+ last = { x: event.clientX, y: event.clientY };
883
+ follow(last.x, last.y);
884
+ if (controller.dragging) scroll.update(last.y);
885
+ }
886
+ function extendFrom(host, x, y) {
887
+ const { selection } = controller.editor.state;
888
+ if (_optionalChain([selection, 'optionalAccess', _42 => _42.kind]) !== "text") return;
889
+ if (sameHost(selection.anchor, host) && !isCrossSelection(selection)) return;
890
+ press = { kind: "text", host, anchor: selection.anchor };
891
+ preventMouseDown = true;
892
+ startDragging();
893
+ follow(x, y);
894
+ }
895
+ function onPointerDown(event) {
896
+ end();
897
+ preventMouseDown = false;
898
+ if (event.button !== 0 || event.pointerType === "touch") return;
899
+ const target = event.target;
900
+ const host = controller.registry.locate(target);
901
+ const block = host === null ? controller.blockAt(target) : void 0;
902
+ const control = target.closest(CONTROLS);
903
+ if (host !== null) {
904
+ press = { kind: "text", host, anchor: null };
905
+ if (event.shiftKey) extendFrom(host, event.clientX, event.clientY);
906
+ } else if (block !== void 0 && !(control && root.contains(control))) {
907
+ press = { kind: "blocks", start: block, x: event.clientX, y: event.clientY };
908
+ if (!event.shiftKey && entryEdge(controller, block, 1).kind === "node") {
909
+ controller.nodeReveal.pressed(block);
910
+ select(_chunkPZ4GT5JOcjs.nodeSelection.call(void 0, block));
911
+ }
912
+ } else {
913
+ return;
914
+ }
915
+ doc.addEventListener("pointermove", onPointerMove);
916
+ doc.addEventListener("pointerup", end);
917
+ doc.addEventListener("pointercancel", end);
918
+ view.addEventListener("blur", end);
919
+ }
920
+ function onMouseDown(event) {
921
+ if (preventMouseDown) event.preventDefault();
922
+ preventMouseDown = false;
923
+ }
924
+ root.addEventListener("pointerdown", onPointerDown);
925
+ root.addEventListener("mousedown", onMouseDown);
926
+ return () => {
927
+ release();
928
+ root.removeEventListener("pointerdown", onPointerDown);
929
+ root.removeEventListener("mousedown", onMouseDown);
930
+ };
931
+ }
932
+ };
933
+ }
934
+
935
+ // src/view/selection/paint-selection.ts
936
+ var NAME = "quoin-selection";
937
+ function coveredRanges(controller) {
938
+ return _chunkPZ4GT5JOcjs.markRanges.call(void 0, controller.editor.state).flatMap(({ block, region, from, to }) => {
939
+ const host = controller.registry.get(block, region);
940
+ const map = host && domMapOf(host);
941
+ if (!host || !map) return [];
942
+ const start = map.toDom(from, 1);
943
+ const end = map.toDom(to, -1);
944
+ const range = host.ownerDocument.createRange();
945
+ range.setStart(start.node, start.offset);
946
+ range.setEnd(end.node, end.offset);
947
+ return [range];
948
+ });
949
+ }
950
+ function highlightPainter(highlights, Highlight) {
951
+ let painted = [];
952
+ function shared() {
953
+ const existing = highlights.get(NAME);
954
+ if (existing) return existing;
955
+ const created2 = new Highlight();
956
+ highlights.set(NAME, created2);
957
+ return created2;
958
+ }
959
+ function clear() {
960
+ if (painted.length === 0) return;
961
+ const highlight = shared();
962
+ for (const range of painted) highlight.delete(range);
963
+ painted = [];
964
+ }
965
+ return {
966
+ mode: "highlight",
967
+ paint(controller) {
968
+ clear();
969
+ if (!isCrossSelection(controller.editor.state.selection)) return;
970
+ painted = coveredRanges(controller);
971
+ const highlight = shared();
972
+ for (const range of painted) highlight.add(range);
973
+ },
974
+ clear
975
+ };
976
+ }
977
+ function decorationPainter() {
978
+ return { mode: "decorations", paint: (controller) => controller.refreshHosts(), clear: () => void 0 };
979
+ }
980
+ function createPainter(win) {
981
+ const { CSS: css, Highlight } = win;
982
+ return _optionalChain([css, 'optionalAccess', _43 => _43.highlights]) && Highlight ? highlightPainter(css.highlights, Highlight) : decorationPainter();
983
+ }
984
+
985
+ // src/view/selection/in-place.ts
986
+ function scrollOf(at) {
987
+ return "scrollY" in at ? { at, left: at.scrollX, top: at.scrollY } : { at, left: at.scrollLeft, top: at.scrollTop };
988
+ }
989
+ function inPlace(element, change) {
990
+ const scrollers = [];
991
+ for (let at = element; at !== null; at = at.parentElement) scrollers.push(at);
992
+ const view = element.ownerDocument.defaultView;
993
+ if (view !== null) scrollers.push(view);
994
+ const before = scrollers.map(scrollOf);
995
+ change();
996
+ for (const { at, left, top } of before) {
997
+ const now = scrollOf(at);
998
+ if (now.left !== left || now.top !== top) at.scrollTo({ left, top, behavior: "instant" });
999
+ }
1000
+ }
1001
+
1002
+ // src/view/selection/selection-sink.ts
1003
+ var wholeBlocks = (selection) => _optionalChain([selection, 'optionalAccess', _44 => _44.kind]) === "blocks" || _optionalChain([selection, 'optionalAccess', _45 => _45.kind]) === "node";
1004
+ function heldBySink(selection) {
1005
+ return wholeBlocks(selection) || isCrossSelection(selection);
1006
+ }
1007
+ var NO_TEXT = "\xA0";
1008
+ function selectionText(state) {
1009
+ const text = _chunkPZ4GT5JOcjs.markRanges.call(void 0, state).map((range) => _chunkND5XJVK2cjs.inlinePlainText.call(void 0, _chunkPZ4GT5JOcjs.rangeContent.call(void 0, state, range), state.schema)).join("\n");
1010
+ return text === "" ? NO_TEXT : text;
1011
+ }
1012
+ var focusInPlace = (sink) => inPlace(sink, () => sink.focus({ preventScroll: true }));
1013
+ var focusing = null;
1014
+ var focusingSink = (controller) => focusing === controller;
1015
+ function focusSink(controller) {
1016
+ const { sink } = controller;
1017
+ if (sink === null) return;
1018
+ const text = selectionText(controller.editor.state);
1019
+ if (sink.textContent !== text) sink.textContent = text;
1020
+ if (sink.ownerDocument.activeElement !== sink) {
1021
+ const outer = focusing;
1022
+ focusing = controller;
1023
+ try {
1024
+ focusInPlace(sink);
1025
+ } finally {
1026
+ focusing = outer;
1027
+ }
1028
+ }
1029
+ _optionalChain([sink, 'access', _46 => _46.ownerDocument, 'access', _47 => _47.getSelection, 'call', _48 => _48(), 'optionalAccess', _49 => _49.selectAllChildren, 'call', _50 => _50(sink)]);
1030
+ }
1031
+ function selectedBlockName({ doc, schema, selection }, blockLabel) {
1032
+ const block = _optionalChain([selection, 'optionalAccess', _51 => _51.kind]) === "node" ? _chunkND5XJVK2cjs.blockById.call(void 0, doc, selection.block) : void 0;
1033
+ return block === void 0 ? null : _nullishCoalesce(_optionalChain([blockLabel, 'optionalCall', _52 => _52(block, schema)]), () => ( ""));
1034
+ }
1035
+ function clearSink(controller) {
1036
+ const { sink } = controller;
1037
+ if (sink !== null && sink.textContent !== "") sink.textContent = "";
1038
+ }
1039
+
1040
+ // src/view/selection/cross-mode.ts
1041
+ var CROSS = "data-quoin-cross";
1042
+ function createCrossMode(controller) {
1043
+ let attached = null;
1044
+ let active = false;
1045
+ let synced = null;
1046
+ let frame = null;
1047
+ function sync() {
1048
+ const { state } = controller.editor;
1049
+ const cross = isCrossSelection(state.selection);
1050
+ synced = state;
1051
+ if (cross) {
1052
+ _optionalChain([attached, 'optionalAccess', _53 => _53.root, 'access', _54 => _54.setAttribute, 'call', _55 => _55(CROSS, "")]);
1053
+ _optionalChain([attached, 'optionalAccess', _56 => _56.painter, 'access', _57 => _57.paint, 'call', _58 => _58(controller)]);
1054
+ } else if (active) {
1055
+ _optionalChain([attached, 'optionalAccess', _59 => _59.root, 'access', _60 => _60.removeAttribute, 'call', _61 => _61(CROSS)]);
1056
+ _optionalChain([attached, 'optionalAccess', _62 => _62.painter, 'access', _63 => _63.clear, 'call', _64 => _64()]);
1057
+ }
1058
+ if (!heldBySink(state.selection)) clearSink(controller);
1059
+ active = cross;
1060
+ }
1061
+ function syncLater() {
1062
+ if (attached === null || frame !== null) return;
1063
+ frame = attached.view.requestAnimationFrame(() => {
1064
+ frame = null;
1065
+ if (controller.editor.state !== synced) sync();
1066
+ });
1067
+ }
1068
+ return {
1069
+ get active() {
1070
+ return active;
1071
+ },
1072
+ get mode() {
1073
+ return _nullishCoalesce(_optionalChain([attached, 'optionalAccess', _65 => _65.painter, 'access', _66 => _66.mode]), () => ( "highlight"));
1074
+ },
1075
+ attach(root) {
1076
+ const view = root.ownerDocument.defaultView;
1077
+ const current = { root, view, painter: createPainter(view) };
1078
+ attached = current;
1079
+ const detachPointer = createCrossBlockPointer(controller).attach(root);
1080
+ sync();
1081
+ return () => {
1082
+ detachPointer();
1083
+ if (frame !== null) view.cancelAnimationFrame(frame);
1084
+ frame = null;
1085
+ current.painter.clear();
1086
+ root.removeAttribute(CROSS);
1087
+ if (attached === current) attached = null;
1088
+ active = false;
1089
+ };
1090
+ },
1091
+ sync,
1092
+ syncLater
1093
+ };
1094
+ }
1095
+
1096
+ // src/view/selection/node-reveal.ts
1097
+ var nodeOf = (selection) => _optionalChain([selection, 'optionalAccess', _67 => _67.kind]) === "node" ? selection.block : null;
1098
+ function createNodeReveal(element) {
1099
+ let shown = null;
1100
+ let pressed = null;
1101
+ return {
1102
+ pressed(block) {
1103
+ pressed = block;
1104
+ },
1105
+ follow(selection) {
1106
+ if (nodeOf(selection) !== shown) shown = null;
1107
+ },
1108
+ settle(selection) {
1109
+ shown = nodeOf(selection);
1110
+ },
1111
+ show(selection) {
1112
+ const block = nodeOf(selection);
1113
+ if (block !== null && block !== shown && block !== pressed) _optionalChain([element, 'call', _68 => _68(block), 'optionalAccess', _69 => _69.scrollIntoView, 'call', _70 => _70({ block: "nearest" })]);
1114
+ shown = block;
1115
+ pressed = null;
1116
+ }
1117
+ };
1118
+ }
1119
+
1120
+ // src/view/controller/announcer.ts
1121
+ var THROTTLE_MS = 500;
1122
+ function createAnnouncer(stateOf, throttleMs = THROTTLE_MS) {
1123
+ let spoken = null;
1124
+ const listeners = /* @__PURE__ */ new Set();
1125
+ return {
1126
+ announce(message) {
1127
+ const state = stateOf();
1128
+ const now = Date.now();
1129
+ if (_optionalChain([spoken, 'optionalAccess', _71 => _71.message]) === message && spoken.state === state && now - spoken.at < throttleMs) return;
1130
+ spoken = { message, state, serial: (_nullishCoalesce(_optionalChain([spoken, 'optionalAccess', _72 => _72.serial]), () => ( 0))) + 1, at: now };
1131
+ for (const listener of [...listeners]) listener();
1132
+ },
1133
+ current: () => _optionalChain([spoken, 'optionalAccess', _73 => _73.state]) === stateOf() ? spoken : null,
1134
+ subscribe(listener) {
1135
+ listeners.add(listener);
1136
+ return () => void listeners.delete(listener);
1137
+ }
1138
+ };
1139
+ }
1140
+
1141
+ // src/view/controller/element-slot.ts
1142
+ function createElementSlot(changed) {
1143
+ let current = null;
1144
+ return {
1145
+ get element() {
1146
+ return current;
1147
+ },
1148
+ register(element) {
1149
+ current = element;
1150
+ changed();
1151
+ return () => {
1152
+ if (current === element) current = null;
1153
+ changed();
1154
+ };
1155
+ }
1156
+ };
1157
+ }
1158
+
1159
+ // src/view/controller/selection-element.ts
1160
+ function elementFor({ doc }, selection, rendered) {
1161
+ const id = _chunkPZ4GT5JOcjs.headBlockId.call(void 0, selection);
1162
+ const index = id === null ? void 0 : doc.index.get(id);
1163
+ if (id === null || index === void 0) return void 0;
1164
+ const hiding = hidingToggleAt(doc, index);
1165
+ if (hiding !== null) return rendered.block(doc.blocks[hiding].id);
1166
+ const host = _optionalChain([selection, 'optionalAccess', _74 => _74.kind]) === "text" ? rendered.hosts.get(id, selection.head.region) : void 0;
1167
+ return _nullishCoalesce(host, () => ( rendered.block(id)));
1168
+ }
1169
+ function selectionElement(state, rendered) {
1170
+ const { doc, schema, selection } = state;
1171
+ if (doc.blocks.length === 0) return _nullishCoalesce(_optionalChain([rendered, 'access', _75 => _75.empty, 'optionalCall', _76 => _76()]), () => ( void 0));
1172
+ const sink = heldBySink(selection) ? _optionalChain([rendered, 'access', _77 => _77.sink, 'optionalCall', _78 => _78()]) : null;
1173
+ if (sink) return sink;
1174
+ return _nullishCoalesce(elementFor(state, selection, rendered), () => ( elementFor(state, _chunkPZ4GT5JOcjs.startOf.call(void 0, doc, schema, doc.blocks[0].id), rendered)));
1175
+ }
1176
+
1177
+ // src/view/controller/tab-stop.ts
1178
+ function createTabStop() {
1179
+ let current;
1180
+ return {
1181
+ move(next) {
1182
+ if (next === current) return;
1183
+ if (current) current.tabIndex = -1;
1184
+ if (next) next.tabIndex = 0;
1185
+ current = next;
1186
+ }
1187
+ };
1188
+ }
1189
+
1190
+ // src/view/controller/drawn-elements.ts
1191
+ function createDrawnElements(hosts, state) {
1192
+ const tabStop = createTabStop();
1193
+ const syncTabStop = () => tabStop.move(focusElement());
1194
+ const blocks = createHostRegistry(() => {
1195
+ syncTabStop();
1196
+ return syncTabStop;
1197
+ });
1198
+ const sink = createElementSlot(syncTabStop);
1199
+ const empty = createElementSlot(syncTabStop);
1200
+ const rendered = {
1201
+ hosts,
1202
+ block: (id) => blocks.get(id, ""),
1203
+ sink: () => sink.element,
1204
+ empty: () => empty.element
1205
+ };
1206
+ function focusElement() {
1207
+ return selectionElement(state(), rendered);
1208
+ }
1209
+ return {
1210
+ get sink() {
1211
+ return sink.element;
1212
+ },
1213
+ get emptyHost() {
1214
+ return empty.element;
1215
+ },
1216
+ registerBlock: (id, element) => blocks.register(id, "", element),
1217
+ block: rendered.block,
1218
+ blockAt: (node) => _optionalChain([blocks, 'access', _79 => _79.locate, 'call', _80 => _80(node), 'optionalAccess', _81 => _81.block]),
1219
+ registerSink: sink.register,
1220
+ registerEmptyHost: empty.register,
1221
+ isEditorElement: (element) => element === sink.element || element === empty.element || [hosts, blocks].some((registry) => _optionalChain([registry, 'access', _82 => _82.locate, 'call', _83 => _83(element), 'optionalAccess', _84 => _84.element]) === element),
1222
+ focusElement,
1223
+ syncTabStop
1224
+ };
1225
+ }
1226
+
1227
+ // src/view/controller/focus-ownership.ts
1228
+ function hasFocus(controller, doc) {
1229
+ const active = _nullishCoalesce(_optionalChain([doc, 'optionalAccess', _85 => _85.activeElement]), () => ( null));
1230
+ return active !== null && controller.isEditorElement(active);
1231
+ }
1232
+ function ownsFocus(controller, doc) {
1233
+ const active = doc.activeElement;
1234
+ return !active || active === doc.body || active === controller.root || hasFocus(controller, doc);
1235
+ }
1236
+
1237
+ // src/view/controller/focus-target.ts
1238
+ function focusTarget(state, target) {
1239
+ const { doc, schema, selection } = state;
1240
+ if (doc.blocks.length === 0) return null;
1241
+ if (target === "end") return _chunkPZ4GT5JOcjs.endOf.call(void 0, doc, schema, doc.blocks[doc.blocks.length - 1].id);
1242
+ if (target === "start" || target === void 0 && selection === null) return _chunkPZ4GT5JOcjs.startOf.call(void 0, doc, schema, doc.blocks[0].id);
1243
+ return target === void 0 ? selection : _chunkPZ4GT5JOcjs.textSelection.call(void 0, target);
1244
+ }
1245
+
1246
+ // src/view/controller/host-decorations.ts
1247
+ var NO_DECORATIONS = [];
1248
+ function decorationsByHost(decorations) {
1249
+ const byHost = /* @__PURE__ */ new Map();
1250
+ for (const decoration of decorations) {
1251
+ const key = hostKey(decoration.block, decoration.kind === "block" ? "" : decoration.region);
1252
+ const group = byHost.get(key);
1253
+ if (group) group.push(decoration);
1254
+ else byHost.set(key, [decoration]);
1255
+ }
1256
+ return byHost;
1257
+ }
1258
+ function sameDecoration(a, b) {
1259
+ if (a.kind !== b.kind || a.block !== b.block || !_chunkND5XJVK2cjs.sameAttrs.call(void 0, a.attrs, b.attrs)) return false;
1260
+ if (a.kind === "inline" && b.kind === "inline") {
1261
+ return a.region === b.region && a.from === b.from && a.to === b.to;
1262
+ }
1263
+ if (a.kind === "widget" && b.kind === "widget") {
1264
+ const samePlace = a.region === b.region && a.offset === b.offset && a.side === b.side;
1265
+ return samePlace && a.key === b.key && a.widget === b.widget;
1266
+ }
1267
+ return true;
1268
+ }
1269
+ function sameDecorations(a, b) {
1270
+ return a === b || _chunkND5XJVK2cjs.sameItems.call(void 0, a, b, sameDecoration);
1271
+ }
1272
+ var byMode = { highlight: /* @__PURE__ */ new WeakMap(), decorations: /* @__PURE__ */ new WeakMap() };
1273
+ function hostDecorations(editor, block, region, mode = "highlight") {
1274
+ const { state } = editor;
1275
+ const byState = byMode[mode];
1276
+ let grouped = byState.get(state);
1277
+ if (grouped === void 0) {
1278
+ grouped = decorationsByHost([...editor.decorations(), ...selectionDecorations(state, mode)]);
1279
+ byState.set(state, grouped);
1280
+ }
1281
+ return _nullishCoalesce(grouped.get(hostKey(block, region)), () => ( NO_DECORATIONS));
1282
+ }
1283
+
1284
+ // src/view/controller/region-inline.ts
1285
+ function regionInline(state, block, region) {
1286
+ const regions = _optionalChain([_chunkND5XJVK2cjs.blockById.call(void 0, state.doc, block), 'optionalAccess', _86 => _86.regions]);
1287
+ return regions && Object.hasOwn(regions, region) ? regions[region] : void 0;
1288
+ }
1289
+
1290
+ // src/view/controller/host-renderer.ts
1291
+ function createHostRenderer(editor, options, decorationsOf, afterRender) {
1292
+ const rendered = /* @__PURE__ */ new WeakMap();
1293
+ function paint(host, always) {
1294
+ const inline = regionInline(editor.state, host.block, host.region);
1295
+ if (inline === void 0) return false;
1296
+ const decorations = decorationsOf(host.block, host.region);
1297
+ const last = rendered.get(host.element);
1298
+ const restyled = _optionalChain([last, 'optionalAccess', _87 => _87.inline]) === inline && !sameDecorations(last.decorations, decorations);
1299
+ if (!always && _optionalChain([last, 'optionalAccess', _88 => _88.inline]) === inline && !restyled) return false;
1300
+ renderInline(host.element, inline, decorations, options);
1301
+ afterRender(host.element);
1302
+ rendered.set(host.element, { inline, decorations });
1303
+ return restyled;
1304
+ }
1305
+ return {
1306
+ render: (host) => paint(host, true),
1307
+ renderIfChanged: (host) => void paint(host, false)
1308
+ };
1309
+ }
1310
+
1311
+ // src/view/controller/outside-changes.ts
1312
+ function createOutsideChanges(editorHasFocus, movedFrom) {
1313
+ let depth = 0;
1314
+ let focused = false;
1315
+ let moved = null;
1316
+ let settled = false;
1317
+ return {
1318
+ inside(change) {
1319
+ depth += 1;
1320
+ try {
1321
+ change();
1322
+ } finally {
1323
+ depth -= 1;
1324
+ }
1325
+ },
1326
+ committed(tr) {
1327
+ const outside = depth === 0 && editorHasFocus();
1328
+ focused ||= outside;
1329
+ if (_optionalChain([tr, 'optionalAccess', _89 => _89.selectionSet]) !== false) {
1330
+ moved = null;
1331
+ settled ||= depth === 0;
1332
+ return;
1333
+ }
1334
+ const from = _nullishCoalesce(moved, () => ( (outside && !settled ? movedFrom(tr.before.selection) : null)));
1335
+ settled ||= outside;
1336
+ moved = from && _chunkPZ4GT5JOcjs.mapSelection.call(void 0, from, (pos, assoc) => tr.mapPos(pos, assoc), tr.before.doc);
1337
+ },
1338
+ takeFocused() {
1339
+ const value = focused;
1340
+ focused = false;
1341
+ return value;
1342
+ },
1343
+ takeMoved() {
1344
+ const value = moved;
1345
+ moved = null;
1346
+ settled = false;
1347
+ return value;
1348
+ }
1349
+ };
1350
+ }
1351
+
1352
+ // src/view/controller/read-only.ts
1353
+ function createReadOnly(registry, initial) {
1354
+ let value = initial;
1355
+ const listeners = /* @__PURE__ */ new Set();
1356
+ return {
1357
+ current: () => value,
1358
+ set(readOnly) {
1359
+ const changed = readOnly !== value;
1360
+ value = readOnly;
1361
+ for (const { element } of registry.all()) element.contentEditable = readOnly ? "false" : "true";
1362
+ if (changed) for (const listener of [...listeners]) listener();
1363
+ },
1364
+ subscribe(listener) {
1365
+ listeners.add(listener);
1366
+ return () => void listeners.delete(listener);
1367
+ }
1368
+ };
1369
+ }
1370
+
1371
+ // src/view/clipboard/slice-marker.ts
1372
+ var QUOIN_SLICE_TYPE = "application/x-quoin+json";
1373
+ var MARKER = /data-quoin-slice=(["'])([A-Za-z0-9+/=]*)\1/;
1374
+ function encode(text) {
1375
+ let binary = "";
1376
+ for (const byte of new TextEncoder().encode(text)) binary += String.fromCharCode(byte);
1377
+ return btoa(binary);
1378
+ }
1379
+ function decode(encoded) {
1380
+ const binary = atob(encoded);
1381
+ return new TextDecoder().decode(Uint8Array.from(binary, (char) => char.charCodeAt(0)));
1382
+ }
1383
+ function wrapSliceHTML(html, json) {
1384
+ return `<div data-quoin-slice="${encode(json)}">${html}</div>`;
1385
+ }
1386
+ function readSliceHTML(html) {
1387
+ const encoded = _optionalChain([MARKER, 'access', _90 => _90.exec, 'call', _91 => _91(html), 'optionalAccess', _92 => _92[2]]);
1388
+ if (!encoded) return null;
1389
+ try {
1390
+ return decode(encoded);
1391
+ } catch (e) {
1392
+ return null;
1393
+ }
1394
+ }
1395
+
1396
+ // src/view/clipboard/paste-transfer.ts
1397
+ function caretOf(controller) {
1398
+ const { selection } = controller.editor.state;
1399
+ return _optionalChain([selection, 'optionalAccess', _93 => _93.kind]) === "text" ? selection.head : null;
1400
+ }
1401
+ function transferFiles(transfer) {
1402
+ const files = Array.from(transfer.files);
1403
+ const textless = !transfer.getData("text/plain") && !transfer.getData("text/html");
1404
+ return files.length > 0 && textless ? files : null;
1405
+ }
1406
+ function pasteTransfer(controller, transfer) {
1407
+ const files = transferFiles(transfer);
1408
+ if (files !== null) {
1409
+ _optionalChain([controller, 'access', _94 => _94.onFiles, 'optionalCall', _95 => _95(files, caretOf(controller))]);
1410
+ return;
1411
+ }
1412
+ const text = transfer.getData("text/plain");
1413
+ const html = transfer.getData("text/html");
1414
+ const json = transfer.getData(QUOIN_SLICE_TYPE) || readSliceHTML(html) || void 0;
1415
+ const data = { json, html: html || void 0, text: text || void 0 };
1416
+ if (controller.edit(() => controller.editor.paste(data))) controller.focus();
1417
+ }
1418
+
1419
+ // src/view/clipboard/clipboard-events.ts
1420
+ function handleCopy(controller, event, cut) {
1421
+ const { clipboardData } = event;
1422
+ if (!clipboardData) return;
1423
+ const copied = controller.editor.copy();
1424
+ if (copied === null) return;
1425
+ event.preventDefault();
1426
+ clipboardData.setData("text/plain", copied.text);
1427
+ clipboardData.setData("text/html", wrapSliceHTML(copied.html, copied.json));
1428
+ clipboardData.setData(QUOIN_SLICE_TYPE, copied.json);
1429
+ if (cut) controller.run(_chunkPZ4GT5JOcjs.deleteSelection);
1430
+ }
1431
+ function handlePaste(controller, event) {
1432
+ if (!event.clipboardData) return;
1433
+ event.preventDefault();
1434
+ pasteTransfer(controller, event.clipboardData);
1435
+ }
1436
+
1437
+ // src/view/clipboard/drop-events.ts
1438
+ var DROPPABLE = ["Files", "text/plain", "text/html"];
1439
+ function dropPos(controller, x, y) {
1440
+ const host = hostAt(controller, x, y);
1441
+ return host && caretPosAt(controller, host, x, y);
1442
+ }
1443
+ function textlessBlockAt(controller, x, y) {
1444
+ const element = _optionalChain([controller, 'access', _96 => _96.root, 'optionalAccess', _97 => _97.ownerDocument, 'access', _98 => _98.elementFromPoint, 'call', _99 => _99(x, y)]);
1445
+ const block = element ? controller.blockAt(element) : void 0;
1446
+ if (block === void 0 || controller.registry.locate(_nullishCoalesce(element, () => ( null))) !== null) return null;
1447
+ const hasText = [...controller.registry.all()].some((host) => host.block === block);
1448
+ return hasText && !entersAsNode(controller, block) ? null : { block, region: "", offset: 0 };
1449
+ }
1450
+ function handleDrop(controller, event) {
1451
+ event.preventDefault();
1452
+ const { dataTransfer } = event;
1453
+ if (!dataTransfer) return;
1454
+ const files = transferFiles(dataTransfer);
1455
+ const onBlock2 = files && textlessBlockAt(controller, event.clientX, event.clientY);
1456
+ if (files && onBlock2) {
1457
+ _optionalChain([controller, 'access', _100 => _100.onFiles, 'optionalCall', _101 => _101(files, onBlock2)]);
1458
+ return;
1459
+ }
1460
+ const pos = dropPos(controller, event.clientX, event.clientY);
1461
+ if (pos) dispatchSelection(controller, _chunkPZ4GT5JOcjs.textSelection.call(void 0, pos));
1462
+ pasteTransfer(controller, dataTransfer);
1463
+ }
1464
+ function handleDragOver(event) {
1465
+ const types = _nullishCoalesce(_optionalChain([event, 'access', _102 => _102.dataTransfer, 'optionalAccess', _103 => _103.types]), () => ( []));
1466
+ if (DROPPABLE.some((type) => types.includes(type))) event.preventDefault();
1467
+ }
1468
+ function handleDragStart(event) {
1469
+ event.preventDefault();
1470
+ }
1471
+
1472
+ // src/view/clipboard/attach-clipboard.ts
1473
+ function attachClipboard(controller, root, catchUp) {
1474
+ const inEmptyHost = (target) => _optionalChain([target, 'access', _104 => _104.closest, 'optionalCall', _105 => _105("[data-quoin-empty]")]) != null;
1475
+ const inEditor = ({ target }) => controller.registry.locate(target) !== null || controller.isEditorElement(target) || inEmptyHost(target);
1476
+ const inField = ({ target }) => _optionalChain([target, 'optionalAccess', _106 => _106.closest, 'optionalCall', _107 => _107("input, textarea, select")]) != null;
1477
+ const droppable = (event) => !controller.readOnly && !inField(event);
1478
+ const onCopy = (event) => {
1479
+ if (!inEditor(event)) return;
1480
+ catchUp();
1481
+ handleCopy(controller, event, false);
1482
+ };
1483
+ const onCut = (event) => {
1484
+ if (controller.readOnly || !inEditor(event)) return;
1485
+ catchUp();
1486
+ handleCopy(controller, event, true);
1487
+ };
1488
+ const onPaste = (event) => {
1489
+ if (controller.readOnly || !inEditor(event)) return;
1490
+ catchUp();
1491
+ handlePaste(controller, event);
1492
+ };
1493
+ const onDragStart = (event) => {
1494
+ if (controller.registry.locate(event.target) !== null) handleDragStart(event);
1495
+ };
1496
+ const onDragOver = (event) => {
1497
+ if (droppable(event)) handleDragOver(event);
1498
+ };
1499
+ const onDrop = (event) => {
1500
+ if (droppable(event)) handleDrop(controller, event);
1501
+ };
1502
+ const listeners = [
1503
+ ["copy", onCopy],
1504
+ ["cut", onCut],
1505
+ ["paste", onPaste],
1506
+ ["dragstart", onDragStart],
1507
+ ["dragover", onDragOver],
1508
+ ["drop", onDrop]
1509
+ ];
1510
+ for (const [type, listener] of listeners) root.addEventListener(type, listener);
1511
+ return () => {
1512
+ for (const [type, listener] of listeners) root.removeEventListener(type, listener);
1513
+ };
1514
+ }
1515
+
1516
+ // src/view/selection/sink-composition.ts
1517
+ var SINK_KEY = "sink";
1518
+ function textBetween(sink, start, startOffset, end, endOffset) {
1519
+ const range = sink.ownerDocument.createRange();
1520
+ range.setStart(start, startOffset);
1521
+ range.setEnd(end, endOffset);
1522
+ return range.toString();
1523
+ }
1524
+ function textOutsideSelection(sink) {
1525
+ const selection = sink.ownerDocument.getSelection();
1526
+ const range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
1527
+ if (range === null || !sink.contains(range.startContainer) || !sink.contains(range.endContainer)) {
1528
+ return { before: "", after: "" };
1529
+ }
1530
+ return {
1531
+ before: textBetween(sink, sink, 0, range.startContainer, range.startOffset),
1532
+ after: textBetween(sink, range.endContainer, range.endOffset, sink, sink.childNodes.length)
1533
+ };
1534
+ }
1535
+ function composedText(sink, { before, after }) {
1536
+ let text = _nullishCoalesce(sink.textContent, () => ( ""));
1537
+ if (text.startsWith(before)) text = text.slice(before.length);
1538
+ return text.endsWith(after) ? text.slice(0, text.length - after.length) : text;
1539
+ }
1540
+ function createSinkComposition(controller) {
1541
+ let outside = null;
1542
+ let stopWaiting = null;
1543
+ function settle() {
1544
+ _optionalChain([stopWaiting, 'optionalCall', _108 => _108()]);
1545
+ stopWaiting = null;
1546
+ const { sink } = controller;
1547
+ const ignored = wholeBlocks(controller.editor.state.selection);
1548
+ const text = sink !== null && outside !== null && !ignored ? composedText(sink, outside) : "";
1549
+ outside = null;
1550
+ controller.composingKey = null;
1551
+ clearSink(controller);
1552
+ if (text === "") controller.syncSelectionToDom();
1553
+ else controller.edit(() => controller.editor.insertText(text));
1554
+ }
1555
+ return {
1556
+ start() {
1557
+ _optionalChain([stopWaiting, 'optionalCall', _109 => _109()]);
1558
+ stopWaiting = null;
1559
+ if (outside === null && controller.sink !== null) outside = textOutsideSelection(controller.sink);
1560
+ controller.composingKey = SINK_KEY;
1561
+ },
1562
+ end() {
1563
+ const { sink } = controller;
1564
+ if (outside === null || stopWaiting !== null || sink === null) return;
1565
+ const onInput = (event) => {
1566
+ if (!event.isComposing) settle();
1567
+ };
1568
+ const timer = setTimeout(settle, 0);
1569
+ sink.addEventListener("input", onInput);
1570
+ stopWaiting = () => {
1571
+ clearTimeout(timer);
1572
+ sink.removeEventListener("input", onInput);
1573
+ };
1574
+ },
1575
+ release() {
1576
+ if (stopWaiting !== null) settle();
1577
+ else if (outside !== null) {
1578
+ outside = null;
1579
+ controller.composingKey = null;
1580
+ }
1581
+ }
1582
+ };
1583
+ }
1584
+
1585
+ // src/engine/commands/transpose-chars.ts
1586
+ var LINE_BREAK = "\n";
1587
+ var transposeChars = (state, dispatch) => {
1588
+ const { selection } = state;
1589
+ if (_optionalChain([selection, 'optionalAccess', _110 => _110.kind]) !== "text" || !_chunkPZ4GT5JOcjs.isCollapsed.call(void 0, selection)) return false;
1590
+ const { head } = selection;
1591
+ const inline = _optionalChain([_chunkND5XJVK2cjs.blockById.call(void 0, state.doc, head.block), 'optionalAccess', _111 => _111.regions, 'access', _112 => _112[head.region]]);
1592
+ if (inline === void 0) return false;
1593
+ const value = _chunkND5XJVK2cjs.inlineText.call(void 0, inline);
1594
+ const atLineEnd = head.offset === value.length || value[head.offset] === LINE_BREAK;
1595
+ const end = atLineEnd ? head.offset : _chunkPZ4GT5JOcjs.nextGraphemeOffset.call(void 0, value, head.offset);
1596
+ const middle = _chunkPZ4GT5JOcjs.prevGraphemeOffset.call(void 0, value, end);
1597
+ const start = _chunkPZ4GT5JOcjs.prevGraphemeOffset.call(void 0, value, middle);
1598
+ if (start === middle || value.slice(start, end).includes(LINE_BREAK)) return false;
1599
+ const swapped = [..._chunkND5XJVK2cjs.sliceInline.call(void 0, inline, middle, end), ..._chunkND5XJVK2cjs.sliceInline.call(void 0, inline, start, middle)];
1600
+ const tr = new (0, _chunkPZ4GT5JOcjs.Transaction)(state).setOrigin("input");
1601
+ tr.step(_chunkPZ4GT5JOcjs.replaceInlineStep.call(void 0, head.block, head.region, start, end, swapped));
1602
+ _optionalChain([dispatch, 'optionalCall', _113 => _113(tr.setSelection(_chunkPZ4GT5JOcjs.textSelection.call(void 0, { ...head, offset: end })))]);
1603
+ return true;
1604
+ };
1605
+
1606
+ // src/view/input/replace-range.ts
1607
+ function replaceRange(editor, range, text, withRules) {
1608
+ const dispatch = (tr) => editor.dispatch(tr);
1609
+ dispatch(editor.createTransaction().setSelection(_chunkPZ4GT5JOcjs.textSelection.call(void 0, range.from, range.to)).setOrigin("input"));
1610
+ if (text === "") return _chunkPZ4GT5JOcjs.deleteSelection.call(void 0, editor.state, (tr) => dispatch(tr.setOrigin("input")));
1611
+ return withRules ? editor.insertText(text) : _chunkPZ4GT5JOcjs.insertText.call(void 0, text, { origin: "input" })(editor.state, dispatch);
1612
+ }
1613
+
1614
+ // src/view/input/target-range.ts
1615
+ function targetRangeToModel(controller, event) {
1616
+ const range = typeof event.getTargetRanges === "function" ? event.getTargetRanges()[0] : void 0;
1617
+ if (range === void 0) return null;
1618
+ const from = domToPos(controller.registry, range.startContainer, range.startOffset);
1619
+ const to = domToPos(controller.registry, range.endContainer, range.endOffset);
1620
+ return from && to && { from, to };
1621
+ }
1622
+ var isEmpty = ({ from, to }) => _chunkPZ4GT5JOcjs.sameSelection.call(void 0, _chunkPZ4GT5JOcjs.textSelection.call(void 0, from), _chunkPZ4GT5JOcjs.textSelection.call(void 0, to));
1623
+ function targetBeyondSelection(controller, event) {
1624
+ const range = targetRangeToModel(controller, event);
1625
+ if (range === null || isEmpty(range)) return null;
1626
+ const { doc, selection } = controller.editor.state;
1627
+ const selected = _chunkPZ4GT5JOcjs.selectionRange.call(void 0, doc, selection);
1628
+ const same = selected !== null && _chunkPZ4GT5JOcjs.sameSelection.call(void 0, _chunkPZ4GT5JOcjs.textSelection.call(void 0, selected.from, selected.to), _chunkPZ4GT5JOcjs.textSelection.call(void 0, range.from, range.to));
1629
+ return same ? null : range;
1630
+ }
1631
+
1632
+ // src/view/input/before-input.ts
1633
+ var ENTER = { key: "Enter", altKey: false, ctrlKey: false, metaKey: false, shiftKey: false };
1634
+ var deleteRange = (controller, range) => controller.edit(() => replaceRange(controller.editor, range, "", false));
1635
+ var run = (command) => (controller) => void controller.run(command);
1636
+ var targetOr = (command) => (controller, event) => {
1637
+ const range = targetBeyondSelection(controller, event);
1638
+ if (range === null) controller.run(command);
1639
+ else deleteRange(controller, range);
1640
+ };
1641
+ var leaveToHandlers = () => void 0;
1642
+ function insertTypedText(controller, event) {
1643
+ const text = _nullishCoalesce(event.data, () => ( _optionalChain([event, 'access', _114 => _114.dataTransfer, 'optionalAccess', _115 => _115.getData, 'call', _116 => _116("text/plain")])));
1644
+ if (!text) return;
1645
+ const range = targetBeyondSelection(controller, event);
1646
+ if (range === null) controller.edit(() => controller.editor.insertText(text));
1647
+ else controller.edit(() => replaceRange(controller.editor, range, text, false));
1648
+ }
1649
+ function deleteContentBackward(controller, event) {
1650
+ if (!controller.run(_chunkPZ4GT5JOcjs.undoInputRule)) targetOr((0, _chunkPZ4GT5JOcjs.deleteBackward))(controller, event);
1651
+ }
1652
+ function insertReplacementText(controller, event) {
1653
+ const text = _optionalChain([event, 'access', _117 => _117.dataTransfer, 'optionalAccess', _118 => _118.getData, 'call', _119 => _119("text/plain")]) || event.data;
1654
+ if (!text) return;
1655
+ const range = targetRangeToModel(controller, event);
1656
+ if (range === null) controller.run(_chunkPZ4GT5JOcjs.insertText.call(void 0, text, { origin: "input" }));
1657
+ else controller.edit(() => replaceRange(controller.editor, range, text, false));
1658
+ }
1659
+ var actions = {
1660
+ insertText: insertTypedText,
1661
+ insertReplacementText,
1662
+ insertParagraph: (controller) => void controller.edit(() => controller.editor.handleKey(ENTER)),
1663
+ insertLineBreak: run(_chunkPZ4GT5JOcjs.insertHardBreak),
1664
+ deleteContentBackward,
1665
+ deleteContentForward: targetOr(_chunkPZ4GT5JOcjs.deleteForward),
1666
+ deleteWordBackward: targetOr(_chunkPZ4GT5JOcjs.deleteWordBackward),
1667
+ deleteWordForward: targetOr(_chunkPZ4GT5JOcjs.deleteWordForward),
1668
+ deleteSoftLineBackward: targetOr(_chunkPZ4GT5JOcjs.deleteToLineStart),
1669
+ deleteHardLineBackward: targetOr(_chunkPZ4GT5JOcjs.deleteToLineStart),
1670
+ deleteSoftLineForward: targetOr(_chunkPZ4GT5JOcjs.deleteToLineEnd),
1671
+ deleteHardLineForward: targetOr(_chunkPZ4GT5JOcjs.deleteToLineEnd),
1672
+ formatBold: run(_chunkPZ4GT5JOcjs.toggleMark.call(void 0, "bold")),
1673
+ formatItalic: run(_chunkPZ4GT5JOcjs.toggleMark.call(void 0, "italic")),
1674
+ formatUnderline: run(_chunkPZ4GT5JOcjs.toggleMark.call(void 0, "underline")),
1675
+ formatStrikeThrough: run(_chunkPZ4GT5JOcjs.toggleMark.call(void 0, "strike")),
1676
+ historyUndo: run(_chunkPZ4GT5JOcjs.undo),
1677
+ historyRedo: run(_chunkPZ4GT5JOcjs.redo),
1678
+ insertTranspose: run(transposeChars),
1679
+ insertFromPaste: leaveToHandlers,
1680
+ insertFromDrop: leaveToHandlers,
1681
+ deleteByCut: leaveToHandlers,
1682
+ deleteByDrag: leaveToHandlers
1683
+ };
1684
+ var isComposingInput = (controller, event) => event.isComposing || controller.composingKey !== null;
1685
+ var TYPED_INPUT = /* @__PURE__ */ new Set(["insertText", "insertReplacementText"]);
1686
+ function handleBeforeInput(controller, event) {
1687
+ const { inputType } = event;
1688
+ if (isComposingInput(controller, event)) return;
1689
+ if (!event.cancelable || !Object.hasOwn(actions, inputType)) return;
1690
+ event.preventDefault();
1691
+ if (TYPED_INPUT.has(inputType) && wholeBlocks(controller.editor.state.selection)) return;
1692
+ actions[inputType](controller, event);
1693
+ }
1694
+
1695
+ // src/view/dom/read-dom-text.ts
1696
+ var ELEMENT_NODE = 1;
1697
+ var TEXT_NODE2 = 3;
1698
+ var OBJECT_REPLACEMENT = "\uFFFC";
1699
+ var BLOCKS = /* @__PURE__ */ new Set(["div", "p"]);
1700
+ function kindOf(element) {
1701
+ if (element.hasAttribute("data-quoin-atom")) return "atom";
1702
+ if (element.hasAttribute("data-quoin-widget")) return "widget";
1703
+ if (element.localName === "br") {
1704
+ if (element.hasAttribute("data-quoin-break")) return "break";
1705
+ return element.hasAttribute("data-quoin-filler") ? "filler" : "br";
1706
+ }
1707
+ return BLOCKS.has(element.localName) ? "block" : "inline";
1708
+ }
1709
+ function collect(parent, out) {
1710
+ for (let node = parent.firstChild; node; node = node.nextSibling) {
1711
+ if (node.nodeType === TEXT_NODE2) {
1712
+ const data = node.data;
1713
+ if (data) out.push({ kind: "text", data });
1714
+ } else if (node.nodeType === ELEMENT_NODE) {
1715
+ const kind = kindOf(node);
1716
+ if (kind !== "widget" && kind !== "filler" && kind !== "inline") out.push({ kind });
1717
+ if (kind === "block" || kind === "inline") collect(node, out);
1718
+ }
1719
+ }
1720
+ }
1721
+ function readDomText(host) {
1722
+ const tokens = [];
1723
+ collect(host, tokens);
1724
+ let last = tokens.length - 1;
1725
+ while (last >= 0 && tokens[last].kind === "block") last--;
1726
+ let result = "";
1727
+ let atLineStart = true;
1728
+ tokens.forEach((token, index) => {
1729
+ switch (token.kind) {
1730
+ case "text":
1731
+ result += token.data;
1732
+ atLineStart = false;
1733
+ break;
1734
+ case "atom":
1735
+ result += OBJECT_REPLACEMENT;
1736
+ atLineStart = false;
1737
+ break;
1738
+ case "break":
1739
+ case "br":
1740
+ if (token.kind === "break" || index < last) {
1741
+ result += "\n";
1742
+ atLineStart = true;
1743
+ }
1744
+ break;
1745
+ case "block":
1746
+ if (!atLineStart) {
1747
+ result += "\n";
1748
+ atLineStart = true;
1749
+ }
1750
+ break;
1751
+ }
1752
+ });
1753
+ return result;
1754
+ }
1755
+
1756
+ // src/view/input/commit-empty-host.ts
1757
+ function commitEmptyHost(controller) {
1758
+ const host = controller.emptyHost;
1759
+ if (host === null) return;
1760
+ const text = readDomText(host);
1761
+ if (text !== "" && controller.edit(() => controller.editor.insertText(text))) controller.focus();
1762
+ else renderInline(host, [], []);
1763
+ }
1764
+
1765
+ // src/view/input/composition-snapshot.ts
1766
+ function composedEnd(controller, block, region) {
1767
+ const { selection } = controller.editor.state;
1768
+ if (_optionalChain([selection, 'optionalAccess', _120 => _120.kind]) !== "text") return void 0;
1769
+ const ends = [selection.anchor, selection.head];
1770
+ if (ends.some((pos) => pos.block !== block || pos.region !== region)) return void 0;
1771
+ return Math.max(...ends.map((pos) => pos.offset));
1772
+ }
1773
+ function snapshotComposition(controller, block, region) {
1774
+ const { editor } = controller;
1775
+ const element = controller.registry.get(block, region);
1776
+ const inline = regionInline(editor.state, block, region);
1777
+ if (element === void 0 || inline === void 0) return null;
1778
+ const text = _chunkND5XJVK2cjs.inlineText.call(void 0, inline);
1779
+ const end = composedEnd(controller, block, region);
1780
+ const since = [];
1781
+ const release = editor.subscribe((_, tr) => void (tr && since.push(tr)));
1782
+ const mapped = (offset, assoc) => since.reduce((pos, tr) => tr.mapPos(pos, assoc), { block, region, offset });
1783
+ return {
1784
+ settle() {
1785
+ release();
1786
+ if (controller.registry.get(block, region) !== element) return false;
1787
+ const after = readDomText(element);
1788
+ const edit = textDiff(text, after, end === void 0 ? void 0 : end + after.length - text.length);
1789
+ if (edit === null) return false;
1790
+ const from = mapped(edit.from, edit.from === edit.to ? -1 : 1);
1791
+ const to = mapped(edit.to, -1);
1792
+ const spans = to.block === from.block && to.region === from.region && to.offset > from.offset;
1793
+ const range = { from, to: spans ? to : from };
1794
+ controller.edit(() => {
1795
+ const applied = replaceRange(editor, range, edit.insert, true);
1796
+ controller.renderHost(block, region);
1797
+ return applied;
1798
+ });
1799
+ return true;
1800
+ },
1801
+ release
1802
+ };
1803
+ }
1804
+
1805
+ // src/view/input/composition.ts
1806
+ var EMPTY_HOST = "empty";
1807
+ function targetAt(controller, node) {
1808
+ const entry = controller.registry.locate(node);
1809
+ if (entry !== null) return { block: entry.block, region: entry.region };
1810
+ return node !== null && _optionalChain([controller, 'access', _121 => _121.emptyHost, 'optionalAccess', _122 => _122.contains, 'call', _123 => _123(node)]) ? EMPTY_HOST : null;
1811
+ }
1812
+ function elementOf(controller, target) {
1813
+ if (target === EMPTY_HOST) return _nullishCoalesce(controller.emptyHost, () => ( void 0));
1814
+ return target === null ? void 0 : controller.registry.get(target.block, target.region);
1815
+ }
1816
+ function createCompositionHandlers(controller) {
1817
+ let active = false;
1818
+ let target = null;
1819
+ let snapshot = null;
1820
+ let stopWaiting = null;
1821
+ function settle() {
1822
+ _optionalChain([stopWaiting, 'optionalCall', _124 => _124()]);
1823
+ stopWaiting = null;
1824
+ active = false;
1825
+ controller.composingKey = null;
1826
+ const host = target;
1827
+ const started = snapshot;
1828
+ target = null;
1829
+ snapshot = null;
1830
+ if (host === EMPTY_HOST) commitEmptyHost(controller);
1831
+ else if (host !== null && !_optionalChain([started, 'optionalAccess', _125 => _125.settle, 'call', _126 => _126()])) {
1832
+ controller.renderHost(host.block, host.region);
1833
+ controller.syncSelectionToDom();
1834
+ }
1835
+ }
1836
+ function waitForInput(element) {
1837
+ const onInput = () => settle();
1838
+ const timer = setTimeout(settle, 0);
1839
+ _optionalChain([element, 'optionalAccess', _127 => _127.addEventListener, 'call', _128 => _128("input", onInput)]);
1840
+ stopWaiting = () => {
1841
+ clearTimeout(timer);
1842
+ _optionalChain([element, 'optionalAccess', _129 => _129.removeEventListener, 'call', _130 => _130("input", onInput)]);
1843
+ };
1844
+ }
1845
+ return {
1846
+ start() {
1847
+ if (stopWaiting !== null) settle();
1848
+ _optionalChain([snapshot, 'optionalAccess', _131 => _131.release, 'call', _132 => _132()]);
1849
+ target = targetAt(controller, _nullishCoalesce(_optionalChain([controller, 'access', _133 => _133.domSelection, 'call', _134 => _134(), 'optionalAccess', _135 => _135.anchorNode]), () => ( null)));
1850
+ const host = target === EMPTY_HOST ? null : target;
1851
+ snapshot = host && snapshotComposition(controller, host.block, host.region);
1852
+ active = true;
1853
+ controller.composingKey = target === null || target === EMPTY_HOST ? target : hostKey(target.block, target.region);
1854
+ },
1855
+ end() {
1856
+ if (!active || stopWaiting !== null) return;
1857
+ waitForInput(elementOf(controller, target));
1858
+ },
1859
+ release() {
1860
+ if (stopWaiting !== null) settle();
1861
+ _optionalChain([snapshot, 'optionalAccess', _136 => _136.release, 'call', _137 => _137()]);
1862
+ snapshot = null;
1863
+ target = null;
1864
+ if (active) controller.composingKey = null;
1865
+ active = false;
1866
+ },
1867
+ get active() {
1868
+ return active;
1869
+ }
1870
+ };
1871
+ }
1872
+
1873
+ // src/view/input/reconcile-host.ts
1874
+ function domCaretOffset(host) {
1875
+ const doc = host.ownerDocument;
1876
+ const selection = doc.getSelection();
1877
+ const node = _optionalChain([selection, 'optionalAccess', _138 => _138.focusNode]);
1878
+ if (!selection || !node || !host.contains(node)) return void 0;
1879
+ const range = doc.createRange();
1880
+ range.setStart(host, 0);
1881
+ range.setEnd(node, selection.focusOffset);
1882
+ const before = doc.createElement("div");
1883
+ before.append(range.cloneContents());
1884
+ return readDomText(before).length;
1885
+ }
1886
+ function reconcileHost(controller, block, region, caretHint) {
1887
+ const element = controller.registry.get(block, region);
1888
+ const inline = regionInline(controller.editor.state, block, region);
1889
+ if (element === void 0 || inline === void 0) return false;
1890
+ const before = _chunkND5XJVK2cjs.inlineText.call(void 0, inline);
1891
+ const after = readDomText(element);
1892
+ const edit = before === after ? null : textDiff(before, after, _nullishCoalesce(caretHint, () => ( domCaretOffset(element))));
1893
+ if (edit === null) return false;
1894
+ const at = (offset) => ({ block, region, offset });
1895
+ controller.edit(() => {
1896
+ const applied = replaceRange(controller.editor, { from: at(edit.from), to: at(edit.to) }, edit.insert, true);
1897
+ controller.renderHost(block, region);
1898
+ return applied;
1899
+ });
1900
+ return true;
1901
+ }
1902
+
1903
+ // src/view/input/dom-input.ts
1904
+ var OBSERVED = { characterData: true, childList: true, subtree: true };
1905
+ function createDomInputFallback(controller) {
1906
+ const pending = /* @__PURE__ */ new Map();
1907
+ const observers = /* @__PURE__ */ new Map();
1908
+ let queued = false;
1909
+ function enqueue(node) {
1910
+ const entry = controller.registry.locate(node);
1911
+ if (entry === null || controller.composingKey !== null || controller.readOnly) return false;
1912
+ pending.set(hostKey(entry.block, entry.region), entry);
1913
+ return true;
1914
+ }
1915
+ function flush() {
1916
+ queued = false;
1917
+ for (const [host, observer] of observers) if (observer.takeRecords().length > 0) enqueue(host);
1918
+ for (const [key, { block, region }] of [...pending]) {
1919
+ pending.delete(key);
1920
+ if (controller.composingKey !== key) reconcileHost(controller, block, region);
1921
+ }
1922
+ }
1923
+ function schedule(node) {
1924
+ if (!enqueue(node) || queued) return;
1925
+ queued = true;
1926
+ queueMicrotask(() => {
1927
+ if (queued) flush();
1928
+ });
1929
+ }
1930
+ return {
1931
+ onInput(event) {
1932
+ const { target } = event;
1933
+ if (event.isComposing || event.defaultPrevented) return;
1934
+ if (target === null || target !== controller.emptyHost) schedule(target);
1935
+ else if (controller.composingKey === null && !controller.readOnly) commitEmptyHost(controller);
1936
+ },
1937
+ observe(host) {
1938
+ const observer = new MutationObserver(() => schedule(host));
1939
+ observer.observe(host, OBSERVED);
1940
+ observers.set(host, observer);
1941
+ return () => {
1942
+ observer.disconnect();
1943
+ if (observers.get(host) === observer) observers.delete(host);
1944
+ };
1945
+ },
1946
+ drain(host) {
1947
+ _optionalChain([observers, 'access', _139 => _139.get, 'call', _140 => _140(host), 'optionalAccess', _141 => _141.takeRecords, 'call', _142 => _142()]);
1948
+ },
1949
+ flush
1950
+ };
1951
+ }
1952
+
1953
+ // src/view/navigation/cross-moves.ts
1954
+ function moveHead(controller, { anchor }, head) {
1955
+ dispatchSelection(controller, _chunkPZ4GT5JOcjs.textSelection.call(void 0, anchor, head));
1956
+ return true;
1957
+ }
1958
+ function blockOf(controller, { block }) {
1959
+ const { doc } = controller.editor.state;
1960
+ return doc.blocks[_chunkND5XJVK2cjs.requireBlockIndex.call(void 0, doc, block)];
1961
+ }
1962
+ function collapseCross(controller, selection, direction) {
1963
+ const { from, to } = _chunkPZ4GT5JOcjs.selectionRange.call(void 0, controller.editor.state.doc, selection);
1964
+ dispatchSelection(controller, _chunkPZ4GT5JOcjs.textSelection.call(void 0, direction < 0 ? from : to));
1965
+ return true;
1966
+ }
1967
+ function crossHeadVertically(controller, selection, direction) {
1968
+ const { head } = selection;
1969
+ const edge = neighbourHead(controller, head.block, direction);
1970
+ if (edge !== null) {
1971
+ const end2 = _chunkPZ4GT5JOcjs.regionLength.call(void 0, blockOf(controller, edge), edge.region);
1972
+ return moveHead(controller, selection, { ...edge, offset: Math.min(head.offset, end2) });
1973
+ }
1974
+ const end = _chunkPZ4GT5JOcjs.regionLength.call(void 0, blockOf(controller, head), head.region);
1975
+ return moveHead(controller, selection, { ...head, offset: direction < 0 ? 0 : end });
1976
+ }
1977
+ function crossHeadHorizontally(controller, selection, direction) {
1978
+ const { head } = selection;
1979
+ const text = _chunkND5XJVK2cjs.inlineText.call(void 0, _nullishCoalesce(blockOf(controller, head).regions[head.region], () => ( [])));
1980
+ if (direction < 0 ? head.offset > 0 : head.offset < text.length) {
1981
+ const offset = direction < 0 ? _chunkPZ4GT5JOcjs.prevGraphemeOffset.call(void 0, text, head.offset) : _chunkPZ4GT5JOcjs.nextGraphemeOffset.call(void 0, text, head.offset);
1982
+ return moveHead(controller, selection, { ...head, offset });
1983
+ }
1984
+ const edge = neighbourHead(controller, head.block, direction);
1985
+ return edge === null || moveHead(controller, selection, edge);
1986
+ }
1987
+
1988
+ // src/view/navigation/horizontal.ts
1989
+ function moveHorizontally(controller, direction, extend) {
1990
+ controller.goalX = null;
1991
+ const { editor } = controller;
1992
+ const { state } = editor;
1993
+ const { selection, doc } = state;
1994
+ if (_optionalChain([selection, 'optionalAccess', _143 => _143.kind]) === "node") return !extend && moveToNeighbour(controller, selection.block, direction);
1995
+ if (_optionalChain([selection, 'optionalAccess', _144 => _144.kind]) !== "text") return false;
1996
+ if (isCrossSelection(selection)) {
1997
+ const move = extend ? crossHeadHorizontally : collapseCross;
1998
+ return move(controller, selection, direction);
1999
+ }
2000
+ if (!extend && !_chunkPZ4GT5JOcjs.isCollapsed.call(void 0, selection)) return false;
2001
+ const { head } = selection;
2002
+ const length = _chunkPZ4GT5JOcjs.regionLength.call(void 0, doc.blocks[_chunkND5XJVK2cjs.requireBlockIndex.call(void 0, doc, head.block)], head.region);
2003
+ if (direction < 0 ? head.offset > 0 : head.offset < length) return false;
2004
+ const own2 = extend ? null : ownNode(controller, head.block, direction);
2005
+ if (own2 !== null) {
2006
+ dispatchSelection(controller, own2);
2007
+ return true;
2008
+ }
2009
+ if (!extend) return moveToNeighbour(controller, head.block, direction);
2010
+ const edge = neighbourHead(controller, head.block, direction);
2011
+ if (edge === null) return false;
2012
+ dispatchSelection(controller, _chunkPZ4GT5JOcjs.textSelection.call(void 0, selection.anchor, edge));
2013
+ return true;
2014
+ }
2015
+
2016
+ // src/view/navigation/line-edges.ts
2017
+ var TEXT_NODE3 = 3;
2018
+ var ELEMENT_NODE2 = 1;
2019
+ var hasSize = (rect) => rect !== void 0 && (rect.width > 0 || rect.height > 0);
2020
+ var edgeOf = (rect, right) => new DOMRect(right ? rect.right : rect.left, rect.top, 0, rect.height);
2021
+ function textCaretRect(text, offset) {
2022
+ const range = text.ownerDocument.createRange();
2023
+ range.setStart(text, offset);
2024
+ const rect = range.getClientRects()[0];
2025
+ return hasSize(rect) ? rect : null;
2026
+ }
2027
+ function besideNode(node, before) {
2028
+ if (node.nodeType === TEXT_NODE3) return textCaretRect(node, before ? 0 : node.length);
2029
+ if (node.nodeType !== ELEMENT_NODE2) return null;
2030
+ const rects = node.getClientRects();
2031
+ const rect = before ? rects[0] : rects[rects.length - 1];
2032
+ return hasSize(rect) ? edgeOf(rect, !before) : null;
2033
+ }
2034
+ function caretClientRect(point) {
2035
+ const { node, offset } = point;
2036
+ if (node.nodeType === TEXT_NODE3) return textCaretRect(node, offset);
2037
+ const after = node.childNodes[offset];
2038
+ const before = node.childNodes[offset - 1];
2039
+ return _nullishCoalesce(_nullishCoalesce((after && besideNode(after, true)), () => ( (before && besideNode(before, false)))), () => ( null));
2040
+ }
2041
+ function contentBox(host) {
2042
+ const box = host.getBoundingClientRect();
2043
+ const style = host.ownerDocument.defaultView.getComputedStyle(host);
2044
+ const left = box.left + host.clientLeft + parseFloat(style.paddingLeft);
2045
+ const top = box.top + host.clientTop + parseFloat(style.paddingTop);
2046
+ const width = host.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
2047
+ const height = host.clientHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);
2048
+ return new DOMRect(left, top, width, height);
2049
+ }
2050
+ function lineEdgeRect(host, last) {
2051
+ const map = domMapOf(host);
2052
+ if (!map || map.length === 0) return contentBox(host);
2053
+ return caretClientRect(map.toDom(last ? map.length : 0, last ? -1 : 1));
2054
+ }
2055
+ function onEdgeLine(host, point, last) {
2056
+ if (_optionalChain([domMapOf, 'call', _145 => _145(host), 'optionalAccess', _146 => _146.length]) === 0) return true;
2057
+ const caret = caretClientRect(point);
2058
+ const edge = lineEdgeRect(host, last);
2059
+ if (!caret || !edge) return false;
2060
+ return Math.abs(caret.top - edge.top) < Math.max(caret.height, edge.height) / 2;
2061
+ }
2062
+ function isOnFirstLine(host, point) {
2063
+ return onEdgeLine(host, point, false);
2064
+ }
2065
+ function isOnLastLine(host, point) {
2066
+ return onEdgeLine(host, point, true);
2067
+ }
2068
+ function adjacentLineMiddle(host, caret, direction) {
2069
+ const range = host.ownerDocument.createRange();
2070
+ range.selectNodeContents(host);
2071
+ let nearest2 = null;
2072
+ for (const rect of Array.from(range.getClientRects())) {
2073
+ const middle = rect.top + rect.height / 2;
2074
+ const beyond = direction > 0 ? middle > caret.bottom : middle < caret.top;
2075
+ const closer = nearest2 === null || (direction > 0 ? middle < nearest2 : middle > nearest2);
2076
+ if (rect.height > 0 && beyond && closer) nearest2 = middle;
2077
+ }
2078
+ return nearest2;
2079
+ }
2080
+
2081
+ // src/view/navigation/stay-on-line.ts
2082
+ var onLine = (rect, y) => rect === null || rect.top <= y && y <= rect.bottom;
2083
+ function stayOnLine(pos, y, rectAt) {
2084
+ if (pos.offset === 0 || onLine(rectAt(pos), y)) return pos;
2085
+ const before = { ...pos, offset: pos.offset - 1 };
2086
+ return onLine(rectAt(before), y) ? before : pos;
2087
+ }
2088
+
2089
+ // src/view/navigation/vertical.ts
2090
+ function lineInView(host, last) {
2091
+ const line = lineEdgeRect(host, last);
2092
+ const view = host.ownerDocument.defaultView;
2093
+ if (!line || !view || line.top >= 0 && line.bottom <= view.innerHeight) return line;
2094
+ host.scrollIntoView({ block: "nearest" });
2095
+ return lineEdgeRect(host, last);
2096
+ }
2097
+ function caretAtGoal(controller, host, at, y) {
2098
+ const { goalX } = controller;
2099
+ const view = host.ownerDocument.defaultView;
2100
+ if (goalX === null || !view || y < 0 || y > view.innerHeight) return null;
2101
+ const pos = caretInHost(controller.registry, host, host.getBoundingClientRect(), at, goalX, y);
2102
+ if (!pos) return null;
2103
+ return stayOnLine(pos, y, (p) => {
2104
+ const dom = posToDom(controller.registry, p);
2105
+ return dom && caretClientRect(dom);
2106
+ });
2107
+ }
2108
+ function landAtGoal(controller, block, direction) {
2109
+ const { schema } = controller.editor.state;
2110
+ const region = _chunkPZ4GT5JOcjs.isTextFlow.call(void 0, schema, block) ? _chunkPZ4GT5JOcjs.primaryRegion.call(void 0, schema, block) : null;
2111
+ const host = region === null ? void 0 : controller.registry.get(block.id, region);
2112
+ if (!host || region === null || controller.goalX === null) return null;
2113
+ const line = lineInView(host, direction < 0);
2114
+ const pos = line && caretAtGoal(controller, host, { block: block.id, region }, line.top + line.height / 2);
2115
+ return pos ? _chunkPZ4GT5JOcjs.textSelection.call(void 0, pos) : null;
2116
+ }
2117
+ function moveInHost(controller, host, head, caret, direction) {
2118
+ const { goalX } = controller;
2119
+ if (goalX === null || !caret || Math.abs(caret.left - goalX) < caret.height / 2) return null;
2120
+ const y = adjacentLineMiddle(host, caret, direction);
2121
+ const pos = y === null ? null : caretAtGoal(controller, host, head, y);
2122
+ return pos && pos.offset !== head.offset ? pos : null;
2123
+ }
2124
+ function landing(controller, { head }, direction, extend) {
2125
+ const { registry } = controller;
2126
+ const host = registry.get(head.block, head.region);
2127
+ const point = posToDom(registry, head);
2128
+ if (!host || !point) return null;
2129
+ const caret = caretClientRect(point);
2130
+ controller.goalX ??= _nullishCoalesce(_optionalChain([caret, 'optionalAccess', _147 => _147.left]), () => ( null));
2131
+ if (!(direction < 0 ? isOnFirstLine(host, point) : isOnLastLine(host, point))) {
2132
+ const pos = moveInHost(controller, host, head, caret, direction);
2133
+ return pos && _chunkPZ4GT5JOcjs.textSelection.call(void 0, pos);
2134
+ }
2135
+ const own2 = extend ? null : ownNode(controller, head.block, direction);
2136
+ if (own2 !== null) return own2;
2137
+ const accept = extend ? holdsTextHead(controller) : void 0;
2138
+ const target = neighbourBlock(controller.editor.state.doc, head.block, direction, accept);
2139
+ return target && (_nullishCoalesce(landAtGoal(controller, target, direction), () => ( entryEdge(controller, target.id, direction))));
2140
+ }
2141
+ function moveText(controller, selection, direction, extend) {
2142
+ const next = landing(controller, selection, direction, extend);
2143
+ if (next === null) {
2144
+ controller.nativeVerticalMove = true;
2145
+ return false;
2146
+ }
2147
+ const moved = extend && next.kind === "text" ? _chunkPZ4GT5JOcjs.textSelection.call(void 0, selection.anchor, next.head) : next;
2148
+ dispatchSelection(controller, moved);
2149
+ return true;
2150
+ }
2151
+ function extendBlocks(controller, { anchor, head }, direction) {
2152
+ const next = neighbourBlock(controller.editor.state.doc, head, direction);
2153
+ if (next === null) return false;
2154
+ dispatchSelection(controller, _chunkPZ4GT5JOcjs.blockRangeSelection.call(void 0, anchor, next.id));
2155
+ return true;
2156
+ }
2157
+ function moveVertically(controller, direction, extend) {
2158
+ const { selection } = controller.editor.state;
2159
+ if (_optionalChain([selection, 'optionalAccess', _148 => _148.kind]) === "node") return !extend && moveToNeighbour(controller, selection.block, direction);
2160
+ if (_optionalChain([selection, 'optionalAccess', _149 => _149.kind]) === "blocks") return extend && extendBlocks(controller, selection, direction);
2161
+ if (_optionalChain([selection, 'optionalAccess', _150 => _150.kind]) !== "text") return false;
2162
+ if (!isCrossSelection(selection)) return moveText(controller, selection, direction, extend);
2163
+ controller.goalX = null;
2164
+ return (extend ? crossHeadVertically : collapseCross)(controller, selection, direction);
2165
+ }
2166
+
2167
+ // src/view/input/flip-block.ts
2168
+ var isTask = ({ type, attrs }) => type === "MARKDOWN" && attrs.kind === "task";
2169
+ function flipCommand(block) {
2170
+ if (isTask(block)) return _chunkPZ4GT5JOcjs.toggleTask;
2171
+ return block.type === "TOGGLE" ? _chunkPZ4GT5JOcjs.setAttrs.call(void 0, { open: block.attrs.open !== true }) : null;
2172
+ }
2173
+ var toggleState = (open) => open ? "expanded" : "collapsed";
2174
+ function stateName(block) {
2175
+ if (isTask(block)) return block.attrs.checked === true ? "checked" : "unchecked";
2176
+ return toggleState(block.attrs.open === true);
2177
+ }
2178
+ function blockHoldingCaret({ doc, selection }) {
2179
+ if (_optionalChain([selection, 'optionalAccess', _151 => _151.kind]) !== "text" || selection.anchor.block !== selection.head.block) return null;
2180
+ return _nullishCoalesce(_chunkND5XJVK2cjs.blockById.call(void 0, doc, selection.head.block), () => ( null));
2181
+ }
2182
+ function flipForReader(controller, block) {
2183
+ if (_optionalChain([block, 'optionalAccess', _152 => _152.type]) !== "TOGGLE") return false;
2184
+ const toggles = readerToggles(controller);
2185
+ toggles.flip(block);
2186
+ controller.announce(toggleState(shownOpen(block, toggles.choices)));
2187
+ return true;
2188
+ }
2189
+ function flipSelectedBlock(controller) {
2190
+ const { editor } = controller;
2191
+ const block = blockHoldingCaret(editor.state);
2192
+ if (controller.readOnly) return flipForReader(controller, block);
2193
+ const command = block && flipCommand(block);
2194
+ if (!block || !command || !controller.run(command)) return false;
2195
+ const after = _chunkND5XJVK2cjs.blockById.call(void 0, editor.state.doc, block.id);
2196
+ if (after) controller.announce(stateName(after));
2197
+ return true;
2198
+ }
2199
+
2200
+ // src/view/input/keydown.ts
2201
+ var ARROWS = {
2202
+ ArrowUp: [moveVertically, -1],
2203
+ ArrowDown: [moveVertically, 1],
2204
+ ArrowLeft: [moveHorizontally, -1],
2205
+ ArrowRight: [moveHorizontally, 1]
2206
+ };
2207
+ function isComposingKey(controller, event) {
2208
+ return event.isComposing || event.keyCode === 229 || controller.composingKey !== null;
2209
+ }
2210
+ var runKeymap = (controller, event) => controller.edit(() => controller.editor.handleKey(event));
2211
+ function arrow(controller, event) {
2212
+ if (!Object.hasOwn(ARROWS, event.key) || event.ctrlKey || event.metaKey) return false;
2213
+ const [move, direction] = ARROWS[event.key];
2214
+ return move(controller, direction, event.shiftKey);
2215
+ }
2216
+ function tab(controller, event) {
2217
+ const { selection } = controller.editor.state;
2218
+ if (selection === null || selection.kind === "blocks") return false;
2219
+ runKeymap(controller, event);
2220
+ return true;
2221
+ }
2222
+ function typeOverNode(controller, event) {
2223
+ const { key } = event;
2224
+ const node = _optionalChain([controller, 'access', _153 => _153.editor, 'access', _154 => _154.state, 'access', _155 => _155.selection, 'optionalAccess', _156 => _156.kind]) === "node";
2225
+ if (!node || event.ctrlKey || event.metaKey || [...key].length !== 1) return false;
2226
+ return controller.edit(() => controller.editor.insertText(key));
2227
+ }
2228
+ function holdHistoryKeys(controller, event) {
2229
+ const kind = _optionalChain([controller, 'access', _157 => _157.editor, 'access', _158 => _158.state, 'access', _159 => _159.selection, 'optionalAccess', _160 => _160.kind]);
2230
+ if (kind !== "node" && kind !== "blocks") return false;
2231
+ return Object.hasOwn(ARROWS, event.key) && (event.metaKey || event.ctrlKey || event.altKey);
2232
+ }
2233
+ function flip(controller, event) {
2234
+ const mod = controller.isMac ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
2235
+ if (event.key !== "Enter" || !mod || event.shiftKey || event.altKey) return false;
2236
+ flipSelectedBlock(controller);
2237
+ return true;
2238
+ }
2239
+ function handle(controller, event) {
2240
+ if (arrow(controller, event)) return true;
2241
+ if (event.key === "Tab" && !event.ctrlKey && !event.metaKey && !event.altKey) return tab(controller, event);
2242
+ return runKeymap(controller, event) || flip(controller, event) || typeOverNode(controller, event) || holdHistoryKeys(controller, event);
2243
+ }
2244
+ function handleReaderKeydown(controller, event) {
2245
+ if (flip(controller, event)) event.preventDefault();
2246
+ }
2247
+ function handleKeydown(controller, event) {
2248
+ if (isComposingKey(controller, event)) return;
2249
+ controller.nativeVerticalMove = false;
2250
+ for (const intercept of controller.keyInterceptors) if (intercept(event)) return;
2251
+ if (handle(controller, event)) event.preventDefault();
2252
+ }
2253
+
2254
+ // src/view/input/attach-input.ts
2255
+ function attachInput(controller, root) {
2256
+ const composition = createCompositionHandlers(controller);
2257
+ const sinkComposition = createSinkComposition(controller);
2258
+ const fallback = createDomInputFallback(controller);
2259
+ const observed = /* @__PURE__ */ new Map();
2260
+ const inHost = (event) => controller.registry.locate(event.target) !== null;
2261
+ const inSink = (event) => controller.sink !== null && event.target === controller.sink;
2262
+ const inEmptyHost = (event) => controller.emptyHost !== null && event.target === controller.emptyHost;
2263
+ const catchUp = () => {
2264
+ fallback.flush();
2265
+ controller.readSelectionFromDom();
2266
+ };
2267
+ const onBeforeInput = (event) => {
2268
+ if (isComposingInput(controller, event) || controller.readOnly) return;
2269
+ if (!inHost(event) && !inSink(event) && !inEmptyHost(event)) return;
2270
+ catchUp();
2271
+ handleBeforeInput(controller, event);
2272
+ };
2273
+ const onKeyDown = (event) => {
2274
+ if (isComposingKey(controller, event) || !controller.isEditorElement(event.target)) return;
2275
+ catchUp();
2276
+ if (controller.readOnly) handleReaderKeydown(controller, event);
2277
+ else handleKeydown(controller, event);
2278
+ };
2279
+ const onInput = (event) => fallback.onInput(event);
2280
+ const onCompositionStart = (event) => {
2281
+ if (inHost(event) || inEmptyHost(event)) composition.start(event);
2282
+ else if (inSink(event)) sinkComposition.start();
2283
+ };
2284
+ const onCompositionEnd = (event) => {
2285
+ if (inSink(event)) sinkComposition.end();
2286
+ else composition.end(event);
2287
+ };
2288
+ const detachClipboard = attachClipboard(controller, root, catchUp);
2289
+ root.addEventListener("keydown", onKeyDown);
2290
+ root.addEventListener("beforeinput", onBeforeInput);
2291
+ root.addEventListener("input", onInput);
2292
+ root.addEventListener("compositionstart", onCompositionStart);
2293
+ root.addEventListener("compositionend", onCompositionEnd);
2294
+ function observe(host) {
2295
+ if (!observed.has(host)) observed.set(host, fallback.observe(host));
2296
+ }
2297
+ for (const { element } of controller.registry.all()) observe(element);
2298
+ return {
2299
+ observe,
2300
+ unobserve(host) {
2301
+ _optionalChain([observed, 'access', _161 => _161.get, 'call', _162 => _162(host), 'optionalCall', _163 => _163()]);
2302
+ observed.delete(host);
2303
+ },
2304
+ drain: fallback.drain,
2305
+ detach() {
2306
+ composition.release();
2307
+ sinkComposition.release();
2308
+ detachClipboard();
2309
+ root.removeEventListener("keydown", onKeyDown);
2310
+ root.removeEventListener("beforeinput", onBeforeInput);
2311
+ root.removeEventListener("input", onInput);
2312
+ root.removeEventListener("compositionstart", onCompositionStart);
2313
+ root.removeEventListener("compositionend", onCompositionEnd);
2314
+ for (const stop of observed.values()) stop();
2315
+ observed.clear();
2316
+ }
2317
+ };
2318
+ }
2319
+
2320
+ // src/view/controller/focus-return.ts
2321
+ function watchFocusReturn(root, handlers) {
2322
+ let pressed = false;
2323
+ const onPointerDown = () => {
2324
+ pressed = true;
2325
+ handlers.pressed();
2326
+ };
2327
+ const onFocusIn = (event) => {
2328
+ const byPress = pressed;
2329
+ pressed = false;
2330
+ if (!byPress) handlers.returned(event.target);
2331
+ };
2332
+ const onFocusOut = (event) => {
2333
+ if (!root.contains(event.relatedTarget)) pressed = false;
2334
+ };
2335
+ root.addEventListener("pointerdown", onPointerDown, true);
2336
+ root.addEventListener("focusin", onFocusIn);
2337
+ root.addEventListener("focusout", onFocusOut);
2338
+ return () => {
2339
+ root.removeEventListener("pointerdown", onPointerDown, true);
2340
+ root.removeEventListener("focusin", onFocusIn);
2341
+ root.removeEventListener("focusout", onFocusOut);
2342
+ };
2343
+ }
2344
+
2345
+ // src/view/controller/selection-sync.ts
2346
+ var selectionPoints = (selection) => [
2347
+ _nullishCoalesce(_optionalChain([selection, 'optionalAccess', _164 => _164.anchorNode]), () => ( null)),
2348
+ _nullishCoalesce(_optionalChain([selection, 'optionalAccess', _165 => _165.anchorOffset]), () => ( 0)),
2349
+ _nullishCoalesce(_optionalChain([selection, 'optionalAccess', _166 => _166.focusNode]), () => ( null)),
2350
+ _nullishCoalesce(_optionalChain([selection, 'optionalAccess', _167 => _167.focusOffset]), () => ( 0))
2351
+ ];
2352
+ var samePoints = (a, b) => a.every((point, index) => point === b[index]);
2353
+ function createSelectionSync(doc, onChange) {
2354
+ let written = null;
2355
+ let expiry = null;
2356
+ function release() {
2357
+ written = null;
2358
+ if (expiry !== null) clearTimeout(expiry);
2359
+ expiry = null;
2360
+ }
2361
+ function handle2() {
2362
+ const suppressed = written;
2363
+ release();
2364
+ if (suppressed === null || !samePoints(suppressed, selectionPoints(doc.getSelection()))) onChange();
2365
+ }
2366
+ return {
2367
+ start: () => doc.addEventListener("selectionchange", handle2),
2368
+ stop() {
2369
+ doc.removeEventListener("selectionchange", handle2);
2370
+ release();
2371
+ },
2372
+ suppressNext() {
2373
+ written = selectionPoints(doc.getSelection());
2374
+ expiry ??= setTimeout(release, 0);
2375
+ }
2376
+ };
2377
+ }
2378
+
2379
+ // src/view/controller/root-binding.ts
2380
+ function bindRoot(controller, root, cross, committed) {
2381
+ const selectionSync = createSelectionSync(root.ownerDocument, () => controller.readSelectionFromDom());
2382
+ const input = attachInput(controller, root);
2383
+ const unsubscribe = controller.editor.subscribe(committed);
2384
+ const detachCross = cross.attach(root);
2385
+ const stopFocusReturn = watchFocusReturn(root, {
2386
+ pressed: () => controller.nativeVerticalMove = false,
2387
+ returned: (target) => {
2388
+ if (controller.isEditorElement(target)) controller.syncSelectionToDom();
2389
+ }
2390
+ });
2391
+ selectionSync.start();
2392
+ return {
2393
+ root,
2394
+ input,
2395
+ selectionSync,
2396
+ detach() {
2397
+ selectionSync.stop();
2398
+ input.detach();
2399
+ unsubscribe();
2400
+ detachCross();
2401
+ stopFocusReturn();
2402
+ }
2403
+ };
2404
+ }
2405
+
2406
+ // src/view/controller/selection-io.ts
2407
+ function movedSelection(controller, doc, model) {
2408
+ if (controller.composingKey !== null || controller.dragging || !doc || !ownsFocus(controller, doc)) return null;
2409
+ const selection = readSelection(controller.registry, doc.getSelection(), (node) => controller.blockAt(node));
2410
+ return selection === null || _chunkPZ4GT5JOcjs.sameSelection.call(void 0, selection, model) ? null : selection;
2411
+ }
2412
+ function writeModelSelection(controller, doc, moved) {
2413
+ const domSelection = _optionalChain([doc, 'optionalAccess', _168 => _168.getSelection, 'call', _169 => _169()]);
2414
+ if (focusingSink(controller) || controller.composingKey !== null || !doc || !domSelection || !ownsFocus(controller, doc)) {
2415
+ return;
2416
+ }
2417
+ const before = selectionPoints(domSelection);
2418
+ const { selection } = controller.editor.state;
2419
+ if (heldBySink(selection) && controller.sink !== null) focusSink(controller);
2420
+ else if (_optionalChain([selection, 'optionalAccess', _170 => _170.kind]) === "blocks") domSelection.removeAllRanges();
2421
+ else writeSelection(controller.registry, selection, domSelection, (id) => controller.blockElement(id));
2422
+ controller.nodeReveal.show(selection);
2423
+ if (!samePoints(before, selectionPoints(domSelection))) moved();
2424
+ }
2425
+
2426
+ // src/view/controller/view-controller.ts
2427
+ var runNow = (fn) => fn();
2428
+ var ViewController = (_class = class {
2429
+
2430
+ __init() {this.registry = createHostRegistry((element) => {
2431
+ _optionalChain([this, 'access', _179 => _179.binding, 'optionalAccess', _180 => _180.input, 'access', _181 => _181.observe, 'call', _182 => _182(element)]);
2432
+ this.drawn.syncTabStop();
2433
+ return () => {
2434
+ _optionalChain([this, 'access', _183 => _183.binding, 'optionalAccess', _184 => _184.input, 'access', _185 => _185.unobserve, 'call', _186 => _186(element)]);
2435
+ this.drawn.syncTabStop();
2436
+ };
2437
+ })}
2438
+ __init2() {this.keyInterceptors = /* @__PURE__ */ new Set()}
2439
+ __init3() {this.nodeReveal = createNodeReveal((id) => this.blockElement(id))}
2440
+ __init4() {this.announcer = createAnnouncer(() => this.said)}
2441
+
2442
+
2443
+ __init5() {this.goalX = null}
2444
+ __init6() {this.nativeVerticalMove = false}
2445
+ __init7() {this.dragging = false}
2446
+ __init8() {this.composingKey = null}
2447
+
2448
+
2449
+ __init9() {this.drawn = createDrawnElements(this.registry, () => this.editor.state)}
2450
+ __init10() {this.cross = createCrossMode(this)}
2451
+ __init11() {this.outside = createOutsideChanges(
2452
+ () => hasFocus(this, this.ownerDocument()),
2453
+ (model) => movedSelection(this, this.ownerDocument(), model)
2454
+ )}
2455
+ __init12() {this.binding = null}
2456
+ __init13() {this.quiet = false}
2457
+
2458
+ __init14() {this.detachRoot = null}
2459
+
2460
+ constructor(options) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);_class.prototype.__init11.call(this);_class.prototype.__init12.call(this);_class.prototype.__init13.call(this);_class.prototype.__init14.call(this);
2461
+ this.editor = options.editor;
2462
+ options.editor.subscribe((state, tr) => {
2463
+ if (_optionalChain([tr, 'optionalAccess', _187 => _187.origin]) !== "remote") this.said = state;
2464
+ });
2465
+ this.flush = _nullishCoalesce(options.flush, () => ( runNow));
2466
+ this.onFiles = options.onFiles;
2467
+ this.isMac = _nullishCoalesce(options.isMac, () => ( _chunkPZ4GT5JOcjs.detectMac.call(void 0, )));
2468
+ const decorationsOf = (block, region) => this.decorationsFor(block, region);
2469
+ this.hosts = createHostRenderer(options.editor, options.render, decorationsOf, (host) => _optionalChain([this, 'access', _188 => _188.binding, 'optionalAccess', _189 => _189.input, 'access', _190 => _190.drain, 'call', _191 => _191(host)]));
2470
+ this.readOnlyState = createReadOnly(this.registry, _nullishCoalesce(options.readOnly, () => ( false)));
2471
+ }
2472
+ get root() {
2473
+ return _nullishCoalesce(_optionalChain([this, 'access', _192 => _192.binding, 'optionalAccess', _193 => _193.root]), () => ( null));
2474
+ }
2475
+ get readOnly() {
2476
+ return this.readOnlyState.current();
2477
+ }
2478
+ get sink() {
2479
+ return this.drawn.sink;
2480
+ }
2481
+ get emptyHost() {
2482
+ return this.drawn.emptyHost;
2483
+ }
2484
+ attach(root) {
2485
+ _optionalChain([this, 'access', _194 => _194.detachRoot, 'optionalCall', _195 => _195()]);
2486
+ const binding = bindRoot(this, root, this.cross, (_, tr) => {
2487
+ this.quiet ||= _optionalChain([tr, 'optionalAccess', _196 => _196.origin]) === "remote";
2488
+ this.outside.committed(tr);
2489
+ this.nodeReveal.follow(this.editor.state.selection);
2490
+ this.drawn.syncTabStop();
2491
+ this.cross.syncLater();
2492
+ });
2493
+ const unmount = this.editor.mount();
2494
+ const detach = () => {
2495
+ unmount();
2496
+ binding.detach();
2497
+ if (this.detachRoot !== detach) return;
2498
+ this.detachRoot = null;
2499
+ this.binding = null;
2500
+ };
2501
+ this.binding = binding;
2502
+ this.detachRoot = detach;
2503
+ this.drawn.syncTabStop();
2504
+ return detach;
2505
+ }
2506
+ registerBlockElement(id, element) {
2507
+ return this.drawn.registerBlock(id, element);
2508
+ }
2509
+ blockElement(id) {
2510
+ return this.drawn.block(id);
2511
+ }
2512
+ blockAt(node) {
2513
+ return this.drawn.blockAt(node);
2514
+ }
2515
+ registerSink(element) {
2516
+ return this.drawn.registerSink(element);
2517
+ }
2518
+ // An empty document draws one host that stands for a paragraph not created yet, so it belongs to no block.
2519
+ registerEmptyHost(element) {
2520
+ return this.drawn.registerEmptyHost(element);
2521
+ }
2522
+ isEditorElement(element) {
2523
+ return this.drawn.isEditorElement(element);
2524
+ }
2525
+ decorationsFor(block, region) {
2526
+ return hostDecorations(this.editor, block, region, this.cross.mode);
2527
+ }
2528
+ dispatch(tr) {
2529
+ this.edit(() => {
2530
+ this.editor.dispatch(tr);
2531
+ return true;
2532
+ });
2533
+ }
2534
+ // Runs editor calls that dispatch on their own (insertText, handleKey, chains), then brings the hosts, the focus and
2535
+ // the DOM selection up to date once. Focus is followed only while it is still on the editor or nowhere: a change can
2536
+ // open a popover that takes it.
2537
+ edit(change) {
2538
+ const before = this.editor.state;
2539
+ const doc = this.ownerDocument();
2540
+ const focused = hasFocus(this, doc);
2541
+ let handled = false;
2542
+ this.flush(
2543
+ () => this.outside.inside(() => {
2544
+ handled = change();
2545
+ })
2546
+ );
2547
+ if (this.editor.state === before) return handled;
2548
+ if (this.editor.state.doc !== before.doc) this.goalX = null;
2549
+ this.refreshHosts();
2550
+ this.cross.sync();
2551
+ if (focused && doc !== null && ownsFocus(this, doc)) this.keepFocus();
2552
+ this.syncSelectionToDom();
2553
+ return handled;
2554
+ }
2555
+ // A change made outside edit() reaches the hosts when React draws them, so the selection is written back once that
2556
+ // has happened, and focus follows a host the change replaced. A collaborator's change scrolls nothing doing so.
2557
+ afterRender() {
2558
+ const { quiet } = this;
2559
+ this.quiet = false;
2560
+ const doc = this.ownerDocument();
2561
+ const moved = this.outside.takeMoved();
2562
+ if (!this.outside.takeFocused() || !doc || !ownsFocus(this, doc)) return;
2563
+ if (moved) this.selectFromDom(moved);
2564
+ this.cross.sync();
2565
+ const follow = () => {
2566
+ this.keepFocus(quiet);
2567
+ this.syncSelectionToDom();
2568
+ };
2569
+ if (!quiet) return follow();
2570
+ this.nodeReveal.settle(this.editor.state.selection);
2571
+ inPlace(this.root, follow);
2572
+ }
2573
+ refreshHosts() {
2574
+ for (const host of this.registry.all()) {
2575
+ if (!this.isComposing(host)) this.hosts.renderIfChanged(host);
2576
+ }
2577
+ }
2578
+ run(command) {
2579
+ return this.edit(() => command(this.editor.state, this.editor.dispatch));
2580
+ }
2581
+ announce(message) {
2582
+ this.announcer.announce(message);
2583
+ }
2584
+ // A redraw of the decorations alone, such as one that follows a selection the browser made, replaces the nodes that
2585
+ // selection sits in, and nothing else writes it back. It is written only while the editor holds focus: written while
2586
+ // focus is on the page, it would pull focus into the line.
2587
+ renderHost(block, region) {
2588
+ const element = this.registry.get(block, region);
2589
+ const host = element && { block, region, element };
2590
+ if (!host || this.isComposing(host) || !this.hosts.render(host)) return;
2591
+ const inSelection = element.contains(_nullishCoalesce(_optionalChain([this, 'access', _197 => _197.domSelection, 'call', _198 => _198(), 'optionalAccess', _199 => _199.focusNode]), () => ( null)));
2592
+ if (inSelection && hasFocus(this, element.ownerDocument)) this.syncSelectionToDom();
2593
+ }
2594
+ domSelection() {
2595
+ return _nullishCoalesce(_optionalChain([this, 'access', _200 => _200.ownerDocument, 'call', _201 => _201(), 'optionalAccess', _202 => _202.getSelection, 'call', _203 => _203()]), () => ( null));
2596
+ }
2597
+ syncSelectionToDom() {
2598
+ writeModelSelection(this, this.ownerDocument(), () => _optionalChain([this, 'access', _204 => _204.binding, 'optionalAccess', _205 => _205.selectionSync, 'access', _206 => _206.suppressNext, 'call', _207 => _207()]));
2599
+ }
2600
+ // A pointer drag across hosts sets the model selection itself, while the browser keeps extending its own. Leaving
2601
+ // cross mode re-renders the hosts that drew it as decorations, which can remove the node holding the caret, so the
2602
+ // selection is written back.
2603
+ readSelectionFromDom() {
2604
+ const selection = movedSelection(this, this.ownerDocument(), this.editor.state.selection);
2605
+ if (selection === null) return;
2606
+ const wasCross = this.cross.active;
2607
+ if (!this.nativeVerticalMove) this.goalX = null;
2608
+ this.flush(() => this.selectFromDom(selection));
2609
+ this.cross.sync();
2610
+ if (!wasCross && !this.cross.active) return;
2611
+ this.keepFocus();
2612
+ this.syncSelectionToDom();
2613
+ }
2614
+ focus(target) {
2615
+ const { editor } = this;
2616
+ const selection = focusTarget(editor.state, target);
2617
+ if (selection !== null && !_chunkPZ4GT5JOcjs.sameSelection.call(void 0, selection, editor.state.selection)) dispatchSelection(this, selection);
2618
+ this.takeFocus();
2619
+ this.syncSelectionToDom();
2620
+ }
2621
+ setReadOnly(readOnly) {
2622
+ this.readOnlyState.set(readOnly);
2623
+ }
2624
+ subscribeReadOnly(listener) {
2625
+ return this.readOnlyState.subscribe(listener);
2626
+ }
2627
+ destroy() {
2628
+ _optionalChain([this, 'access', _208 => _208.detachRoot, 'optionalCall', _209 => _209()]);
2629
+ }
2630
+ selectFromDom(selection) {
2631
+ const { editor } = this;
2632
+ this.outside.inside(() => editor.dispatch(editor.createTransaction().setSelection(selection).setOrigin("input")));
2633
+ }
2634
+ isComposing(host) {
2635
+ return this.composingKey !== null && hostKey(host.block, host.region) === this.composingKey;
2636
+ }
2637
+ ownerDocument() {
2638
+ return _nullishCoalesce(_optionalChain([(_nullishCoalesce(_optionalChain([this, 'access', _210 => _210.binding, 'optionalAccess', _211 => _211.root]), () => ( _optionalChain([this, 'access', _212 => _212.registry, 'access', _213 => _213.all, 'call', _214 => _214(), 'access', _215 => _215.next, 'call', _216 => _216(), 'access', _217 => _217.value, 'optionalAccess', _218 => _218.element])))), 'optionalAccess', _219 => _219.ownerDocument]), () => ( null));
2639
+ }
2640
+ // React replaces a host whose block changes kind, which drops focus, and Firefox does not move focus into another
2641
+ // host when a script selects inside it.
2642
+ keepFocus(preventScroll) {
2643
+ if (this.composingKey === null) this.takeFocus(preventScroll);
2644
+ }
2645
+ takeFocus(preventScroll) {
2646
+ const element = this.drawn.focusElement();
2647
+ if (!element || element.ownerDocument.activeElement === element) return;
2648
+ if (element === this.sink) focusSink(this);
2649
+ else element.focus({ preventScroll });
2650
+ }
2651
+ }, _class);
2652
+
2653
+ // src/view/react/editor-context.ts
2654
+
2655
+ var QuoinEditorContext = _react.createContext.call(void 0, null);
2656
+ function useViewController() {
2657
+ const controller = _react.useContext.call(void 0, QuoinEditorContext);
2658
+ if (controller === null) throw new Error("useViewController must be called inside an EditorView");
2659
+ return controller;
2660
+ }
2661
+
2662
+ // src/view/react/use-quoin-editor.ts
2663
+
2664
+ var _reactdom = require('react-dom');
2665
+
2666
+ // src/view/react/use-isomorphic-layout-effect.ts
2667
+
2668
+ var useIsomorphicLayoutEffect = typeof document === "undefined" ? _react.useEffect : _react.useLayoutEffect;
2669
+
2670
+ // src/view/react/use-quoin-editor.ts
2671
+ function useQuoinEditor(options) {
2672
+ const { blocks, readOnly = false } = options;
2673
+ const callbacks = _react.useRef.call(void 0, options);
2674
+ const [controller] = _react.useState.call(void 0, () => {
2675
+ const editor = _chunkPZ4GT5JOcjs.createEditor.call(void 0, {
2676
+ blocks,
2677
+ extensions: options.extensions,
2678
+ onChange: (next) => callbacks.current.onChange(next),
2679
+ onTransaction: (tr, state) => _optionalChain([callbacks, 'access', _220 => _220.current, 'access', _221 => _221.onTransaction, 'optionalCall', _222 => _222(tr, state)])
2680
+ });
2681
+ return new ViewController({
2682
+ editor,
2683
+ flush: _reactdom.flushSync,
2684
+ render: options.render,
2685
+ readOnly,
2686
+ onFiles: (files, at) => _optionalChain([callbacks, 'access', _223 => _223.current, 'access', _224 => _224.onFiles, 'optionalCall', _225 => _225(files, at)])
2687
+ });
2688
+ });
2689
+ useIsomorphicLayoutEffect(() => {
2690
+ callbacks.current = options;
2691
+ });
2692
+ useIsomorphicLayoutEffect(() => {
2693
+ controller.editor.setBlocks(blocks);
2694
+ }, [controller, blocks]);
2695
+ useIsomorphicLayoutEffect(() => {
2696
+ controller.setReadOnly(readOnly);
2697
+ }, [controller, readOnly]);
2698
+ _react.useEffect.call(void 0, () => () => controller.destroy(), [controller]);
2699
+ return controller;
2700
+ }
2701
+
2702
+ // src/view/react/SelectionSink.tsx
2703
+
2704
+
2705
+ // src/view/react/use-read-only.ts
2706
+
2707
+ function useReadOnly(controller) {
2708
+ const subscribe = _react.useCallback.call(void 0, (notify) => controller.subscribeReadOnly(notify), [controller]);
2709
+ const read = _react.useCallback.call(void 0, () => controller.readOnly, [controller]);
2710
+ return _react.useSyncExternalStore.call(void 0, subscribe, read, read);
2711
+ }
2712
+
2713
+ // src/view/react/SelectionSink.tsx
2714
+ var _jsxruntime = require('react/jsx-runtime');
2715
+ function SelectionSink({ label = "Selected content", blockLabel }) {
2716
+ const controller = useViewController();
2717
+ const ref = _react.useRef.call(void 0, null);
2718
+ const readOnly = useReadOnly(controller);
2719
+ const name = useEditorState(
2720
+ controller,
2721
+ _react.useCallback.call(void 0, (state) => selectedBlockName(state, blockLabel), [blockLabel])
2722
+ );
2723
+ useIsomorphicLayoutEffect(() => {
2724
+ const sink = ref.current;
2725
+ return sink === null ? void 0 : controller.registerSink(sink);
2726
+ }, [controller]);
2727
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2728
+ "div",
2729
+ {
2730
+ ref,
2731
+ contentEditable: !readOnly,
2732
+ suppressContentEditableWarning: true,
2733
+ role: "textbox",
2734
+ "aria-label": name || label,
2735
+ inputMode: name === null ? void 0 : "none",
2736
+ "aria-multiline": name === null ? "true" : void 0,
2737
+ "aria-readonly": readOnly ? "true" : void 0,
2738
+ tabIndex: -1,
2739
+ spellCheck: false,
2740
+ "data-quoin-sink": "",
2741
+ onFocus: () => {
2742
+ if (heldBySink(controller.editor.state.selection)) focusSink(controller);
2743
+ }
2744
+ }
2745
+ );
2746
+ }
2747
+
2748
+ // src/view/react/EditableRegion.tsx
2749
+
2750
+
2751
+ // src/view/react/block-placeholder.ts
2752
+
2753
+ var headingPlaceholder = (_state, { type, attrs }) => type === "MARKDOWN" && attrs.kind === "heading" ? `Heading ${_chunkND5XJVK2cjs.headingLevel.call(void 0, attrs.level)}` : void 0;
2754
+ var BlockPlaceholderContext = _react.createContext.call(void 0, headingPlaceholder);
2755
+ function regionPlaceholder(state, blockId, region, placeholderOf) {
2756
+ const block = _chunkND5XJVK2cjs.blockById.call(void 0, state.doc, blockId);
2757
+ return block !== void 0 && _chunkPZ4GT5JOcjs.primaryRegion.call(void 0, state.schema, block) === region ? placeholderOf(state, block) : void 0;
2758
+ }
2759
+
2760
+ // src/view/react/EditableRegion.tsx
2761
+
2762
+ var samePainted = (a, b) => a.inline === b.inline && a.placeholder === b.placeholder && sameDecorations(a.decorations, b.decorations);
2763
+ function EditableRegion({ block, region, label, id, placeholder, className }) {
2764
+ const controller = useViewController();
2765
+ const placeholderOf = _react.useContext.call(void 0, BlockPlaceholderContext);
2766
+ const ref = _react.useRef.call(void 0, null);
2767
+ const readOnly = useReadOnly(controller);
2768
+ const select = _react.useCallback.call(void 0,
2769
+ (state) => {
2770
+ const inline2 = regionInline(state, block, region);
2771
+ const empty = inline2 === void 0 || _chunkND5XJVK2cjs.inlineLength.call(void 0, inline2) === 0;
2772
+ return {
2773
+ inline: inline2,
2774
+ decorations: controller.decorationsFor(block, region),
2775
+ placeholder: empty ? _nullishCoalesce(placeholder, () => ( regionPlaceholder(state, block, region, placeholderOf))) : void 0
2776
+ };
2777
+ },
2778
+ [controller, block, region, placeholder, placeholderOf]
2779
+ );
2780
+ const { inline, decorations, placeholder: shown } = useEditorState(controller, select, samePainted);
2781
+ useIsomorphicLayoutEffect(() => {
2782
+ const host = ref.current;
2783
+ return host === null ? void 0 : controller.registry.register(block, region, host);
2784
+ }, [controller, block, region]);
2785
+ useIsomorphicLayoutEffect(() => {
2786
+ controller.renderHost(block, region);
2787
+ }, [controller, block, region, inline, decorations]);
2788
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2789
+ "div",
2790
+ {
2791
+ ref,
2792
+ id,
2793
+ className,
2794
+ contentEditable: !readOnly,
2795
+ suppressContentEditableWarning: true,
2796
+ tabIndex: -1,
2797
+ role: "textbox",
2798
+ "aria-multiline": "true",
2799
+ "aria-readonly": readOnly ? "true" : void 0,
2800
+ "aria-label": label,
2801
+ "data-quoin-host": "",
2802
+ "data-placeholder": readOnly ? void 0 : shown,
2803
+ "aria-placeholder": readOnly ? void 0 : shown,
2804
+ spellCheck: true
2805
+ }
2806
+ );
2807
+ }
2808
+
2809
+ // src/view/react/default-renderers.tsx
2810
+
2811
+
2812
+ // src/view/react/callout-block.tsx
2813
+
2814
+ var CalloutBlock = ({ block }) => {
2815
+ const tone = typeof block.attrs.tone === "string" && block.attrs.tone !== "" ? block.attrs.tone : "info";
2816
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "quoin-callout", "data-tone": tone, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditableRegion, { block: block.id, region: "body", label: `Callout, ${tone}` }) });
2817
+ };
2818
+
2819
+ // src/view/react/block-command.ts
2820
+ function onBlock(id, command) {
2821
+ return (state, dispatch) => {
2822
+ const { ok, tr } = _chunkPZ4GT5JOcjs.runCommands.call(void 0, state, [_chunkPZ4GT5JOcjs.setSelection.call(void 0, _chunkPZ4GT5JOcjs.blockRangeSelection.call(void 0, id)), command]);
2823
+ if (!ok || tr === null) return false;
2824
+ _optionalChain([dispatch, 'optionalCall', _226 => _226(tr.setSelection(state.selection))]);
2825
+ return true;
2826
+ };
2827
+ }
2828
+
2829
+ // src/view/react/prevent-mouse-focus.ts
2830
+ var keepFocus = (event) => event.preventDefault();
2831
+
2832
+ // src/view/react/TaskCheckbox.tsx
2833
+
2834
+ function TaskCheckbox({ block, controller, label = "Task", labelledBy }) {
2835
+ const checked = block.attrs.checked === true;
2836
+ const flip2 = flipCommand(block);
2837
+ const readOnly = useReadOnly(controller);
2838
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2839
+ "button",
2840
+ {
2841
+ type: "button",
2842
+ role: "checkbox",
2843
+ "aria-checked": checked,
2844
+ "aria-readonly": readOnly ? "true" : void 0,
2845
+ "aria-label": label,
2846
+ "aria-labelledby": labelledBy,
2847
+ className: "quoin-task-box",
2848
+ contentEditable: false,
2849
+ tabIndex: -1,
2850
+ onMouseDown: keepFocus,
2851
+ onClick: () => {
2852
+ if (flip2 && !controller.readOnly) controller.run(onBlock(block.id, flip2));
2853
+ },
2854
+ children: checked ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M3.5 8.5l3 3 6-7", fill: "none", stroke: "currentColor", strokeWidth: "2" }) }) : null
2855
+ }
2856
+ );
2857
+ }
2858
+
2859
+ // src/view/react/list-numbers.ts
2860
+ var isOrdered = (block) => block.type === "MARKDOWN" && block.attrs.kind === "ordered";
2861
+ function numberDoc(doc) {
2862
+ const numbers = doc.blocks.map(() => 1);
2863
+ const runs = [];
2864
+ doc.blocks.forEach((block, index) => {
2865
+ while (runs.length > 0 && runs[runs.length - 1].indent > block.indent) runs.pop();
2866
+ const run2 = runs[runs.length - 1];
2867
+ const sameLevel = run2 !== void 0 && run2.indent === block.indent;
2868
+ if (!isOrdered(block)) {
2869
+ if (sameLevel) runs.pop();
2870
+ return;
2871
+ }
2872
+ if (sameLevel) {
2873
+ numbers[index] = run2.next++;
2874
+ return;
2875
+ }
2876
+ const start = _chunkND5XJVK2cjs.orderedStart.call(void 0, block.attrs.start);
2877
+ numbers[index] = start;
2878
+ runs.push({ indent: block.indent, next: start + 1 });
2879
+ });
2880
+ return numbers;
2881
+ }
2882
+ var cache = /* @__PURE__ */ new WeakMap();
2883
+ function orderedNumber(doc, index) {
2884
+ let numbers = cache.get(doc);
2885
+ if (numbers === void 0) {
2886
+ numbers = numberDoc(doc);
2887
+ cache.set(doc, numbers);
2888
+ }
2889
+ return _nullishCoalesce(numbers[index], () => ( 1));
2890
+ }
2891
+
2892
+ // src/view/react/use-ordered-number.ts
2893
+ function useOrderedNumber(controller, blockId) {
2894
+ return useEditorState(controller, ({ doc }) => {
2895
+ const index = doc.index.get(blockId);
2896
+ return index === void 0 ? 1 : orderedNumber(doc, index);
2897
+ });
2898
+ }
2899
+
2900
+ // src/view/react/markdown-block.tsx
2901
+
2902
+ var REGION = "text";
2903
+ function TextRegion({ block, label }) {
2904
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditableRegion, { block: block.id, region: REGION, label });
2905
+ }
2906
+ function NumberedItem({ block, controller }) {
2907
+ const number = useOrderedNumber(controller, block.id);
2908
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "quoin-list-item", "data-list": "ordered", children: [
2909
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "quoin-marker", "aria-hidden": "true", children: `${number}.` }),
2910
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextRegion, { block, label: `Numbered list item ${number}` })
2911
+ ] });
2912
+ }
2913
+ var MarkdownBlock = (props) => {
2914
+ const { block } = props;
2915
+ switch (block.attrs.kind) {
2916
+ case "heading": {
2917
+ const level = _chunkND5XJVK2cjs.headingLevel.call(void 0, block.attrs.level);
2918
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { role: "heading", "aria-level": level, className: "quoin-heading", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextRegion, { block, label: `Heading ${level}` }) });
2919
+ }
2920
+ case "bullet":
2921
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "quoin-list-item", "data-list": "bullet", children: [
2922
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "quoin-marker", "aria-hidden": "true", children: "\u2022" }),
2923
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextRegion, { block, label: "Bullet list item" })
2924
+ ] });
2925
+ case "ordered":
2926
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, NumberedItem, { ...props });
2927
+ case "task":
2928
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "quoin-task", "data-checked": block.attrs.checked === true, children: [
2929
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TaskCheckbox, { block, controller: props.controller }),
2930
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextRegion, { block, label: "Task" })
2931
+ ] });
2932
+ case "quote":
2933
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "quoin-quote", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextRegion, { block, label: "Quote" }) });
2934
+ default:
2935
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TextRegion, { block, label: "Paragraph" });
2936
+ }
2937
+ };
2938
+
2939
+ // src/view/react/use-reader-choices.ts
2940
+
2941
+ function useReaderChoices(controller) {
2942
+ const store = readerToggles(controller);
2943
+ return _react.useSyncExternalStore.call(void 0, store.subscribe, () => store.choices, () => store.choices);
2944
+ }
2945
+ function useToggleOpen(controller, block) {
2946
+ return shownOpen(block, useReaderChoices(controller));
2947
+ }
2948
+
2949
+ // src/view/react/ToggleChevron.tsx
2950
+
2951
+ function ToggleChevron({ block, controller, label = "Toggle", controls }) {
2952
+ const open = useToggleOpen(controller, block);
2953
+ const readOnly = useReadOnly(controller);
2954
+ const flip2 = flipCommand(block);
2955
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2956
+ "button",
2957
+ {
2958
+ type: "button",
2959
+ "aria-expanded": open,
2960
+ "aria-controls": controls,
2961
+ "aria-label": label,
2962
+ className: "quoin-toggle-button",
2963
+ contentEditable: false,
2964
+ tabIndex: readOnly ? 0 : -1,
2965
+ onMouseDown: keepFocus,
2966
+ onClick: () => {
2967
+ if (controller.readOnly) readerToggles(controller).flip(block);
2968
+ else if (flip2) controller.run(onBlock(block.id, flip2));
2969
+ },
2970
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M6 3.5l4.5 4.5L6 12.5", fill: "none", stroke: "currentColor", strokeWidth: "2" }) })
2971
+ }
2972
+ );
2973
+ }
2974
+
2975
+ // src/view/react/toggle-block.tsx
2976
+
2977
+ var ToggleBlock = ({ block, controller }) => {
2978
+ const open = useToggleOpen(controller, block);
2979
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "quoin-toggle", "data-open": open, children: [
2980
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ToggleChevron, { block, controller }),
2981
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditableRegion, { block: block.id, region: "text", label: "Toggle" })
2982
+ ] });
2983
+ };
2984
+
2985
+ // src/view/react/unsupported-block-label.ts
2986
+
2987
+ var UNSUPPORTED_BLOCK = "Unsupported block";
2988
+ var UnsupportedBlockLabel = _react.createContext.call(void 0, UNSUPPORTED_BLOCK);
2989
+
2990
+ // src/view/react/default-renderers.tsx
2991
+
2992
+ var defaultRenderers = {
2993
+ MARKDOWN: MarkdownBlock,
2994
+ CALLOUT: CalloutBlock,
2995
+ TOGGLE: ToggleBlock
2996
+ };
2997
+ var VoidBlockFallback = ({ block }) => {
2998
+ const label = _react.useContext.call(void 0, UnsupportedBlockLabel);
2999
+ if (block.type === "DIVIDER") return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "quoin-void", contentEditable: false, role: "separator" });
3000
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "quoin-void", contentEditable: false, role: "group", "aria-label": label, children: label });
3001
+ };
3002
+
3003
+ // src/view/react/BlockShell.tsx
3004
+
3005
+
3006
+ // src/view/react/block-dom-id.ts
3007
+
3008
+ var BlockDomIdScope = _react.createContext.call(void 0, "quoin");
3009
+ function useBlockDomId() {
3010
+ const scope = _react.useContext.call(void 0, BlockDomIdScope);
3011
+ return _react.useCallback.call(void 0, (block) => `${scope}-${encodeURIComponent(block)}`, [scope]);
3012
+ }
3013
+ function useRegionDomId(block, region) {
3014
+ return `${useBlockDomId()(block)}/${encodeURIComponent(region)}`;
3015
+ }
3016
+
3017
+ // src/view/react/block-status.ts
3018
+ function within(state, index, ends) {
3019
+ const [anchor, head] = ends.map((id) => state.doc.index.get(id));
3020
+ if (anchor === void 0 || head === void 0) return false;
3021
+ return index >= Math.min(anchor, head) && index <= Math.max(anchor, head);
3022
+ }
3023
+ function covers(state, id, index) {
3024
+ const { doc, schema, selection } = state;
3025
+ if (selection === null) return false;
3026
+ if (selection.kind === "node") return selection.block === id;
3027
+ if (selection.kind === "blocks") return within(state, index, [selection.anchor, selection.head]);
3028
+ if (!isCrossSelection(selection) || _chunkPZ4GT5JOcjs.isTextFlow.call(void 0, schema, doc.blocks[index])) return false;
3029
+ return within(state, index, [selection.anchor.block, selection.head.block]);
3030
+ }
3031
+ function blockStatus(state, id, choices = NO_CHOICES) {
3032
+ const index = state.doc.index.get(id);
3033
+ if (index === void 0) return "plain";
3034
+ if (isHiddenAt(state.doc, index, choices)) return "hidden";
3035
+ return covers(state, id, index) ? "selected" : "plain";
3036
+ }
3037
+
3038
+ // src/view/react/BlockShell.tsx
3039
+
3040
+ function rendererFor(renderers, type) {
3041
+ for (const table of [renderers, defaultRenderers]) if (Object.hasOwn(table, type)) return table[type];
3042
+ return VoidBlockFallback;
3043
+ }
3044
+ var BlockShell = _react.memo.call(void 0, function BlockShell2({ block, renderers }) {
3045
+ const controller = useViewController();
3046
+ const ref = _react.useRef.call(void 0, null);
3047
+ const domId = useBlockDomId();
3048
+ const choices = useReaderChoices(controller);
3049
+ const status = useEditorState(controller, (state) => blockStatus(state, block.id, choices));
3050
+ const decorations = useEditorState(controller, () => hostDecorations(controller.editor, block.id, ""), sameDecorations);
3051
+ const hidden = status === "hidden";
3052
+ useIsomorphicLayoutEffect(() => {
3053
+ const element = ref.current;
3054
+ return hidden || element === null ? void 0 : controller.registerBlockElement(block.id, element);
3055
+ }, [controller, block.id, hidden]);
3056
+ if (hidden) return null;
3057
+ const Renderer = rendererFor(renderers, block.type);
3058
+ const { kind, color, background, align } = block.attrs;
3059
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3060
+ "div",
3061
+ {
3062
+ ...Object.assign({}, ...decorations.map((decoration) => decoration.attrs)),
3063
+ ref,
3064
+ id: domId(block.id),
3065
+ "data-quoin-block": "",
3066
+ "data-block-id": block.id,
3067
+ "data-type": block.type,
3068
+ "data-kind": _chunkND5XJVK2cjs.optionalString.call(void 0, kind),
3069
+ "data-indent": block.indent,
3070
+ "data-color": _chunkND5XJVK2cjs.paletteOrUndefined.call(void 0, color),
3071
+ "data-bg": _chunkND5XJVK2cjs.paletteOrUndefined.call(void 0, background),
3072
+ "data-align": _chunkND5XJVK2cjs.alignOrUndefined.call(void 0, align),
3073
+ style: { "--quoin-indent": block.indent },
3074
+ tabIndex: -1,
3075
+ "data-selected": status === "selected" ? "true" : void 0,
3076
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Renderer, { block, controller })
3077
+ }
3078
+ );
3079
+ });
3080
+
3081
+ // src/view/react/EditorView.tsx
3082
+
3083
+
3084
+ // src/view/react/EmptyDocumentHost.tsx
3085
+
3086
+
3087
+ function EmptyDocumentHost({ placeholder, label = "Paragraph" }) {
3088
+ const controller = useViewController();
3089
+ const ref = _react.useRef.call(void 0, null);
3090
+ const readOnly = useReadOnly(controller);
3091
+ useIsomorphicLayoutEffect(() => {
3092
+ const host = ref.current;
3093
+ if (host === null) return void 0;
3094
+ renderInline(host, [], []);
3095
+ return controller.registerEmptyHost(host);
3096
+ }, [controller]);
3097
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
3098
+ "div",
3099
+ {
3100
+ ref,
3101
+ contentEditable: !readOnly,
3102
+ suppressContentEditableWarning: true,
3103
+ role: "textbox",
3104
+ "aria-multiline": "true",
3105
+ "aria-readonly": readOnly ? "true" : void 0,
3106
+ "aria-label": label,
3107
+ "data-quoin-host": "",
3108
+ "data-quoin-empty": "",
3109
+ "data-placeholder": readOnly ? void 0 : placeholder,
3110
+ "aria-placeholder": readOnly ? void 0 : placeholder,
3111
+ spellCheck: true,
3112
+ onClick: () => _optionalChain([ref, 'access', _227 => _227.current, 'optionalAccess', _228 => _228.focus, 'call', _229 => _229()])
3113
+ }
3114
+ );
3115
+ }
3116
+
3117
+ // src/view/react/focus-owners.ts
3118
+ var focusOwners = /* @__PURE__ */ new WeakMap();
3119
+
3120
+ // src/view/react/LiveRegion.tsx
3121
+
3122
+
3123
+ // src/view/selection/selection-announcement.ts
3124
+ var SILENCE = { key: "", message: "" };
3125
+ function selectionAnnouncement({ doc, selection }, blocksSelected) {
3126
+ if (selection === null || selection.kind === "text" && !isCrossSelection(selection)) return SILENCE;
3127
+ if (selection.kind === "node") return { key: `node:${selection.block}`, message: blocksSelected(1) };
3128
+ const anchor = selection.kind === "blocks" ? selection.anchor : selection.anchor.block;
3129
+ const head = selection.kind === "blocks" ? selection.head : selection.head.block;
3130
+ const first = doc.index.get(anchor);
3131
+ const last = doc.index.get(head);
3132
+ if (first === void 0 || last === void 0) return SILENCE;
3133
+ const count = drawnBlockCount(doc, Math.min(first, last), Math.max(first, last) + 1);
3134
+ return { key: `${selection.kind}:${anchor}:${head}`, message: blocksSelected(count) };
3135
+ }
3136
+
3137
+ // src/view/react/LiveRegion.tsx
3138
+
3139
+ function heardNow({ editor, announcer }, blocksSelected) {
3140
+ const spoken = announcer.current();
3141
+ if (spoken !== null) return { key: `said:${spoken.serial}`, message: spoken.message };
3142
+ return selectionAnnouncement(editor.state, blocksSelected);
3143
+ }
3144
+ function LiveRegion({ blocksSelected }) {
3145
+ const controller = useViewController();
3146
+ const subscribe = _react.useCallback.call(void 0,
3147
+ (notify) => {
3148
+ const stopState = controller.editor.subscribe(() => notify());
3149
+ const stopSpoken = controller.announcer.subscribe(notify);
3150
+ return () => {
3151
+ stopState();
3152
+ stopSpoken();
3153
+ };
3154
+ },
3155
+ [controller]
3156
+ );
3157
+ const read = _react.useMemo.call(void 0, () => {
3158
+ let last = SILENCE;
3159
+ return () => {
3160
+ const next = heardNow(controller, blocksSelected);
3161
+ if (next.key !== last.key || next.message !== last.message) last = next;
3162
+ return last;
3163
+ };
3164
+ }, [controller, blocksSelected]);
3165
+ const { key, message } = _react.useSyncExternalStore.call(void 0, subscribe, read, read);
3166
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "quoin-visually-hidden", role: "status", "aria-live": "polite", "aria-atomic": "true", children: message !== "" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: message }, key) });
3167
+ }
3168
+
3169
+ // src/view/react/SelectionWriter.tsx
3170
+ function SelectionWriter() {
3171
+ const controller = useViewController();
3172
+ const state = useEditorState(controller, wholeState);
3173
+ useIsomorphicLayoutEffect(() => controller.afterRender(), [controller, state]);
3174
+ return null;
3175
+ }
3176
+
3177
+ // src/view/react/use-block-elements.tsx
3178
+
3179
+
3180
+ function useBlockElements(blocks, renderers) {
3181
+ const [made] = _react.useState.call(void 0, () => /* @__PURE__ */ new WeakMap());
3182
+ return blocks.map((block) => {
3183
+ const known2 = made.get(block);
3184
+ if (_optionalChain([known2, 'optionalAccess', _230 => _230.renderers]) === renderers) return known2.element;
3185
+ const element = /* @__PURE__ */ _jsxruntime.jsx.call(void 0, BlockShell, { block, renderers }, block.id);
3186
+ made.set(block, { renderers, element });
3187
+ return element;
3188
+ });
3189
+ }
3190
+
3191
+ // src/view/react/EditorView.tsx
3192
+
3193
+ var NO_RENDERERS = {};
3194
+ var HINT = "Press Escape, then Tab, to leave the editor.";
3195
+ var BLOCKS_SELECTED = (count) => `${count} ${count === 1 ? "block" : "blocks"} selected`;
3196
+ function EditorView({
3197
+ controller,
3198
+ renderers = NO_RENDERERS,
3199
+ className,
3200
+ id,
3201
+ "aria-label": label = "Editor",
3202
+ hint = HINT,
3203
+ paragraphLabel,
3204
+ selectedContentLabel,
3205
+ blockLabel,
3206
+ unsupportedBlockLabel = UNSUPPORTED_BLOCK,
3207
+ placeholder,
3208
+ blockPlaceholder = headingPlaceholder,
3209
+ blocksSelectedLabel = BLOCKS_SELECTED,
3210
+ children
3211
+ }) {
3212
+ const root = _react.useRef.call(void 0, null);
3213
+ const hintId = _react.useId.call(void 0, );
3214
+ const blockIdScope = _react.useId.call(void 0, );
3215
+ const blocks = useEditorState(controller, (state) => state.doc.blocks);
3216
+ const shells = useBlockElements(blocks, renderers);
3217
+ useIsomorphicLayoutEffect(() => root.current === null ? void 0 : controller.attach(root.current), [controller]);
3218
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, QuoinEditorContext.Provider, { value: controller, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
3219
+ "div",
3220
+ {
3221
+ ref: root,
3222
+ id,
3223
+ className: ["quoin", "quoin-editor", className].filter(Boolean).join(" "),
3224
+ role: "group",
3225
+ "aria-label": label,
3226
+ "aria-describedby": hintId,
3227
+ onFocus: (event) => focusOwners.set(event.target, controller),
3228
+ children: [
3229
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, BlockDomIdScope.Provider, { value: blockIdScope, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, UnsupportedBlockLabel.Provider, { value: unsupportedBlockLabel, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, BlockPlaceholderContext.Provider, { value: blockPlaceholder, children: blocks.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EmptyDocumentHost, { placeholder, label: paragraphLabel }) : shells }) }) }),
3230
+ children,
3231
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, SelectionSink, { label: selectedContentLabel, blockLabel }),
3232
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { id: hintId, className: "quoin-visually-hidden", children: hint }),
3233
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, LiveRegion, { blocksSelected: blocksSelectedLabel }),
3234
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, SelectionWriter, {})
3235
+ ]
3236
+ }
3237
+ ) });
3238
+ }
3239
+
3240
+
3241
+
3242
+
3243
+
3244
+
3245
+
3246
+
3247
+
3248
+
3249
+
3250
+
3251
+
3252
+
3253
+
3254
+
3255
+
3256
+
3257
+
3258
+
3259
+
3260
+
3261
+
3262
+
3263
+
3264
+
3265
+
3266
+
3267
+
3268
+
3269
+
3270
+
3271
+
3272
+
3273
+
3274
+
3275
+
3276
+
3277
+
3278
+
3279
+
3280
+
3281
+
3282
+
3283
+
3284
+
3285
+ exports.dispatchSelection = dispatchSelection; exports.wholeState = wholeState; exports.useEditorState = useEditorState; exports.useReadOnly = useReadOnly; exports.isCrossSelection = isCrossSelection; exports.NODE_ENTRY = NODE_ENTRY; exports.visibleBlockIndexes = visibleBlockIndexes; exports.drawnBlockCount = drawnBlockCount; exports.neighbourBlock = neighbourBlock; exports.moveToNeighbour = moveToNeighbour; exports.textDiff = textDiff; exports.domMapOf = domMapOf; exports.renderInline = renderInline; exports.domToPos = domToPos; exports.posToDom = posToDom; exports.caretFromPoint = caretFromPoint; exports.caretClientRect = caretClientRect; exports.lineEdgeRect = lineEdgeRect; exports.isOnFirstLine = isOnFirstLine; exports.isOnLastLine = isOnLastLine; exports.isComposingKey = isComposingKey; exports.NATIVE_INTERACTION = NATIVE_INTERACTION; exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect; exports.onBlock = onBlock; exports.createAutoScroll = createAutoScroll; exports.hostKey = hostKey; exports.regionInline = regionInline; exports.handleCopy = handleCopy; exports.ViewController = ViewController; exports.useViewController = useViewController; exports.useQuoinEditor = useQuoinEditor; exports.useBlockDomId = useBlockDomId; exports.useRegionDomId = useRegionDomId; exports.focusOwners = focusOwners; exports.SelectionSink = SelectionSink; exports.EditableRegion = EditableRegion; exports.TaskCheckbox = TaskCheckbox; exports.useOrderedNumber = useOrderedNumber; exports.useToggleOpen = useToggleOpen; exports.ToggleChevron = ToggleChevron; exports.defaultRenderers = defaultRenderers; exports.VoidBlockFallback = VoidBlockFallback; exports.BlockShell = BlockShell; exports.EditorView = EditorView;
3286
+ //# sourceMappingURL=chunk-GTTPVJSD.cjs.map