@thomasfosterau/effect-richtext 0.0.0-stage → 0.1.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 (176) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +118 -2
  3. package/dist/_virtual/_rolldown/runtime.js +12 -0
  4. package/dist/commands/commands.d.ts +323 -0
  5. package/dist/commands/commands.js +722 -0
  6. package/dist/commands/internal.js +43 -0
  7. package/dist/commands/keymaps.d.ts +46 -0
  8. package/dist/commands/keymaps.js +64 -0
  9. package/dist/commands.d.ts +3 -0
  10. package/dist/commands.js +3 -0
  11. package/dist/compat/commands.d.ts +250 -0
  12. package/dist/compat/commands.js +275 -0
  13. package/dist/compat/internal/core.d.ts +20 -0
  14. package/dist/compat/internal/core.js +148 -0
  15. package/dist/compat/internal/props.js +82 -0
  16. package/dist/compat/keymap.d.ts +21 -0
  17. package/dist/compat/keymap.js +38 -0
  18. package/dist/compat/model.d.ts +893 -0
  19. package/dist/compat/model.js +1212 -0
  20. package/dist/compat/orderedmap.d.ts +74 -0
  21. package/dist/compat/orderedmap.js +124 -0
  22. package/dist/compat/schema-basic.d.ts +38 -0
  23. package/dist/compat/schema-basic.js +238 -0
  24. package/dist/compat/schema-list.d.ts +81 -0
  25. package/dist/compat/schema-list.js +156 -0
  26. package/dist/compat/state.d.ts +415 -0
  27. package/dist/compat/state.js +689 -0
  28. package/dist/compat/test-builder.d.ts +98 -0
  29. package/dist/compat/test-builder.js +197 -0
  30. package/dist/compat/transform.d.ts +458 -0
  31. package/dist/compat/transform.js +803 -0
  32. package/dist/compat/view.d.ts +411 -0
  33. package/dist/compat/view.js +409 -0
  34. package/dist/dropcursor.d.ts +46 -0
  35. package/dist/dropcursor.js +176 -0
  36. package/dist/extensions/defaults.d.ts +716 -0
  37. package/dist/extensions/defaults.js +209 -0
  38. package/dist/extensions/editor.d.ts +246 -0
  39. package/dist/extensions/editor.js +271 -0
  40. package/dist/extensions/extension.d.ts +375 -0
  41. package/dist/extensions/extension.js +233 -0
  42. package/dist/extensions.d.ts +4 -0
  43. package/dist/extensions.js +4 -0
  44. package/dist/gapcursor.d.ts +66 -0
  45. package/dist/gapcursor.js +273 -0
  46. package/dist/history.d.ts +178 -0
  47. package/dist/history.js +412 -0
  48. package/dist/html.d.ts +158 -0
  49. package/dist/html.js +164 -0
  50. package/dist/inputrules.d.ts +271 -0
  51. package/dist/inputrules.js +321 -0
  52. package/dist/internal/attempt.js +15 -0
  53. package/dist/internal/json.js +29 -0
  54. package/dist/keymap/keymap.d.ts +130 -0
  55. package/dist/keymap/keymap.js +159 -0
  56. package/dist/keymap.d.ts +2 -0
  57. package/dist/keymap.js +1 -0
  58. package/dist/markdown/errors.d.ts +23 -0
  59. package/dist/markdown/errors.js +26 -0
  60. package/dist/markdown/from_markdown.d.ts +185 -0
  61. package/dist/markdown/from_markdown.js +266 -0
  62. package/dist/markdown/schema.d.ts +522 -0
  63. package/dist/markdown/schema.js +292 -0
  64. package/dist/markdown/to_markdown.d.ts +251 -0
  65. package/dist/markdown/to_markdown.js +413 -0
  66. package/dist/markdown.d.ts +5 -0
  67. package/dist/markdown.js +5 -0
  68. package/dist/model/attrsCodec.js +48 -0
  69. package/dist/model/comparedeep.js +17 -0
  70. package/dist/model/content.d.ts +232 -0
  71. package/dist/model/content.js +630 -0
  72. package/dist/model/diff.js +66 -0
  73. package/dist/model/dom.d.ts +3 -0
  74. package/dist/model/errors.d.ts +90 -0
  75. package/dist/model/errors.js +76 -0
  76. package/dist/model/fragment.d.ts +131 -0
  77. package/dist/model/fragment.js +262 -0
  78. package/dist/model/from_dom.d.ts +299 -0
  79. package/dist/model/from_dom.js +618 -0
  80. package/dist/model/json.d.ts +37 -0
  81. package/dist/model/json.js +126 -0
  82. package/dist/model/mark.d.ts +73 -0
  83. package/dist/model/mark.js +121 -0
  84. package/dist/model/node.d.ts +267 -0
  85. package/dist/model/node.js +434 -0
  86. package/dist/model/replace.d.ts +78 -0
  87. package/dist/model/replace.js +207 -0
  88. package/dist/model/resolvedpos.d.ts +196 -0
  89. package/dist/model/resolvedpos.js +305 -0
  90. package/dist/model/schema.d.ts +740 -0
  91. package/dist/model/schema.js +451 -0
  92. package/dist/model/to_dom.d.ts +169 -0
  93. package/dist/model/to_dom.js +238 -0
  94. package/dist/model.d.ts +12 -0
  95. package/dist/model.js +12 -0
  96. package/dist/placeholder.d.ts +53 -0
  97. package/dist/placeholder.js +79 -0
  98. package/dist/reactive/observer.d.ts +54 -0
  99. package/dist/reactive/observer.js +61 -0
  100. package/dist/reactive/selectors.d.ts +22 -0
  101. package/dist/reactive/selectors.js +61 -0
  102. package/dist/reactive/stateref.d.ts +72 -0
  103. package/dist/reactive/stateref.js +71 -0
  104. package/dist/reactive/streams.d.ts +83 -0
  105. package/dist/reactive/streams.js +85 -0
  106. package/dist/reactive.d.ts +16 -0
  107. package/dist/reactive.js +26 -0
  108. package/dist/schema-basic.d.ts +448 -0
  109. package/dist/schema-basic.js +269 -0
  110. package/dist/schema-list/commands.d.ts +66 -0
  111. package/dist/schema-list/commands.js +203 -0
  112. package/dist/schema-list.d.ts +92 -0
  113. package/dist/schema-list.js +100 -0
  114. package/dist/state/errors.d.ts +39 -0
  115. package/dist/state/errors.js +32 -0
  116. package/dist/state/plugin.d.ts +232 -0
  117. package/dist/state/plugin.js +82 -0
  118. package/dist/state/selection.d.ts +297 -0
  119. package/dist/state/selection.js +486 -0
  120. package/dist/state/state.d.ts +212 -0
  121. package/dist/state/state.js +489 -0
  122. package/dist/state/transaction.d.ts +145 -0
  123. package/dist/state/transaction.js +195 -0
  124. package/dist/state.d.ts +6 -0
  125. package/dist/state.js +6 -0
  126. package/dist/test-builder.d.ts +3582 -0
  127. package/dist/test-builder.js +239 -0
  128. package/dist/transform/attr_step.d.ts +60 -0
  129. package/dist/transform/attr_step.js +105 -0
  130. package/dist/transform/errors.d.ts +50 -0
  131. package/dist/transform/errors.js +35 -0
  132. package/dist/transform/map.d.ts +182 -0
  133. package/dist/transform/map.js +280 -0
  134. package/dist/transform/mark.js +98 -0
  135. package/dist/transform/mark_set_step.js +69 -0
  136. package/dist/transform/mark_step.d.ts +113 -0
  137. package/dist/transform/mark_step.js +225 -0
  138. package/dist/transform/replace.d.ts +18 -0
  139. package/dist/transform/replace.js +381 -0
  140. package/dist/transform/replace_step.d.ts +109 -0
  141. package/dist/transform/replace_step.js +175 -0
  142. package/dist/transform/step.d.ts +157 -0
  143. package/dist/transform/step.js +168 -0
  144. package/dist/transform/structure.d.ts +89 -0
  145. package/dist/transform/structure.js +350 -0
  146. package/dist/transform/transform.d.ts +248 -0
  147. package/dist/transform/transform.js +373 -0
  148. package/dist/transform.d.ts +10 -0
  149. package/dist/transform.js +10 -0
  150. package/dist/view/browser.js +39 -0
  151. package/dist/view/capturekeys.js +282 -0
  152. package/dist/view/clipboard.d.ts +8 -0
  153. package/dist/view/clipboard.js +245 -0
  154. package/dist/view/decoration.d.ts +339 -0
  155. package/dist/view/decoration.js +566 -0
  156. package/dist/view/dom.d.ts +18 -0
  157. package/dist/view/dom.js +140 -0
  158. package/dist/view/domchange.js +295 -0
  159. package/dist/view/domcoords.d.ts +22 -0
  160. package/dist/view/domcoords.js +448 -0
  161. package/dist/view/domobserver.d.ts +47 -0
  162. package/dist/view/domobserver.js +328 -0
  163. package/dist/view/errors.d.ts +18 -0
  164. package/dist/view/errors.js +21 -0
  165. package/dist/view/input.d.ts +58 -0
  166. package/dist/view/input.js +704 -0
  167. package/dist/view/selection.js +198 -0
  168. package/dist/view/view.d.ts +699 -0
  169. package/dist/view/view.js +623 -0
  170. package/dist/view/viewdesc.d.ts +236 -0
  171. package/dist/view/viewdesc.js +1205 -0
  172. package/dist/view-css.d.ts +43 -0
  173. package/dist/view-css.js +124 -0
  174. package/dist/view.d.ts +6 -0
  175. package/dist/view.js +4 -0
  176. package/package.json +206 -3
