@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.
- package/LICENSE +48 -0
- package/README.md +117 -1
- 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 +200 -4
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { AnySchema } from "../model/schema.js";
|
|
2
|
+
import "../model.js";
|
|
3
|
+
import { EditorProps, EditorView } from "../view/view.js";
|
|
4
|
+
import { EditorState, EditorStateConfig } from "./state.js";
|
|
5
|
+
import { Transaction } from "./transaction.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
/**
|
|
9
|
+
* A value, or an Effect producing it. Plugin hooks may return either:
|
|
10
|
+
* plain values keep simple plugins simple, while Effects let plugins use
|
|
11
|
+
* services (reflected in the plugin's `R` type) and fail with typed
|
|
12
|
+
* errors (`E`).
|
|
13
|
+
*
|
|
14
|
+
* Note that a hook result is treated as an Effect whenever
|
|
15
|
+
* `Effect.isEffect` is true for it, so a plugin whose state is itself
|
|
16
|
+
* an Effect must wrap it in `Effect.succeed`.
|
|
17
|
+
*
|
|
18
|
+
* @since 0.1.0
|
|
19
|
+
* @category models
|
|
20
|
+
*/
|
|
21
|
+
export type MaybeEffect<A, E = never, R = never> = A | Effect.Effect<A, E, R>;
|
|
22
|
+
/**
|
|
23
|
+
* The editor view type, as passed to commands (which may be called
|
|
24
|
+
* without a view, hence `undefined`).
|
|
25
|
+
*
|
|
26
|
+
* @since 0.1.0
|
|
27
|
+
* @category models
|
|
28
|
+
*/
|
|
29
|
+
export type EditorViewLike = EditorView<any, any, any> | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The props a plugin adds to the editor view: the view's `EditorProps`.
|
|
32
|
+
* Function-valued props are bound to the plugin instance.
|
|
33
|
+
*
|
|
34
|
+
* @since 0.1.0
|
|
35
|
+
* @category models
|
|
36
|
+
*/
|
|
37
|
+
export type PluginProps<Sc extends AnySchema = AnySchema, E = never, R = never> = EditorProps<Sc, R, E, Plugin<any, Sc, never, never>>;
|
|
38
|
+
/**
|
|
39
|
+
* A stateful object that can be installed in an editor by a plugin's
|
|
40
|
+
* `view` hook.
|
|
41
|
+
*
|
|
42
|
+
* @since 0.1.0
|
|
43
|
+
* @category models
|
|
44
|
+
*/
|
|
45
|
+
export interface PluginView<Sc extends AnySchema = AnySchema> {
|
|
46
|
+
/** Called whenever the view's state is updated. */
|
|
47
|
+
readonly update?: ((view: EditorView<Sc, any, any>, prevState: EditorState<Sc, any, any>) => void) | undefined;
|
|
48
|
+
/** Called when the view is destroyed or receives a state with different plugins. */
|
|
49
|
+
readonly destroy?: (() => void) | undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A plugin spec may provide a state field (under its `state` property)
|
|
53
|
+
* of this type, which describes the state it wants to keep. Functions
|
|
54
|
+
* provided here are always called with the plugin instance as their
|
|
55
|
+
* `this` binding, and may return either a plain value or an Effect.
|
|
56
|
+
*
|
|
57
|
+
* @since 0.1.0
|
|
58
|
+
* @category models
|
|
59
|
+
*/
|
|
60
|
+
export interface StateField<PState, Sc extends AnySchema = AnySchema, E = never, R = never> {
|
|
61
|
+
/**
|
|
62
|
+
* Initialize the value of the field. `config` will be the object
|
|
63
|
+
* passed to `EditorState.create`. Note that `instance` is a
|
|
64
|
+
* half-initialized state instance, and will not have values for
|
|
65
|
+
* plugin fields initialized after this one.
|
|
66
|
+
*/
|
|
67
|
+
readonly init: (config: EditorStateConfig<Sc>, instance: EditorState<Sc, any, any>) => MaybeEffect<PState, E, R>;
|
|
68
|
+
/**
|
|
69
|
+
* Apply the given transaction to this state field, producing a new
|
|
70
|
+
* field value. Note that the `newState` argument is again a partially
|
|
71
|
+
* constructed state does not yet contain the state from plugins coming
|
|
72
|
+
* after this one.
|
|
73
|
+
*/
|
|
74
|
+
readonly apply: (tr: Transaction<Sc>, value: PState, oldState: EditorState<Sc, any, any>, newState: EditorState<Sc, any, any>) => MaybeEffect<PState, E, R>;
|
|
75
|
+
/**
|
|
76
|
+
* Convert this field to JSON. Optional, can be left off to disable
|
|
77
|
+
* JSON serialization for the field.
|
|
78
|
+
*/
|
|
79
|
+
readonly toJSON?: ((value: PState) => unknown) | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Deserialize the JSON representation of this field. Note that the
|
|
82
|
+
* `state` argument is again a half-initialized state.
|
|
83
|
+
*/
|
|
84
|
+
readonly fromJSON?: ((config: EditorStateConfig<Sc>, value: unknown, state: EditorState<Sc, any, any>) => MaybeEffect<PState, E, R>) | undefined;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* This is the type passed to the `Plugin` constructor. It provides a
|
|
88
|
+
* definition for a plugin.
|
|
89
|
+
*
|
|
90
|
+
* The hooks that run while creating or updating states (`state.init`,
|
|
91
|
+
* `state.apply`, `state.fromJSON`, `filterTransaction` and
|
|
92
|
+
* `appendTransaction`) may return Effects; their error and requirement
|
|
93
|
+
* types become the plugin's `E` and `R`.
|
|
94
|
+
*
|
|
95
|
+
* @since 0.1.0
|
|
96
|
+
* @category models
|
|
97
|
+
*/
|
|
98
|
+
export interface PluginSpec<PState = any, Sc extends AnySchema = AnySchema, E = never, R = never> {
|
|
99
|
+
/**
|
|
100
|
+
* The view props added by this plugin. Props that are functions will
|
|
101
|
+
* be bound to have the plugin instance as their `this` binding.
|
|
102
|
+
*/
|
|
103
|
+
readonly props?: PluginProps<Sc, E, R> | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Allows a plugin to define a state field, an extra slot in the state
|
|
106
|
+
* object in which it can keep its own data.
|
|
107
|
+
*/
|
|
108
|
+
readonly state?: StateField<PState, Sc, E, R> | undefined;
|
|
109
|
+
/**
|
|
110
|
+
* Can be used to make this a keyed plugin. You can have only one
|
|
111
|
+
* plugin with a given key in a given state, but it is possible to
|
|
112
|
+
* access the plugin's configuration and state through the key,
|
|
113
|
+
* without having access to the plugin instance object.
|
|
114
|
+
*/
|
|
115
|
+
readonly key?: PluginKey<PState> | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* When the plugin needs to interact with the editor view, or set
|
|
118
|
+
* something up in the DOM, use this field. The function will be
|
|
119
|
+
* called when the plugin's state is associated with an editor view.
|
|
120
|
+
*/
|
|
121
|
+
readonly view?: ((view: EditorView<Sc, any, any>) => PluginView<Sc>) | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* When present, this will be called before a transaction is applied
|
|
124
|
+
* by the state, allowing the plugin to cancel it (by returning
|
|
125
|
+
* false).
|
|
126
|
+
*/
|
|
127
|
+
readonly filterTransaction?: ((tr: Transaction<Sc>, state: EditorState<Sc, any, any>) => MaybeEffect<boolean, E, R>) | undefined;
|
|
128
|
+
/**
|
|
129
|
+
* Allows the plugin to append another transaction to be applied after
|
|
130
|
+
* the given array of transactions. When another plugin appends a
|
|
131
|
+
* transaction after this was called, it is called again with the new
|
|
132
|
+
* state and new transactions—but only the new transactions, i.e. it
|
|
133
|
+
* won't be passed transactions that it already saw.
|
|
134
|
+
*/
|
|
135
|
+
readonly appendTransaction?: ((transactions: ReadonlyArray<Transaction<Sc>>, oldState: EditorState<Sc, any, any>, newState: EditorState<Sc, any, any>) => MaybeEffect<Transaction<Sc> | null | undefined | void, E, R>) | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* Additional properties are allowed on plugin specs, which can be
|
|
138
|
+
* read via `Plugin.spec`.
|
|
139
|
+
*/
|
|
140
|
+
readonly [key: string]: unknown;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* @since 0.1.0
|
|
144
|
+
* @category symbols
|
|
145
|
+
*/
|
|
146
|
+
export declare const PluginTypeId: unique symbol;
|
|
147
|
+
/**
|
|
148
|
+
* @since 0.1.0
|
|
149
|
+
* @category symbols
|
|
150
|
+
*/
|
|
151
|
+
export type PluginTypeId = typeof PluginTypeId;
|
|
152
|
+
/**
|
|
153
|
+
* Plugins bundle functionality that can be added to an editor. They are
|
|
154
|
+
* part of the editor state and may influence that state and the view
|
|
155
|
+
* that contains it.
|
|
156
|
+
*
|
|
157
|
+
* The type parameters are the plugin's state type, the schema it works
|
|
158
|
+
* with, and the errors (`E`) and requirements (`R`) of its effectful
|
|
159
|
+
* hooks, which are inferred from the spec:
|
|
160
|
+
*
|
|
161
|
+
* ```ts skip-type-checking
|
|
162
|
+
* const counter = new Plugin({
|
|
163
|
+
* state: {
|
|
164
|
+
* init: () => 0,
|
|
165
|
+
* apply: (_tr, n) => Effect.map(Clock.currentTimeMillis, () => n + 1)
|
|
166
|
+
* }
|
|
167
|
+
* }) // Plugin<number, AnySchema, never, never>
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* @since 0.1.0
|
|
171
|
+
* @category models
|
|
172
|
+
*/
|
|
173
|
+
export declare class Plugin<PState = any, Sc extends AnySchema = AnySchema, E = never, R = never> {
|
|
174
|
+
/** The plugin's spec object. */
|
|
175
|
+
readonly spec: PluginSpec<PState, Sc, E, R>;
|
|
176
|
+
readonly [PluginTypeId]: {
|
|
177
|
+
readonly _PState: (_: never) => PState;
|
|
178
|
+
readonly _E: (_: never) => E;
|
|
179
|
+
readonly _R: (_: never) => R;
|
|
180
|
+
};
|
|
181
|
+
/** The props exported by this plugin. */
|
|
182
|
+
readonly props: PluginProps<Sc, E, R>;
|
|
183
|
+
/** @internal */
|
|
184
|
+
readonly key: string;
|
|
185
|
+
/** Create a plugin. */
|
|
186
|
+
constructor(
|
|
187
|
+
/** The plugin's spec object. */
|
|
188
|
+
spec: PluginSpec<PState, Sc, E, R>);
|
|
189
|
+
/** Extract the plugin's state field from an editor state. */
|
|
190
|
+
getState(state: EditorState<any, any, any>): Option.Option<PState>;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Any plugin.
|
|
194
|
+
*
|
|
195
|
+
* @since 0.1.0
|
|
196
|
+
* @category models
|
|
197
|
+
*/
|
|
198
|
+
export type AnyPlugin = Plugin<any, any, any, any>;
|
|
199
|
+
/**
|
|
200
|
+
* The union of the error types of the given plugins.
|
|
201
|
+
*
|
|
202
|
+
* @since 0.1.0
|
|
203
|
+
* @category type-level
|
|
204
|
+
*/
|
|
205
|
+
export type PluginErrors<P extends ReadonlyArray<AnyPlugin>> = P[number] extends (infer X) ? X extends Plugin<any, any, infer E, any> ? E : never : never;
|
|
206
|
+
/**
|
|
207
|
+
* The union of the requirements of the given plugins.
|
|
208
|
+
*
|
|
209
|
+
* @since 0.1.0
|
|
210
|
+
* @category type-level
|
|
211
|
+
*/
|
|
212
|
+
export type PluginRequirements<P extends ReadonlyArray<AnyPlugin>> = P[number] extends (infer X) ? X extends Plugin<any, any, any, infer R> ? R : never : never;
|
|
213
|
+
/** @internal */
|
|
214
|
+
export declare const getField: <A>(state: EditorState<any, any, any>, key: string) => Option.Option<A>;
|
|
215
|
+
/**
|
|
216
|
+
* A key is used to tag plugins in a way that makes it possible to find
|
|
217
|
+
* them, given an editor state. Assigning a key does mean only one plugin
|
|
218
|
+
* of that type can be active in a state.
|
|
219
|
+
*
|
|
220
|
+
* @since 0.1.0
|
|
221
|
+
* @category models
|
|
222
|
+
*/
|
|
223
|
+
export declare class PluginKey<PState = any> {
|
|
224
|
+
/** @internal */
|
|
225
|
+
readonly key: string;
|
|
226
|
+
/** Create a plugin key. */
|
|
227
|
+
constructor(name?: string);
|
|
228
|
+
/** Get the active plugin with this key, if any, from an editor state. */
|
|
229
|
+
get(state: EditorState<any, any, any>): Option.Option<Plugin<PState, any, any, any>>;
|
|
230
|
+
/** Get the plugin's state from an editor state. */
|
|
231
|
+
getState(state: EditorState<any, any, any>): Option.Option<PState>;
|
|
232
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as Option from "effect/Option";
|
|
2
|
+
import * as Predicate from "effect/Predicate";
|
|
3
|
+
// src/state/plugin.ts
|
|
4
|
+
function bindProps(obj, self, target) {
|
|
5
|
+
for (const prop in obj) {
|
|
6
|
+
let val = obj[prop];
|
|
7
|
+
if (Predicate.isFunction(val)) val = val.bind(self);
|
|
8
|
+
else if (prop === "handleDOMEvents") val = bindProps(val, self, {});
|
|
9
|
+
target[prop] = val;
|
|
10
|
+
}
|
|
11
|
+
return target;
|
|
12
|
+
}
|
|
13
|
+
const keys = Object.create(null);
|
|
14
|
+
function createKey(name) {
|
|
15
|
+
if (name in keys) return name + "$" + ++keys[name];
|
|
16
|
+
keys[name] = 0;
|
|
17
|
+
return name + "$";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
* @category symbols
|
|
22
|
+
*/
|
|
23
|
+
const PluginTypeId = Symbol.for("effect-richtext/state/Plugin");
|
|
24
|
+
/**
|
|
25
|
+
* Plugins bundle functionality that can be added to an editor. They are
|
|
26
|
+
* part of the editor state and may influence that state and the view
|
|
27
|
+
* that contains it.
|
|
28
|
+
*
|
|
29
|
+
* The type parameters are the plugin's state type, the schema it works
|
|
30
|
+
* with, and the errors (`E`) and requirements (`R`) of its effectful
|
|
31
|
+
* hooks, which are inferred from the spec:
|
|
32
|
+
*
|
|
33
|
+
* ```ts skip-type-checking
|
|
34
|
+
* const counter = new Plugin({
|
|
35
|
+
* state: {
|
|
36
|
+
* init: () => 0,
|
|
37
|
+
* apply: (_tr, n) => Effect.map(Clock.currentTimeMillis, () => n + 1)
|
|
38
|
+
* }
|
|
39
|
+
* }) // Plugin<number, AnySchema, never, never>
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @since 0.1.0
|
|
43
|
+
* @category models
|
|
44
|
+
*/
|
|
45
|
+
var Plugin = class {
|
|
46
|
+
/** Create a plugin. */
|
|
47
|
+
constructor(spec) {
|
|
48
|
+
this.spec = spec;
|
|
49
|
+
this.props = {};
|
|
50
|
+
if (spec.props) bindProps(spec.props, this, this.props);
|
|
51
|
+
this.key = spec.key ? spec.key.key : createKey("plugin");
|
|
52
|
+
}
|
|
53
|
+
/** Extract the plugin's state field from an editor state. */
|
|
54
|
+
getState(state) {
|
|
55
|
+
return getField(state, this.key);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/** @internal */
|
|
59
|
+
const getField = (state, key) => Object.prototype.hasOwnProperty.call(state.slots, key) ? Option.some(state.slots[key]) : Option.none();
|
|
60
|
+
/**
|
|
61
|
+
* A key is used to tag plugins in a way that makes it possible to find
|
|
62
|
+
* them, given an editor state. Assigning a key does mean only one plugin
|
|
63
|
+
* of that type can be active in a state.
|
|
64
|
+
*
|
|
65
|
+
* @since 0.1.0
|
|
66
|
+
* @category models
|
|
67
|
+
*/
|
|
68
|
+
var PluginKey = class {
|
|
69
|
+
/** Create a plugin key. */
|
|
70
|
+
constructor(name = "key") {
|
|
71
|
+
this.key = createKey(name);
|
|
72
|
+
}
|
|
73
|
+
/** Get the active plugin with this key, if any, from an editor state. */
|
|
74
|
+
get(state) {
|
|
75
|
+
return Option.fromUndefinedOr(state.config.pluginsByKey[this.key]);
|
|
76
|
+
}
|
|
77
|
+
/** Get the plugin's state from an editor state. */
|
|
78
|
+
getState(state) {
|
|
79
|
+
return getField(state, this.key);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
export { Plugin, PluginKey, PluginTypeId, getField };
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { ModelError, PositionError } from "../model/errors.js";
|
|
2
|
+
import { ResolvedPos } from "../model/resolvedpos.js";
|
|
3
|
+
import { Slice } from "../model/replace.js";
|
|
4
|
+
import { AnySchema } from "../model/schema.js";
|
|
5
|
+
import { Node } from "../model/node.js";
|
|
6
|
+
import "../model.js";
|
|
7
|
+
import "../transform.js";
|
|
8
|
+
import { Mappable } from "../transform/map.js";
|
|
9
|
+
import { SelectionError } from "./errors.js";
|
|
10
|
+
import { Transaction } from "./transaction.js";
|
|
11
|
+
import * as Effect from "effect/Effect";
|
|
12
|
+
import * as Option from "effect/Option";
|
|
13
|
+
import * as S from "effect/Schema";
|
|
14
|
+
/**
|
|
15
|
+
* The JSON representation of a selection. It always includes the
|
|
16
|
+
* selection class's JSON id (see `Selection.jsonID`) under `type`.
|
|
17
|
+
*
|
|
18
|
+
* @since 0.1.0
|
|
19
|
+
* @category models
|
|
20
|
+
*/
|
|
21
|
+
export interface SelectionJSON {
|
|
22
|
+
readonly type: string;
|
|
23
|
+
readonly [key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Something that can deserialize selections of a given type: usually
|
|
27
|
+
* the selection class itself, via a static `fromJSONUnsafe` method that
|
|
28
|
+
* throws a tagged error for invalid input.
|
|
29
|
+
*
|
|
30
|
+
* @since 0.1.0
|
|
31
|
+
* @category models
|
|
32
|
+
*/
|
|
33
|
+
export interface SelectionDeserializer {
|
|
34
|
+
readonly fromJSONUnsafe: (doc: Node<any>, json: any) => Selection<any>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Superclass for editor selections. Every selection type should extend
|
|
38
|
+
* this. Should not be instantiated directly.
|
|
39
|
+
*
|
|
40
|
+
* @since 0.1.0
|
|
41
|
+
* @category models
|
|
42
|
+
*/
|
|
43
|
+
export declare abstract class Selection<Sc extends AnySchema = AnySchema> {
|
|
44
|
+
/**
|
|
45
|
+
* The resolved anchor of the selection (the side that stays in
|
|
46
|
+
* place when the selection is modified).
|
|
47
|
+
*/
|
|
48
|
+
readonly $anchor: ResolvedPos<Sc>;
|
|
49
|
+
/**
|
|
50
|
+
* The resolved head of the selection (the side that moves when the
|
|
51
|
+
* selection is modified).
|
|
52
|
+
*/
|
|
53
|
+
readonly $head: ResolvedPos<Sc>;
|
|
54
|
+
/** The ranges covered by the selection. */
|
|
55
|
+
readonly ranges: ReadonlyArray<SelectionRange<Sc>>;
|
|
56
|
+
/**
|
|
57
|
+
* Initialize a selection with the head and anchor and ranges. If no
|
|
58
|
+
* ranges are given, constructs a single range across `$anchor` and
|
|
59
|
+
* `$head`.
|
|
60
|
+
*/
|
|
61
|
+
constructor(
|
|
62
|
+
/**
|
|
63
|
+
* The resolved anchor of the selection (the side that stays in
|
|
64
|
+
* place when the selection is modified).
|
|
65
|
+
*/
|
|
66
|
+
$anchor: ResolvedPos<Sc>,
|
|
67
|
+
/**
|
|
68
|
+
* The resolved head of the selection (the side that moves when the
|
|
69
|
+
* selection is modified).
|
|
70
|
+
*/
|
|
71
|
+
$head: ResolvedPos<Sc>, ranges?: ReadonlyArray<SelectionRange<Sc>>);
|
|
72
|
+
/** The selection's anchor, as an unresolved position. */
|
|
73
|
+
get anchor(): number;
|
|
74
|
+
/** The selection's head. */
|
|
75
|
+
get head(): number;
|
|
76
|
+
/** The lower bound of the selection's main range. */
|
|
77
|
+
get from(): number;
|
|
78
|
+
/** The upper bound of the selection's main range. */
|
|
79
|
+
get to(): number;
|
|
80
|
+
/** The resolved lower bound of the selection's main range. */
|
|
81
|
+
get $from(): ResolvedPos<Sc>;
|
|
82
|
+
/** The resolved upper bound of the selection's main range. */
|
|
83
|
+
get $to(): ResolvedPos<Sc>;
|
|
84
|
+
/** Indicates whether the selection contains any content. */
|
|
85
|
+
get empty(): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Controls whether, when a selection of this type is active in the
|
|
88
|
+
* browser, the selected range should be visible to the user.
|
|
89
|
+
* Defaults to `true`.
|
|
90
|
+
*/
|
|
91
|
+
get visible(): boolean;
|
|
92
|
+
/** Test whether the selection is the same as another selection. */
|
|
93
|
+
abstract eq(selection: Selection<any>): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Map this selection through a mappable thing. `doc` should be the
|
|
96
|
+
* new document to which we are mapping.
|
|
97
|
+
*/
|
|
98
|
+
abstract map(doc: Node<Sc>, mapping: Mappable): Selection<Sc>;
|
|
99
|
+
/** Get the content of this selection as a slice. */
|
|
100
|
+
content(): Slice<Sc>;
|
|
101
|
+
/**
|
|
102
|
+
* Replace the selection with a slice or, if no slice is given, delete
|
|
103
|
+
* the selection. Will append to the given transaction.
|
|
104
|
+
*/
|
|
105
|
+
replace(tr: Transaction<Sc>, content?: Slice<Sc>): void;
|
|
106
|
+
/** Replace the selection with the given node, appending the changes to the given transaction. */
|
|
107
|
+
replaceWith(tr: Transaction<Sc>, node: Node<Sc>): void;
|
|
108
|
+
/**
|
|
109
|
+
* Convert the selection to a JSON representation. When implementing
|
|
110
|
+
* this for a custom selection class, make sure to give the object a
|
|
111
|
+
* `type` property whose value matches the ID under which you
|
|
112
|
+
* registered your class.
|
|
113
|
+
*/
|
|
114
|
+
abstract toJSON(): SelectionJSON;
|
|
115
|
+
/**
|
|
116
|
+
* Find a valid cursor or leaf node selection starting at the given
|
|
117
|
+
* position and searching back if `dir` is negative, and forward if
|
|
118
|
+
* positive. When `textOnly` is true, only consider cursor selections.
|
|
119
|
+
* Will return `None` when no valid selection position is found.
|
|
120
|
+
*/
|
|
121
|
+
static findFrom<Sc extends AnySchema>($pos: ResolvedPos<Sc>, dir: number, textOnly?: boolean): Option.Option<Selection<Sc>>;
|
|
122
|
+
/** @internal */
|
|
123
|
+
static findFromOrNull<Sc extends AnySchema>($pos: ResolvedPos<Sc>, dir: number, textOnly?: boolean): Selection<Sc> | null;
|
|
124
|
+
/**
|
|
125
|
+
* Find a valid cursor or leaf node selection near the given position.
|
|
126
|
+
* Searches forward first by default, but if `bias` is negative, it
|
|
127
|
+
* will search backwards first.
|
|
128
|
+
*/
|
|
129
|
+
static near<Sc extends AnySchema>($pos: ResolvedPos<Sc>, bias?: number): Selection<Sc>;
|
|
130
|
+
/**
|
|
131
|
+
* Find the cursor or leaf node selection closest to the start of the
|
|
132
|
+
* given document. Will return an `AllSelection` if no valid position
|
|
133
|
+
* exists.
|
|
134
|
+
*/
|
|
135
|
+
static atStart<Sc extends AnySchema>(doc: Node<Sc>): Selection<Sc>;
|
|
136
|
+
/** Find the cursor or leaf node selection closest to the end of the given document. */
|
|
137
|
+
static atEnd<Sc extends AnySchema>(doc: Node<Sc>): Selection<Sc>;
|
|
138
|
+
/**
|
|
139
|
+
* Deserialize the JSON representation of a selection. Dispatches to
|
|
140
|
+
* the selection class registered (with `jsonID`) for the JSON's
|
|
141
|
+
* `type`.
|
|
142
|
+
*/
|
|
143
|
+
static fromJSON<Sc extends AnySchema>(doc: Node<Sc>, json: unknown): Effect.Effect<Selection<Sc>, ModelError | SelectionError>;
|
|
144
|
+
/** Like `fromJSON`, but throws on invalid input. */
|
|
145
|
+
static fromJSONUnsafe<Sc extends AnySchema>(doc: Node<Sc>, json: any): Selection<Sc>;
|
|
146
|
+
/**
|
|
147
|
+
* To be able to deserialize selections from JSON, custom selection
|
|
148
|
+
* classes must register themselves with an ID string, so that they
|
|
149
|
+
* can be disambiguated. Try to pick something that's unlikely to
|
|
150
|
+
* clash with classes from other modules. Throws a `SelectionError`
|
|
151
|
+
* when the ID is already in use.
|
|
152
|
+
*/
|
|
153
|
+
static jsonID<C extends SelectionDeserializer>(id: string, selectionClass: C): C;
|
|
154
|
+
/**
|
|
155
|
+
* Get a bookmark for this selection, which is a value that can be
|
|
156
|
+
* mapped without having access to a current document, and later
|
|
157
|
+
* resolved to a real selection for a given document again. (This is
|
|
158
|
+
* used mostly by the history to track and restore old selections.)
|
|
159
|
+
* The default implementation of this method just converts the
|
|
160
|
+
* selection to a text selection and returns the bookmark for that.
|
|
161
|
+
*/
|
|
162
|
+
getBookmark(): SelectionBookmark<Sc>;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* A lightweight, document-independent representation of a selection.
|
|
166
|
+
* You can define a custom bookmark type for a custom selection class to
|
|
167
|
+
* make the history handle it well.
|
|
168
|
+
*
|
|
169
|
+
* @since 0.1.0
|
|
170
|
+
* @category models
|
|
171
|
+
*/
|
|
172
|
+
export interface SelectionBookmark<Sc extends AnySchema = AnySchema> {
|
|
173
|
+
/** Map the bookmark through a set of changes. */
|
|
174
|
+
readonly map: (mapping: Mappable) => SelectionBookmark<Sc>;
|
|
175
|
+
/**
|
|
176
|
+
* Resolve the bookmark to a real selection again. This may need to do
|
|
177
|
+
* some error checking and may fall back to a default (usually
|
|
178
|
+
* `TextSelection.between`) if mapping made the bookmark invalid.
|
|
179
|
+
*/
|
|
180
|
+
readonly resolve: (doc: Node<Sc>) => Selection<Sc>;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Represents a selected range in a document.
|
|
184
|
+
*
|
|
185
|
+
* @since 0.1.0
|
|
186
|
+
* @category models
|
|
187
|
+
*/
|
|
188
|
+
export declare class SelectionRange<Sc extends AnySchema = AnySchema> {
|
|
189
|
+
/** The lower bound of the range. */
|
|
190
|
+
readonly $from: ResolvedPos<Sc>;
|
|
191
|
+
/** The upper bound of the range. */
|
|
192
|
+
readonly $to: ResolvedPos<Sc>;
|
|
193
|
+
/** Create a range. */
|
|
194
|
+
constructor(
|
|
195
|
+
/** The lower bound of the range. */
|
|
196
|
+
$from: ResolvedPos<Sc>,
|
|
197
|
+
/** The upper bound of the range. */
|
|
198
|
+
$to: ResolvedPos<Sc>);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* A text selection represents a classical editor selection, with a head
|
|
202
|
+
* (the moving side) and anchor (immobile side), both of which point into
|
|
203
|
+
* textblock nodes. It can be empty (a regular cursor position).
|
|
204
|
+
*
|
|
205
|
+
* @since 0.1.0
|
|
206
|
+
* @category models
|
|
207
|
+
*/
|
|
208
|
+
export declare class TextSelection<Sc extends AnySchema = AnySchema> extends Selection<Sc> {
|
|
209
|
+
/** Construct a text selection between the given points. */
|
|
210
|
+
constructor($anchor: ResolvedPos<Sc>, $head?: ResolvedPos<Sc>);
|
|
211
|
+
/** Returns a resolved position if this is a cursor selection (an empty text selection). */
|
|
212
|
+
get $cursor(): Option.Option<ResolvedPos<Sc>>;
|
|
213
|
+
/** @internal */
|
|
214
|
+
get $cursorOrNull(): ResolvedPos<Sc> | null;
|
|
215
|
+
map(doc: Node<Sc>, mapping: Mappable): Selection<Sc>;
|
|
216
|
+
replace(tr: Transaction<Sc>, content?: Slice<Sc>): void;
|
|
217
|
+
eq(other: Selection<any>): boolean;
|
|
218
|
+
getBookmark(): SelectionBookmark<Sc>;
|
|
219
|
+
toJSON(): SelectionJSON;
|
|
220
|
+
/** @internal */
|
|
221
|
+
static fromJSONUnsafe<Sc extends AnySchema>(doc: Node<Sc>, json: any): TextSelection<Sc>;
|
|
222
|
+
/** Create a text selection from non-resolved positions. */
|
|
223
|
+
static create<Sc extends AnySchema>(doc: Node<Sc>, anchor: number, head?: number): Effect.Effect<TextSelection<Sc>, PositionError>;
|
|
224
|
+
/** Like `create`, but throws a `PositionError` for positions outside of the document. */
|
|
225
|
+
static createUnsafe<Sc extends AnySchema>(doc: Node<Sc>, anchor: number, head?: number): TextSelection<Sc>;
|
|
226
|
+
/**
|
|
227
|
+
* Return a text selection that spans the given positions or, if they
|
|
228
|
+
* aren't text positions, find a text selection near them. `bias`
|
|
229
|
+
* determines whether the method searches forward (default) or
|
|
230
|
+
* backwards (negative number) first. Will fall back to calling
|
|
231
|
+
* `Selection.near` when the document doesn't contain a valid text
|
|
232
|
+
* position.
|
|
233
|
+
*/
|
|
234
|
+
static between<Sc extends AnySchema>($anchor: ResolvedPos<Sc>, $head: ResolvedPos<Sc>, bias?: number): Selection<Sc>;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* A node selection is a selection that points at a single node. All
|
|
238
|
+
* nodes marked selectable can be the target of a node selection. In
|
|
239
|
+
* such a selection, `from` and `to` point directly before and after the
|
|
240
|
+
* selected node, `anchor` equals `from`, and `head` equals `to`.
|
|
241
|
+
*
|
|
242
|
+
* @since 0.1.0
|
|
243
|
+
* @category models
|
|
244
|
+
*/
|
|
245
|
+
export declare class NodeSelection<Sc extends AnySchema = AnySchema> extends Selection<Sc> {
|
|
246
|
+
/** The selected node. */
|
|
247
|
+
readonly node: Node<Sc>;
|
|
248
|
+
/**
|
|
249
|
+
* Create a node selection. Does not verify the validity of its
|
|
250
|
+
* argument beyond requiring a node after `$pos` (throwing a
|
|
251
|
+
* `SelectionError` otherwise).
|
|
252
|
+
*/
|
|
253
|
+
constructor($pos: ResolvedPos<Sc>);
|
|
254
|
+
get visible(): boolean;
|
|
255
|
+
map(doc: Node<Sc>, mapping: Mappable): Selection<Sc>;
|
|
256
|
+
content(): Slice<Sc>;
|
|
257
|
+
eq(other: Selection<any>): boolean;
|
|
258
|
+
toJSON(): SelectionJSON;
|
|
259
|
+
getBookmark(): SelectionBookmark<Sc>;
|
|
260
|
+
/** @internal */
|
|
261
|
+
static fromJSONUnsafe<Sc extends AnySchema>(doc: Node<Sc>, json: any): NodeSelection<Sc>;
|
|
262
|
+
/** Create a node selection from a non-resolved position. */
|
|
263
|
+
static create<Sc extends AnySchema>(doc: Node<Sc>, from: number): Effect.Effect<NodeSelection<Sc>, PositionError | SelectionError>;
|
|
264
|
+
/** Like `create`, but throws for invalid positions. */
|
|
265
|
+
static createUnsafe<Sc extends AnySchema>(doc: Node<Sc>, from: number): NodeSelection<Sc>;
|
|
266
|
+
/** Determines whether the given node may be selected as a node selection. */
|
|
267
|
+
static isSelectable(node: Node<any>): boolean;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* A selection type that represents selecting the whole document (which
|
|
271
|
+
* can not necessarily be expressed with a text selection, when there
|
|
272
|
+
* are for example leaf block nodes at the start or end of the
|
|
273
|
+
* document).
|
|
274
|
+
*
|
|
275
|
+
* @since 0.1.0
|
|
276
|
+
* @category models
|
|
277
|
+
*/
|
|
278
|
+
export declare class AllSelection<Sc extends AnySchema = AnySchema> extends Selection<Sc> {
|
|
279
|
+
/** Create an all-selection over the given document. */
|
|
280
|
+
constructor(doc: Node<Sc>);
|
|
281
|
+
replace(tr: Transaction<Sc>, content?: Slice<Sc>): void;
|
|
282
|
+
toJSON(): SelectionJSON;
|
|
283
|
+
/** @internal */
|
|
284
|
+
static fromJSONUnsafe<Sc extends AnySchema>(doc: Node<Sc>): AllSelection<Sc>;
|
|
285
|
+
map(doc: Node<Sc>): Selection<Sc>;
|
|
286
|
+
eq(other: Selection<any>): boolean;
|
|
287
|
+
getBookmark(): SelectionBookmark<Sc>;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* An Effect `Schema` that decodes the JSON representation of a
|
|
291
|
+
* selection (using the `Selection.jsonID` registry) into a selection in
|
|
292
|
+
* the given document, and encodes selections back to JSON.
|
|
293
|
+
*
|
|
294
|
+
* @since 0.1.0
|
|
295
|
+
* @category schemas
|
|
296
|
+
*/
|
|
297
|
+
export declare const SelectionFromJSON: <Sc extends AnySchema>(doc: Node<Sc>) => S.Codec<Selection<Sc>, SelectionJSON>;
|