@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.
- package/LICENSE +48 -0
- package/README.md +118 -2
- package/dist/_virtual/_rolldown/runtime.js +12 -0
- package/dist/commands/commands.d.ts +323 -0
- package/dist/commands/commands.js +722 -0
- package/dist/commands/internal.js +43 -0
- package/dist/commands/keymaps.d.ts +46 -0
- package/dist/commands/keymaps.js +64 -0
- package/dist/commands.d.ts +3 -0
- package/dist/commands.js +3 -0
- package/dist/compat/commands.d.ts +250 -0
- package/dist/compat/commands.js +275 -0
- package/dist/compat/internal/core.d.ts +20 -0
- package/dist/compat/internal/core.js +148 -0
- package/dist/compat/internal/props.js +82 -0
- package/dist/compat/keymap.d.ts +21 -0
- package/dist/compat/keymap.js +38 -0
- package/dist/compat/model.d.ts +893 -0
- package/dist/compat/model.js +1212 -0
- package/dist/compat/orderedmap.d.ts +74 -0
- package/dist/compat/orderedmap.js +124 -0
- package/dist/compat/schema-basic.d.ts +38 -0
- package/dist/compat/schema-basic.js +238 -0
- package/dist/compat/schema-list.d.ts +81 -0
- package/dist/compat/schema-list.js +156 -0
- package/dist/compat/state.d.ts +415 -0
- package/dist/compat/state.js +689 -0
- package/dist/compat/test-builder.d.ts +98 -0
- package/dist/compat/test-builder.js +197 -0
- package/dist/compat/transform.d.ts +458 -0
- package/dist/compat/transform.js +803 -0
- package/dist/compat/view.d.ts +411 -0
- package/dist/compat/view.js +409 -0
- package/dist/dropcursor.d.ts +46 -0
- package/dist/dropcursor.js +176 -0
- package/dist/extensions/defaults.d.ts +716 -0
- package/dist/extensions/defaults.js +209 -0
- package/dist/extensions/editor.d.ts +246 -0
- package/dist/extensions/editor.js +271 -0
- package/dist/extensions/extension.d.ts +375 -0
- package/dist/extensions/extension.js +233 -0
- package/dist/extensions.d.ts +4 -0
- package/dist/extensions.js +4 -0
- package/dist/gapcursor.d.ts +66 -0
- package/dist/gapcursor.js +273 -0
- package/dist/history.d.ts +178 -0
- package/dist/history.js +412 -0
- package/dist/html.d.ts +158 -0
- package/dist/html.js +164 -0
- package/dist/inputrules.d.ts +271 -0
- package/dist/inputrules.js +321 -0
- package/dist/internal/attempt.js +15 -0
- package/dist/internal/json.js +29 -0
- package/dist/keymap/keymap.d.ts +130 -0
- package/dist/keymap/keymap.js +159 -0
- package/dist/keymap.d.ts +2 -0
- package/dist/keymap.js +1 -0
- package/dist/markdown/errors.d.ts +23 -0
- package/dist/markdown/errors.js +26 -0
- package/dist/markdown/from_markdown.d.ts +185 -0
- package/dist/markdown/from_markdown.js +266 -0
- package/dist/markdown/schema.d.ts +522 -0
- package/dist/markdown/schema.js +292 -0
- package/dist/markdown/to_markdown.d.ts +251 -0
- package/dist/markdown/to_markdown.js +413 -0
- package/dist/markdown.d.ts +5 -0
- package/dist/markdown.js +5 -0
- package/dist/model/attrsCodec.js +48 -0
- package/dist/model/comparedeep.js +17 -0
- package/dist/model/content.d.ts +232 -0
- package/dist/model/content.js +630 -0
- package/dist/model/diff.js +66 -0
- package/dist/model/dom.d.ts +3 -0
- package/dist/model/errors.d.ts +90 -0
- package/dist/model/errors.js +76 -0
- package/dist/model/fragment.d.ts +131 -0
- package/dist/model/fragment.js +262 -0
- package/dist/model/from_dom.d.ts +299 -0
- package/dist/model/from_dom.js +618 -0
- package/dist/model/json.d.ts +37 -0
- package/dist/model/json.js +126 -0
- package/dist/model/mark.d.ts +73 -0
- package/dist/model/mark.js +121 -0
- package/dist/model/node.d.ts +267 -0
- package/dist/model/node.js +434 -0
- package/dist/model/replace.d.ts +78 -0
- package/dist/model/replace.js +207 -0
- package/dist/model/resolvedpos.d.ts +196 -0
- package/dist/model/resolvedpos.js +305 -0
- package/dist/model/schema.d.ts +740 -0
- package/dist/model/schema.js +451 -0
- package/dist/model/to_dom.d.ts +169 -0
- package/dist/model/to_dom.js +238 -0
- package/dist/model.d.ts +12 -0
- package/dist/model.js +12 -0
- package/dist/placeholder.d.ts +53 -0
- package/dist/placeholder.js +79 -0
- package/dist/reactive/observer.d.ts +54 -0
- package/dist/reactive/observer.js +61 -0
- package/dist/reactive/selectors.d.ts +22 -0
- package/dist/reactive/selectors.js +61 -0
- package/dist/reactive/stateref.d.ts +72 -0
- package/dist/reactive/stateref.js +71 -0
- package/dist/reactive/streams.d.ts +83 -0
- package/dist/reactive/streams.js +85 -0
- package/dist/reactive.d.ts +16 -0
- package/dist/reactive.js +26 -0
- package/dist/schema-basic.d.ts +448 -0
- package/dist/schema-basic.js +269 -0
- package/dist/schema-list/commands.d.ts +66 -0
- package/dist/schema-list/commands.js +203 -0
- package/dist/schema-list.d.ts +92 -0
- package/dist/schema-list.js +100 -0
- package/dist/state/errors.d.ts +39 -0
- package/dist/state/errors.js +32 -0
- package/dist/state/plugin.d.ts +232 -0
- package/dist/state/plugin.js +82 -0
- package/dist/state/selection.d.ts +297 -0
- package/dist/state/selection.js +486 -0
- package/dist/state/state.d.ts +212 -0
- package/dist/state/state.js +489 -0
- package/dist/state/transaction.d.ts +145 -0
- package/dist/state/transaction.js +195 -0
- package/dist/state.d.ts +6 -0
- package/dist/state.js +6 -0
- package/dist/test-builder.d.ts +3582 -0
- package/dist/test-builder.js +239 -0
- package/dist/transform/attr_step.d.ts +60 -0
- package/dist/transform/attr_step.js +105 -0
- package/dist/transform/errors.d.ts +50 -0
- package/dist/transform/errors.js +35 -0
- package/dist/transform/map.d.ts +182 -0
- package/dist/transform/map.js +280 -0
- package/dist/transform/mark.js +98 -0
- package/dist/transform/mark_set_step.js +69 -0
- package/dist/transform/mark_step.d.ts +113 -0
- package/dist/transform/mark_step.js +225 -0
- package/dist/transform/replace.d.ts +18 -0
- package/dist/transform/replace.js +381 -0
- package/dist/transform/replace_step.d.ts +109 -0
- package/dist/transform/replace_step.js +175 -0
- package/dist/transform/step.d.ts +157 -0
- package/dist/transform/step.js +168 -0
- package/dist/transform/structure.d.ts +89 -0
- package/dist/transform/structure.js +350 -0
- package/dist/transform/transform.d.ts +248 -0
- package/dist/transform/transform.js +373 -0
- package/dist/transform.d.ts +10 -0
- package/dist/transform.js +10 -0
- package/dist/view/browser.js +39 -0
- package/dist/view/capturekeys.js +282 -0
- package/dist/view/clipboard.d.ts +8 -0
- package/dist/view/clipboard.js +245 -0
- package/dist/view/decoration.d.ts +339 -0
- package/dist/view/decoration.js +566 -0
- package/dist/view/dom.d.ts +18 -0
- package/dist/view/dom.js +140 -0
- package/dist/view/domchange.js +295 -0
- package/dist/view/domcoords.d.ts +22 -0
- package/dist/view/domcoords.js +448 -0
- package/dist/view/domobserver.d.ts +47 -0
- package/dist/view/domobserver.js +328 -0
- package/dist/view/errors.d.ts +18 -0
- package/dist/view/errors.js +21 -0
- package/dist/view/input.d.ts +58 -0
- package/dist/view/input.js +704 -0
- package/dist/view/selection.js +198 -0
- package/dist/view/view.d.ts +699 -0
- package/dist/view/view.js +623 -0
- package/dist/view/viewdesc.d.ts +236 -0
- package/dist/view/viewdesc.js +1205 -0
- package/dist/view-css.d.ts +43 -0
- package/dist/view-css.js +124 -0
- package/dist/view.d.ts +6 -0
- package/dist/view.js +4 -0
- package/package.json +206 -3
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { attempt } from "../internal/attempt.js";
|
|
2
|
+
import { Fragment } from "../model/fragment.js";
|
|
3
|
+
import { JSONError, isModelError } from "../model/errors.js";
|
|
4
|
+
import { Slice } from "../model/replace.js";
|
|
5
|
+
import { ReplaceAroundStep, ReplaceStep } from "../transform/replace_step.js";
|
|
6
|
+
import { SelectionError, isStateFailure } from "./errors.js";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
9
|
+
import * as Predicate from "effect/Predicate";
|
|
10
|
+
import * as S from "effect/Schema";
|
|
11
|
+
import * as SchemaGetter from "effect/SchemaGetter";
|
|
12
|
+
import * as SchemaIssue from "effect/SchemaIssue";
|
|
13
|
+
// src/state/selection.ts
|
|
14
|
+
/**
|
|
15
|
+
* Editor selections.
|
|
16
|
+
*
|
|
17
|
+
* @since 0.1.0
|
|
18
|
+
*/
|
|
19
|
+
const isPositionOrSelectionError = (u) => isModelError(u) || u instanceof SelectionError;
|
|
20
|
+
const classesById = Object.create(null);
|
|
21
|
+
/**
|
|
22
|
+
* Superclass for editor selections. Every selection type should extend
|
|
23
|
+
* this. Should not be instantiated directly.
|
|
24
|
+
*
|
|
25
|
+
* @since 0.1.0
|
|
26
|
+
* @category models
|
|
27
|
+
*/
|
|
28
|
+
var Selection = class Selection {
|
|
29
|
+
/**
|
|
30
|
+
* Initialize a selection with the head and anchor and ranges. If no
|
|
31
|
+
* ranges are given, constructs a single range across `$anchor` and
|
|
32
|
+
* `$head`.
|
|
33
|
+
*/
|
|
34
|
+
constructor($anchor, $head, ranges) {
|
|
35
|
+
this.$anchor = $anchor;
|
|
36
|
+
this.$head = $head;
|
|
37
|
+
this.ranges = ranges || [new SelectionRange($anchor.min($head), $anchor.max($head))];
|
|
38
|
+
}
|
|
39
|
+
/** The selection's anchor, as an unresolved position. */
|
|
40
|
+
get anchor() {
|
|
41
|
+
return this.$anchor.pos;
|
|
42
|
+
}
|
|
43
|
+
/** The selection's head. */
|
|
44
|
+
get head() {
|
|
45
|
+
return this.$head.pos;
|
|
46
|
+
}
|
|
47
|
+
/** The lower bound of the selection's main range. */
|
|
48
|
+
get from() {
|
|
49
|
+
return this.$from.pos;
|
|
50
|
+
}
|
|
51
|
+
/** The upper bound of the selection's main range. */
|
|
52
|
+
get to() {
|
|
53
|
+
return this.$to.pos;
|
|
54
|
+
}
|
|
55
|
+
/** The resolved lower bound of the selection's main range. */
|
|
56
|
+
get $from() {
|
|
57
|
+
return this.ranges[0].$from;
|
|
58
|
+
}
|
|
59
|
+
/** The resolved upper bound of the selection's main range. */
|
|
60
|
+
get $to() {
|
|
61
|
+
return this.ranges[0].$to;
|
|
62
|
+
}
|
|
63
|
+
/** Indicates whether the selection contains any content. */
|
|
64
|
+
get empty() {
|
|
65
|
+
const ranges = this.ranges;
|
|
66
|
+
for (let i = 0; i < ranges.length; i++) if (ranges[i].$from.pos !== ranges[i].$to.pos) return false;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Controls whether, when a selection of this type is active in the
|
|
71
|
+
* browser, the selected range should be visible to the user.
|
|
72
|
+
* Defaults to `true`.
|
|
73
|
+
*/
|
|
74
|
+
get visible() {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
/** Get the content of this selection as a slice. */
|
|
78
|
+
content() {
|
|
79
|
+
return this.$from.doc.sliceUnsafe(this.from, this.to, true);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Replace the selection with a slice or, if no slice is given, delete
|
|
83
|
+
* the selection. Will append to the given transaction.
|
|
84
|
+
*/
|
|
85
|
+
replace(tr, content = Slice.empty) {
|
|
86
|
+
let lastNode = content.content.content[content.content.childCount - 1];
|
|
87
|
+
let lastParent = null;
|
|
88
|
+
for (let i = 0; i < content.openEnd; i++) {
|
|
89
|
+
lastParent = lastNode;
|
|
90
|
+
lastNode = lastNode.content.content[lastNode.childCount - 1];
|
|
91
|
+
}
|
|
92
|
+
const mapFrom = tr.steps.length, ranges = this.ranges;
|
|
93
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
94
|
+
const { $from, $to } = ranges[i], mapping = tr.mapping.slice(mapFrom);
|
|
95
|
+
tr.replaceRangeUnsafe(mapping.map($from.pos), mapping.map($to.pos), i ? Slice.empty : content);
|
|
96
|
+
if (i === 0) selectionToInsertionEnd(tr, mapFrom, (lastNode ? lastNode.isInline : lastParent && lastParent.isTextblock) ? -1 : 1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Replace the selection with the given node, appending the changes to the given transaction. */
|
|
100
|
+
replaceWith(tr, node) {
|
|
101
|
+
const mapFrom = tr.steps.length, ranges = this.ranges;
|
|
102
|
+
for (let i = 0; i < ranges.length; i++) {
|
|
103
|
+
const { $from, $to } = ranges[i], mapping = tr.mapping.slice(mapFrom);
|
|
104
|
+
const from = mapping.map($from.pos), to = mapping.map($to.pos);
|
|
105
|
+
if (i) tr.deleteRangeUnsafe(from, to);
|
|
106
|
+
else {
|
|
107
|
+
tr.replaceRangeWithUnsafe(from, to, node);
|
|
108
|
+
selectionToInsertionEnd(tr, mapFrom, node.isInline ? -1 : 1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Find a valid cursor or leaf node selection starting at the given
|
|
114
|
+
* position and searching back if `dir` is negative, and forward if
|
|
115
|
+
* positive. When `textOnly` is true, only consider cursor selections.
|
|
116
|
+
* Will return `None` when no valid selection position is found.
|
|
117
|
+
*/
|
|
118
|
+
static findFrom($pos, dir, textOnly = false) {
|
|
119
|
+
return Option.fromNullishOr(Selection.findFromOrNull($pos, dir, textOnly));
|
|
120
|
+
}
|
|
121
|
+
/** @internal */
|
|
122
|
+
static findFromOrNull($pos, dir, textOnly = false) {
|
|
123
|
+
const inner = $pos.parent.inlineContent ? new TextSelection($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
|
|
124
|
+
if (inner) return inner;
|
|
125
|
+
for (let depth = $pos.depth - 1; depth >= 0; depth--) {
|
|
126
|
+
const found = dir < 0 ? findSelectionIn($pos.node(0), $pos.node(depth), $pos.beforeUnsafe(depth + 1), $pos.index(depth), dir, textOnly) : findSelectionIn($pos.node(0), $pos.node(depth), $pos.afterUnsafe(depth + 1), $pos.index(depth) + 1, dir, textOnly);
|
|
127
|
+
if (found) return found;
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Find a valid cursor or leaf node selection near the given position.
|
|
133
|
+
* Searches forward first by default, but if `bias` is negative, it
|
|
134
|
+
* will search backwards first.
|
|
135
|
+
*/
|
|
136
|
+
static near($pos, bias = 1) {
|
|
137
|
+
return this.findFromOrNull($pos, bias) || this.findFromOrNull($pos, -bias) || new AllSelection($pos.node(0));
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Find the cursor or leaf node selection closest to the start of the
|
|
141
|
+
* given document. Will return an `AllSelection` if no valid position
|
|
142
|
+
* exists.
|
|
143
|
+
*/
|
|
144
|
+
static atStart(doc) {
|
|
145
|
+
return findSelectionIn(doc, doc, 0, 0, 1) || new AllSelection(doc);
|
|
146
|
+
}
|
|
147
|
+
/** Find the cursor or leaf node selection closest to the end of the given document. */
|
|
148
|
+
static atEnd(doc) {
|
|
149
|
+
return findSelectionIn(doc, doc, doc.content.size, doc.childCount, -1) || new AllSelection(doc);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Deserialize the JSON representation of a selection. Dispatches to
|
|
153
|
+
* the selection class registered (with `jsonID`) for the JSON's
|
|
154
|
+
* `type`.
|
|
155
|
+
*/
|
|
156
|
+
static fromJSON(doc, json) {
|
|
157
|
+
return attempt(() => Selection.fromJSONUnsafe(doc, json), (u) => isModelError(u) || isStateFailure(u));
|
|
158
|
+
}
|
|
159
|
+
/** Like `fromJSON`, but throws on invalid input. */
|
|
160
|
+
static fromJSONUnsafe(doc, json) {
|
|
161
|
+
if (!json || typeof json !== "object" || !json.type) throw new JSONError({ message: "Invalid input for Selection.fromJSON" });
|
|
162
|
+
const cls = classesById[json.type];
|
|
163
|
+
if (!cls) throw new JSONError({ message: `No selection type ${json.type} defined` });
|
|
164
|
+
return cls.fromJSONUnsafe(doc, json);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* To be able to deserialize selections from JSON, custom selection
|
|
168
|
+
* classes must register themselves with an ID string, so that they
|
|
169
|
+
* can be disambiguated. Try to pick something that's unlikely to
|
|
170
|
+
* clash with classes from other modules. Throws a `SelectionError`
|
|
171
|
+
* when the ID is already in use.
|
|
172
|
+
*/
|
|
173
|
+
static jsonID(id, selectionClass) {
|
|
174
|
+
if (id in classesById) throw new SelectionError({ message: "Duplicate use of selection JSON ID " + id });
|
|
175
|
+
classesById[id] = selectionClass;
|
|
176
|
+
const proto = selectionClass.prototype;
|
|
177
|
+
if (proto) proto.jsonID = id;
|
|
178
|
+
return selectionClass;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get a bookmark for this selection, which is a value that can be
|
|
182
|
+
* mapped without having access to a current document, and later
|
|
183
|
+
* resolved to a real selection for a given document again. (This is
|
|
184
|
+
* used mostly by the history to track and restore old selections.)
|
|
185
|
+
* The default implementation of this method just converts the
|
|
186
|
+
* selection to a text selection and returns the bookmark for that.
|
|
187
|
+
*/
|
|
188
|
+
getBookmark() {
|
|
189
|
+
return TextSelection.between(this.$anchor, this.$head).getBookmark();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Represents a selected range in a document.
|
|
194
|
+
*
|
|
195
|
+
* @since 0.1.0
|
|
196
|
+
* @category models
|
|
197
|
+
*/
|
|
198
|
+
var SelectionRange = class {
|
|
199
|
+
/** Create a range. */
|
|
200
|
+
constructor($from, $to) {
|
|
201
|
+
this.$from = $from;
|
|
202
|
+
this.$to = $to;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
let warnedAboutTextSelection = false;
|
|
206
|
+
function checkTextSelection($pos) {
|
|
207
|
+
if (!warnedAboutTextSelection && !$pos.parent.inlineContent) {
|
|
208
|
+
warnedAboutTextSelection = true;
|
|
209
|
+
console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* A text selection represents a classical editor selection, with a head
|
|
214
|
+
* (the moving side) and anchor (immobile side), both of which point into
|
|
215
|
+
* textblock nodes. It can be empty (a regular cursor position).
|
|
216
|
+
*
|
|
217
|
+
* @since 0.1.0
|
|
218
|
+
* @category models
|
|
219
|
+
*/
|
|
220
|
+
var TextSelection = class TextSelection extends Selection {
|
|
221
|
+
/** Construct a text selection between the given points. */
|
|
222
|
+
constructor($anchor, $head = $anchor) {
|
|
223
|
+
checkTextSelection($anchor);
|
|
224
|
+
checkTextSelection($head);
|
|
225
|
+
super($anchor, $head);
|
|
226
|
+
}
|
|
227
|
+
/** Returns a resolved position if this is a cursor selection (an empty text selection). */
|
|
228
|
+
get $cursor() {
|
|
229
|
+
return Option.fromNullishOr(this.$cursorOrNull);
|
|
230
|
+
}
|
|
231
|
+
/** @internal */
|
|
232
|
+
get $cursorOrNull() {
|
|
233
|
+
return this.$anchor.pos === this.$head.pos ? this.$head : null;
|
|
234
|
+
}
|
|
235
|
+
map(doc, mapping) {
|
|
236
|
+
const $head = doc.resolveUnsafe(mapping.map(this.head));
|
|
237
|
+
if (!$head.parent.inlineContent) return Selection.near($head);
|
|
238
|
+
const $anchor = doc.resolveUnsafe(mapping.map(this.anchor));
|
|
239
|
+
return new TextSelection($anchor.parent.inlineContent ? $anchor : $head, $head);
|
|
240
|
+
}
|
|
241
|
+
replace(tr, content = Slice.empty) {
|
|
242
|
+
super.replace(tr, content);
|
|
243
|
+
if (content === Slice.empty) {
|
|
244
|
+
const marks = this.$from.marksAcrossOrNull(this.$to);
|
|
245
|
+
if (marks) tr.ensureMarks(marks);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
eq(other) {
|
|
249
|
+
return other instanceof TextSelection && other.anchor === this.anchor && other.head === this.head;
|
|
250
|
+
}
|
|
251
|
+
getBookmark() {
|
|
252
|
+
return new TextBookmark(this.anchor, this.head);
|
|
253
|
+
}
|
|
254
|
+
toJSON() {
|
|
255
|
+
return {
|
|
256
|
+
type: "text",
|
|
257
|
+
anchor: this.anchor,
|
|
258
|
+
head: this.head
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/** @internal */
|
|
262
|
+
static fromJSONUnsafe(doc, json) {
|
|
263
|
+
if (typeof json.anchor !== "number" || typeof json.head !== "number") throw new JSONError({ message: "Invalid input for TextSelection.fromJSON" });
|
|
264
|
+
return new TextSelection(doc.resolveUnsafe(json.anchor), doc.resolveUnsafe(json.head));
|
|
265
|
+
}
|
|
266
|
+
/** Create a text selection from non-resolved positions. */
|
|
267
|
+
static create(doc, anchor, head = anchor) {
|
|
268
|
+
return attempt(() => TextSelection.createUnsafe(doc, anchor, head), isModelError);
|
|
269
|
+
}
|
|
270
|
+
/** Like `create`, but throws a `PositionError` for positions outside of the document. */
|
|
271
|
+
static createUnsafe(doc, anchor, head = anchor) {
|
|
272
|
+
const $anchor = doc.resolveUnsafe(anchor);
|
|
273
|
+
return new TextSelection($anchor, head === anchor ? $anchor : doc.resolveUnsafe(head));
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Return a text selection that spans the given positions or, if they
|
|
277
|
+
* aren't text positions, find a text selection near them. `bias`
|
|
278
|
+
* determines whether the method searches forward (default) or
|
|
279
|
+
* backwards (negative number) first. Will fall back to calling
|
|
280
|
+
* `Selection.near` when the document doesn't contain a valid text
|
|
281
|
+
* position.
|
|
282
|
+
*/
|
|
283
|
+
static between($anchor, $head, bias) {
|
|
284
|
+
const dPos = $anchor.pos - $head.pos;
|
|
285
|
+
if (!bias || dPos) bias = dPos >= 0 ? 1 : -1;
|
|
286
|
+
if (!$head.parent.inlineContent) {
|
|
287
|
+
const found = Selection.findFromOrNull($head, bias, true) || Selection.findFromOrNull($head, -bias, true);
|
|
288
|
+
if (found) $head = found.$head;
|
|
289
|
+
else return Selection.near($head, bias);
|
|
290
|
+
}
|
|
291
|
+
if (!$anchor.parent.inlineContent) {
|
|
292
|
+
if (dPos === 0) $anchor = $head;
|
|
293
|
+
else {
|
|
294
|
+
$anchor = (Selection.findFromOrNull($anchor, -bias, true) || Selection.findFromOrNull($anchor, bias, true)).$anchor;
|
|
295
|
+
if ($anchor.pos < $head.pos !== dPos < 0) $anchor = $head;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return new TextSelection($anchor, $head);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
Selection.jsonID("text", TextSelection);
|
|
302
|
+
var TextBookmark = class TextBookmark {
|
|
303
|
+
constructor(anchor, head) {
|
|
304
|
+
this.anchor = anchor;
|
|
305
|
+
this.head = head;
|
|
306
|
+
}
|
|
307
|
+
map(mapping) {
|
|
308
|
+
return new TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
|
|
309
|
+
}
|
|
310
|
+
resolve(doc) {
|
|
311
|
+
return TextSelection.between(doc.resolveUnsafe(this.anchor), doc.resolveUnsafe(this.head));
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* A node selection is a selection that points at a single node. All
|
|
316
|
+
* nodes marked selectable can be the target of a node selection. In
|
|
317
|
+
* such a selection, `from` and `to` point directly before and after the
|
|
318
|
+
* selected node, `anchor` equals `from`, and `head` equals `to`.
|
|
319
|
+
*
|
|
320
|
+
* @since 0.1.0
|
|
321
|
+
* @category models
|
|
322
|
+
*/
|
|
323
|
+
var NodeSelection = class NodeSelection extends Selection {
|
|
324
|
+
/**
|
|
325
|
+
* Create a node selection. Does not verify the validity of its
|
|
326
|
+
* argument beyond requiring a node after `$pos` (throwing a
|
|
327
|
+
* `SelectionError` otherwise).
|
|
328
|
+
*/
|
|
329
|
+
constructor($pos) {
|
|
330
|
+
const node = $pos.nodeAfterOrNull;
|
|
331
|
+
if (!node) throw new SelectionError({ message: "No node after position " + $pos.pos + " for NodeSelection" });
|
|
332
|
+
const $end = $pos.node(0).resolveUnsafe($pos.pos + node.nodeSize);
|
|
333
|
+
super($pos, $end);
|
|
334
|
+
this.node = node;
|
|
335
|
+
}
|
|
336
|
+
get visible() {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
map(doc, mapping) {
|
|
340
|
+
const { deleted, pos } = mapping.mapResult(this.anchor);
|
|
341
|
+
const $pos = doc.resolveUnsafe(pos);
|
|
342
|
+
if (deleted) return Selection.near($pos);
|
|
343
|
+
return new NodeSelection($pos);
|
|
344
|
+
}
|
|
345
|
+
content() {
|
|
346
|
+
return new Slice(Fragment.from(this.node), 0, 0);
|
|
347
|
+
}
|
|
348
|
+
eq(other) {
|
|
349
|
+
return other instanceof NodeSelection && other.anchor === this.anchor;
|
|
350
|
+
}
|
|
351
|
+
toJSON() {
|
|
352
|
+
return {
|
|
353
|
+
type: "node",
|
|
354
|
+
anchor: this.anchor
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
getBookmark() {
|
|
358
|
+
return new NodeBookmark(this.anchor);
|
|
359
|
+
}
|
|
360
|
+
/** @internal */
|
|
361
|
+
static fromJSONUnsafe(doc, json) {
|
|
362
|
+
if (typeof json.anchor !== "number") throw new JSONError({ message: "Invalid input for NodeSelection.fromJSON" });
|
|
363
|
+
return new NodeSelection(doc.resolveUnsafe(json.anchor));
|
|
364
|
+
}
|
|
365
|
+
/** Create a node selection from a non-resolved position. */
|
|
366
|
+
static create(doc, from) {
|
|
367
|
+
return attempt(() => NodeSelection.createUnsafe(doc, from), isPositionOrSelectionError);
|
|
368
|
+
}
|
|
369
|
+
/** Like `create`, but throws for invalid positions. */
|
|
370
|
+
static createUnsafe(doc, from) {
|
|
371
|
+
return new NodeSelection(doc.resolveUnsafe(from));
|
|
372
|
+
}
|
|
373
|
+
/** Determines whether the given node may be selected as a node selection. */
|
|
374
|
+
static isSelectable(node) {
|
|
375
|
+
return !node.isText && node.type.spec.selectable !== false;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
Selection.jsonID("node", NodeSelection);
|
|
379
|
+
var NodeBookmark = class NodeBookmark {
|
|
380
|
+
constructor(anchor) {
|
|
381
|
+
this.anchor = anchor;
|
|
382
|
+
}
|
|
383
|
+
map(mapping) {
|
|
384
|
+
const { deleted, pos } = mapping.mapResult(this.anchor);
|
|
385
|
+
return deleted ? new TextBookmark(pos, pos) : new NodeBookmark(pos);
|
|
386
|
+
}
|
|
387
|
+
resolve(doc) {
|
|
388
|
+
const $pos = doc.resolveUnsafe(this.anchor), node = $pos.nodeAfterOrNull;
|
|
389
|
+
if (node && NodeSelection.isSelectable(node)) return new NodeSelection($pos);
|
|
390
|
+
return Selection.near($pos);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
/**
|
|
394
|
+
* A selection type that represents selecting the whole document (which
|
|
395
|
+
* can not necessarily be expressed with a text selection, when there
|
|
396
|
+
* are for example leaf block nodes at the start or end of the
|
|
397
|
+
* document).
|
|
398
|
+
*
|
|
399
|
+
* @since 0.1.0
|
|
400
|
+
* @category models
|
|
401
|
+
*/
|
|
402
|
+
var AllSelection = class AllSelection extends Selection {
|
|
403
|
+
/** Create an all-selection over the given document. */
|
|
404
|
+
constructor(doc) {
|
|
405
|
+
super(doc.resolveUnsafe(0), doc.resolveUnsafe(doc.content.size));
|
|
406
|
+
}
|
|
407
|
+
replace(tr, content = Slice.empty) {
|
|
408
|
+
if (content === Slice.empty) {
|
|
409
|
+
tr.deleteUnsafe(0, tr.doc.content.size);
|
|
410
|
+
const sel = Selection.atStart(tr.doc);
|
|
411
|
+
if (!sel.eq(tr.selection)) tr.setSelectionUnsafe(sel);
|
|
412
|
+
} else super.replace(tr, content);
|
|
413
|
+
}
|
|
414
|
+
toJSON() {
|
|
415
|
+
return { type: "all" };
|
|
416
|
+
}
|
|
417
|
+
/** @internal */
|
|
418
|
+
static fromJSONUnsafe(doc) {
|
|
419
|
+
return new AllSelection(doc);
|
|
420
|
+
}
|
|
421
|
+
map(doc) {
|
|
422
|
+
return new AllSelection(doc);
|
|
423
|
+
}
|
|
424
|
+
eq(other) {
|
|
425
|
+
return other instanceof AllSelection;
|
|
426
|
+
}
|
|
427
|
+
getBookmark() {
|
|
428
|
+
return AllBookmark;
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
Selection.jsonID("all", AllSelection);
|
|
432
|
+
const AllBookmark = {
|
|
433
|
+
map() {
|
|
434
|
+
return this;
|
|
435
|
+
},
|
|
436
|
+
resolve(doc) {
|
|
437
|
+
return new AllSelection(doc);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
function findSelectionIn(doc, node, pos, index, dir, text = false) {
|
|
441
|
+
if (node.inlineContent) return TextSelection.createUnsafe(doc, pos);
|
|
442
|
+
for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
|
|
443
|
+
const child = node.content.content[i];
|
|
444
|
+
if (!child.isAtom) {
|
|
445
|
+
const inner = findSelectionIn(doc, child, pos + dir, dir < 0 ? child.childCount : 0, dir, text);
|
|
446
|
+
if (inner) return inner;
|
|
447
|
+
} else if (!text && NodeSelection.isSelectable(child)) return NodeSelection.createUnsafe(doc, pos - (dir < 0 ? child.nodeSize : 0));
|
|
448
|
+
pos += child.nodeSize * dir;
|
|
449
|
+
}
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
function selectionToInsertionEnd(tr, startLen, bias) {
|
|
453
|
+
const last = tr.steps.length - 1;
|
|
454
|
+
if (last < startLen) return;
|
|
455
|
+
const step = tr.steps[last];
|
|
456
|
+
if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep)) return;
|
|
457
|
+
const map = tr.mapping.maps[last];
|
|
458
|
+
let end;
|
|
459
|
+
map.forEach((_from, _to, _newFrom, newTo) => {
|
|
460
|
+
if (end == null) end = newTo;
|
|
461
|
+
});
|
|
462
|
+
tr.setSelectionUnsafe(Selection.near(tr.doc.resolveUnsafe(end), bias));
|
|
463
|
+
}
|
|
464
|
+
const SelectionJSONSchema = S.declare((u) => typeof u === "object" && u !== null && typeof u.type === "string", { title: "SelectionJSON" });
|
|
465
|
+
/**
|
|
466
|
+
* An Effect `Schema` that decodes the JSON representation of a
|
|
467
|
+
* selection (using the `Selection.jsonID` registry) into a selection in
|
|
468
|
+
* the given document, and encodes selections back to JSON.
|
|
469
|
+
*
|
|
470
|
+
* @since 0.1.0
|
|
471
|
+
* @category schemas
|
|
472
|
+
*/
|
|
473
|
+
const SelectionFromJSON = (doc) => {
|
|
474
|
+
const target = S.declare((u) => u instanceof Selection && u.$anchor.doc === doc, { title: "Selection" });
|
|
475
|
+
return SelectionJSONSchema.pipe(S.decodeTo(target, {
|
|
476
|
+
decode: SchemaGetter.transformEffect((json) => Effect.suspend(() => {
|
|
477
|
+
try {
|
|
478
|
+
return Effect.succeed(Selection.fromJSONUnsafe(doc, json));
|
|
479
|
+
} catch (e) {
|
|
480
|
+
return Effect.fail(new SchemaIssue.InvalidValue({ message: Predicate.hasProperty(e, "message") ? String(e.message) : String(e) }, json));
|
|
481
|
+
}
|
|
482
|
+
})),
|
|
483
|
+
encode: SchemaGetter.transform((selection) => selection.toJSON())
|
|
484
|
+
}));
|
|
485
|
+
};
|
|
486
|
+
export { AllSelection, NodeSelection, Selection, SelectionFromJSON, SelectionRange, TextSelection };
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { ModelError } from "../model/errors.js";
|
|
2
|
+
import { AnySchema } from "../model/schema.js";
|
|
3
|
+
import { Mark, MarkJSON } from "../model/mark.js";
|
|
4
|
+
import { Node, NodeJSON } from "../model/node.js";
|
|
5
|
+
import "../model.js";
|
|
6
|
+
import { SelectionError, StateError } from "./errors.js";
|
|
7
|
+
import { AnyPlugin, Plugin, PluginErrors, PluginRequirements, StateField } from "./plugin.js";
|
|
8
|
+
import { Selection, SelectionJSON } from "./selection.js";
|
|
9
|
+
import { Transaction } from "./transaction.js";
|
|
10
|
+
import * as Effect from "effect/Effect";
|
|
11
|
+
import * as Option from "effect/Option";
|
|
12
|
+
/**
|
|
13
|
+
* The type of object passed to `EditorState.create`.
|
|
14
|
+
*
|
|
15
|
+
* @since 0.1.0
|
|
16
|
+
* @category models
|
|
17
|
+
*/
|
|
18
|
+
export interface EditorStateConfig<Sc extends AnySchema = AnySchema, P extends ReadonlyArray<AnyPlugin> = ReadonlyArray<AnyPlugin>> {
|
|
19
|
+
/** The schema to use (only relevant if no `doc` is specified). */
|
|
20
|
+
readonly schema?: Sc | undefined;
|
|
21
|
+
/** The starting document. Either this or `schema` _must_ be provided. */
|
|
22
|
+
readonly doc?: Node<Sc> | undefined;
|
|
23
|
+
/** A valid selection in the document. */
|
|
24
|
+
readonly selection?: Selection<Sc> | undefined;
|
|
25
|
+
/** The initial set of stored marks. */
|
|
26
|
+
readonly storedMarks?: ReadonlyArray<Mark<Sc>> | null | undefined;
|
|
27
|
+
/** The plugins that should be active in this state. */
|
|
28
|
+
readonly plugins?: P | undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The JSON representation of an editor state (see `EditorState.toJSON`).
|
|
32
|
+
*
|
|
33
|
+
* @since 0.1.0
|
|
34
|
+
* @category models
|
|
35
|
+
*/
|
|
36
|
+
export interface EditorStateJSON {
|
|
37
|
+
readonly doc: NodeJSON;
|
|
38
|
+
readonly selection: SelectionJSON;
|
|
39
|
+
readonly storedMarks?: ReadonlyArray<MarkJSON>;
|
|
40
|
+
readonly [field: string]: unknown;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The result of `EditorState.applyTransaction`.
|
|
44
|
+
*
|
|
45
|
+
* @since 0.1.0
|
|
46
|
+
* @category models
|
|
47
|
+
*/
|
|
48
|
+
export interface ApplyResult<Sc extends AnySchema, R, E> {
|
|
49
|
+
readonly state: EditorState<Sc, R, E>;
|
|
50
|
+
readonly transactions: ReadonlyArray<Transaction<Sc>>;
|
|
51
|
+
}
|
|
52
|
+
interface FieldDesc {
|
|
53
|
+
readonly name: string;
|
|
54
|
+
readonly self: unknown;
|
|
55
|
+
readonly init: StateField<any, any, any, any>["init"];
|
|
56
|
+
readonly apply: StateField<any, any, any, any>["apply"];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Object wrapping the part of a state object that stays the same across
|
|
60
|
+
* transactions. Stored in the state's `config` property.
|
|
61
|
+
*
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
|
+
export declare class Configuration<Sc extends AnySchema> {
|
|
65
|
+
readonly schema: Sc;
|
|
66
|
+
readonly fields: Array<FieldDesc>;
|
|
67
|
+
readonly plugins: Array<AnyPlugin>;
|
|
68
|
+
readonly pluginsByKey: Record<string, AnyPlugin>;
|
|
69
|
+
constructor(schema: Sc, plugins?: ReadonlyArray<AnyPlugin>);
|
|
70
|
+
}
|
|
71
|
+
/** Marker returned by the synchronous fast path when a hook returned an Effect. */
|
|
72
|
+
declare const NeedsEffect: unique symbol;
|
|
73
|
+
/**
|
|
74
|
+
* Run an effect synchronously, throwing its (first) failure or defect.
|
|
75
|
+
*
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
export declare const runSyncUnsafe: <A, E>(effect: Effect.Effect<A, E>) => A;
|
|
79
|
+
/**
|
|
80
|
+
* @since 0.1.0
|
|
81
|
+
* @category symbols
|
|
82
|
+
*/
|
|
83
|
+
export declare const EditorStateTypeId: unique symbol;
|
|
84
|
+
/**
|
|
85
|
+
* @since 0.1.0
|
|
86
|
+
* @category symbols
|
|
87
|
+
*/
|
|
88
|
+
export type EditorStateTypeId = typeof EditorStateTypeId;
|
|
89
|
+
/**
|
|
90
|
+
* The state of an editor is represented by an object of this type. A
|
|
91
|
+
* state is a persistent data structure—it isn't updated, but rather a
|
|
92
|
+
* new state value is computed from an old one using the `apply` method.
|
|
93
|
+
*
|
|
94
|
+
* A state holds a number of built-in fields, and plugins can define
|
|
95
|
+
* additional fields.
|
|
96
|
+
*
|
|
97
|
+
* `R` and `E` are the unions of the requirements and errors of the
|
|
98
|
+
* state's plugins: applying a transaction runs the plugins' hooks, so
|
|
99
|
+
* `apply` returns an `Effect<EditorState<Sc, R, E>, E | StateError, R>`.
|
|
100
|
+
* States whose plugins require no services (`R = never`) can also be
|
|
101
|
+
* updated synchronously with `applyUnsafe`.
|
|
102
|
+
*
|
|
103
|
+
* @since 0.1.0
|
|
104
|
+
* @category models
|
|
105
|
+
*/
|
|
106
|
+
export declare class EditorState<Sc extends AnySchema = AnySchema, R = never, E = never> {
|
|
107
|
+
/** @internal */
|
|
108
|
+
readonly config: Configuration<Sc>;
|
|
109
|
+
/** @internal */
|
|
110
|
+
readonly slots: Record<string, any>;
|
|
111
|
+
readonly [EditorStateTypeId]: {
|
|
112
|
+
readonly _R: (_: never) => R;
|
|
113
|
+
readonly _E: (_: never) => E;
|
|
114
|
+
};
|
|
115
|
+
/** @internal */
|
|
116
|
+
constructor(
|
|
117
|
+
/** @internal */
|
|
118
|
+
config: Configuration<Sc>,
|
|
119
|
+
/** @internal */
|
|
120
|
+
slots?: Record<string, any>);
|
|
121
|
+
/** The current document. */
|
|
122
|
+
get doc(): Node<Sc>;
|
|
123
|
+
/** The selection. */
|
|
124
|
+
get selection(): Selection<Sc>;
|
|
125
|
+
/** A set of marks to apply to the next input. `None` when no explicit marks have been set. */
|
|
126
|
+
get storedMarks(): Option.Option<ReadonlyArray<Mark<Sc>>>;
|
|
127
|
+
/** @internal */
|
|
128
|
+
get storedMarksOrNull(): ReadonlyArray<Mark<Sc>> | null;
|
|
129
|
+
/** @internal */
|
|
130
|
+
get scrollToSelection(): number;
|
|
131
|
+
/** The schema of the state's document. */
|
|
132
|
+
get schema(): Sc;
|
|
133
|
+
/** The plugins that are active in this state. */
|
|
134
|
+
get plugins(): ReadonlyArray<Plugin<any, Sc, E, R>>;
|
|
135
|
+
/** Create a new transaction from this state. */
|
|
136
|
+
get tr(): Transaction<Sc>;
|
|
137
|
+
/** Apply the given transaction to produce a new state. */
|
|
138
|
+
apply(tr: Transaction<Sc>): Effect.Effect<EditorState<Sc, R, E>, E | StateError, R>;
|
|
139
|
+
/**
|
|
140
|
+
* Like `apply`, but runs synchronously, throwing failures. Only
|
|
141
|
+
* available for states whose plugins require no services; fails with
|
|
142
|
+
* a defect (thrown) when a plugin hook is asynchronous.
|
|
143
|
+
*/
|
|
144
|
+
applyUnsafe(this: EditorState<Sc, never, E>, tr: Transaction<Sc>): EditorState<Sc, never, E>;
|
|
145
|
+
/** @internal */
|
|
146
|
+
filterTransaction(tr: Transaction<Sc>, ignore?: number): Effect.Effect<boolean, E, R>;
|
|
147
|
+
/**
|
|
148
|
+
* Verbose variant of `apply` that returns the precise transactions
|
|
149
|
+
* that were applied (which might be influenced by the transaction
|
|
150
|
+
* hooks of plugins) along with the new state.
|
|
151
|
+
*/
|
|
152
|
+
applyTransaction(rootTr: Transaction<Sc>): Effect.Effect<ApplyResult<Sc, R, E>, E | StateError, R>;
|
|
153
|
+
/** @internal */
|
|
154
|
+
applyTransactionSync(rootTr: Transaction<Sc>): ApplyResult<Sc, R, E> | typeof NeedsEffect;
|
|
155
|
+
/** @internal */
|
|
156
|
+
filterTransactionSync(tr: Transaction<Sc>, ignore?: number): boolean | typeof NeedsEffect;
|
|
157
|
+
/** @internal */
|
|
158
|
+
applyInnerSync(tr: Transaction<Sc>): EditorState<Sc, R, E> | typeof NeedsEffect;
|
|
159
|
+
/** @internal */
|
|
160
|
+
applyTransactionEffect(rootTr: Transaction<Sc>): Effect.Effect<ApplyResult<Sc, R, E>, E | StateError, R>;
|
|
161
|
+
/** Like `applyTransaction`, but runs synchronously (see `applyUnsafe`). */
|
|
162
|
+
applyTransactionUnsafe(this: EditorState<Sc, never, E>, rootTr: Transaction<Sc>): ApplyResult<Sc, never, E>;
|
|
163
|
+
/** @internal */
|
|
164
|
+
applyInner(tr: Transaction<Sc>): Effect.Effect<EditorState<Sc, R, E>, E | StateError, R>;
|
|
165
|
+
/**
|
|
166
|
+
* Create a new state based on this one, but with an adjusted set of
|
|
167
|
+
* active plugins. State fields that exist in both sets of plugins are
|
|
168
|
+
* kept unchanged. Those that no longer exist are dropped, and those
|
|
169
|
+
* that are new are initialized using their `init` method, passing in
|
|
170
|
+
* the new configuration object.
|
|
171
|
+
*/
|
|
172
|
+
reconfigure<const P extends ReadonlyArray<AnyPlugin> = readonly []>(config: {
|
|
173
|
+
readonly plugins?: P | undefined;
|
|
174
|
+
}): Effect.Effect<EditorState<Sc, PluginRequirements<P>, PluginErrors<P>>, PluginErrors<P> | StateError, PluginRequirements<P>>;
|
|
175
|
+
/** Like `reconfigure`, but runs synchronously (see `applyUnsafe`). */
|
|
176
|
+
reconfigureUnsafe<const P extends ReadonlyArray<Plugin<any, any, any, never>> = readonly []>(config: {
|
|
177
|
+
readonly plugins?: P | undefined;
|
|
178
|
+
}): EditorState<Sc, never, PluginErrors<P>>;
|
|
179
|
+
/**
|
|
180
|
+
* Serialize this state to JSON. If you want to serialize the state of
|
|
181
|
+
* plugins, pass an object mapping property names to use in the
|
|
182
|
+
* resulting JSON object to plugin objects. The argument may also be a
|
|
183
|
+
* string or number, in which case it is ignored, to support the way
|
|
184
|
+
* `JSON.stringify` calls `toJSON` methods. Throws a `StateError` when
|
|
185
|
+
* a plugin is mapped to the reserved `doc` or `selection` fields.
|
|
186
|
+
*/
|
|
187
|
+
toJSON(pluginFields?: Record<string, AnyPlugin> | string | number): EditorStateJSON;
|
|
188
|
+
/** Create a new state. */
|
|
189
|
+
static create<Sc extends AnySchema, const P extends ReadonlyArray<AnyPlugin> = readonly []>(config: EditorStateConfig<Sc, P>): Effect.Effect<EditorState<Sc, PluginRequirements<P>, PluginErrors<P>>, PluginErrors<P> | StateError, PluginRequirements<P>>;
|
|
190
|
+
/**
|
|
191
|
+
* Like `create`, but runs synchronously. Only available when the
|
|
192
|
+
* plugins require no services; throws on failure.
|
|
193
|
+
*/
|
|
194
|
+
static createUnsafe<Sc extends AnySchema, const P extends ReadonlyArray<Plugin<any, any, any, never>> = readonly []>(config: EditorStateConfig<Sc, P>): EditorState<Sc, never, PluginErrors<P>>;
|
|
195
|
+
/**
|
|
196
|
+
* Deserialize a JSON representation of a state. `config` should have
|
|
197
|
+
* at least a `schema` field, and should contain an array of plugins to
|
|
198
|
+
* initialize the state with. `pluginFields` can be used to deserialize
|
|
199
|
+
* the state of plugins, by associating plugin instances with the
|
|
200
|
+
* property names they use in the JSON object.
|
|
201
|
+
*/
|
|
202
|
+
static fromJSON<Sc extends AnySchema, const P extends ReadonlyArray<AnyPlugin> = readonly []>(config: {
|
|
203
|
+
readonly schema: Sc;
|
|
204
|
+
readonly plugins?: P | undefined;
|
|
205
|
+
}, json: unknown, pluginFields?: Record<string, AnyPlugin>): Effect.Effect<EditorState<Sc, PluginRequirements<P>, PluginErrors<P>>, PluginErrors<P> | StateError | SelectionError | ModelError, PluginRequirements<P>>;
|
|
206
|
+
/** Like `fromJSON`, but runs synchronously (see `createUnsafe`). */
|
|
207
|
+
static fromJSONUnsafe<Sc extends AnySchema, const P extends ReadonlyArray<Plugin<any, any, any, never>> = readonly []>(config: {
|
|
208
|
+
readonly schema: Sc;
|
|
209
|
+
readonly plugins?: P | undefined;
|
|
210
|
+
}, json: unknown, pluginFields?: Record<string, AnyPlugin>): EditorState<Sc, never, PluginErrors<P>>;
|
|
211
|
+
}
|
|
212
|
+
export {};
|