@@ -0,0 +1,282 @@
1
+ import { AllSelection, NodeSelection, Selection, TextSelection } from "../state/selection.js";
2
+ import { chrome, gecko, mac, safari, windows } from "./browser.js";
3
+ import { domIndex, hasBlockDesc, isElt, selectionCollapsed } from "./dom.js";
4
+ import { selectionToDOM } from "./selection.js";
5
+ import * as Arr from "effect/Array";
6
+ import * as Option from "effect/Option";
7
+ // src/view/capturekeys.ts
8
+ /**
9
+ * A backdrop key mapping that suppresses keys with dangerous default
10
+ * effects and handles block-level cursor motion.
11
+ *
12
+ * @internal
13
+ */
14
+ function moveSelectionBlock(state, dir) {
15
+ const { $anchor, $head } = state.selection;
16
+ const $side = dir > 0 ? $anchor.max($head) : $anchor.min($head);
17
+ const $start = !$side.parent.inlineContent ? $side : $side.depth ? state.doc.resolveUnsafe(dir > 0 ? $side.afterUnsafe() : $side.beforeUnsafe()) : null;
18
+ return $start && Selection.findFromOrNull($start, dir);
19
+ }
20
+ function apply(view, sel) {
21
+ view.dispatchInternal(view.state.tr.setSelectionUnsafe(sel).scrollIntoView());
22
+ return true;
23
+ }
24
+ function selectHorizontally(view, dir, mods) {
25
+ const sel = view.state.selection;
26
+ if (sel instanceof TextSelection) {
27
+ if (mods.indexOf("s") > -1) {
28
+ const { $head } = sel, node = $head.textOffset ? null : dir < 0 ? $head.nodeBeforeOrNull : $head.nodeAfterOrNull;
29
+ if (!node || node.isText || !node.isLeaf) return false;
30
+ const $newHead = view.state.doc.resolveUnsafe($head.pos + node.nodeSize * (dir < 0 ? -1 : 1));
31
+ return apply(view, new TextSelection(sel.$anchor, $newHead));
32
+ } else if (!sel.empty) return false;
33
+ else if (view.endOfTextblock(dir > 0 ? "forward" : "backward")) {
34
+ const next = moveSelectionBlock(view.state, dir);
35
+ if (next && next instanceof NodeSelection) return apply(view, next);
36
+ return false;
37
+ } else if (!(mac && mods.indexOf("m") > -1)) {
38
+ const $head = sel.$head, node = $head.textOffset ? null : dir < 0 ? $head.nodeBeforeOrNull : $head.nodeAfterOrNull;
39
+ let desc;
40
+ if (!node || node.isText) return false;
41
+ const nodePos = dir < 0 ? $head.pos - node.nodeSize : $head.pos;
42
+ if (!(node.isAtom || (desc = view.docView.descAt(nodePos)) && !desc.contentDOM)) return false;
43
+ if (NodeSelection.isSelectable(node)) return apply(view, new NodeSelection(dir < 0 ? view.state.doc.resolveUnsafe($head.pos - node.nodeSize) : $head));
44
+ else return apply(view, new TextSelection(view.state.doc.resolveUnsafe(dir < 0 ? nodePos : nodePos + node.nodeSize)));
45
+ }
46
+ return false;
47
+ } else if (sel instanceof NodeSelection && sel.node.isInline) return apply(view, new TextSelection(dir > 0 ? sel.$to : sel.$from));
48
+ else {
49
+ const next = moveSelectionBlock(view.state, dir);
50
+ if (next) return apply(view, next);
51
+ return false;
52
+ }
53
+ }
54
+ function nodeLen(node) {
55
+ return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
56
+ }
57
+ function isIgnorable(dom, dir) {
58
+ const desc = dom.pmViewDesc;
59
+ return desc ? desc.size == 0 && (dir < 0 || dom.nextSibling || dom.nodeName != "BR") : isElt(dom) && dom.contentEditable == "false";
60
+ }
61
+ function skipIgnoredNodes(view, dir) {
62
+ if (dir < 0) skipIgnoredNodesBefore(view);
63
+ else skipIgnoredNodesAfter(view);
64
+ return false;
65
+ }
66
+ function skipIgnoredNodesBefore(view) {
67
+ const sel = view.domSelectionRange();
68
+ let node = sel.focusNode, offset = sel.focusOffset;
69
+ if (!node) return;
70
+ let moveNode, moveOffset, force = false;
71
+ if (gecko && node.nodeType == 1 && offset < nodeLen(node) && isIgnorable(node.childNodes[offset], -1)) force = true;
72
+ for (;;) if (offset > 0) {
73
+ if (node.nodeType != 1) break;
74
+ else {
75
+ const before = node.childNodes[offset - 1];
76
+ if (isIgnorable(before, -1)) {
77
+ moveNode = node;
78
+ moveOffset = --offset;
79
+ } else if (before.nodeType == 3) {
80
+ node = before;
81
+ offset = node.nodeValue.length;
82
+ } else break;
83
+ }
84
+ } else if (isBlockNode(node)) break;
85
+ else {
86
+ let prev = node.previousSibling;
87
+ while (prev && isIgnorable(prev, -1)) {
88
+ moveNode = node.parentNode;
89
+ moveOffset = domIndex(prev);
90
+ prev = prev.previousSibling;
91
+ }
92
+ if (!prev) {
93
+ node = node.parentNode;
94
+ if (node == view.dom) break;
95
+ offset = 0;
96
+ } else {
97
+ node = prev;
98
+ offset = nodeLen(node);
99
+ }
100
+ }
101
+ if (force) setSelFocus(view, node, offset);
102
+ else if (moveNode) setSelFocus(view, moveNode, moveOffset);
103
+ }
104
+ function skipIgnoredNodesAfter(view) {
105
+ const sel = view.domSelectionRange();
106
+ let node = sel.focusNode, offset = sel.focusOffset;
107
+ if (!node) return;
108
+ let len = nodeLen(node);
109
+ let moveNode, moveOffset;
110
+ for (;;) if (offset < len) {
111
+ if (node.nodeType != 1) break;
112
+ const after = node.childNodes[offset];
113
+ if (isIgnorable(after, 1)) {
114
+ moveNode = node;
115
+ moveOffset = ++offset;
116
+ } else break;
117
+ } else if (isBlockNode(node)) break;
118
+ else {
119
+ let next = node.nextSibling;
120
+ while (next && isIgnorable(next, 1)) {
121
+ moveNode = next.parentNode;
122
+ moveOffset = domIndex(next) + 1;
123
+ next = next.nextSibling;
124
+ }
125
+ if (!next) {
126
+ node = node.parentNode;
127
+ if (node == view.dom) break;
128
+ offset = len = 0;
129
+ } else {
130
+ node = next;
131
+ offset = 0;
132
+ len = nodeLen(node);
133
+ }
134
+ }
135
+ if (moveNode) setSelFocus(view, moveNode, moveOffset);
136
+ }
137
+ function isBlockNode(dom) {
138
+ const desc = dom.pmViewDesc;
139
+ return desc && desc.node && desc.node.isBlock;
140
+ }
141
+ function textNodeAfter(node, offset) {
142
+ while (node && offset == node.childNodes.length && !hasBlockDesc(node)) {
143
+ offset = domIndex(node) + 1;
144
+ node = node.parentNode;
145
+ }
146
+ while (node && offset < node.childNodes.length) {
147
+ const next = node.childNodes[offset];
148
+ if (next.nodeType == 3) return next;
149
+ if (next.nodeType == 1 && next.contentEditable == "false") break;
150
+ node = next;
151
+ offset = 0;
152
+ }
153
+ }
154
+ function textNodeBefore(node, offset) {
155
+ while (node && !offset && !hasBlockDesc(node)) {
156
+ offset = domIndex(node);
157
+ node = node.parentNode;
158
+ }
159
+ while (node && offset) {
160
+ const next = node.childNodes[offset - 1];
161
+ if (next.nodeType == 3) return next;
162
+ if (next.nodeType == 1 && next.contentEditable == "false") break;
163
+ node = next;
164
+ offset = node.childNodes.length;
165
+ }
166
+ }
167
+ function setSelFocus(view, node, offset) {
168
+ if (node.nodeType != 3) {
169
+ let before, after;
170
+ if (after = textNodeAfter(node, offset)) {
171
+ node = after;
172
+ offset = 0;
173
+ } else if (before = textNodeBefore(node, offset)) {
174
+ node = before;
175
+ offset = before.nodeValue.length;
176
+ }
177
+ }
178
+ const sel = view.domSelection(), range = view.domSelectionRange();
179
+ if (!sel) return;
180
+ if (selectionCollapsed(range)) sel.collapse(node, offset);
181
+ else if (sel.extend) {
182
+ sel.collapse(range.anchorNode, range.anchorOffset);
183
+ sel.extend(node, offset);
184
+ }
185
+ view.domObserver.setCurSelection();
186
+ const { state } = view;
187
+ setTimeout(() => {
188
+ if (view.state == state) selectionToDOM(view);
189
+ }, 50);
190
+ }
191
+ function findDirection(view, pos) {
192
+ const $pos = view.state.doc.resolveUnsafe(pos);
193
+ if (!(chrome || windows) && $pos.parent.inlineContent) {
194
+ const coords = view.coordsAtPos(pos);
195
+ if (pos > $pos.start()) {
196
+ const before = view.coordsAtPos(pos - 1);
197
+ const mid = (before.top + before.bottom) / 2;
198
+ if (mid > coords.top && mid < coords.bottom && Math.abs(before.left - coords.left) > 1) return before.left < coords.left ? "ltr" : "rtl";
199
+ }
200
+ if (pos < $pos.end()) {
201
+ const after = view.coordsAtPos(pos + 1);
202
+ const mid = (after.top + after.bottom) / 2;
203
+ if (mid > coords.top && mid < coords.bottom && Math.abs(after.left - coords.left) > 1) return after.left > coords.left ? "ltr" : "rtl";
204
+ }
205
+ }
206
+ const depths = $pos.depth > 0 ? Arr.range(1, $pos.depth).reverse() : [];
207
+ const blockDOM = Arr.findFirst(depths, (d) => $pos.node(d).isBlock).pipe(Option.flatMap((d) => view.nodeDOM($pos.beforeUnsafe(d))), Option.filter(isElt));
208
+ return getComputedStyle(Option.getOrElse(blockDOM, () => view.dom)).direction == "rtl" ? "rtl" : "ltr";
209
+ }
210
+ function selectVertically(view, dir, mods) {
211
+ const sel = view.state.selection;
212
+ if (sel instanceof TextSelection && !sel.empty || mods.indexOf("s") > -1) return false;
213
+ if (mac && mods.indexOf("m") > -1) return false;
214
+ const { $from, $to } = sel;
215
+ if (!$from.parent.inlineContent || view.endOfTextblock(dir < 0 ? "up" : "down")) {
216
+ const next = moveSelectionBlock(view.state, dir);
217
+ if (next && next instanceof NodeSelection) return apply(view, next);
218
+ }
219
+ if (!$from.parent.inlineContent) {
220
+ const side = dir < 0 ? $from : $to;
221
+ const beyond = sel instanceof AllSelection ? Selection.near(side, dir) : Selection.findFromOrNull(side, dir);
222
+ return beyond ? apply(view, beyond) : false;
223
+ }
224
+ return false;
225
+ }
226
+ function stopNativeHorizontalDelete(view, dir) {
227
+ if (!(view.state.selection instanceof TextSelection)) return true;
228
+ const { $anchor, $head, empty } = view.state.selection;
229
+ if (!$head.sameParent($anchor)) return true;
230
+ if (!empty) return false;
231
+ if (view.endOfTextblock(dir > 0 ? "forward" : "backward")) return true;
232
+ const nextNode = !$head.textOffset && (dir < 0 ? $head.nodeBeforeOrNull : $head.nodeAfterOrNull);
233
+ if (nextNode && !nextNode.isText) {
234
+ const tr = view.state.tr;
235
+ if (dir < 0) tr.deleteUnsafe($head.pos - nextNode.nodeSize, $head.pos);
236
+ else tr.deleteUnsafe($head.pos, $head.pos + nextNode.nodeSize);
237
+ view.dispatchInternal(tr);
238
+ return true;
239
+ }
240
+ return false;
241
+ }
242
+ function switchEditable(view, node, state) {
243
+ view.domObserver.stop();
244
+ node.contentEditable = state;
245
+ view.domObserver.start();
246
+ }
247
+ function safariDownArrowBug(view) {
248
+ if (!safari || view.state.selection.$head.parentOffset > 0) return false;
249
+ const { focusNode, focusOffset } = view.domSelectionRange();
250
+ if (focusNode && focusNode.nodeType == 1 && focusOffset == 0 && focusNode.firstChild && focusNode.firstChild.contentEditable == "false") {
251
+ const child = focusNode.firstChild;
252
+ switchEditable(view, child, "true");
253
+ setTimeout(() => switchEditable(view, child, "false"), 20);
254
+ }
255
+ return false;
256
+ }
257
+ function getMods(event) {
258
+ let result = "";
259
+ if (event.ctrlKey) result += "c";
260
+ if (event.metaKey) result += "m";
261
+ if (event.altKey) result += "a";
262
+ if (event.shiftKey) result += "s";
263
+ return result;
264
+ }
265
+ /** @internal */
266
+ function captureKeyDown(view, event) {
267
+ const code = event.keyCode, mods = getMods(event);
268
+ if (code == 8 || mac && code == 72 && mods == "c") return stopNativeHorizontalDelete(view, -1) || skipIgnoredNodes(view, -1);
269
+ else if (code == 46 && !event.shiftKey || mac && code == 68 && mods == "c") return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodes(view, 1);
270
+ else if (code == 13 || code == 27) return true;
271
+ else if (code == 37 || mac && code == 66 && mods == "c") {
272
+ const dir = code == 37 ? findDirection(view, view.state.selection.from) == "ltr" ? -1 : 1 : -1;
273
+ return selectHorizontally(view, dir, mods) || skipIgnoredNodes(view, dir);
274
+ } else if (code == 39 || mac && code == 70 && mods == "c") {
275
+ const dir = code == 39 ? findDirection(view, view.state.selection.from) == "ltr" ? 1 : -1 : 1;
276
+ return selectHorizontally(view, dir, mods) || skipIgnoredNodes(view, dir);
277
+ } else if (code == 38 || mac && code == 80 && mods == "c") return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1);
278
+ else if (code == 40 || mac && code == 78 && mods == "c") return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodes(view, 1);
279
+ else if (mods == (mac ? "m" : "c") && (code == 66 || code == 73 || code == 89 || code == 90)) return true;
280
+ return false;
281
+ }
282
+ export { captureKeyDown };
@@ -0,0 +1,8 @@
1
+ import { ResolvedPos } from "../model/resolvedpos.js";
2
+ import { Slice } from "../model/replace.js";
3
+ import "../model.js";
4
+ import { EditorView } from "./view.js";
5
+ type AnyView = EditorView<any, any, any>;
6
+ /** @internal */
7
+ export declare function parseFromClipboard(view: AnyView, text: string, html: string | null, plainText: boolean, $context: ResolvedPos<any>): Slice<any> | null;
8
+ export {};
@@ -0,0 +1,245 @@
1
+ import { DOMParser } from "../model/from_dom.js";
2
+ import { DOMSerializer } from "../model/to_dom.js";
3
+ import { Fragment } from "../model/fragment.js";
4
+ import { Slice } from "../model/replace.js";
5
+ import { parseJson, stringifyJson } from "../internal/json.js";
6
+ import { chrome, webkit } from "./browser.js";
7
+ import * as Option from "effect/Option";
8
+ import * as Predicate from "effect/Predicate";
9
+ // src/view/clipboard.ts
10
+ /**
11
+ * Clipboard serialization and parsing.
12
+ *
13
+ * @internal
14
+ */
15
+ /** @internal */
16
+ function serializeForClipboard(view, slice) {
17
+ view.someProp("transformCopied", (f) => {
18
+ slice = f(slice, view);
19
+ });
20
+ const context = [];
21
+ let { content, openEnd, openStart } = slice;
22
+ while (openStart > 1 && openEnd > 1 && content.childCount == 1 && content.content[0].childCount == 1) {
23
+ openStart--;
24
+ openEnd--;
25
+ const node = content.content[0];
26
+ context.push(node.type.name, node.attrs != node.type.defaultAttrs ? node.attrs : null);
27
+ content = node.content;
28
+ }
29
+ const serializer = view.someProp("clipboardSerializer") || DOMSerializer.fromSchema(view.state.schema);
30
+ const doc = detachedDoc(), wrap = doc.createElement("div");
31
+ wrap.appendChild(serializer.serializeFragment(content, { document: doc }));
32
+ let firstChild = wrap.firstChild, needsWrap, wrappers = 0;
33
+ while (firstChild && firstChild.nodeType == 1 && (needsWrap = wrapMap[firstChild.nodeName.toLowerCase()])) {
34
+ for (let i = needsWrap.length - 1; i >= 0; i--) {
35
+ const wrapper = doc.createElement(needsWrap[i]);
36
+ while (wrap.firstChild) wrapper.appendChild(wrap.firstChild);
37
+ wrap.appendChild(wrapper);
38
+ wrappers++;
39
+ }
40
+ firstChild = wrap.firstChild;
41
+ }
42
+ if (firstChild && firstChild.nodeType == 1) firstChild.setAttribute("data-pm-slice", `${openStart} ${openEnd}${wrappers ? ` -${wrappers}` : ""} ${Option.getOrElse(stringifyJson(context), () => "[]")}`);
43
+ return {
44
+ dom: wrap,
45
+ text: view.someProp("clipboardTextSerializer", (f) => f(slice, view)) || slice.content.textBetween(0, slice.content.size, "\n\n"),
46
+ slice
47
+ };
48
+ }
49
+ /** @internal */
50
+ function parseFromClipboard(view, text, html, plainText, $context) {
51
+ const inCode = $context.parent.type.spec.code;
52
+ let dom, slice;
53
+ if (!html && !text) return null;
54
+ const asText = !!text && (plainText || inCode || !html);
55
+ if (asText) {
56
+ view.someProp("transformPastedText", (f) => {
57
+ text = f(text, !!(inCode || plainText), view);
58
+ });
59
+ if (inCode) {
60
+ slice = new Slice(Fragment.from(view.state.schema.textUnsafe(text.replace(/\r\n?/g, "\n"))), 0, 0);
61
+ view.someProp("transformPasted", (f) => {
62
+ slice = f(slice, view, true);
63
+ });
64
+ return slice;
65
+ }
66
+ const parsed = view.someProp("clipboardTextParser", (f) => f(text, $context, plainText, view));
67
+ if (parsed) slice = parsed;
68
+ else {
69
+ const marks = $context.marks();
70
+ const { schema } = view.state, serializer = DOMSerializer.fromSchema(schema);
71
+ dom = document.createElement("div");
72
+ text.split(/(?:\r\n?|\n)+/).forEach((block) => {
73
+ const p = dom.appendChild(document.createElement("p"));
74
+ if (block) p.appendChild(serializer.serializeNode(schema.textUnsafe(block, marks)));
75
+ });
76
+ }
77
+ } else {
78
+ view.someProp("transformPastedHTML", (f) => {
79
+ html = f(html, view);
80
+ });
81
+ dom = readHTML(html);
82
+ if (webkit) restoreReplacedSpaces(dom);
83
+ }
84
+ const contextNode = dom && dom.querySelector("[data-pm-slice]");
85
+ const sliceData = contextNode && /^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(contextNode.getAttribute("data-pm-slice") || "");
86
+ if (sliceData && sliceData[3]) for (let i = +sliceData[3]; i > 0; i--) {
87
+ let child = dom.firstChild;
88
+ while (child && child.nodeType != 1) child = child.nextSibling;
89
+ if (!child) break;
90
+ dom = child;
91
+ }
92
+ if (!slice) slice = (view.someProp("clipboardParser") || view.someProp("domParser") || DOMParser.fromSchema(view.state.schema)).parseSliceUnsafe(dom, {
93
+ preserveWhitespace: !!(asText || sliceData),
94
+ context: $context,
95
+ ruleFromNode(dom) {
96
+ if (dom.nodeName == "BR" && !dom.nextSibling && dom.parentNode && !inlineParents.test(dom.parentNode.nodeName)) return { ignore: true };
97
+ return null;
98
+ }
99
+ });
100
+ if (sliceData) slice = addContext(closeSlice(slice, +sliceData[1], +sliceData[2]), sliceData[4]);
101
+ else {
102
+ slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
103
+ if (slice.openStart || slice.openEnd) {
104
+ let openStart = 0, openEnd = 0;
105
+ for (let node = slice.content.content[0]; openStart < slice.openStart && !node.type.spec.isolating; openStart++, node = node.content.content[0]);
106
+ for (let node = slice.content.content[slice.content.childCount - 1]; openEnd < slice.openEnd && !node.type.spec.isolating; openEnd++, node = node.content.content[node.childCount - 1]);
107
+ slice = closeSlice(slice, openStart, openEnd);
108
+ }
109
+ }
110
+ view.someProp("transformPasted", (f) => {
111
+ slice = f(slice, view, asText);
112
+ });
113
+ return slice;
114
+ }
115
+ const inlineParents = /^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var)$/i;
116
+ function normalizeSiblings(fragment, $context) {
117
+ if (fragment.childCount < 2) return fragment;
118
+ for (let d = $context.depth; d >= 0; d--) {
119
+ let match = $context.node(d).contentMatchAtUnsafe($context.index(d));
120
+ let lastWrap, result = [];
121
+ fragment.forEach((node) => {
122
+ if (!result) return;
123
+ const wrap = match.findWrappingOrNull(node.type);
124
+ let inLast;
125
+ if (!wrap) return result = null;
126
+ if (inLast = result.length && lastWrap.length && addToSibling(wrap, lastWrap, node, result[result.length - 1], 0)) result[result.length - 1] = inLast;
127
+ else {
128
+ if (result.length) result[result.length - 1] = closeRight(result[result.length - 1], lastWrap.length);
129
+ const wrapped = withWrappers(node, wrap);
130
+ result.push(wrapped);
131
+ match = match.matchTypeOrNull(wrapped.type);
132
+ lastWrap = wrap;
133
+ }
134
+ });
135
+ if (result) return Fragment.from(result);
136
+ }
137
+ return fragment;
138
+ }
139
+ function withWrappers(node, wrap, from = 0) {
140
+ for (let i = wrap.length - 1; i >= from; i--) node = wrap[i].createUnsafe(null, Fragment.from(node));
141
+ return node;
142
+ }
143
+ function addToSibling(wrap, lastWrap, node, sibling, depth) {
144
+ if (depth < wrap.length && depth < lastWrap.length && wrap[depth] == lastWrap[depth]) {
145
+ const inner = addToSibling(wrap, lastWrap, node, sibling.content.content[sibling.childCount - 1], depth + 1);
146
+ if (inner) return sibling.copy(sibling.content.replaceChild(sibling.childCount - 1, inner));
147
+ if (sibling.contentMatchAtUnsafe(sibling.childCount).matchTypeOrNull(depth == wrap.length - 1 ? node.type : wrap[depth + 1])) return sibling.copy(sibling.content.append(Fragment.from(withWrappers(node, wrap, depth + 1))));
148
+ }
149
+ }
150
+ function closeRight(node, depth) {
151
+ if (depth == 0) return node;
152
+ const fragment = node.content.replaceChild(node.childCount - 1, closeRight(node.content.content[node.childCount - 1], depth - 1));
153
+ const fill = node.contentMatchAtUnsafe(node.childCount).fillBeforeOrNull(Fragment.empty, true);
154
+ return node.copy(fragment.append(fill));
155
+ }
156
+ function closeRange(fragment, side, from, to, depth, openEnd) {
157
+ const node = side < 0 ? fragment.content[0] : fragment.content[fragment.childCount - 1];
158
+ let inner = node.content;
159
+ if (fragment.childCount > 1) openEnd = 0;
160
+ if (depth < to - 1) inner = closeRange(inner, side, from, to, depth + 1, openEnd);
161
+ if (depth >= from) inner = side < 0 ? node.contentMatchAtUnsafe(0).fillBeforeOrNull(inner, openEnd <= depth).append(inner) : inner.append(node.contentMatchAtUnsafe(node.childCount).fillBeforeOrNull(Fragment.empty, true));
162
+ return fragment.replaceChild(side < 0 ? 0 : fragment.childCount - 1, node.copy(inner));
163
+ }
164
+ function closeSlice(slice, openStart, openEnd) {
165
+ if (openStart < slice.openStart) slice = new Slice(closeRange(slice.content, -1, openStart, slice.openStart, 0, slice.openEnd), openStart, slice.openEnd);
166
+ if (openEnd < slice.openEnd) slice = new Slice(closeRange(slice.content, 1, openEnd, slice.openEnd, 0, 0), slice.openStart, openEnd);
167
+ return slice;
168
+ }
169
+ const wrapMap = {
170
+ thead: ["table"],
171
+ tbody: ["table"],
172
+ tfoot: ["table"],
173
+ caption: ["table"],
174
+ colgroup: ["table"],
175
+ col: ["table", "colgroup"],
176
+ tr: ["table", "tbody"],
177
+ td: [
178
+ "table",
179
+ "tbody",
180
+ "tr"
181
+ ],
182
+ th: [
183
+ "table",
184
+ "tbody",
185
+ "tr"
186
+ ]
187
+ };
188
+ function detachedDoc() {
189
+ return document.implementation.createHTMLDocument("title");
190
+ }
191
+ let _policy = null;
192
+ function maybeWrapTrusted(html) {
193
+ const trustedTypes = window.trustedTypes;
194
+ if (!trustedTypes) return html;
195
+ if (!_policy) {
196
+ const fallback = trustedTypes.defaultPolicy;
197
+ _policy = fallback && Predicate.isFunction(fallback.createHTML) ? fallback : trustedTypes.createPolicy("ProseMirrorClipboard", { createHTML: (s) => s });
198
+ }
199
+ return _policy.createHTML(html);
200
+ }
201
+ function readHTML(html) {
202
+ const metas = /^(\s*<meta [^>]*>)*/.exec(html);
203
+ if (metas) html = html.slice(metas[0].length);
204
+ const doc = detachedDoc();
205
+ let elt = doc.body;
206
+ const firstTag = /<([a-z][^>\s]+)/i.exec(html);
207
+ let wrap;
208
+ if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()]) html = wrap.map((n) => "<" + n + ">").join("") + html + wrap.map((n) => "</" + n + ">").reverse().join("");
209
+ elt.innerHTML = maybeWrapTrusted(html);
210
+ if (wrap) for (let i = 0; i < wrap.length; i++) elt = elt.querySelector(wrap[i]) || elt;
211
+ const root = elt;
212
+ Array.from(doc.styleSheets).forEach((sheet) => Array.from(sheet.cssRules).forEach((rule) => {
213
+ if (rule instanceof CSSStyleRule) root.querySelectorAll(rule.selectorText).forEach((match) => {
214
+ match.style.cssText += rule.style.cssText;
215
+ });
216
+ }));
217
+ return elt;
218
+ }
219
+ function restoreReplacedSpaces(dom) {
220
+ const nodes = dom.querySelectorAll(chrome ? "span:not([class]):not([style])" : "span.Apple-converted-space");
221
+ for (let i = 0; i < nodes.length; i++) {
222
+ const node = nodes[i];
223
+ if (node.childNodes.length == 1 && node.textContent == "\xA0" && node.parentNode) node.parentNode.replaceChild(dom.ownerDocument.createTextNode(" "), node);
224
+ }
225
+ }
226
+ function addContext(slice, context) {
227
+ if (!slice.size) return slice;
228
+ const schema = slice.content.content[0].type.schema;
229
+ const array = Option.getOrUndefined(parseJson(context));
230
+ if (!Array.isArray(array)) return slice;
231
+ let { content, openEnd, openStart } = slice;
232
+ for (let i = array.length - 2; i >= 0; i -= 2) {
233
+ const type = schema.nodes[array[i]];
234
+ if (!type || type.hasRequiredAttrs()) break;
235
+ try {
236
+ content = Fragment.from(type.createUnsafe(array[i + 1], content));
237
+ } catch {
238
+ break;
239
+ }
240
+ openStart++;
241
+ openEnd++;
242
+ }
243
+ return new Slice(content, openStart, openEnd);
244
+ }
245
+ export { parseFromClipboard, serializeForClipboard };