@thomasfosterau/effect-richtext 0.0.0 → 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 +117 -1
  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 +200 -4
@@ -0,0 +1,89 @@
1
+ import { NodeRange } from "../model/resolvedpos.js";
2
+ import { Slice } from "../model/replace.js";
3
+ import { AnySchema, NodeAttrsInput, NodeNames, NodeType } from "../model/schema.js";
4
+ import { Node } from "../model/node.js";
5
+ import "../model.js";
6
+ import "./transform.js";
7
+ import * as Option from "effect/Option";
8
+ /**
9
+ * A node type paired with (optional) attributes, as used by `wrap`,
10
+ * `split` and `findWrapping`. For typed schemas, the attributes are
11
+ * typed according to the node type. A node type whose name isn't known
12
+ * statically can be given without attributes (defaults are used).
13
+ *
14
+ * @since 0.1.0
15
+ * @category models
16
+ */
17
+ export type NodeTypeWithAttrs<Sc extends AnySchema = AnySchema> = { readonly [N in NodeNames<Sc>]: {
18
+ readonly type: NodeType<Sc, N>;
19
+ readonly attrs?: NodeAttrsInput<Sc, N> | null | undefined;
20
+ }; }[NodeNames<Sc>] | {
21
+ readonly type: NodeType<Sc>;
22
+ readonly attrs?: null | undefined;
23
+ };
24
+ /**
25
+ * Try to find a target depth to which the content in the given range
26
+ * can be lifted. Will not go across isolating parent nodes.
27
+ *
28
+ * @since 0.1.0
29
+ * @category queries
30
+ */
31
+ export declare const liftTarget: <Sc extends AnySchema>(range: NodeRange<Sc>) => Option.Option<number>;
32
+ /**
33
+ * Try to find a valid way to wrap the content in the given range in a
34
+ * node of the given type. May introduce extra nodes around and inside
35
+ * the wrapper node, if necessary. Returns `None` if no valid wrapping
36
+ * could be found. When `innerRange` is given, that range's content is
37
+ * used as the content to fit into the wrapping, instead of the content
38
+ * of `range`.
39
+ *
40
+ * @since 0.1.0
41
+ * @category queries
42
+ */
43
+ export declare const findWrapping: <Sc extends AnySchema, N extends NodeNames<Sc>>(range: NodeRange<Sc>, nodeType: NodeType<Sc, N>, attrs?: NodeAttrsInput<Sc, N> | null, innerRange?: NodeRange<Sc>) => Option.Option<ReadonlyArray<NodeTypeWithAttrs<Sc>>>;
44
+ /**
45
+ * Check whether splitting at the given position is allowed. Throws a
46
+ * `PositionError` when `pos` is outside of the document.
47
+ *
48
+ * @since 0.1.0
49
+ * @category queries
50
+ */
51
+ export declare function canSplit<Sc extends AnySchema>(doc: Node<Sc>, pos: number, depth?: number, typesAfter?: ReadonlyArray<NodeTypeWithAttrs<Sc> | null | undefined>): boolean;
52
+ /**
53
+ * Test whether the blocks before and after a given position can be
54
+ * joined. Throws a `PositionError` when `pos` is outside of the
55
+ * document.
56
+ *
57
+ * @since 0.1.0
58
+ * @category queries
59
+ */
60
+ export declare function canJoin<Sc extends AnySchema>(doc: Node<Sc>, pos: number): boolean;
61
+ /**
62
+ * Find an ancestor of the given position that can be joined to the
63
+ * block before (or after if `dir` is positive). Returns the joinable
64
+ * point, if any.
65
+ *
66
+ * @since 0.1.0
67
+ * @category queries
68
+ */
69
+ export declare function joinPoint<Sc extends AnySchema>(doc: Node<Sc>, pos: number, dir?: number): Option.Option<number>;
70
+ /**
71
+ * Try to find a point where a node of the given type can be inserted
72
+ * near `pos`, by searching up the node hierarchy when `pos` itself
73
+ * isn't a valid place but is at the start or end of a node. Returns
74
+ * `None` if no position was found.
75
+ *
76
+ * @since 0.1.0
77
+ * @category queries
78
+ */
79
+ export declare const insertPoint: <Sc extends AnySchema>(doc: Node<Sc>, pos: number, nodeType: NodeType<Sc, any>) => Option.Option<number>;
80
+ /**
81
+ * Finds a position at or around the given position where the given
82
+ * slice can be inserted. Will look at parent nodes' nearest boundary
83
+ * and try there, even if the original position wasn't directly at the
84
+ * start or end of that node. Returns `None` when no position was found.
85
+ *
86
+ * @since 0.1.0
87
+ * @category queries
88
+ */
89
+ export declare function dropPoint<Sc extends AnySchema>(doc: Node<Sc>, pos: number, slice: Slice<Sc>): Option.Option<number>;
@@ -0,0 +1,350 @@
1
+ import { Fragment } from "../model/fragment.js";
2
+ import { Slice } from "../model/replace.js";
3
+ import { TransformError } from "./errors.js";
4
+ import { clearIncompatible } from "./mark.js";
5
+ import { ReplaceAroundStep, ReplaceStep } from "./replace_step.js";
6
+ import * as Option from "effect/Option";
7
+ // src/transform/structure.ts
8
+ /**
9
+ * Structural queries (`liftTarget`, `findWrapping`, `canSplit`, …) and
10
+ * the structure-changing transform implementations.
11
+ *
12
+ * @since 0.1.0
13
+ */
14
+ function canCut(node, start, end) {
15
+ return (start === 0 || node.canReplace(start, node.childCount)) && (end === node.childCount || node.canReplace(0, end));
16
+ }
17
+ /**
18
+ * Try to find a target depth to which the content in the given range
19
+ * can be lifted. Will not go across isolating parent nodes.
20
+ *
21
+ * @since 0.1.0
22
+ * @category queries
23
+ */
24
+ const liftTarget = (range) => Option.fromNullishOr(liftTargetOrNull(range));
25
+ /** @internal */
26
+ function liftTargetOrNull(range) {
27
+ const content = range.parent.content.cutByIndex(range.startIndex, range.endIndex);
28
+ for (let depth = range.depth, contentBefore = 0, contentAfter = 0;; --depth) {
29
+ const node = range.$from.node(depth);
30
+ const index = range.$from.index(depth) + contentBefore, endIndex = range.$to.indexAfter(depth) - contentAfter;
31
+ if (depth < range.depth && node.canReplace(index, endIndex, content)) return depth;
32
+ if (depth === 0 || node.type.spec.isolating || !canCut(node, index, endIndex)) break;
33
+ if (index) contentBefore = 1;
34
+ if (endIndex < node.childCount) contentAfter = 1;
35
+ }
36
+ return null;
37
+ }
38
+ /** @internal */
39
+ function lift(tr, range, target) {
40
+ const { $from, $to, depth } = range;
41
+ const gapStart = $from.beforeUnsafe(depth + 1), gapEnd = $to.afterUnsafe(depth + 1);
42
+ let start = gapStart, end = gapEnd;
43
+ let before = Fragment.empty, openStart = 0;
44
+ for (let d = depth, splitting = false; d > target; d--) if (splitting || $from.index(d) > 0) {
45
+ splitting = true;
46
+ before = Fragment.from($from.node(d).copy(before));
47
+ openStart++;
48
+ } else start--;
49
+ let after = Fragment.empty, openEnd = 0;
50
+ for (let d = depth, splitting = false; d > target; d--) if (splitting || $to.afterUnsafe(d + 1) < $to.end(d)) {
51
+ splitting = true;
52
+ after = Fragment.from($to.node(d).copy(after));
53
+ openEnd++;
54
+ } else end++;
55
+ tr.stepUnsafe(new ReplaceAroundStep(start, end, gapStart, gapEnd, new Slice(before.append(after), openStart, openEnd), before.size - openStart, true));
56
+ }
57
+ /**
58
+ * Try to find a valid way to wrap the content in the given range in a
59
+ * node of the given type. May introduce extra nodes around and inside
60
+ * the wrapper node, if necessary. Returns `None` if no valid wrapping
61
+ * could be found. When `innerRange` is given, that range's content is
62
+ * used as the content to fit into the wrapping, instead of the content
63
+ * of `range`.
64
+ *
65
+ * @since 0.1.0
66
+ * @category queries
67
+ */
68
+ const findWrapping = (range, nodeType, attrs = null, innerRange = range) => Option.fromNullishOr(findWrappingOrNull(range, nodeType, attrs, innerRange));
69
+ /** @internal */
70
+ function findWrappingOrNull(range, nodeType, attrs = null, innerRange = range) {
71
+ const around = findWrappingOutside(range, nodeType);
72
+ const inner = around && findWrappingInside(innerRange, nodeType);
73
+ if (!inner) return null;
74
+ return around.map(withAttrs).concat({
75
+ type: nodeType,
76
+ attrs
77
+ }).concat(inner.map(withAttrs));
78
+ }
79
+ const withAttrs = (type) => ({
80
+ type,
81
+ attrs: null
82
+ });
83
+ function findWrappingOutside(range, type) {
84
+ const { parent, startIndex, endIndex } = range;
85
+ const around = parent.contentMatchAtUnsafe(startIndex).findWrappingOrNull(type);
86
+ if (!around) return null;
87
+ const outer = around.length ? around[0] : type;
88
+ return parent.canReplaceWith(startIndex, endIndex, outer) ? around : null;
89
+ }
90
+ function findWrappingInside(range, type) {
91
+ const { endIndex, parent, startIndex } = range;
92
+ const inner = parent.content.content[startIndex];
93
+ const inside = type.contentMatch.findWrappingOrNull(inner.type);
94
+ if (!inside) return null;
95
+ let innerMatch = (inside.length ? inside[inside.length - 1] : type).contentMatch;
96
+ for (let i = startIndex; innerMatch && i < endIndex; i++) innerMatch = innerMatch.matchTypeOrNull(parent.content.content[i].type);
97
+ if (!innerMatch || !innerMatch.validEnd) return null;
98
+ return inside;
99
+ }
100
+ /** @internal */
101
+ function wrap(tr, range, wrappers) {
102
+ let content = Fragment.empty;
103
+ for (let i = wrappers.length - 1; i >= 0; i--) {
104
+ if (content.size) {
105
+ const match = wrappers[i].type.contentMatch.matchFragmentOrNull(content);
106
+ if (!match || !match.validEnd) throw new TransformError({ message: "Wrapper type given to Transform.wrap does not form valid content of its parent wrapper" });
107
+ }
108
+ content = Fragment.from(wrappers[i].type.createUnsafe(wrappers[i].attrs, content));
109
+ }
110
+ const start = range.start, end = range.end;
111
+ tr.stepUnsafe(new ReplaceAroundStep(start, end, start, end, new Slice(content, 0, 0), wrappers.length, true));
112
+ }
113
+ /** @internal */
114
+ function setBlockType(tr, from, to, type, attrs) {
115
+ if (!type.isTextblock) throw new TransformError({ message: "Type given to setBlockType should be a textblock" });
116
+ const mapFrom = tr.steps.length;
117
+ const linebreak = Option.getOrNull(type.schema.linebreakReplacement);
118
+ tr.doc.nodesBetween(from, to, (node, pos) => {
119
+ const attrsHere = typeof attrs === "function" ? attrs(node) : attrs;
120
+ if (node.isTextblock && !node.hasMarkup(type, attrsHere) && canChangeType(tr.doc, tr.mapping.slice(mapFrom).map(pos), type)) {
121
+ let convertNewlines = null;
122
+ if (linebreak) {
123
+ const pre = type.whitespace === "pre", supportLinebreak = !!type.contentMatch.matchTypeOrNull(linebreak);
124
+ if (pre && !supportLinebreak) convertNewlines = false;
125
+ else if (!pre && supportLinebreak) convertNewlines = true;
126
+ }
127
+ if (convertNewlines === false) replaceLinebreaks(tr, node, pos, mapFrom);
128
+ clearIncompatible(tr, tr.mapping.slice(mapFrom).map(pos, 1), type, void 0, convertNewlines === null);
129
+ const mapping = tr.mapping.slice(mapFrom);
130
+ const startM = mapping.map(pos, 1), endM = mapping.map(pos + node.nodeSize, 1);
131
+ tr.stepUnsafe(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new Slice(Fragment.from(type.createUnsafe(attrsHere, null, node.marks)), 0, 0), 1, true));
132
+ if (convertNewlines === true) replaceNewlines(tr, node, pos, mapFrom);
133
+ return false;
134
+ }
135
+ });
136
+ }
137
+ function replaceNewlines(tr, node, pos, mapFrom) {
138
+ const replacement = node.type.schema.linebreakReplacement;
139
+ if (Option.isNone(replacement)) return;
140
+ const linebreak = replacement.value;
141
+ node.forEach((child, offset) => {
142
+ if (child.isText) {
143
+ let m;
144
+ const newline = /\r?\n|\r/g;
145
+ while (m = newline.exec(child.text)) {
146
+ const start = tr.mapping.slice(mapFrom).map(pos + 1 + offset + m.index);
147
+ tr.replaceWithUnsafe(start, start + 1, linebreak.createUnsafe());
148
+ }
149
+ }
150
+ });
151
+ }
152
+ function replaceLinebreaks(tr, node, pos, mapFrom) {
153
+ const linebreak = Option.getOrNull(node.type.schema.linebreakReplacement);
154
+ node.forEach((child, offset) => {
155
+ if (child.type === linebreak) {
156
+ const start = tr.mapping.slice(mapFrom).map(pos + 1 + offset);
157
+ tr.replaceWithUnsafe(start, start + 1, node.type.schema.textUnsafe("\n"));
158
+ }
159
+ });
160
+ }
161
+ function canChangeType(doc, pos, type) {
162
+ const $pos = doc.resolveUnsafe(pos), index = $pos.index();
163
+ return $pos.parent.canReplaceWith(index, index + 1, type);
164
+ }
165
+ /** @internal */
166
+ function setNodeMarkup(tr, pos, type, attrs, marks) {
167
+ const node = tr.doc.nodeAtOrNull(pos);
168
+ if (!node) throw new TransformError({ message: "No node at given position" });
169
+ if (!type) type = node.type;
170
+ const newNode = type.createUnsafe(attrs, null, marks || node.marks);
171
+ if (node.isLeaf) {
172
+ tr.replaceWithUnsafe(pos, pos + node.nodeSize, newNode);
173
+ return;
174
+ }
175
+ if (!type.validContent(node.content)) throw new TransformError({ message: "Invalid content for node type " + type.name });
176
+ tr.stepUnsafe(new ReplaceAroundStep(pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1, new Slice(Fragment.from(newNode), 0, 0), 1, true));
177
+ }
178
+ /**
179
+ * Check whether splitting at the given position is allowed. Throws a
180
+ * `PositionError` when `pos` is outside of the document.
181
+ *
182
+ * @since 0.1.0
183
+ * @category queries
184
+ */
185
+ function canSplit(doc, pos, depth = 1, typesAfter) {
186
+ const types = typesAfter;
187
+ const $pos = doc.resolveUnsafe(pos), base = $pos.depth - depth;
188
+ const innerType = types && types[types.length - 1] || $pos.parent;
189
+ if (base < 0 || $pos.parent.type.spec.isolating || !$pos.parent.canReplace($pos.index(), $pos.parent.childCount) || !innerType.type.validContent($pos.parent.content.cutByIndex($pos.index(), $pos.parent.childCount))) return false;
190
+ for (let d = $pos.depth - 1, i = depth - 2; d > base; d--, i--) {
191
+ const node = $pos.node(d), index = $pos.index(d);
192
+ if (node.type.spec.isolating) return false;
193
+ let rest = node.content.cutByIndex(index, node.childCount);
194
+ const overrideChild = types && types[i + 1];
195
+ if (overrideChild) rest = rest.replaceChild(0, overrideChild.type.createUnsafe(overrideChild.attrs));
196
+ const after = types && types[i] || node;
197
+ if (!node.canReplace(index + 1, node.childCount) || !after.type.validContent(rest)) return false;
198
+ }
199
+ const index = $pos.indexAfter(base);
200
+ const baseType = types && types[0];
201
+ return $pos.node(base).canReplaceWith(index, index, baseType ? baseType.type : $pos.node(base + 1).type);
202
+ }
203
+ /** @internal */
204
+ function split(tr, pos, depth = 1, typesAfter) {
205
+ const $pos = tr.doc.resolveUnsafe(pos);
206
+ let before = Fragment.empty, after = Fragment.empty;
207
+ for (let d = $pos.depth, e = $pos.depth - depth, i = depth - 1; d > e; d--, i--) {
208
+ before = Fragment.from($pos.node(d).copy(before));
209
+ const typeAfter = typesAfter && typesAfter[i];
210
+ after = Fragment.from(typeAfter ? typeAfter.type.createUnsafe(typeAfter.attrs, after) : $pos.node(d).copy(after));
211
+ }
212
+ tr.stepUnsafe(new ReplaceStep(pos, pos, new Slice(before.append(after), depth, depth), true));
213
+ }
214
+ /**
215
+ * Test whether the blocks before and after a given position can be
216
+ * joined. Throws a `PositionError` when `pos` is outside of the
217
+ * document.
218
+ *
219
+ * @since 0.1.0
220
+ * @category queries
221
+ */
222
+ function canJoin(doc, pos) {
223
+ const $pos = doc.resolveUnsafe(pos), index = $pos.index();
224
+ return joinable($pos.nodeBeforeOrNull, $pos.nodeAfterOrNull) && $pos.parent.canReplace(index, index + 1);
225
+ }
226
+ function canAppendWithSubstitutedLinebreaks(a, b) {
227
+ if (!b.content.size) a.type.compatibleContent(b.type);
228
+ let match = a.contentMatchAtUnsafe(a.childCount);
229
+ const linebreakReplacement = Option.getOrNull(a.type.schema.linebreakReplacement);
230
+ for (let i = 0; i < b.childCount; i++) {
231
+ const child = b.content.content[i];
232
+ const type = child.type === linebreakReplacement ? a.type.schema.nodes.text : child.type;
233
+ match = match.matchTypeOrNull(type);
234
+ if (!match) return false;
235
+ if (!a.type.allowsMarks(child.marks)) return false;
236
+ }
237
+ return match.validEnd;
238
+ }
239
+ function joinable(a, b) {
240
+ return !!(a && b && !a.isLeaf && canAppendWithSubstitutedLinebreaks(a, b));
241
+ }
242
+ /**
243
+ * Find an ancestor of the given position that can be joined to the
244
+ * block before (or after if `dir` is positive). Returns the joinable
245
+ * point, if any.
246
+ *
247
+ * @since 0.1.0
248
+ * @category queries
249
+ */
250
+ function joinPoint(doc, pos, dir = -1) {
251
+ const $pos = doc.resolveUnsafe(pos);
252
+ for (let d = $pos.depth;; d--) {
253
+ let before, after;
254
+ let index = $pos.index(d);
255
+ if (d === $pos.depth) {
256
+ before = $pos.nodeBeforeOrNull;
257
+ after = $pos.nodeAfterOrNull;
258
+ } else if (dir > 0) {
259
+ before = $pos.node(d + 1);
260
+ index++;
261
+ after = $pos.node(d).content.content[index];
262
+ } else {
263
+ before = $pos.node(d).content.content[index - 1];
264
+ after = $pos.node(d + 1);
265
+ }
266
+ if (before && !before.isTextblock && joinable(before, after) && $pos.node(d).canReplace(index, index + 1)) return Option.some(pos);
267
+ if (d === 0) break;
268
+ pos = dir < 0 ? $pos.beforeUnsafe(d) : $pos.afterUnsafe(d);
269
+ }
270
+ return Option.none();
271
+ }
272
+ /** @internal */
273
+ function join(tr, pos, depth) {
274
+ let convertNewlines = null;
275
+ const linebreakReplacement = Option.getOrNull(tr.doc.type.schema.linebreakReplacement);
276
+ const $before = tr.doc.resolveUnsafe(pos - depth), beforeType = $before.node().type;
277
+ if (linebreakReplacement && beforeType.inlineContent) {
278
+ const pre = beforeType.whitespace === "pre";
279
+ const supportLinebreak = !!beforeType.contentMatch.matchTypeOrNull(linebreakReplacement);
280
+ if (pre && !supportLinebreak) convertNewlines = false;
281
+ else if (!pre && supportLinebreak) convertNewlines = true;
282
+ }
283
+ const mapFrom = tr.steps.length;
284
+ if (convertNewlines === false) {
285
+ const $after = tr.doc.resolveUnsafe(pos + depth);
286
+ replaceLinebreaks(tr, $after.node(), $after.beforeUnsafe(), mapFrom);
287
+ }
288
+ if (beforeType.inlineContent) clearIncompatible(tr, pos + depth - 1, beforeType, $before.node().contentMatchAtUnsafe($before.index()), convertNewlines == null);
289
+ const mapping = tr.mapping.slice(mapFrom), start = mapping.map(pos - depth);
290
+ tr.stepUnsafe(new ReplaceStep(start, mapping.map(pos + depth, -1), Slice.empty, true));
291
+ if (convertNewlines === true) {
292
+ const $full = tr.doc.resolveUnsafe(start);
293
+ replaceNewlines(tr, $full.node(), $full.beforeUnsafe(), tr.steps.length);
294
+ }
295
+ }
296
+ /**
297
+ * Try to find a point where a node of the given type can be inserted
298
+ * near `pos`, by searching up the node hierarchy when `pos` itself
299
+ * isn't a valid place but is at the start or end of a node. Returns
300
+ * `None` if no position was found.
301
+ *
302
+ * @since 0.1.0
303
+ * @category queries
304
+ */
305
+ const insertPoint = (doc, pos, nodeType) => Option.fromNullishOr(insertPointOrNull(doc, pos, nodeType));
306
+ /** @internal */
307
+ function insertPointOrNull(doc, pos, nodeType) {
308
+ const $pos = doc.resolveUnsafe(pos);
309
+ if ($pos.parent.canReplaceWith($pos.index(), $pos.index(), nodeType)) return pos;
310
+ if ($pos.parentOffset === 0) for (let d = $pos.depth - 1; d >= 0; d--) {
311
+ const index = $pos.index(d);
312
+ if ($pos.node(d).canReplaceWith(index, index, nodeType)) return $pos.beforeUnsafe(d + 1);
313
+ if (index > 0) return null;
314
+ }
315
+ if ($pos.parentOffset === $pos.parent.content.size) for (let d = $pos.depth - 1; d >= 0; d--) {
316
+ const index = $pos.indexAfter(d);
317
+ if ($pos.node(d).canReplaceWith(index, index, nodeType)) return $pos.afterUnsafe(d + 1);
318
+ if (index < $pos.node(d).childCount) return null;
319
+ }
320
+ return null;
321
+ }
322
+ /**
323
+ * Finds a position at or around the given position where the given
324
+ * slice can be inserted. Will look at parent nodes' nearest boundary
325
+ * and try there, even if the original position wasn't directly at the
326
+ * start or end of that node. Returns `None` when no position was found.
327
+ *
328
+ * @since 0.1.0
329
+ * @category queries
330
+ */
331
+ function dropPoint(doc, pos, slice) {
332
+ const $pos = doc.resolveUnsafe(pos);
333
+ if (!slice.content.size) return Option.some(pos);
334
+ let content = slice.content;
335
+ for (let i = 0; i < slice.openStart; i++) content = content.content[0].content;
336
+ for (let pass = 1; pass <= (slice.openStart === 0 && slice.size ? 2 : 1); pass++) for (let d = $pos.depth; d >= 0; d--) {
337
+ const bias = d === $pos.depth ? 0 : $pos.pos <= ($pos.start(d + 1) + $pos.end(d + 1)) / 2 ? -1 : 1;
338
+ const insertPos = $pos.index(d) + (bias > 0 ? 1 : 0);
339
+ const parent = $pos.node(d);
340
+ let fits = false;
341
+ if (pass === 1) fits = parent.canReplace(insertPos, insertPos, content);
342
+ else {
343
+ const wrapping = parent.contentMatchAtUnsafe(insertPos).findWrappingOrNull(content.content[0].type);
344
+ fits = wrapping && parent.canReplaceWith(insertPos, insertPos, wrapping[0]);
345
+ }
346
+ if (fits) return Option.some(bias === 0 ? $pos.pos : bias < 0 ? $pos.beforeUnsafe(d + 1) : $pos.afterUnsafe(d + 1));
347
+ }
348
+ return Option.none();
349
+ }
350
+ export { canJoin, canSplit, dropPoint, findWrapping, findWrappingOrNull, insertPoint, insertPointOrNull, join, joinPoint, lift, liftTarget, liftTargetOrNull, setBlockType, setNodeMarkup, split, wrap };