@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,489 @@
1
+ import { StateError } from "./errors.js";
2
+ import { Selection } from "./selection.js";
3
+ import { Transaction } from "./transaction.js";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Option from "effect/Option";
6
+ import * as Cause from "effect/Cause";
7
+ import * as Exit from "effect/Exit";
8
+ // src/state/state.ts
9
+ /**
10
+ * The editor state.
11
+ *
12
+ * @since 0.1.0
13
+ */
14
+ const baseFields = [
15
+ {
16
+ name: "doc",
17
+ self: void 0,
18
+ init(config) {
19
+ if (config.doc) return config.doc;
20
+ const node = config.schema.topNodeType.createAndFillOrNull();
21
+ if (!node) throw new StateError({ message: "Can't create a default document for the schema" });
22
+ return node;
23
+ },
24
+ apply(tr) {
25
+ return tr.doc;
26
+ }
27
+ },
28
+ {
29
+ name: "selection",
30
+ self: void 0,
31
+ init(config, instance) {
32
+ return config.selection || Selection.atStart(instance.doc);
33
+ },
34
+ apply(tr) {
35
+ return tr.selection;
36
+ }
37
+ },
38
+ {
39
+ name: "storedMarks",
40
+ self: void 0,
41
+ init(config) {
42
+ return config.storedMarks || null;
43
+ },
44
+ apply(tr, _marks, _old, state) {
45
+ return state.selection.$cursorOrNull ? tr.storedMarksOrNull : null;
46
+ }
47
+ },
48
+ {
49
+ name: "scrollToSelection",
50
+ self: void 0,
51
+ init() {
52
+ return 0;
53
+ },
54
+ apply(tr, prev) {
55
+ return tr.scrolledIntoView ? prev + 1 : prev;
56
+ }
57
+ }
58
+ ];
59
+ /**
60
+ * Object wrapping the part of a state object that stays the same across
61
+ * transactions. Stored in the state's `config` property.
62
+ *
63
+ * @internal
64
+ */
65
+ var Configuration = class {
66
+ constructor(schema, plugins) {
67
+ this.schema = schema;
68
+ this.plugins = [];
69
+ this.pluginsByKey = Object.create(null);
70
+ this.fields = baseFields.slice();
71
+ if (plugins) plugins.forEach((plugin) => {
72
+ if (this.pluginsByKey[plugin.key]) throw new StateError({ message: "Adding different instances of a keyed plugin (" + plugin.key + ")" });
73
+ this.plugins.push(plugin);
74
+ this.pluginsByKey[plugin.key] = plugin;
75
+ const state = plugin.spec.state;
76
+ if (state) this.fields.push({
77
+ name: plugin.key,
78
+ self: plugin,
79
+ init: state.init,
80
+ apply: state.apply
81
+ });
82
+ });
83
+ }
84
+ };
85
+ const isStateError = (u) => u instanceof StateError;
86
+ function startCreate(config) {
87
+ const schema = config.doc ? config.doc.type.schema : config.schema;
88
+ if (!schema) throw new StateError({ message: "Either `doc` or `schema` must be provided" });
89
+ const instance = new EditorState(new Configuration(schema, config.plugins));
90
+ return {
91
+ instance,
92
+ pending: instance.config.fields.findIndex((field) => Effect.isEffect(instance.slots[field.name] = field.init.call(field.self, config, instance)))
93
+ };
94
+ }
95
+ const finishCreate = (config, instance, pending) => Effect.gen(function* () {
96
+ for (const [i, field] of instance.config.fields.entries()) {
97
+ if (i < pending) continue;
98
+ const result = i === pending ? instance.slots[field.name] : yield* trySync(() => field.init.call(field.self, config, instance));
99
+ instance.slots[field.name] = Effect.isEffect(result) ? yield* result : result;
100
+ }
101
+ return instance;
102
+ });
103
+ /** Marker returned by the synchronous fast path when a hook returned an Effect. */
104
+ const NeedsEffect = Symbol("effect-richtext/state/NeedsEffect");
105
+ /** Lift a synchronous computation that may throw a `StateError` into an Effect. */
106
+ const trySync = (f) => Effect.suspend(() => {
107
+ try {
108
+ return Effect.succeed(f());
109
+ } catch (e) {
110
+ return isStateError(e) ? Effect.fail(e) : Effect.die(e);
111
+ }
112
+ });
113
+ /**
114
+ * Run an effect synchronously, throwing its (first) failure or defect.
115
+ *
116
+ * @internal
117
+ */
118
+ const runSyncUnsafe = (effect) => {
119
+ const exit = Effect.runSyncExit(effect);
120
+ if (Exit.isSuccess(exit)) return exit.value;
121
+ throw Cause.squash(exit.cause);
122
+ };
123
+ /**
124
+ * @since 0.1.0
125
+ * @category symbols
126
+ */
127
+ const EditorStateTypeId = Symbol.for("effect-richtext/state/EditorState");
128
+ /**
129
+ * The state of an editor is represented by an object of this type. A
130
+ * state is a persistent data structure—it isn't updated, but rather a
131
+ * new state value is computed from an old one using the `apply` method.
132
+ *
133
+ * A state holds a number of built-in fields, and plugins can define
134
+ * additional fields.
135
+ *
136
+ * `R` and `E` are the unions of the requirements and errors of the
137
+ * state's plugins: applying a transaction runs the plugins' hooks, so
138
+ * `apply` returns an `Effect<EditorState<Sc, R, E>, E | StateError, R>`.
139
+ * States whose plugins require no services (`R = never`) can also be
140
+ * updated synchronously with `applyUnsafe`.
141
+ *
142
+ * @since 0.1.0
143
+ * @category models
144
+ */
145
+ var EditorState = class EditorState {
146
+ /** @internal */
147
+ constructor(config, slots = Object.create(null)) {
148
+ this.config = config;
149
+ this.slots = slots;
150
+ }
151
+ /** The current document. */
152
+ get doc() {
153
+ return this.slots.doc;
154
+ }
155
+ /** The selection. */
156
+ get selection() {
157
+ return this.slots.selection;
158
+ }
159
+ /** A set of marks to apply to the next input. `None` when no explicit marks have been set. */
160
+ get storedMarks() {
161
+ return Option.fromNullishOr(this.storedMarksOrNull);
162
+ }
163
+ /** @internal */
164
+ get storedMarksOrNull() {
165
+ return this.slots.storedMarks;
166
+ }
167
+ /** @internal */
168
+ get scrollToSelection() {
169
+ return this.slots.scrollToSelection;
170
+ }
171
+ /** The schema of the state's document. */
172
+ get schema() {
173
+ return this.config.schema;
174
+ }
175
+ /** The plugins that are active in this state. */
176
+ get plugins() {
177
+ return this.config.plugins;
178
+ }
179
+ /** Create a new transaction from this state. */
180
+ get tr() {
181
+ return new Transaction(this);
182
+ }
183
+ /** Apply the given transaction to produce a new state. */
184
+ apply(tr) {
185
+ return Effect.map(this.applyTransaction(tr), (r) => r.state);
186
+ }
187
+ /**
188
+ * Like `apply`, but runs synchronously, throwing failures. Only
189
+ * available for states whose plugins require no services; fails with
190
+ * a defect (thrown) when a plugin hook is asynchronous.
191
+ */
192
+ applyUnsafe(tr) {
193
+ return this.applyTransactionUnsafe(tr).state;
194
+ }
195
+ /** @internal */
196
+ filterTransaction(tr, ignore = -1) {
197
+ const plugins = this.config.plugins;
198
+ const self = this;
199
+ return Effect.gen(function* () {
200
+ for (let i = 0; i < plugins.length; i++) {
201
+ if (i === ignore) continue;
202
+ const plugin = plugins[i], filter = plugin.spec.filterTransaction;
203
+ if (filter) {
204
+ const result = filter.call(plugin, tr, self);
205
+ if (!(Effect.isEffect(result) ? yield* result : result)) return false;
206
+ }
207
+ }
208
+ return true;
209
+ });
210
+ }
211
+ /**
212
+ * Verbose variant of `apply` that returns the precise transactions
213
+ * that were applied (which might be influenced by the transaction
214
+ * hooks of plugins) along with the new state.
215
+ */
216
+ applyTransaction(rootTr) {
217
+ return Effect.suspend(() => {
218
+ let result;
219
+ try {
220
+ result = this.applyTransactionSync(rootTr);
221
+ } catch (e) {
222
+ return isStateError(e) ? Effect.fail(e) : Effect.die(e);
223
+ }
224
+ return result === NeedsEffect ? this.applyTransactionEffect(rootTr) : Effect.succeed(result);
225
+ });
226
+ }
227
+ /** @internal */
228
+ applyTransactionSync(rootTr) {
229
+ const filtered = this.filterTransactionSync(rootTr);
230
+ if (filtered === NeedsEffect) return NeedsEffect;
231
+ if (!filtered) return {
232
+ state: this,
233
+ transactions: []
234
+ };
235
+ const trs = [rootTr];
236
+ let newState = this.applyInnerSync(rootTr);
237
+ if (newState === NeedsEffect) return NeedsEffect;
238
+ let seen = null;
239
+ const plugins = this.config.plugins;
240
+ for (;;) {
241
+ let haveNew = false;
242
+ for (let i = 0; i < plugins.length; i++) {
243
+ const plugin = plugins[i], append = plugin.spec.appendTransaction;
244
+ if (append) {
245
+ const n = seen ? seen[i].n : 0, oldState = seen ? seen[i].state : this;
246
+ let tr = void 0;
247
+ if (n < trs.length) {
248
+ const result = append.call(plugin, n ? trs.slice(n) : trs, oldState, newState);
249
+ if (Effect.isEffect(result)) return NeedsEffect;
250
+ tr = result;
251
+ }
252
+ if (tr) {
253
+ const ok = newState.filterTransactionSync(tr, i);
254
+ if (ok === NeedsEffect) return NeedsEffect;
255
+ if (ok) {
256
+ tr.setMeta("appendedTransaction", rootTr);
257
+ if (!seen) {
258
+ seen = [];
259
+ for (let j = 0; j < plugins.length; j++) seen.push(j < i ? {
260
+ state: newState,
261
+ n: trs.length
262
+ } : {
263
+ state: this,
264
+ n: 0
265
+ });
266
+ }
267
+ trs.push(tr);
268
+ const next = newState.applyInnerSync(tr);
269
+ if (next === NeedsEffect) return NeedsEffect;
270
+ newState = next;
271
+ haveNew = true;
272
+ }
273
+ }
274
+ if (seen) seen[i] = {
275
+ state: newState,
276
+ n: trs.length
277
+ };
278
+ }
279
+ }
280
+ if (!haveNew) return {
281
+ state: newState,
282
+ transactions: trs
283
+ };
284
+ }
285
+ }
286
+ /** @internal */
287
+ filterTransactionSync(tr, ignore = -1) {
288
+ const plugins = this.config.plugins;
289
+ for (let i = 0; i < plugins.length; i++) {
290
+ if (i === ignore) continue;
291
+ const plugin = plugins[i], filter = plugin.spec.filterTransaction;
292
+ if (filter) {
293
+ const result = filter.call(plugin, tr, this);
294
+ if (Effect.isEffect(result)) return NeedsEffect;
295
+ if (!result) return false;
296
+ }
297
+ }
298
+ return true;
299
+ }
300
+ /** @internal */
301
+ applyInnerSync(tr) {
302
+ if (!tr.before.eq(this.doc)) throw new StateError({ message: "Applying a mismatched transaction" });
303
+ const newInstance = new EditorState(this.config), fields = this.config.fields;
304
+ for (let i = 0; i < fields.length; i++) {
305
+ const field = fields[i];
306
+ const result = field.apply.call(field.self, tr, this.slots[field.name], this, newInstance);
307
+ if (Effect.isEffect(result)) return NeedsEffect;
308
+ newInstance.slots[field.name] = result;
309
+ }
310
+ return newInstance;
311
+ }
312
+ /** @internal */
313
+ applyTransactionEffect(rootTr) {
314
+ const self = this;
315
+ return Effect.gen(function* () {
316
+ if (!(yield* self.filterTransaction(rootTr))) return {
317
+ state: self,
318
+ transactions: []
319
+ };
320
+ const trs = [rootTr];
321
+ let newState = yield* self.applyInner(rootTr);
322
+ let seen = null;
323
+ const plugins = self.config.plugins;
324
+ for (;;) {
325
+ let haveNew = false;
326
+ for (let i = 0; i < plugins.length; i++) {
327
+ const plugin = plugins[i], append = plugin.spec.appendTransaction;
328
+ if (append) {
329
+ const n = seen ? seen[i].n : 0, oldState = seen ? seen[i].state : self;
330
+ let tr = void 0;
331
+ if (n < trs.length) {
332
+ const result = append.call(plugin, n ? trs.slice(n) : trs, oldState, newState);
333
+ tr = Effect.isEffect(result) ? yield* result : result;
334
+ }
335
+ if (tr && (yield* newState.filterTransaction(tr, i))) {
336
+ tr.setMeta("appendedTransaction", rootTr);
337
+ if (!seen) {
338
+ seen = [];
339
+ for (let j = 0; j < plugins.length; j++) seen.push(j < i ? {
340
+ state: newState,
341
+ n: trs.length
342
+ } : {
343
+ state: self,
344
+ n: 0
345
+ });
346
+ }
347
+ trs.push(tr);
348
+ newState = yield* newState.applyInner(tr);
349
+ haveNew = true;
350
+ }
351
+ if (seen) seen[i] = {
352
+ state: newState,
353
+ n: trs.length
354
+ };
355
+ }
356
+ }
357
+ if (!haveNew) return {
358
+ state: newState,
359
+ transactions: trs
360
+ };
361
+ }
362
+ });
363
+ }
364
+ /** Like `applyTransaction`, but runs synchronously (see `applyUnsafe`). */
365
+ applyTransactionUnsafe(rootTr) {
366
+ const result = this.applyTransactionSync(rootTr);
367
+ return result === NeedsEffect ? runSyncUnsafe(this.applyTransactionEffect(rootTr)) : result;
368
+ }
369
+ /** @internal */
370
+ applyInner(tr) {
371
+ const self = this;
372
+ return Effect.gen(function* () {
373
+ if (!tr.before.eq(self.doc)) return yield* Effect.fail(new StateError({ message: "Applying a mismatched transaction" }));
374
+ const newInstance = new EditorState(self.config), fields = self.config.fields;
375
+ for (let i = 0; i < fields.length; i++) {
376
+ const field = fields[i];
377
+ const result = field.apply.call(field.self, tr, self.slots[field.name], self, newInstance);
378
+ newInstance.slots[field.name] = Effect.isEffect(result) ? yield* result : result;
379
+ }
380
+ return newInstance;
381
+ });
382
+ }
383
+ /**
384
+ * Create a new state based on this one, but with an adjusted set of
385
+ * active plugins. State fields that exist in both sets of plugins are
386
+ * kept unchanged. Those that no longer exist are dropped, and those
387
+ * that are new are initialized using their `init` method, passing in
388
+ * the new configuration object.
389
+ */
390
+ reconfigure(config) {
391
+ const self = this;
392
+ return Effect.gen(function* () {
393
+ const $config = yield* trySync(() => new Configuration(self.schema, config.plugins));
394
+ const fields = $config.fields, instance = new EditorState($config);
395
+ for (let i = 0; i < fields.length; i++) {
396
+ const field = fields[i], name = field.name;
397
+ if (Object.prototype.hasOwnProperty.call(self.slots, name)) instance.slots[name] = self.slots[name];
398
+ else {
399
+ const result = field.init.call(field.self, config, instance);
400
+ instance.slots[name] = Effect.isEffect(result) ? yield* result : result;
401
+ }
402
+ }
403
+ return instance;
404
+ });
405
+ }
406
+ /** Like `reconfigure`, but runs synchronously (see `applyUnsafe`). */
407
+ reconfigureUnsafe(config) {
408
+ return runSyncUnsafe(this.reconfigure(config));
409
+ }
410
+ /**
411
+ * Serialize this state to JSON. If you want to serialize the state of
412
+ * plugins, pass an object mapping property names to use in the
413
+ * resulting JSON object to plugin objects. The argument may also be a
414
+ * string or number, in which case it is ignored, to support the way
415
+ * `JSON.stringify` calls `toJSON` methods. Throws a `StateError` when
416
+ * a plugin is mapped to the reserved `doc` or `selection` fields.
417
+ */
418
+ toJSON(pluginFields) {
419
+ const result = {
420
+ doc: this.doc.toJSON(),
421
+ selection: this.selection.toJSON()
422
+ };
423
+ if (this.storedMarksOrNull) result.storedMarks = this.storedMarksOrNull.map((m) => m.toJSON());
424
+ if (pluginFields && typeof pluginFields === "object") for (const prop in pluginFields) {
425
+ if (prop === "doc" || prop === "selection") throw new StateError({ message: "The JSON fields `doc` and `selection` are reserved" });
426
+ const plugin = pluginFields[prop], state = plugin.spec.state;
427
+ if (state && state.toJSON) result[prop] = state.toJSON.call(plugin, this.slots[plugin.key]);
428
+ }
429
+ return result;
430
+ }
431
+ /** Create a new state. */
432
+ static create(config) {
433
+ return Effect.flatMap(trySync(() => startCreate(config)), ({ instance, pending }) => pending < 0 ? Effect.succeed(instance) : finishCreate(config, instance, pending));
434
+ }
435
+ /**
436
+ * Like `create`, but runs synchronously. Only available when the
437
+ * plugins require no services; throws on failure.
438
+ */
439
+ static createUnsafe(config) {
440
+ const { instance, pending } = startCreate(config);
441
+ return pending < 0 ? instance : runSyncUnsafe(finishCreate(config, instance, pending));
442
+ }
443
+ /**
444
+ * Deserialize a JSON representation of a state. `config` should have
445
+ * at least a `schema` field, and should contain an array of plugins to
446
+ * initialize the state with. `pluginFields` can be used to deserialize
447
+ * the state of plugins, by associating plugin instances with the
448
+ * property names they use in the JSON object.
449
+ */
450
+ static fromJSON(config, json, pluginFields) {
451
+ return Effect.gen(function* () {
452
+ const data = json;
453
+ if (!data || typeof data !== "object") return yield* Effect.fail(new StateError({ message: "Invalid input for EditorState.fromJSON" }));
454
+ if (!config.schema) return yield* Effect.fail(new StateError({ message: "Required config field 'schema' missing" }));
455
+ const $config = yield* trySync(() => new Configuration(config.schema, config.plugins));
456
+ const instance = new EditorState($config);
457
+ for (const field of $config.fields) if (field.name === "doc") instance.slots.doc = yield* config.schema.nodeFromJSON(data.doc);
458
+ else if (field.name === "selection") instance.slots.selection = yield* Selection.fromJSON(instance.doc, data.selection);
459
+ else if (field.name === "storedMarks") {
460
+ if (data.storedMarks) {
461
+ const marks = [];
462
+ for (const m of data.storedMarks) marks.push(yield* config.schema.markFromJSON(m));
463
+ instance.slots.storedMarks = marks;
464
+ } else instance.slots.storedMarks = null;
465
+ } else {
466
+ let done = false;
467
+ if (pluginFields) for (const prop in pluginFields) {
468
+ const plugin = pluginFields[prop], state = plugin.spec.state;
469
+ if (plugin.key === field.name && state && state.fromJSON && Object.prototype.hasOwnProperty.call(data, prop)) {
470
+ const result = state.fromJSON.call(plugin, config, data[prop], instance);
471
+ instance.slots[field.name] = Effect.isEffect(result) ? yield* result : result;
472
+ done = true;
473
+ break;
474
+ }
475
+ }
476
+ if (!done) {
477
+ const result = field.init.call(field.self, config, instance);
478
+ instance.slots[field.name] = Effect.isEffect(result) ? yield* result : result;
479
+ }
480
+ }
481
+ return instance;
482
+ });
483
+ }
484
+ /** Like `fromJSON`, but runs synchronously (see `createUnsafe`). */
485
+ static fromJSONUnsafe(config, json, pluginFields) {
486
+ return runSyncUnsafe(EditorState.fromJSON(config, json, pluginFields));
487
+ }
488
+ };
489
+ export { Configuration, EditorState, EditorStateTypeId, runSyncUnsafe };
@@ -0,0 +1,145 @@
1
+ import { Slice } from "../model/replace.js";
2
+ import { AnySchema, MarkType } from "../model/schema.js";
3
+ import { Mark } from "../model/mark.js";
4
+ import { Node } from "../model/node.js";
5
+ import "../model.js";
6
+ import "../transform.js";
7
+ import { Step } from "../transform/step.js";
8
+ import { ReplaceFailure, Transform } from "../transform/transform.js";
9
+ import { SelectionError } from "./errors.js";
10
+ import { EditorViewLike, Plugin, PluginKey } from "./plugin.js";
11
+ import { Selection } from "./selection.js";
12
+ import { EditorState } from "./state.js";
13
+ import * as Effect from "effect/Effect";
14
+ import * as Option from "effect/Option";
15
+ /**
16
+ * A function that applies a transaction, as passed to commands.
17
+ *
18
+ * @since 0.1.0
19
+ * @category models
20
+ */
21
+ export type Dispatch<Sc extends AnySchema = AnySchema> = (tr: Transaction<Sc>) => Effect.Effect<void>;
22
+ /**
23
+ * Commands are functions that take a state and an optional transaction
24
+ * dispatch function and...
25
+ *
26
+ * - determine whether they apply to this state
27
+ * - if not, succeed with `false`
28
+ * - if `dispatch` was passed, perform their effect, possibly by passing
29
+ * a transaction to `dispatch`
30
+ * - succeed with `true`
31
+ *
32
+ * In some cases, the editor view is passed as a third argument.
33
+ * Commands carry the errors (`E`) and requirements (`R`) of the effects
34
+ * they run.
35
+ *
36
+ * @since 0.1.0
37
+ * @category models
38
+ */
39
+ export type Command<Sc extends AnySchema = AnySchema, E = never, R = never> = (state: EditorState<Sc, any, any>, dispatch?: Dispatch<Sc>, view?: EditorViewLike) => Effect.Effect<boolean, E, R>;
40
+ /**
41
+ * Keys under which transaction metadata can be stored.
42
+ *
43
+ * @since 0.1.0
44
+ * @category models
45
+ */
46
+ export type MetaKey = string | Plugin<any, any, any, any> | PluginKey<any>;
47
+ /**
48
+ * An editor state transaction, which can be applied to a state to
49
+ * create an updated state. Use `EditorState.tr` to create an instance.
50
+ *
51
+ * Transactions track changes to the document (they are a subclass of
52
+ * `Transform`), but also other state changes, like selection updates and
53
+ * adjustments of the set of stored marks. In addition, you can store
54
+ * metadata properties in a transaction, which are extra pieces of
55
+ * information that client code or plugins can use to describe what a
56
+ * transaction represents, so that they can update their own state
57
+ * accordingly.
58
+ *
59
+ * Like transforms, transactions are mutable builders.
60
+ *
61
+ * @since 0.1.0
62
+ * @category models
63
+ */
64
+ export declare class Transaction<Sc extends AnySchema = AnySchema> extends Transform<Sc> {
65
+ /** The timestamp associated with this transaction, in the same format as `Date.now()`. */
66
+ time: number;
67
+ private curSelection;
68
+ private curSelectionFor;
69
+ private updated;
70
+ private readonly meta;
71
+ /** @internal */
72
+ storedMarksOrNull: ReadonlyArray<Mark<Sc>> | null;
73
+ /** @internal */
74
+ constructor(state: EditorState<Sc, any, any>);
75
+ /** The stored marks set by this transaction, if any. */
76
+ get storedMarks(): Option.Option<ReadonlyArray<Mark<Sc>>>;
77
+ /**
78
+ * The transaction's current selection. This defaults to the editor
79
+ * selection mapped through the steps in the transaction, but can be
80
+ * overwritten with `setSelection`.
81
+ */
82
+ get selection(): Selection<Sc>;
83
+ /**
84
+ * Update the transaction's current selection. Will determine the
85
+ * selection that the editor gets when the transaction is applied.
86
+ * Fails with a `SelectionError` when the selection doesn't point into
87
+ * the transaction's current document.
88
+ */
89
+ setSelection(selection: Selection<Sc>): Effect.Effect<this, SelectionError>;
90
+ /** Like `setSelection`, but throws a `SelectionError` on failure. */
91
+ setSelectionUnsafe(selection: Selection<Sc>): this;
92
+ /** Whether the selection was explicitly updated by this transaction. */
93
+ get selectionSet(): boolean;
94
+ /** Set the current stored marks. */
95
+ setStoredMarks(marks: ReadonlyArray<Mark<Sc>> | null): this;
96
+ /**
97
+ * Make sure the current stored marks or, if that is null, the marks at
98
+ * the selection, match the given set of marks. Does nothing if this is
99
+ * already the case.
100
+ */
101
+ ensureMarks(marks: ReadonlyArray<Mark<Sc>>): this;
102
+ /** Add a mark to the set of stored marks. */
103
+ addStoredMark(mark: Mark<Sc>): this;
104
+ /** Remove a mark or mark type from the set of stored marks. */
105
+ removeStoredMark(mark: Mark<Sc> | MarkType<Sc>): this;
106
+ /** Whether the stored marks were explicitly set for this transaction. */
107
+ get storedMarksSet(): boolean;
108
+ /** @internal */
109
+ addStep(step: Step<Sc>, doc: Node<Sc>): void;
110
+ /** Update the timestamp for the transaction. */
111
+ setTime(time: number): this;
112
+ /** Replace the current selection with the given slice. */
113
+ replaceSelection(slice: Slice<Sc>): this;
114
+ /**
115
+ * Replace the selection with the given node. When `inheritMarks` is
116
+ * true and the content is inline, it inherits the marks from the place
117
+ * where it is inserted.
118
+ */
119
+ replaceSelectionWith(node: Node<Sc>, inheritMarks?: boolean): this;
120
+ /** Delete the selection. */
121
+ deleteSelection(): this;
122
+ /**
123
+ * Replace the given range, or the selection if no range is given,
124
+ * with a text node containing the given string.
125
+ */
126
+ insertText(text: string, from?: number, to?: number): Effect.Effect<this, ReplaceFailure>;
127
+ /** Like `insertText`, but throws on failure (for example for positions outside of the document). */
128
+ insertTextUnsafe(text: string, from?: number, to?: number): this;
129
+ /** Store a metadata property in this transaction, keyed either by name or by plugin. */
130
+ setMeta(key: MetaKey, value: unknown): this;
131
+ /** Retrieve a metadata property for a given name or plugin, if it was set. */
132
+ getMeta<A = unknown>(key: MetaKey): Option.Option<A>;
133
+ /**
134
+ * Returns true if this transaction doesn't contain any metadata, and
135
+ * can thus safely be extended.
136
+ */
137
+ get isGeneric(): boolean;
138
+ /**
139
+ * Indicate that the editor should scroll the selection into view when
140
+ * updated to the state produced by this transaction.
141
+ */
142
+ scrollIntoView(): this;
143
+ /** True when this transaction has had `scrollIntoView` called on it. */
144
+ get scrolledIntoView(): boolean;
145
+ }