@prosekit/core 0.7.15 → 0.8.1
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/dist/editor-BOiNwODb.js +1237 -0
- package/dist/editor-BP9kgR6R.d.ts +750 -0
- package/dist/prosekit-core-test.d.ts +37 -2
- package/dist/prosekit-core-test.js +94 -116
- package/dist/prosekit-core.d.ts +1519 -203
- package/dist/prosekit-core.js +1451 -1426
- package/package.json +21 -11
- package/dist/_tsup-dts-rollup.d.ts +0 -3076
- package/dist/chunk-B3WEP4DD.js +0 -1188
package/dist/prosekit-core.js
CHANGED
@@ -1,1611 +1,1636 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
isMarkAbsent,
|
22
|
-
isMarkActive,
|
23
|
-
isNodeActive,
|
24
|
-
isNodeSelection,
|
25
|
-
isNotNullish,
|
26
|
-
isProseMirrorNode,
|
27
|
-
isSelection,
|
28
|
-
isSlice,
|
29
|
-
isTextSelection,
|
30
|
-
jsonFromHTML,
|
31
|
-
jsonFromNode,
|
32
|
-
jsonFromState,
|
33
|
-
nodeFromElement,
|
34
|
-
nodeFromHTML,
|
35
|
-
nodeFromJSON,
|
36
|
-
rootFacet,
|
37
|
-
schemaFacet,
|
38
|
-
stateFacet,
|
39
|
-
stateFromJSON,
|
40
|
-
toReversed,
|
41
|
-
union
|
42
|
-
} from "./chunk-B3WEP4DD.js";
|
43
|
-
|
44
|
-
// src/commands/add-mark.ts
|
1
|
+
import { Editor, EditorNotFoundError, Priority, ProseKitError, assert, createEditor, defineDefaultState, defineFacet, defineFacetPayload, elementFromJSON, elementFromNode, getMarkType, getNodeType, htmlFromJSON, htmlFromNode, isAllSelection, isFragment, isMark, isMarkAbsent, isMarkActive, isNodeActive, isNodeSelection, isNotNullish, isProseMirrorNode, isSelection, isSlice, isTextSelection, jsonFromHTML, jsonFromNode, jsonFromState, nodeFromElement, nodeFromHTML, nodeFromJSON, rootFacet, schemaFacet, stateFacet, stateFromJSON, toReversed, union } from "./editor-BOiNwODb.js";
|
2
|
+
import { AllSelection, Plugin, PluginKey, ProseMirrorPlugin, TextSelection } from "@prosekit/pm/state";
|
3
|
+
import { ReplaceAroundStep, findWrapping, insertPoint } from "@prosekit/pm/transform";
|
4
|
+
import { baseKeymap, chainCommands, createParagraphNear, deleteSelection, joinTextblockBackward, lift, liftEmptyBlock, newlineInCode, selectNodeBackward, setBlockType as setBlockType$1, toggleMark as toggleMark$1 } from "@prosekit/pm/commands";
|
5
|
+
import { DOMSerializer, Fragment, ProseMirrorFragment, ProseMirrorNode, Slice } from "@prosekit/pm/model";
|
6
|
+
import { isElementLike } from "@ocavue/utils";
|
7
|
+
import OrderedMap from "orderedmap";
|
8
|
+
import mapValues from "just-map-values";
|
9
|
+
import clone from "just-clone";
|
10
|
+
import { history, redo, undo } from "@prosekit/pm/history";
|
11
|
+
import { keydownHandler } from "@prosekit/pm/keymap";
|
12
|
+
import { splitSplittableBlock } from "prosemirror-splittable";
|
13
|
+
import clsxLite from "clsx/lite";
|
14
|
+
|
15
|
+
//#region src/commands/add-mark.ts
|
16
|
+
/**
|
17
|
+
* Returns a command that adds the given mark with the given attributes.
|
18
|
+
*
|
19
|
+
* @public
|
20
|
+
*/
|
45
21
|
function addMark(options) {
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
22
|
+
return (state, dispatch) => {
|
23
|
+
const mark = getMarkType(state.schema, options.type).create(options.attrs);
|
24
|
+
const from = options.from ?? state.selection.from;
|
25
|
+
const to = options.to ?? state.selection.to;
|
26
|
+
if (from > to) return false;
|
27
|
+
dispatch?.(state.tr.addMark(from, to, mark));
|
28
|
+
return true;
|
29
|
+
};
|
30
|
+
}
|
31
|
+
|
32
|
+
//#endregion
|
33
|
+
//#region src/commands/expand-mark.ts
|
34
|
+
/**
|
35
|
+
* Expands the selection to include the entire mark at the current position.
|
36
|
+
*
|
37
|
+
* @public
|
38
|
+
*/
|
62
39
|
function expandMark(options) {
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
dispatch(state.tr.setSelection(TextSelection.create(state.doc, from, to)));
|
73
|
-
}
|
74
|
-
return true;
|
75
|
-
};
|
40
|
+
return (state, dispatch) => {
|
41
|
+
const markType = getMarkType(state.schema, options.type);
|
42
|
+
const predicate = (mark) => mark.type === markType;
|
43
|
+
const from = expandMarkBefore(state.selection.$from, predicate);
|
44
|
+
const to = expandMarkAfter(state.selection.$to, predicate);
|
45
|
+
if (from === state.selection.from && to === state.selection.to) return false;
|
46
|
+
if (dispatch) dispatch(state.tr.setSelection(TextSelection.create(state.doc, from, to)));
|
47
|
+
return true;
|
48
|
+
};
|
76
49
|
}
|
77
50
|
function expandMarkBefore($pos, predicate) {
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
} else {
|
89
|
-
break;
|
90
|
-
}
|
91
|
-
}
|
92
|
-
return $pos.posAtIndex(boundaryIndex);
|
51
|
+
const { parent } = $pos;
|
52
|
+
if (!$pos.marks().some(predicate)) return $pos.pos;
|
53
|
+
const index = $pos.index();
|
54
|
+
let boundaryIndex = index;
|
55
|
+
for (let i = index; i >= 0; i--) {
|
56
|
+
const node = parent.child(i);
|
57
|
+
if (node.marks.some(predicate)) boundaryIndex = i;
|
58
|
+
else break;
|
59
|
+
}
|
60
|
+
return $pos.posAtIndex(boundaryIndex);
|
93
61
|
}
|
94
62
|
function expandMarkAfter($pos, predicate) {
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
// src/commands/insert-default-block.ts
|
114
|
-
import {
|
115
|
-
TextSelection as TextSelection2
|
116
|
-
} from "@prosekit/pm/state";
|
117
|
-
|
118
|
-
// src/utils/default-block-at.ts
|
63
|
+
const { parent } = $pos;
|
64
|
+
if (!$pos.marks().some(predicate)) return $pos.pos;
|
65
|
+
const index = Math.max(0, $pos.indexAfter() - 1);
|
66
|
+
const childCount = parent.childCount;
|
67
|
+
let boundaryIndex = index;
|
68
|
+
for (let i = index; i < childCount; i++) {
|
69
|
+
const node = parent.child(i);
|
70
|
+
if (node.marks.some(predicate)) boundaryIndex = i;
|
71
|
+
else break;
|
72
|
+
}
|
73
|
+
return $pos.posAtIndex(boundaryIndex) + parent.child(boundaryIndex).nodeSize;
|
74
|
+
}
|
75
|
+
|
76
|
+
//#endregion
|
77
|
+
//#region src/utils/default-block-at.ts
|
78
|
+
/**
|
79
|
+
* @internal
|
80
|
+
*/
|
119
81
|
function defaultBlockAt(match) {
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
}
|
126
|
-
|
127
|
-
|
82
|
+
for (let i = 0; i < match.edgeCount; i++) {
|
83
|
+
const { type } = match.edge(i);
|
84
|
+
if (type.isTextblock && !type.hasRequiredAttrs()) return type;
|
85
|
+
}
|
86
|
+
return null;
|
87
|
+
}
|
88
|
+
|
89
|
+
//#endregion
|
90
|
+
//#region src/commands/insert-default-block.ts
|
91
|
+
/**
|
92
|
+
* Returns a command that inserts a default block after current selection or at
|
93
|
+
* the given position.
|
94
|
+
*
|
95
|
+
* @public
|
96
|
+
*/
|
128
97
|
function insertDefaultBlock(options) {
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
}
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
// src/utils/set-selection-around.ts
|
154
|
-
import {
|
155
|
-
TextSelection as TextSelection3
|
156
|
-
} from "@prosekit/pm/state";
|
98
|
+
return (state, dispatch) => {
|
99
|
+
const $pos = options?.pos == null ? state.selection.$to : state.doc.resolve(options.pos);
|
100
|
+
const depth = $pos.parent.isTextblock ? $pos.depth - 1 : $pos.depth;
|
101
|
+
const parent = $pos.node(depth);
|
102
|
+
const index = $pos.indexAfter(depth);
|
103
|
+
const type = defaultBlockAt(parent.contentMatchAt(index));
|
104
|
+
if (!type) return false;
|
105
|
+
if (dispatch) {
|
106
|
+
const pos = $pos.posAtIndex(index, depth);
|
107
|
+
const node = type.createAndFill();
|
108
|
+
if (!node) return false;
|
109
|
+
const tr = state.tr.insert(pos, node);
|
110
|
+
const selection = TextSelection.findFrom(tr.doc.resolve(pos), 1);
|
111
|
+
if (!selection) return false;
|
112
|
+
tr.setSelection(selection);
|
113
|
+
dispatch(tr.scrollIntoView());
|
114
|
+
}
|
115
|
+
return true;
|
116
|
+
};
|
117
|
+
}
|
118
|
+
|
119
|
+
//#endregion
|
120
|
+
//#region src/utils/set-selection-around.ts
|
157
121
|
function setSelectionAround(tr, pos) {
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
}
|
163
|
-
|
164
|
-
|
122
|
+
const docSize = tr.doc.content.size;
|
123
|
+
const $pos = tr.doc.resolve(pos > docSize ? docSize : pos < 0 ? 0 : pos);
|
124
|
+
const selection = TextSelection.between($pos, $pos);
|
125
|
+
tr.setSelection(selection);
|
126
|
+
}
|
127
|
+
|
128
|
+
//#endregion
|
129
|
+
//#region src/commands/insert-node.ts
|
130
|
+
/**
|
131
|
+
* Returns a command that inserts the given node at the current selection or at
|
132
|
+
* the given position.
|
133
|
+
*
|
134
|
+
* @public
|
135
|
+
*/
|
165
136
|
function insertNode(options) {
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
137
|
+
return (state, dispatch) => {
|
138
|
+
const node = options.node ? options.node : options.type ? getNodeType(state.schema, options.type).createAndFill(options.attrs) : null;
|
139
|
+
assert(node, "You must provide either a node or a type");
|
140
|
+
const insertPos = insertPoint(state.doc, options.pos ?? state.selection.anchor, node.type);
|
141
|
+
if (insertPos == null) return false;
|
142
|
+
if (dispatch) {
|
143
|
+
const tr = state.tr.insert(insertPos, node);
|
144
|
+
setSelectionAround(tr, insertPos + node.nodeSize);
|
145
|
+
dispatch(tr);
|
146
|
+
}
|
147
|
+
return true;
|
148
|
+
};
|
149
|
+
}
|
150
|
+
|
151
|
+
//#endregion
|
152
|
+
//#region src/commands/remove-mark.ts
|
153
|
+
/**
|
154
|
+
* Returns a command that removes the given mark.
|
155
|
+
*
|
156
|
+
* @public
|
157
|
+
*/
|
185
158
|
function removeMark(options) {
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
159
|
+
return (state, dispatch) => {
|
160
|
+
const markType = getMarkType(state.schema, options.type);
|
161
|
+
const mark = options.attrs ? markType.create(options.attrs) : markType;
|
162
|
+
const from = options.from ?? state.selection.from;
|
163
|
+
const to = options.to ?? state.selection.to;
|
164
|
+
if (from > to) return false;
|
165
|
+
dispatch?.(state.tr.removeMark(from, to, mark));
|
166
|
+
return true;
|
167
|
+
};
|
168
|
+
}
|
169
|
+
|
170
|
+
//#endregion
|
171
|
+
//#region src/utils/find-parent-node.ts
|
172
|
+
/**
|
173
|
+
* Find the closest parent node that satisfies the predicate.
|
174
|
+
*
|
175
|
+
* @public
|
176
|
+
*/
|
200
177
|
function findParentNode(predicate, $pos) {
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
178
|
+
for (let depth = $pos.depth; depth >= 0; depth -= 1) {
|
179
|
+
const node = $pos.node(depth);
|
180
|
+
if (predicate(node)) {
|
181
|
+
const pos = depth === 0 ? 0 : $pos.before(depth);
|
182
|
+
const start = $pos.start(depth);
|
183
|
+
return {
|
184
|
+
node,
|
185
|
+
pos,
|
186
|
+
start,
|
187
|
+
depth
|
188
|
+
};
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
|
193
|
+
//#endregion
|
194
|
+
//#region src/utils/find-parent-node-of-type.ts
|
195
|
+
/**
|
196
|
+
* Finds the closest parent node that matches the given node type.
|
197
|
+
*
|
198
|
+
* @public
|
199
|
+
*/
|
212
200
|
function findParentNodeOfType(type, $pos) {
|
213
|
-
|
214
|
-
|
201
|
+
const nodeType = getNodeType($pos.doc.type.schema, type);
|
202
|
+
return findParentNode((node) => node.type === nodeType, $pos);
|
215
203
|
}
|
216
204
|
|
217
|
-
|
205
|
+
//#endregion
|
206
|
+
//#region src/commands/remove-node.ts
|
207
|
+
/**
|
208
|
+
* Returns a command to remove the nearest ancestor node of a specific type from the current position.
|
209
|
+
*
|
210
|
+
* @public
|
211
|
+
*/
|
218
212
|
function removeNode(options) {
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
}
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
TextSelection as TextSelection4
|
232
|
-
} from "@prosekit/pm/state";
|
213
|
+
return (state, dispatch) => {
|
214
|
+
const $pos = typeof options.pos === "number" ? state.doc.resolve(options.pos) : state.selection.$anchor;
|
215
|
+
const found = findParentNodeOfType(options.type, $pos);
|
216
|
+
if (!found) return false;
|
217
|
+
const { pos, node } = found;
|
218
|
+
dispatch?.(state.tr.delete(pos, pos + node.nodeSize));
|
219
|
+
return true;
|
220
|
+
};
|
221
|
+
}
|
222
|
+
|
223
|
+
//#endregion
|
224
|
+
//#region src/utils/get-custom-selection.ts
|
233
225
|
function getCustomSelection(state, from, to) {
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
}
|
242
|
-
|
243
|
-
|
226
|
+
const pos = from ?? to;
|
227
|
+
if (pos != null) {
|
228
|
+
const $from = state.doc.resolve(from ?? pos);
|
229
|
+
const $to = state.doc.resolve(to ?? pos);
|
230
|
+
return TextSelection.between($from, $to);
|
231
|
+
}
|
232
|
+
return state.selection;
|
233
|
+
}
|
234
|
+
|
235
|
+
//#endregion
|
236
|
+
//#region src/commands/set-block-type.ts
|
237
|
+
/**
|
238
|
+
* Returns a command that tries to set the selected textblocks to the given node
|
239
|
+
* type with the given attributes.
|
240
|
+
*
|
241
|
+
* @public
|
242
|
+
*/
|
244
243
|
function setBlockType(options) {
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
}
|
281
|
-
|
282
|
-
// src/utils/get-node-types.ts
|
244
|
+
return (state, dispatch) => {
|
245
|
+
const nodeType = getNodeType(state.schema, options.type);
|
246
|
+
const selection = getCustomSelection(state, options.from, options.to);
|
247
|
+
const attrs = options.attrs;
|
248
|
+
let applicable = false;
|
249
|
+
for (let i = 0; i < selection.ranges.length && !applicable; i++) {
|
250
|
+
const { $from: { pos: from }, $to: { pos: to } } = selection.ranges[i];
|
251
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
252
|
+
if (applicable) return false;
|
253
|
+
if (!node.isTextblock || node.hasMarkup(nodeType, attrs)) return;
|
254
|
+
if (node.type == nodeType) applicable = true;
|
255
|
+
else {
|
256
|
+
const $pos = state.doc.resolve(pos), index = $pos.index();
|
257
|
+
applicable = $pos.parent.canReplaceWith(index, index + 1, nodeType);
|
258
|
+
}
|
259
|
+
});
|
260
|
+
}
|
261
|
+
if (!applicable) return false;
|
262
|
+
if (dispatch) {
|
263
|
+
const tr = state.tr;
|
264
|
+
for (const range of selection.ranges) {
|
265
|
+
const { $from: { pos: from }, $to: { pos: to } } = range;
|
266
|
+
tr.setBlockType(from, to, nodeType, attrs);
|
267
|
+
}
|
268
|
+
dispatch(tr.scrollIntoView());
|
269
|
+
}
|
270
|
+
return true;
|
271
|
+
};
|
272
|
+
}
|
273
|
+
|
274
|
+
//#endregion
|
275
|
+
//#region src/utils/get-node-types.ts
|
276
|
+
/**
|
277
|
+
* @internal
|
278
|
+
*/
|
283
279
|
function getNodeTypes(schema, types) {
|
284
|
-
|
285
|
-
|
286
|
-
}
|
287
|
-
return [getNodeType(schema, types)];
|
280
|
+
if (Array.isArray(types)) return types.map((type) => getNodeType(schema, type));
|
281
|
+
return [getNodeType(schema, types)];
|
288
282
|
}
|
289
283
|
|
290
|
-
|
284
|
+
//#endregion
|
285
|
+
//#region src/commands/set-node-attrs.ts
|
286
|
+
/**
|
287
|
+
* Returns a command that set the attributes of the current node.
|
288
|
+
*
|
289
|
+
* @public
|
290
|
+
*/
|
291
291
|
function setNodeAttrs(options) {
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
292
|
+
return (state, dispatch) => {
|
293
|
+
const nodeTypes = getNodeTypes(state.schema, options.type);
|
294
|
+
const from = options.pos ?? state.selection.from;
|
295
|
+
const to = options.pos ?? state.selection.to;
|
296
|
+
const positions = [];
|
297
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
298
|
+
if (nodeTypes.includes(node.type)) positions.push(pos);
|
299
|
+
if (!dispatch && positions.length > 0) return false;
|
300
|
+
});
|
301
|
+
if (positions.length === 0) return false;
|
302
|
+
if (dispatch) {
|
303
|
+
const { tr } = state;
|
304
|
+
for (const pos of positions) for (const [key, value] of Object.entries(options.attrs)) tr.setNodeAttribute(pos, key, value);
|
305
|
+
dispatch(tr);
|
306
|
+
}
|
307
|
+
return true;
|
308
|
+
};
|
309
|
+
}
|
310
|
+
|
311
|
+
//#endregion
|
312
|
+
//#region src/commands/toggle-mark.ts
|
313
|
+
/**
|
314
|
+
* Returns a command that toggles the given mark with the given attributes.
|
315
|
+
*
|
316
|
+
* @param options
|
317
|
+
*
|
318
|
+
* @public
|
319
|
+
*/
|
320
|
+
function toggleMark({ type, attrs, removeWhenPresent = false, enterInlineAtoms = true }) {
|
321
|
+
return (state, dispatch, view) => {
|
322
|
+
return toggleMark$1(getMarkType(state.schema, type), attrs, {
|
323
|
+
removeWhenPresent,
|
324
|
+
enterInlineAtoms
|
325
|
+
})(state, dispatch, view);
|
326
|
+
};
|
327
|
+
}
|
328
|
+
|
329
|
+
//#endregion
|
330
|
+
//#region src/commands/toggle-node.ts
|
331
|
+
/**
|
332
|
+
* Returns a command that set the selected textblocks to the given node type
|
333
|
+
* with the given attributes.
|
334
|
+
*
|
335
|
+
* @param options
|
336
|
+
*
|
337
|
+
* @public
|
338
|
+
*/
|
339
339
|
function toggleNode({ type, attrs }) {
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
340
|
+
return (state, dispatch, view) => {
|
341
|
+
if (isNodeActive(state, type, attrs)) {
|
342
|
+
const defaultType = state.schema.topNodeType.contentMatch.defaultType;
|
343
|
+
if (!defaultType) return false;
|
344
|
+
return setBlockType$1(defaultType)(state, dispatch, view);
|
345
|
+
} else {
|
346
|
+
const nodeType = getNodeType(state.schema, type);
|
347
|
+
return setBlockType$1(nodeType, attrs)(state, dispatch, view);
|
348
|
+
}
|
349
|
+
};
|
350
|
+
}
|
351
|
+
|
352
|
+
//#endregion
|
353
|
+
//#region src/commands/wrap.ts
|
354
|
+
/**
|
355
|
+
* Returns a command that wraps the selected textblock with the given node type.
|
356
|
+
*
|
357
|
+
* @param options
|
358
|
+
*
|
359
|
+
* @public
|
360
|
+
*/
|
359
361
|
function wrap(options) {
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
}
|
371
|
-
|
372
|
-
|
362
|
+
return (state, dispatch) => {
|
363
|
+
const { $from, $to } = state.selection;
|
364
|
+
const range = $from.blockRange($to);
|
365
|
+
if (!range) return false;
|
366
|
+
const nodeType = getNodeType(state.schema, options.nodeType || options.type);
|
367
|
+
const wrapping = findWrapping(range, nodeType, options.attrs);
|
368
|
+
if (!wrapping) return false;
|
369
|
+
dispatch?.(state.tr.wrap(range, wrapping));
|
370
|
+
return true;
|
371
|
+
};
|
372
|
+
}
|
373
|
+
|
374
|
+
//#endregion
|
375
|
+
//#region src/commands/toggle-wrap.ts
|
376
|
+
/**
|
377
|
+
* Toggle between wrapping an inactive node with the provided node type, and
|
378
|
+
* lifting it up into it's parent.
|
379
|
+
*
|
380
|
+
* @param options
|
381
|
+
*
|
382
|
+
* @public
|
383
|
+
*/
|
373
384
|
function toggleWrap(options) {
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
}
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
385
|
+
const { type, attrs } = options;
|
386
|
+
return (state, dispatch) => {
|
387
|
+
if (isNodeActive(state, type, attrs)) return lift(state, dispatch);
|
388
|
+
return wrap({
|
389
|
+
type,
|
390
|
+
attrs
|
391
|
+
})(state, dispatch);
|
392
|
+
};
|
393
|
+
}
|
394
|
+
|
395
|
+
//#endregion
|
396
|
+
//#region src/commands/unset-block-type.ts
|
397
|
+
/**
|
398
|
+
* Returns a command that set the type of all textblocks between the given range
|
399
|
+
* to the default type (usually `paragraph`).
|
400
|
+
*
|
401
|
+
* @public
|
402
|
+
*/
|
389
403
|
function unsetBlockType(options) {
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
404
|
+
return (state, dispatch) => {
|
405
|
+
const from = options?.from ?? state.selection.from;
|
406
|
+
const to = options?.to ?? state.selection.to;
|
407
|
+
if (from > to) return false;
|
408
|
+
const tr = state.tr;
|
409
|
+
if (unsetTextBlockType(tr, from, to)) {
|
410
|
+
dispatch?.(tr);
|
411
|
+
return true;
|
412
|
+
}
|
413
|
+
return false;
|
414
|
+
};
|
401
415
|
}
|
402
416
|
function unsetTextBlockType(tr, from, to) {
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
// src/commands/unset-mark.ts
|
417
|
+
const mapFrom = tr.steps.length;
|
418
|
+
tr.doc.nodesBetween(from, to, (node, pos, parent, index) => {
|
419
|
+
if (!parent || !node.isTextblock) return true;
|
420
|
+
const defaultType = parent.contentMatchAt(index).defaultType;
|
421
|
+
if (defaultType && defaultType.isTextblock && node.type !== defaultType && defaultType.validContent(node.content)) {
|
422
|
+
const mapping = tr.mapping.slice(mapFrom);
|
423
|
+
const start = mapping.map(pos, 1);
|
424
|
+
const end = mapping.map(pos + node.nodeSize, 1);
|
425
|
+
const step = new ReplaceAroundStep(start, end, start + 1, end - 1, new Slice(Fragment.from(defaultType.create()), 0, 0), 1, true);
|
426
|
+
tr.step(step);
|
427
|
+
}
|
428
|
+
return false;
|
429
|
+
});
|
430
|
+
return tr.steps.length > mapFrom;
|
431
|
+
}
|
432
|
+
|
433
|
+
//#endregion
|
434
|
+
//#region src/commands/unset-mark.ts
|
435
|
+
/**
|
436
|
+
* Returns a command that removes all marks.
|
437
|
+
*
|
438
|
+
* @public
|
439
|
+
*/
|
428
440
|
function unsetMark(options) {
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
}
|
437
|
-
|
438
|
-
|
441
|
+
return (state, dispatch) => {
|
442
|
+
const from = options?.from ?? state.selection.from;
|
443
|
+
const to = options?.to ?? state.selection.to;
|
444
|
+
if (from > to) return false;
|
445
|
+
dispatch?.(state.tr.removeMark(from, to));
|
446
|
+
return true;
|
447
|
+
};
|
448
|
+
}
|
449
|
+
|
450
|
+
//#endregion
|
451
|
+
//#region src/editor/with-priority.ts
|
452
|
+
/**
|
453
|
+
* Return an new extension with the given priority.
|
454
|
+
*
|
455
|
+
* @example
|
456
|
+
* ```ts
|
457
|
+
* import { Priority, withPriority } from 'prosekit/core'
|
458
|
+
*
|
459
|
+
* const extension = withPriority(defineMyExtension(), Priority.high)
|
460
|
+
* ```
|
461
|
+
*
|
462
|
+
* @public
|
463
|
+
*/
|
439
464
|
function withPriority(extension, priority) {
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
}
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
import {
|
456
|
-
Plugin
|
457
|
-
} from "@prosekit/pm/state";
|
465
|
+
const result = union(extension);
|
466
|
+
result.priority = priority;
|
467
|
+
return result;
|
468
|
+
}
|
469
|
+
|
470
|
+
//#endregion
|
471
|
+
//#region src/extensions/plugin.ts
|
472
|
+
/**
|
473
|
+
* Adds a ProseMirror plugin to the editor.
|
474
|
+
*
|
475
|
+
* @param plugin - The ProseMirror plugin to add, or an array of plugins, or a
|
476
|
+
* function that returns one or multiple plugins.
|
477
|
+
*
|
478
|
+
* @public
|
479
|
+
*/
|
458
480
|
function definePlugin(plugin) {
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
if (typeof plugin === "function") {
|
463
|
-
return definePluginPayload(plugin);
|
464
|
-
}
|
465
|
-
throw new TypeError("Invalid plugin");
|
481
|
+
if (plugin instanceof Plugin || Array.isArray(plugin) && plugin.every((p) => p instanceof Plugin)) return definePluginPayload(() => plugin);
|
482
|
+
if (typeof plugin === "function") return definePluginPayload(plugin);
|
483
|
+
throw new TypeError("Invalid plugin");
|
466
484
|
}
|
467
485
|
function definePluginPayload(payload) {
|
468
|
-
|
469
|
-
}
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
return { plugins };
|
487
|
-
};
|
488
|
-
},
|
489
|
-
parent: stateFacet
|
486
|
+
return defineFacetPayload(pluginFacet, [payload]);
|
487
|
+
}
|
488
|
+
/**
|
489
|
+
* @internal
|
490
|
+
*/
|
491
|
+
const pluginFacet = defineFacet({
|
492
|
+
reducer: (payloads) => {
|
493
|
+
return ({ schema }) => {
|
494
|
+
const plugins = [];
|
495
|
+
for (const payload of payloads) if (payload instanceof Plugin) plugins.push(payload);
|
496
|
+
else if (Array.isArray(payload) && payload.every((p) => p instanceof Plugin)) plugins.push(...payload);
|
497
|
+
else if (typeof payload === "function") plugins.push(...[payload({ schema })].flat());
|
498
|
+
else throw new ProseKitError("Invalid plugin");
|
499
|
+
plugins.reverse();
|
500
|
+
return { plugins };
|
501
|
+
};
|
502
|
+
},
|
503
|
+
parent: stateFacet
|
490
504
|
});
|
491
505
|
|
492
|
-
|
506
|
+
//#endregion
|
507
|
+
//#region src/extensions/clipboard-serializer.ts
|
493
508
|
function mergeWrappers(wrappers) {
|
494
|
-
|
509
|
+
return (fn) => wrappers.filter(isNotNullish).reduce((fn$1, wrapper) => wrapper(fn$1), fn);
|
495
510
|
}
|
496
511
|
function wrapFunction(fn, wrapper) {
|
497
|
-
|
512
|
+
return wrapper ? wrapper(fn) : fn;
|
498
513
|
}
|
499
514
|
var CustomDOMSerializer = class extends DOMSerializer {
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
515
|
+
constructor(nodes, marks, serializeFragmentWrapper, serializeNodeWrapper) {
|
516
|
+
super(nodes, marks);
|
517
|
+
this.serializeFragmentWrapper = serializeFragmentWrapper;
|
518
|
+
this.serializeNodeWrapper = serializeNodeWrapper;
|
519
|
+
}
|
520
|
+
serializeFragment(...args) {
|
521
|
+
const fn = (...args$1) => super.serializeFragment(...args$1);
|
522
|
+
return wrapFunction(fn, this.serializeFragmentWrapper)(...args);
|
523
|
+
}
|
524
|
+
serializeNode(...args) {
|
525
|
+
const fn = (...args$1) => super.serializeNode(...args$1);
|
526
|
+
return wrapFunction(fn, this.serializeNodeWrapper)(...args);
|
527
|
+
}
|
513
528
|
};
|
514
529
|
function createCustomDOMSerializer(schema, options) {
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
}
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
singleton: true,
|
540
|
-
parent: pluginFacet
|
530
|
+
const nodesFromSchema = (...args) => DOMSerializer.nodesFromSchema(...args);
|
531
|
+
const marksFromSchema = (...args) => DOMSerializer.marksFromSchema(...args);
|
532
|
+
const nodes = wrapFunction(nodesFromSchema, options.nodesFromSchemaWrapper)(schema);
|
533
|
+
const marks = wrapFunction(marksFromSchema, options.marksFromSchemaWrapper)(schema);
|
534
|
+
return new CustomDOMSerializer(nodes, marks, options.serializeFragmentWrapper, options.serializeNodeWrapper);
|
535
|
+
}
|
536
|
+
const clipboardSerializerFacet = defineFacet({
|
537
|
+
reducer: (inputs) => {
|
538
|
+
const options = {
|
539
|
+
serializeFragmentWrapper: mergeWrappers(inputs.map((input) => input.serializeFragmentWrapper)),
|
540
|
+
serializeNodeWrapper: mergeWrappers(inputs.map((input) => input.serializeNodeWrapper)),
|
541
|
+
nodesFromSchemaWrapper: mergeWrappers(inputs.map((input) => input.nodesFromSchemaWrapper)),
|
542
|
+
marksFromSchemaWrapper: mergeWrappers(inputs.map((input) => input.marksFromSchemaWrapper))
|
543
|
+
};
|
544
|
+
return ({ schema }) => {
|
545
|
+
const clipboardSerializer = createCustomDOMSerializer(schema, options);
|
546
|
+
return [new ProseMirrorPlugin({
|
547
|
+
key: new PluginKey("prosekit-clipboard-serializer"),
|
548
|
+
props: { clipboardSerializer }
|
549
|
+
})];
|
550
|
+
};
|
551
|
+
},
|
552
|
+
singleton: true,
|
553
|
+
parent: pluginFacet
|
541
554
|
});
|
555
|
+
/**
|
556
|
+
* @internal
|
557
|
+
*/
|
542
558
|
function defineClipboardSerializer(options) {
|
543
|
-
|
559
|
+
return defineFacetPayload(clipboardSerializerFacet, [options]);
|
544
560
|
}
|
545
561
|
|
546
|
-
|
562
|
+
//#endregion
|
563
|
+
//#region src/commands/insert-text.ts
|
564
|
+
/**
|
565
|
+
* Returns a command that inserts the given text.
|
566
|
+
*
|
567
|
+
* @public
|
568
|
+
*/
|
547
569
|
function insertText({ text, from, to }) {
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
570
|
+
return (state, dispatch) => {
|
571
|
+
if (text) dispatch?.(state.tr.insertText(text, from, to));
|
572
|
+
return true;
|
573
|
+
};
|
574
|
+
}
|
575
|
+
|
576
|
+
//#endregion
|
577
|
+
//#region src/commands/select-all.ts
|
578
|
+
/**
|
579
|
+
* Returns a command that selects the whole document.
|
580
|
+
*
|
581
|
+
* @public
|
582
|
+
*/
|
560
583
|
function selectAll() {
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
}
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
584
|
+
return (state, dispatch) => {
|
585
|
+
dispatch?.(state.tr.setSelection(new AllSelection(state.doc)));
|
586
|
+
return true;
|
587
|
+
};
|
588
|
+
}
|
589
|
+
|
590
|
+
//#endregion
|
591
|
+
//#region src/facets/command.ts
|
592
|
+
const commandFacet = defineFacet({
|
593
|
+
reducer: (inputs) => {
|
594
|
+
const commands$1 = Object.assign({}, ...inputs);
|
595
|
+
return { commands: commands$1 };
|
596
|
+
},
|
597
|
+
parent: rootFacet,
|
598
|
+
singleton: true
|
575
599
|
});
|
576
600
|
|
577
|
-
|
578
|
-
|
579
|
-
|
601
|
+
//#endregion
|
602
|
+
//#region src/extensions/command.ts
|
603
|
+
function defineCommands(commands$1) {
|
604
|
+
return defineFacetPayload(commandFacet, [commands$1]);
|
580
605
|
}
|
606
|
+
/**
|
607
|
+
* Add some base commands
|
608
|
+
*
|
609
|
+
* @public
|
610
|
+
*/
|
581
611
|
function defineBaseCommands() {
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
}
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
612
|
+
return defineCommands({
|
613
|
+
insertText,
|
614
|
+
insertNode,
|
615
|
+
removeNode,
|
616
|
+
wrap,
|
617
|
+
toggleWrap,
|
618
|
+
setBlockType,
|
619
|
+
setNodeAttrs,
|
620
|
+
insertDefaultBlock,
|
621
|
+
selectAll,
|
622
|
+
addMark,
|
623
|
+
removeMark,
|
624
|
+
unsetBlockType,
|
625
|
+
unsetMark
|
626
|
+
});
|
627
|
+
}
|
628
|
+
|
629
|
+
//#endregion
|
630
|
+
//#region src/facets/schema-spec.ts
|
631
|
+
const schemaSpecFacet = defineFacet({
|
632
|
+
reducer: (specs) => {
|
633
|
+
let nodes = OrderedMap.from({});
|
634
|
+
let marks = OrderedMap.from({});
|
635
|
+
let topNode = void 0;
|
636
|
+
for (const spec of specs) {
|
637
|
+
nodes = nodes.append(spec.nodes);
|
638
|
+
marks = marks.append(spec.marks ?? {});
|
639
|
+
topNode = topNode ?? spec.topNode;
|
640
|
+
}
|
641
|
+
return {
|
642
|
+
nodes,
|
643
|
+
marks,
|
644
|
+
topNode
|
645
|
+
};
|
646
|
+
},
|
647
|
+
parent: schemaFacet,
|
648
|
+
singleton: true
|
619
649
|
});
|
620
650
|
|
621
|
-
|
651
|
+
//#endregion
|
652
|
+
//#region src/utils/array-grouping.ts
|
622
653
|
function groupBy(items, keySelector) {
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
654
|
+
const result = {};
|
655
|
+
for (const item of items) {
|
656
|
+
const key = keySelector(item);
|
657
|
+
const values = result[key] ||= [];
|
658
|
+
values.push(item);
|
659
|
+
}
|
660
|
+
return result;
|
630
661
|
}
|
631
662
|
function groupEntries(entries) {
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
663
|
+
const result = {};
|
664
|
+
for (const [key, value] of entries) {
|
665
|
+
const values = result[key] ||= [];
|
666
|
+
values.push(value);
|
667
|
+
}
|
668
|
+
return result;
|
638
669
|
}
|
639
670
|
|
640
|
-
|
671
|
+
//#endregion
|
672
|
+
//#region src/utils/remove-undefined-values.ts
|
641
673
|
function removeUndefinedValues(obj) {
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
result[key] = value;
|
646
|
-
}
|
647
|
-
}
|
648
|
-
return result;
|
674
|
+
const result = {};
|
675
|
+
for (const [key, value] of Object.entries(obj)) if (value !== void 0) result[key] = value;
|
676
|
+
return result;
|
649
677
|
}
|
650
678
|
|
651
|
-
|
679
|
+
//#endregion
|
680
|
+
//#region src/utils/merge-objects.ts
|
652
681
|
function mergeObjects(...objects) {
|
653
|
-
|
654
|
-
|
682
|
+
const filteredObjects = objects.filter(isNotNullish).map(removeUndefinedValues);
|
683
|
+
return Object.assign({}, ...filteredObjects);
|
655
684
|
}
|
656
685
|
|
657
|
-
|
686
|
+
//#endregion
|
687
|
+
//#region src/utils/merge-specs.ts
|
658
688
|
function mergeSpecs(a, b) {
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
// src/utils/output-spec.ts
|
689
|
+
const attrs = {};
|
690
|
+
const attrNames = new Set([...Object.keys(a.attrs ?? {}), ...Object.keys(b.attrs ?? {})]);
|
691
|
+
for (const name of attrNames) {
|
692
|
+
const attrSpecA = a.attrs?.[name];
|
693
|
+
const attrSpecB = b.attrs?.[name];
|
694
|
+
const attrSpecMerged = mergeObjects(attrSpecA, attrSpecB);
|
695
|
+
if (attrSpecMerged) attrs[name] = attrSpecMerged;
|
696
|
+
}
|
697
|
+
const parseDOM = [...a.parseDOM ?? [], ...b.parseDOM ?? []];
|
698
|
+
return mergeObjects(a, b, {
|
699
|
+
attrs,
|
700
|
+
parseDOM
|
701
|
+
});
|
702
|
+
}
|
703
|
+
|
704
|
+
//#endregion
|
705
|
+
//#region src/utils/output-spec.ts
|
677
706
|
function wrapOutputSpecAttrs(toDOM, options) {
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
707
|
+
return (node, ...args) => {
|
708
|
+
const dom = toDOM(node, ...args);
|
709
|
+
const pairs = options.map((option) => option.toDOM?.(node.attrs[option.attr])).filter(isNotNullish);
|
710
|
+
return insertOutputSpecAttrs(dom, pairs);
|
711
|
+
};
|
683
712
|
}
|
684
713
|
function wrapTagParseRuleAttrs(rule, options) {
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
return { ...baseAttrs, ...insertedAttrs };
|
701
|
-
}
|
702
|
-
};
|
714
|
+
const existingGetAttrs = rule.getAttrs;
|
715
|
+
const existingAttrs = rule.attrs;
|
716
|
+
return {
|
717
|
+
...rule,
|
718
|
+
getAttrs: (dom) => {
|
719
|
+
const baseAttrs = existingGetAttrs?.(dom) ?? existingAttrs ?? {};
|
720
|
+
if (baseAttrs === false || !dom || !isElementLike(dom)) return baseAttrs ?? null;
|
721
|
+
const insertedAttrs = {};
|
722
|
+
for (const option of options) if (option.parseDOM) insertedAttrs[option.attr] = option.parseDOM(dom);
|
723
|
+
return {
|
724
|
+
...baseAttrs,
|
725
|
+
...insertedAttrs
|
726
|
+
};
|
727
|
+
}
|
728
|
+
};
|
703
729
|
}
|
704
730
|
function insertOutputSpecAttrs(dom, attrs) {
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
}
|
725
|
-
return dom;
|
731
|
+
if (!dom) return dom;
|
732
|
+
if (Array.isArray(dom)) {
|
733
|
+
const rest = dom.slice(1);
|
734
|
+
let oldAttrs;
|
735
|
+
if (rest.length > 0 && (rest[0] == null || typeof rest[0] === "object")) oldAttrs = rest.shift();
|
736
|
+
else oldAttrs = {};
|
737
|
+
const newAttrs = setObjectAttributes(oldAttrs, attrs);
|
738
|
+
return [
|
739
|
+
dom[0],
|
740
|
+
newAttrs,
|
741
|
+
...rest
|
742
|
+
];
|
743
|
+
}
|
744
|
+
if (isElementLike(dom)) return setElementAttributes(dom, attrs);
|
745
|
+
if (typeof dom === "object" && "dom" in dom && isElementLike(dom.dom)) return {
|
746
|
+
...dom,
|
747
|
+
dom: setElementAttributes(dom.dom, attrs)
|
748
|
+
};
|
749
|
+
return dom;
|
726
750
|
}
|
727
751
|
function setObjectAttributes(obj, attrs) {
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
752
|
+
obj = { ...obj };
|
753
|
+
for (const [key, value] of attrs) {
|
754
|
+
const oldValue = obj[key];
|
755
|
+
const newValue = key === "style" ? joinStyles(value, typeof oldValue === "string" ? oldValue : "") : value;
|
756
|
+
obj[key] = newValue;
|
757
|
+
}
|
758
|
+
return obj;
|
735
759
|
}
|
736
760
|
function setElementAttributes(element, attrs) {
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
761
|
+
element = element.cloneNode(true);
|
762
|
+
for (const [key, value] of attrs) {
|
763
|
+
const oldValue = element.getAttribute(key);
|
764
|
+
const newValue = key === "style" ? joinStyles(value, typeof oldValue === "string" ? oldValue : "") : value;
|
765
|
+
element.setAttribute(key, newValue);
|
766
|
+
}
|
767
|
+
return element;
|
744
768
|
}
|
745
769
|
function joinStyles(...styles) {
|
746
|
-
|
770
|
+
return styles.map((style) => style.trim().replace(/;$/, "")).filter(Boolean).join("; ");
|
747
771
|
}
|
748
772
|
|
749
|
-
|
773
|
+
//#endregion
|
774
|
+
//#region src/extensions/node-spec.ts
|
775
|
+
/**
|
776
|
+
* Defines a node type.
|
777
|
+
*
|
778
|
+
* @public
|
779
|
+
*/
|
750
780
|
function defineNodeSpec(options) {
|
751
|
-
|
752
|
-
|
753
|
-
}
|
781
|
+
const payload = [options, void 0];
|
782
|
+
return defineFacetPayload(nodeSpecFacet, [payload]);
|
783
|
+
}
|
784
|
+
/**
|
785
|
+
* Defines an attribute for a node type.
|
786
|
+
*
|
787
|
+
* @public
|
788
|
+
*/
|
754
789
|
function defineNodeAttr(options) {
|
755
|
-
|
756
|
-
|
757
|
-
}
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
}
|
794
|
-
if (spec.parseDOM) {
|
795
|
-
spec.parseDOM = spec.parseDOM.map((rule) => wrapTagParseRuleAttrs(rule, attrs));
|
796
|
-
}
|
797
|
-
specs = specs.update(type, spec);
|
798
|
-
}
|
799
|
-
return { nodes: specs, topNode: topNodeName };
|
800
|
-
},
|
801
|
-
parent: schemaSpecFacet,
|
802
|
-
singleton: true
|
790
|
+
const payload = [void 0, options];
|
791
|
+
return defineFacetPayload(nodeSpecFacet, [payload]);
|
792
|
+
}
|
793
|
+
const nodeSpecFacet = defineFacet({
|
794
|
+
reducer: (payloads) => {
|
795
|
+
let specs = OrderedMap.from({});
|
796
|
+
let topNodeName = void 0;
|
797
|
+
const specPayloads = payloads.map((input) => input[0]).filter(isNotNullish);
|
798
|
+
const attrPayloads = payloads.map((input) => input[1]).filter(isNotNullish);
|
799
|
+
for (const { name, topNode,...spec } of specPayloads) {
|
800
|
+
if (topNode) topNodeName = name;
|
801
|
+
const prevSpec = specs.get(name);
|
802
|
+
if (prevSpec) specs = specs.update(name, mergeSpecs(prevSpec, spec));
|
803
|
+
else specs = specs.addToStart(name, spec);
|
804
|
+
}
|
805
|
+
const groupedAttrs = groupBy(attrPayloads, (payload) => payload.type);
|
806
|
+
for (const [type, attrs] of Object.entries(groupedAttrs)) {
|
807
|
+
if (!attrs) continue;
|
808
|
+
const maybeSpec = specs.get(type);
|
809
|
+
assert(maybeSpec, `Node type ${type} must be defined`);
|
810
|
+
const spec = clone(maybeSpec);
|
811
|
+
if (!spec.attrs) spec.attrs = {};
|
812
|
+
for (const attr of attrs) spec.attrs[attr.attr] = {
|
813
|
+
default: attr.default,
|
814
|
+
validate: attr.validate,
|
815
|
+
splittable: attr.splittable
|
816
|
+
};
|
817
|
+
if (spec.toDOM) spec.toDOM = wrapOutputSpecAttrs(spec.toDOM, attrs);
|
818
|
+
if (spec.parseDOM) spec.parseDOM = spec.parseDOM.map((rule) => wrapTagParseRuleAttrs(rule, attrs));
|
819
|
+
specs = specs.update(type, spec);
|
820
|
+
}
|
821
|
+
return {
|
822
|
+
nodes: specs,
|
823
|
+
topNode: topNodeName
|
824
|
+
};
|
825
|
+
},
|
826
|
+
parent: schemaSpecFacet,
|
827
|
+
singleton: true
|
803
828
|
});
|
804
829
|
|
805
|
-
|
830
|
+
//#endregion
|
831
|
+
//#region src/extensions/doc.ts
|
832
|
+
/**
|
833
|
+
* @public
|
834
|
+
*
|
835
|
+
* @deprecated Use the following import instead:
|
836
|
+
*
|
837
|
+
* ```ts
|
838
|
+
* import { defineDoc } from 'prosekit/extensions/doc'
|
839
|
+
* ```
|
840
|
+
*/
|
806
841
|
function defineDoc() {
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
}
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
842
|
+
console.warn("[prosekit] The `defineDoc` function from `prosekit/core` is deprecated. Use the following import instead: `import { defineDoc } from \"prosekit/extensions/doc\"`.");
|
843
|
+
return defineNodeSpec({
|
844
|
+
name: "doc",
|
845
|
+
content: "block+",
|
846
|
+
topNode: true
|
847
|
+
});
|
848
|
+
}
|
849
|
+
|
850
|
+
//#endregion
|
851
|
+
//#region src/extensions/events/plugin-view.ts
|
852
|
+
/**
|
853
|
+
* Registers a event handler that is called when the editor view is mounted.
|
854
|
+
*
|
855
|
+
* @public
|
856
|
+
*/
|
819
857
|
function defineMountHandler(handler) {
|
820
|
-
|
858
|
+
return definePluginViewFacetPayload(["mount", handler]);
|
821
859
|
}
|
860
|
+
/**
|
861
|
+
* Registers a event handler that is called when the editor state is updated.
|
862
|
+
*
|
863
|
+
* @public
|
864
|
+
*/
|
822
865
|
function defineUpdateHandler(handler) {
|
823
|
-
|
866
|
+
return definePluginViewFacetPayload(["update", handler]);
|
824
867
|
}
|
868
|
+
/**
|
869
|
+
* Registers a event handler that is called when the editor view is unmounted.
|
870
|
+
*
|
871
|
+
* @public
|
872
|
+
*/
|
825
873
|
function defineUnmountHandler(handler) {
|
826
|
-
|
874
|
+
return definePluginViewFacetPayload(["unmount", handler]);
|
827
875
|
}
|
828
876
|
function definePluginViewFacetPayload(input) {
|
829
|
-
|
830
|
-
}
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
parent: pluginFacet,
|
874
|
-
singleton: true
|
877
|
+
return defineFacetPayload(pluginViewFacet, [input]);
|
878
|
+
}
|
879
|
+
const pluginViewFacet = defineFacet({
|
880
|
+
reduce: () => {
|
881
|
+
let mountHandlers = [];
|
882
|
+
let updateHandlers = [];
|
883
|
+
let unmountHandlers = [];
|
884
|
+
const plugin = new ProseMirrorPlugin({
|
885
|
+
key: pluginKey,
|
886
|
+
view: (view) => {
|
887
|
+
mountHandlers.forEach((fn) => fn(view));
|
888
|
+
return {
|
889
|
+
update: (view$1, prevState) => {
|
890
|
+
updateHandlers.forEach((fn) => fn(view$1, prevState));
|
891
|
+
},
|
892
|
+
destroy: () => {
|
893
|
+
unmountHandlers.forEach((fn) => fn());
|
894
|
+
}
|
895
|
+
};
|
896
|
+
}
|
897
|
+
});
|
898
|
+
const register = (input) => {
|
899
|
+
mountHandlers = [];
|
900
|
+
updateHandlers = [];
|
901
|
+
unmountHandlers = [];
|
902
|
+
for (const args of input) switch (args[0]) {
|
903
|
+
case "mount":
|
904
|
+
mountHandlers.push(args[1]);
|
905
|
+
break;
|
906
|
+
case "update":
|
907
|
+
updateHandlers.push(args[1]);
|
908
|
+
break;
|
909
|
+
case "unmount":
|
910
|
+
unmountHandlers.push(args[1]);
|
911
|
+
break;
|
912
|
+
}
|
913
|
+
};
|
914
|
+
return function reducer(input) {
|
915
|
+
register(input);
|
916
|
+
return plugin;
|
917
|
+
};
|
918
|
+
},
|
919
|
+
parent: pluginFacet,
|
920
|
+
singleton: true
|
875
921
|
});
|
876
|
-
|
877
|
-
|
878
|
-
|
922
|
+
const pluginKey = new PluginKey("prosekit-plugin-view-handler");
|
923
|
+
|
924
|
+
//#endregion
|
925
|
+
//#region src/extensions/events/doc-change.ts
|
926
|
+
/**
|
927
|
+
* Registers a event handler that is called when the editor document is changed.
|
928
|
+
*
|
929
|
+
* @public
|
930
|
+
*/
|
879
931
|
function defineDocChangeHandler(handler) {
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
}
|
884
|
-
});
|
932
|
+
return defineUpdateHandler((view, prevState) => {
|
933
|
+
if (!view.state.doc.eq(prevState.doc)) handler(view, prevState);
|
934
|
+
});
|
885
935
|
}
|
886
936
|
|
887
|
-
|
888
|
-
|
889
|
-
PluginKey as PluginKey3,
|
890
|
-
ProseMirrorPlugin as ProseMirrorPlugin3
|
891
|
-
} from "@prosekit/pm/state";
|
892
|
-
|
893
|
-
// src/utils/combine-event-handlers.ts
|
937
|
+
//#endregion
|
938
|
+
//#region src/utils/combine-event-handlers.ts
|
894
939
|
function combineEventHandlers() {
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
940
|
+
let handlers = [];
|
941
|
+
function setHandlers(eventHandlers) {
|
942
|
+
handlers = toReversed(eventHandlers);
|
943
|
+
}
|
944
|
+
function combinedEventHandler(...args) {
|
945
|
+
for (const handler of handlers) if (handler(...args)) return true;
|
946
|
+
return false;
|
947
|
+
}
|
948
|
+
return [setHandlers, combinedEventHandler];
|
949
|
+
}
|
950
|
+
|
951
|
+
//#endregion
|
952
|
+
//#region src/extensions/events/dom-event.ts
|
953
|
+
/**
|
954
|
+
* @internal
|
955
|
+
*/
|
911
956
|
function defineDomEventFacetPayload(...payloads) {
|
912
|
-
|
913
|
-
domEventFacet,
|
914
|
-
payloads
|
915
|
-
);
|
957
|
+
return defineFacetPayload(domEventFacet, payloads);
|
916
958
|
}
|
959
|
+
/**
|
960
|
+
* Register a new event handler for the given event type.
|
961
|
+
*
|
962
|
+
* @public
|
963
|
+
*/
|
917
964
|
function defineDOMEventHandler(event, handler) {
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
singleton: true
|
961
|
-
}
|
962
|
-
);
|
963
|
-
|
964
|
-
// src/extensions/events/editor-event.ts
|
965
|
-
import {
|
966
|
-
PluginKey as PluginKey4,
|
967
|
-
ProseMirrorPlugin as ProseMirrorPlugin4
|
968
|
-
} from "@prosekit/pm/state";
|
965
|
+
return defineDomEventFacetPayload([event, handler]);
|
966
|
+
}
|
967
|
+
/**
|
968
|
+
* @internal
|
969
|
+
*/
|
970
|
+
const domEventFacet = defineFacet({
|
971
|
+
reduce: () => {
|
972
|
+
const setHandlersMap = {};
|
973
|
+
const combinedHandlerMap = {};
|
974
|
+
let plugin;
|
975
|
+
const update = (payloads) => {
|
976
|
+
let hasNewEvent = false;
|
977
|
+
for (const [event] of payloads) if (!setHandlersMap[event]) {
|
978
|
+
hasNewEvent = true;
|
979
|
+
const [setHandlers, combinedHandler] = combineEventHandlers();
|
980
|
+
setHandlersMap[event] = setHandlers;
|
981
|
+
const e = (view, eventObject) => {
|
982
|
+
return combinedHandler(view, eventObject);
|
983
|
+
};
|
984
|
+
combinedHandlerMap[event] = e;
|
985
|
+
}
|
986
|
+
const map = groupEntries(payloads);
|
987
|
+
for (const [event, setHandlers] of Object.entries(setHandlersMap)) {
|
988
|
+
const handlers = map[event] ?? [];
|
989
|
+
setHandlers(handlers);
|
990
|
+
}
|
991
|
+
if (hasNewEvent) plugin = new ProseMirrorPlugin({
|
992
|
+
key: new PluginKey("prosekit-dom-event-handler"),
|
993
|
+
props: { handleDOMEvents: combinedHandlerMap }
|
994
|
+
});
|
995
|
+
};
|
996
|
+
return function reducer(inputs) {
|
997
|
+
update(inputs);
|
998
|
+
return plugin ?? [];
|
999
|
+
};
|
1000
|
+
},
|
1001
|
+
parent: pluginFacet,
|
1002
|
+
singleton: true
|
1003
|
+
});
|
1004
|
+
|
1005
|
+
//#endregion
|
1006
|
+
//#region src/extensions/events/editor-event.ts
|
969
1007
|
function defineEventFacetPayload(payload) {
|
970
|
-
|
1008
|
+
return defineFacetPayload(editorEventFacet, [payload]);
|
971
1009
|
}
|
1010
|
+
/**
|
1011
|
+
* @public
|
1012
|
+
*
|
1013
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleKeyDown}
|
1014
|
+
*/
|
972
1015
|
function defineKeyDownHandler(handler) {
|
973
|
-
|
1016
|
+
return defineEventFacetPayload(["keyDown", handler]);
|
974
1017
|
}
|
1018
|
+
/**
|
1019
|
+
* @public
|
1020
|
+
*
|
1021
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleKeyPress}
|
1022
|
+
*/
|
975
1023
|
function defineKeyPressHandler(handler) {
|
976
|
-
|
1024
|
+
return defineEventFacetPayload(["keyPress", handler]);
|
977
1025
|
}
|
1026
|
+
/**
|
1027
|
+
* @public
|
1028
|
+
*
|
1029
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleTextInput}
|
1030
|
+
*/
|
978
1031
|
function defineTextInputHandler(handler) {
|
979
|
-
|
1032
|
+
return defineEventFacetPayload(["textInput", handler]);
|
980
1033
|
}
|
1034
|
+
/**
|
1035
|
+
* @public
|
1036
|
+
*
|
1037
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleClickOn}
|
1038
|
+
*/
|
981
1039
|
function defineClickOnHandler(handler) {
|
982
|
-
|
1040
|
+
return defineEventFacetPayload(["clickOn", handler]);
|
983
1041
|
}
|
1042
|
+
/**
|
1043
|
+
* @public
|
1044
|
+
*
|
1045
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleClick}
|
1046
|
+
*/
|
984
1047
|
function defineClickHandler(handler) {
|
985
|
-
|
1048
|
+
return defineEventFacetPayload(["click", handler]);
|
986
1049
|
}
|
1050
|
+
/**
|
1051
|
+
* @public
|
1052
|
+
*
|
1053
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleDoubleClickOn}
|
1054
|
+
*/
|
987
1055
|
function defineDoubleClickOnHandler(handler) {
|
988
|
-
|
1056
|
+
return defineEventFacetPayload(["doubleClickOn", handler]);
|
989
1057
|
}
|
1058
|
+
/**
|
1059
|
+
* @public
|
1060
|
+
*
|
1061
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleDoubleClick}
|
1062
|
+
*/
|
990
1063
|
function defineDoubleClickHandler(handler) {
|
991
|
-
|
1064
|
+
return defineEventFacetPayload(["doubleClick", handler]);
|
992
1065
|
}
|
1066
|
+
/**
|
1067
|
+
* @public
|
1068
|
+
*
|
1069
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleTripleClickOn}
|
1070
|
+
*/
|
993
1071
|
function defineTripleClickOnHandler(handler) {
|
994
|
-
|
1072
|
+
return defineEventFacetPayload(["tripleClickOn", handler]);
|
995
1073
|
}
|
1074
|
+
/**
|
1075
|
+
* @public
|
1076
|
+
*
|
1077
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleTripleClick}
|
1078
|
+
*/
|
996
1079
|
function defineTripleClickHandler(handler) {
|
997
|
-
|
1080
|
+
return defineEventFacetPayload(["tripleClick", handler]);
|
998
1081
|
}
|
1082
|
+
/**
|
1083
|
+
* @public
|
1084
|
+
*
|
1085
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handlePaste}
|
1086
|
+
*/
|
999
1087
|
function definePasteHandler(handler) {
|
1000
|
-
|
1088
|
+
return defineEventFacetPayload(["paste", handler]);
|
1001
1089
|
}
|
1090
|
+
/**
|
1091
|
+
* @public
|
1092
|
+
*
|
1093
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleDrop}
|
1094
|
+
*/
|
1002
1095
|
function defineDropHandler(handler) {
|
1003
|
-
|
1096
|
+
return defineEventFacetPayload(["drop", handler]);
|
1004
1097
|
}
|
1098
|
+
/**
|
1099
|
+
* @public
|
1100
|
+
*
|
1101
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleScrollToSelection}
|
1102
|
+
*/
|
1005
1103
|
function defineScrollToSelectionHandler(handler) {
|
1006
|
-
|
1007
|
-
}
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1104
|
+
return defineEventFacetPayload(["scrollToSelection", handler]);
|
1105
|
+
}
|
1106
|
+
/**
|
1107
|
+
* @internal
|
1108
|
+
*/
|
1109
|
+
const editorEventFacet = defineFacet({
|
1110
|
+
reduce: () => {
|
1111
|
+
const [update, plugin] = setupEditorEventPlugin();
|
1112
|
+
return (entries) => {
|
1113
|
+
update(entries);
|
1114
|
+
return plugin;
|
1115
|
+
};
|
1116
|
+
},
|
1117
|
+
parent: pluginFacet,
|
1118
|
+
singleton: true
|
1018
1119
|
});
|
1019
1120
|
function setupEditorEventPlugin() {
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
}
|
1066
|
-
|
1067
|
-
|
1121
|
+
const [setKeyDownHandlers, handleKeyDown] = combineEventHandlers();
|
1122
|
+
const [setKeyPressHandlers, handleKeyPress] = combineEventHandlers();
|
1123
|
+
const [setTextInputHandlers, handleTextInput] = combineEventHandlers();
|
1124
|
+
const [setClickOnHandlers, handleClickOn] = combineEventHandlers();
|
1125
|
+
const [setClickHandlers, handleClick] = combineEventHandlers();
|
1126
|
+
const [setDoubleClickOnHandlers, handleDoubleClickOn] = combineEventHandlers();
|
1127
|
+
const [setDoubleClickHandlers, handleDoubleClick] = combineEventHandlers();
|
1128
|
+
const [setTripleClickOnHandlers, handleTripleClickOn] = combineEventHandlers();
|
1129
|
+
const [setTripleClickHandlers, handleTripleClick] = combineEventHandlers();
|
1130
|
+
const [setPasteHandlers, handlePaste] = combineEventHandlers();
|
1131
|
+
const [setDropHandlers, handleDrop] = combineEventHandlers();
|
1132
|
+
const [setScrollToSelectionHandlers, handleScrollToSelection] = combineEventHandlers();
|
1133
|
+
const update = (entries) => {
|
1134
|
+
const map = groupEntries(entries);
|
1135
|
+
setKeyDownHandlers(map.keyDown ?? []);
|
1136
|
+
setKeyPressHandlers(map.keyPress ?? []);
|
1137
|
+
setTextInputHandlers(map.textInput ?? []);
|
1138
|
+
setClickOnHandlers(map.clickOn ?? []);
|
1139
|
+
setClickHandlers(map.click ?? []);
|
1140
|
+
setDoubleClickOnHandlers(map.doubleClickOn ?? []);
|
1141
|
+
setDoubleClickHandlers(map.doubleClick ?? []);
|
1142
|
+
setTripleClickOnHandlers(map.tripleClickOn ?? []);
|
1143
|
+
setTripleClickHandlers(map.tripleClick ?? []);
|
1144
|
+
setPasteHandlers(map.paste ?? []);
|
1145
|
+
setDropHandlers(map.drop ?? []);
|
1146
|
+
setScrollToSelectionHandlers(map.scrollToSelection ?? []);
|
1147
|
+
};
|
1148
|
+
const plugin = new ProseMirrorPlugin({
|
1149
|
+
key: new PluginKey("prosekit-editor-event"),
|
1150
|
+
props: {
|
1151
|
+
handleKeyDown,
|
1152
|
+
handleKeyPress,
|
1153
|
+
handleTextInput,
|
1154
|
+
handleClickOn,
|
1155
|
+
handleClick,
|
1156
|
+
handleDoubleClickOn,
|
1157
|
+
handleDoubleClick,
|
1158
|
+
handleTripleClickOn,
|
1159
|
+
handleTripleClick,
|
1160
|
+
handlePaste,
|
1161
|
+
handleDrop,
|
1162
|
+
handleScrollToSelection
|
1163
|
+
}
|
1164
|
+
});
|
1165
|
+
return [update, plugin];
|
1166
|
+
}
|
1167
|
+
|
1168
|
+
//#endregion
|
1169
|
+
//#region src/extensions/events/focus.ts
|
1170
|
+
/**
|
1171
|
+
* Registers a event handler that is called when the editor gains or loses focus.
|
1172
|
+
*
|
1173
|
+
* @public
|
1174
|
+
*/
|
1068
1175
|
function defineFocusChangeHandler(handler) {
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
toReversed(keymaps)
|
1114
|
-
)
|
1115
|
-
);
|
1116
|
-
return plugin;
|
1117
|
-
};
|
1118
|
-
},
|
1119
|
-
parent: pluginFacet,
|
1120
|
-
singleton: true
|
1176
|
+
const handleFocus = () => handler(true);
|
1177
|
+
const handleBlur = () => handler(false);
|
1178
|
+
return defineDomEventFacetPayload(["focus", handleFocus], ["blur", handleBlur]);
|
1179
|
+
}
|
1180
|
+
|
1181
|
+
//#endregion
|
1182
|
+
//#region src/utils/env.ts
|
1183
|
+
/**
|
1184
|
+
* @private
|
1185
|
+
*/
|
1186
|
+
const isApple = typeof navigator !== "undefined" ? /Mac|iP(hone|[ao]d)/.test(navigator.platform) : false;
|
1187
|
+
|
1188
|
+
//#endregion
|
1189
|
+
//#region src/extensions/keymap.ts
|
1190
|
+
/**
|
1191
|
+
* @public
|
1192
|
+
*/
|
1193
|
+
function defineKeymap(keymap$1) {
|
1194
|
+
return defineFacetPayload(keymapFacet, [keymap$1]);
|
1195
|
+
}
|
1196
|
+
/**
|
1197
|
+
* @internal
|
1198
|
+
*/
|
1199
|
+
const keymapFacet = defineFacet({
|
1200
|
+
reduce: () => {
|
1201
|
+
let handler;
|
1202
|
+
const handlerWrapper = (view, event) => {
|
1203
|
+
if (handler) return handler(view, event);
|
1204
|
+
return false;
|
1205
|
+
};
|
1206
|
+
const plugin = new Plugin({
|
1207
|
+
key: keymapPluginKey,
|
1208
|
+
props: { handleKeyDown: handlerWrapper }
|
1209
|
+
});
|
1210
|
+
return (keymaps) => {
|
1211
|
+
handler = keydownHandler(mergeKeymaps(
|
1212
|
+
// The keymap at the end have a higher priority.
|
1213
|
+
toReversed(keymaps)
|
1214
|
+
));
|
1215
|
+
return plugin;
|
1216
|
+
};
|
1217
|
+
},
|
1218
|
+
parent: pluginFacet,
|
1219
|
+
singleton: true
|
1121
1220
|
});
|
1122
1221
|
function mergeKeymaps(keymaps) {
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
"Shift-Mod-z": redo
|
1222
|
+
const bindings = {};
|
1223
|
+
for (const keymap$1 of keymaps) for (const [key, command] of Object.entries(keymap$1)) {
|
1224
|
+
const commands$1 = bindings[key] || (bindings[key] = []);
|
1225
|
+
commands$1.push(command);
|
1226
|
+
}
|
1227
|
+
return mapValues(bindings, mergeCommands);
|
1228
|
+
}
|
1229
|
+
function mergeCommands(commands$1) {
|
1230
|
+
return chainCommands(...commands$1);
|
1231
|
+
}
|
1232
|
+
const keymapPluginKey = new PluginKey("prosekit-keymap");
|
1233
|
+
|
1234
|
+
//#endregion
|
1235
|
+
//#region src/extensions/history.ts
|
1236
|
+
const keymap = {
|
1237
|
+
"Mod-z": undo,
|
1238
|
+
"Shift-Mod-z": redo
|
1141
1239
|
};
|
1142
|
-
if (!isApple)
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
undo: () => undo,
|
1147
|
-
redo: () => redo
|
1240
|
+
if (!isApple) keymap["Mod-y"] = redo;
|
1241
|
+
const commands = {
|
1242
|
+
undo: () => undo,
|
1243
|
+
redo: () => redo
|
1148
1244
|
};
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
import { splitSplittableBlock } from "prosemirror-splittable";
|
1172
|
-
var customEnter = chainCommands2(
|
1173
|
-
newlineInCode,
|
1174
|
-
createParagraphNear,
|
1175
|
-
liftEmptyBlock,
|
1176
|
-
splitSplittableBlock
|
1177
|
-
);
|
1178
|
-
var customBackspace = chainCommands2(
|
1179
|
-
deleteSelection,
|
1180
|
-
joinTextblockBackward,
|
1181
|
-
selectNodeBackward
|
1182
|
-
);
|
1183
|
-
var customBaseKeymap = {
|
1184
|
-
...baseKeymap,
|
1185
|
-
Enter: customEnter,
|
1186
|
-
Backspace: customBackspace
|
1245
|
+
/**
|
1246
|
+
* Add undo/redo history to the editor.
|
1247
|
+
*
|
1248
|
+
* @param options
|
1249
|
+
*
|
1250
|
+
* @public
|
1251
|
+
*/
|
1252
|
+
function defineHistory({ depth = 200, newGroupDelay = 250 } = {}) {
|
1253
|
+
return union(definePlugin(history({
|
1254
|
+
depth,
|
1255
|
+
newGroupDelay
|
1256
|
+
})), defineKeymap(keymap), defineCommands(commands));
|
1257
|
+
}
|
1258
|
+
|
1259
|
+
//#endregion
|
1260
|
+
//#region src/extensions/keymap-base.ts
|
1261
|
+
const customEnter = chainCommands(newlineInCode, createParagraphNear, liftEmptyBlock, splitSplittableBlock);
|
1262
|
+
const customBackspace = chainCommands(deleteSelection, joinTextblockBackward, selectNodeBackward);
|
1263
|
+
const customBaseKeymap = {
|
1264
|
+
...baseKeymap,
|
1265
|
+
Enter: customEnter,
|
1266
|
+
Backspace: customBackspace
|
1187
1267
|
};
|
1268
|
+
/**
|
1269
|
+
* Defines some basic key bindings.
|
1270
|
+
*
|
1271
|
+
* @public
|
1272
|
+
*/
|
1188
1273
|
function defineBaseKeymap(options) {
|
1189
|
-
|
1190
|
-
|
1274
|
+
const priority = options?.priority ?? Priority.low;
|
1275
|
+
return withPriority(defineKeymap(customBaseKeymap), priority);
|
1191
1276
|
}
|
1192
1277
|
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1278
|
+
//#endregion
|
1279
|
+
//#region src/extensions/mark-spec.ts
|
1280
|
+
/**
|
1281
|
+
* @public
|
1282
|
+
*/
|
1196
1283
|
function defineMarkSpec(options) {
|
1197
|
-
|
1198
|
-
|
1284
|
+
const payload = [options, void 0];
|
1285
|
+
return defineFacetPayload(markSpecFacet, [payload]);
|
1199
1286
|
}
|
1287
|
+
/**
|
1288
|
+
* @public
|
1289
|
+
*/
|
1200
1290
|
function defineMarkAttr(options) {
|
1201
|
-
|
1202
|
-
|
1203
|
-
}
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
spec.parseDOM = spec.parseDOM.map((rule) => wrapParseRuleAttrs(rule, attrs));
|
1237
|
-
}
|
1238
|
-
specs = specs.update(type, spec);
|
1239
|
-
}
|
1240
|
-
return { marks: specs, nodes: {} };
|
1241
|
-
},
|
1242
|
-
parent: schemaSpecFacet,
|
1243
|
-
singleton: true
|
1291
|
+
const payload = [void 0, options];
|
1292
|
+
return defineFacetPayload(markSpecFacet, [payload]);
|
1293
|
+
}
|
1294
|
+
const markSpecFacet = defineFacet({
|
1295
|
+
reducer: (payloads) => {
|
1296
|
+
let specs = OrderedMap.from({});
|
1297
|
+
const specPayloads = payloads.map((input) => input[0]).filter(isNotNullish);
|
1298
|
+
const attrPayloads = payloads.map((input) => input[1]).filter(isNotNullish);
|
1299
|
+
for (const { name,...spec } of specPayloads) {
|
1300
|
+
const prevSpec = specs.get(name);
|
1301
|
+
if (prevSpec) specs = specs.update(name, mergeSpecs(prevSpec, spec));
|
1302
|
+
else specs = specs.addToStart(name, spec);
|
1303
|
+
}
|
1304
|
+
const groupedAttrs = groupBy(attrPayloads, (payload) => payload.type);
|
1305
|
+
for (const [type, attrs] of Object.entries(groupedAttrs)) {
|
1306
|
+
if (!attrs) continue;
|
1307
|
+
const maybeSpec = specs.get(type);
|
1308
|
+
assert(maybeSpec, `Mark type ${type} must be defined`);
|
1309
|
+
const spec = clone(maybeSpec);
|
1310
|
+
if (!spec.attrs) spec.attrs = {};
|
1311
|
+
for (const attr of attrs) spec.attrs[attr.attr] = {
|
1312
|
+
default: attr.default,
|
1313
|
+
validate: attr.validate
|
1314
|
+
};
|
1315
|
+
if (spec.toDOM) spec.toDOM = wrapOutputSpecAttrs(spec.toDOM, attrs);
|
1316
|
+
if (spec.parseDOM) spec.parseDOM = spec.parseDOM.map((rule) => wrapParseRuleAttrs(rule, attrs));
|
1317
|
+
specs = specs.update(type, spec);
|
1318
|
+
}
|
1319
|
+
return {
|
1320
|
+
marks: specs,
|
1321
|
+
nodes: {}
|
1322
|
+
};
|
1323
|
+
},
|
1324
|
+
parent: schemaSpecFacet,
|
1325
|
+
singleton: true
|
1244
1326
|
});
|
1245
1327
|
function wrapParseRuleAttrs(rule, attrs) {
|
1246
|
-
|
1247
|
-
|
1248
|
-
}
|
1249
|
-
return rule;
|
1328
|
+
if (rule.tag) return wrapTagParseRuleAttrs(rule, attrs);
|
1329
|
+
return rule;
|
1250
1330
|
}
|
1251
1331
|
|
1252
|
-
|
1253
|
-
|
1254
|
-
PluginKey as PluginKey6,
|
1255
|
-
ProseMirrorPlugin as ProseMirrorPlugin5
|
1256
|
-
} from "@prosekit/pm/state";
|
1332
|
+
//#endregion
|
1333
|
+
//#region src/extensions/mark-view.ts
|
1257
1334
|
function defineMarkView(options) {
|
1258
|
-
|
1259
|
-
}
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
key: new PluginKey6("prosekit-mark-view"),
|
1271
|
-
props: { markViews }
|
1272
|
-
})
|
1273
|
-
];
|
1274
|
-
},
|
1275
|
-
parent: pluginFacet
|
1335
|
+
return defineFacetPayload(markViewFacet, [options]);
|
1336
|
+
}
|
1337
|
+
const markViewFacet = defineFacet({
|
1338
|
+
reducer: (inputs) => {
|
1339
|
+
const markViews = {};
|
1340
|
+
for (const input of inputs) if (!markViews[input.name]) markViews[input.name] = input.constructor;
|
1341
|
+
return () => [new ProseMirrorPlugin({
|
1342
|
+
key: new PluginKey("prosekit-mark-view"),
|
1343
|
+
props: { markViews }
|
1344
|
+
})];
|
1345
|
+
},
|
1346
|
+
parent: pluginFacet
|
1276
1347
|
});
|
1277
1348
|
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1349
|
+
//#endregion
|
1350
|
+
//#region src/extensions/mark-view-effect.ts
|
1351
|
+
/**
|
1352
|
+
* @internal
|
1353
|
+
*/
|
1283
1354
|
function defineMarkViewFactory(options) {
|
1284
|
-
|
1285
|
-
|
1355
|
+
const input = [options, null];
|
1356
|
+
return defineFacetPayload(markViewFactoryFacet, [input]);
|
1286
1357
|
}
|
1358
|
+
/**
|
1359
|
+
* @internal
|
1360
|
+
*/
|
1287
1361
|
function defineMarkViewComponent(options) {
|
1288
|
-
|
1289
|
-
|
1290
|
-
}
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
},
|
1310
|
-
parent: pluginFacet
|
1362
|
+
const input = [null, options];
|
1363
|
+
return defineFacetPayload(markViewFactoryFacet, [input]);
|
1364
|
+
}
|
1365
|
+
const isServer$1 = typeof window === "undefined";
|
1366
|
+
const markViewFactoryFacet = defineFacet({
|
1367
|
+
reducer: (inputs) => {
|
1368
|
+
if (isServer$1) return [];
|
1369
|
+
const markViews = {};
|
1370
|
+
const factories = inputs.map((x) => x[0]).filter(isNotNullish);
|
1371
|
+
const options = inputs.map((x) => x[1]).filter(isNotNullish);
|
1372
|
+
for (const { group, name, args } of options) {
|
1373
|
+
const factory = factories.find((factory$1) => factory$1.group === group);
|
1374
|
+
if (!factory) continue;
|
1375
|
+
markViews[name] = factory.factory(args);
|
1376
|
+
}
|
1377
|
+
return () => [new ProseMirrorPlugin({
|
1378
|
+
key: new PluginKey("prosekit-mark-view-effect"),
|
1379
|
+
props: { markViews }
|
1380
|
+
})];
|
1381
|
+
},
|
1382
|
+
parent: pluginFacet
|
1311
1383
|
});
|
1312
1384
|
|
1313
|
-
|
1314
|
-
|
1315
|
-
PluginKey as PluginKey8,
|
1316
|
-
ProseMirrorPlugin as ProseMirrorPlugin7
|
1317
|
-
} from "@prosekit/pm/state";
|
1385
|
+
//#endregion
|
1386
|
+
//#region src/extensions/node-view.ts
|
1318
1387
|
function defineNodeView(options) {
|
1319
|
-
|
1320
|
-
}
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
key: new PluginKey8("prosekit-node-view"),
|
1332
|
-
props: { nodeViews }
|
1333
|
-
})
|
1334
|
-
];
|
1335
|
-
},
|
1336
|
-
parent: pluginFacet
|
1388
|
+
return defineFacetPayload(nodeViewFacet, [options]);
|
1389
|
+
}
|
1390
|
+
const nodeViewFacet = defineFacet({
|
1391
|
+
reducer: (inputs) => {
|
1392
|
+
const nodeViews = {};
|
1393
|
+
for (const input of inputs) if (!nodeViews[input.name]) nodeViews[input.name] = input.constructor;
|
1394
|
+
return () => [new ProseMirrorPlugin({
|
1395
|
+
key: new PluginKey("prosekit-node-view"),
|
1396
|
+
props: { nodeViews }
|
1397
|
+
})];
|
1398
|
+
},
|
1399
|
+
parent: pluginFacet
|
1337
1400
|
});
|
1338
1401
|
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1402
|
+
//#endregion
|
1403
|
+
//#region src/extensions/node-view-effect.ts
|
1404
|
+
/**
|
1405
|
+
* @internal
|
1406
|
+
*/
|
1344
1407
|
function defineNodeViewFactory(options) {
|
1345
|
-
|
1346
|
-
|
1408
|
+
const input = [options, null];
|
1409
|
+
return defineFacetPayload(nodeViewFactoryFacet, [input]);
|
1347
1410
|
}
|
1411
|
+
/**
|
1412
|
+
* @internal
|
1413
|
+
*/
|
1348
1414
|
function defineNodeViewComponent(options) {
|
1349
|
-
|
1350
|
-
|
1351
|
-
}
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
},
|
1371
|
-
parent: pluginFacet
|
1415
|
+
const input = [null, options];
|
1416
|
+
return defineFacetPayload(nodeViewFactoryFacet, [input]);
|
1417
|
+
}
|
1418
|
+
const isServer = typeof window === "undefined";
|
1419
|
+
const nodeViewFactoryFacet = defineFacet({
|
1420
|
+
reducer: (inputs) => {
|
1421
|
+
if (isServer) return [];
|
1422
|
+
const nodeViews = {};
|
1423
|
+
const factories = inputs.map((x) => x[0]).filter(isNotNullish);
|
1424
|
+
const options = inputs.map((x) => x[1]).filter(isNotNullish);
|
1425
|
+
for (const { group, name, args } of options) {
|
1426
|
+
const factory = factories.find((factory$1) => factory$1.group === group);
|
1427
|
+
if (!factory) continue;
|
1428
|
+
nodeViews[name] = factory.factory(args);
|
1429
|
+
}
|
1430
|
+
return () => [new ProseMirrorPlugin({
|
1431
|
+
key: new PluginKey("prosekit-node-view-effect"),
|
1432
|
+
props: { nodeViews }
|
1433
|
+
})];
|
1434
|
+
},
|
1435
|
+
parent: pluginFacet
|
1372
1436
|
});
|
1373
1437
|
|
1374
|
-
|
1438
|
+
//#endregion
|
1439
|
+
//#region src/extensions/paragraph.ts
|
1440
|
+
/**
|
1441
|
+
* Defines a paragraph node spec.
|
1442
|
+
*/
|
1375
1443
|
function defineParagraphSpec() {
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
}
|
1444
|
+
return defineNodeSpec({
|
1445
|
+
name: "paragraph",
|
1446
|
+
content: "inline*",
|
1447
|
+
group: "block",
|
1448
|
+
parseDOM: [{ tag: "p" }],
|
1449
|
+
toDOM() {
|
1450
|
+
return ["p", 0];
|
1451
|
+
}
|
1452
|
+
});
|
1453
|
+
}
|
1454
|
+
/**
|
1455
|
+
* @public
|
1456
|
+
*
|
1457
|
+
* Defines a paragraph node spec as the highest priority, because it should be the default block node for most cases.
|
1458
|
+
*
|
1459
|
+
* @deprecated Use the following import instead:
|
1460
|
+
*
|
1461
|
+
* ```ts
|
1462
|
+
* import { defineParagraph } from 'prosekit/extensions/paragraph'
|
1463
|
+
* ```
|
1464
|
+
*/
|
1386
1465
|
function defineParagraph() {
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1466
|
+
console.warn("[prosekit] The `defineParagraph` function from `prosekit/core` is deprecated. Use the following import instead: `import { defineParagraph } from \"prosekit/extensions/paragraph\"`.");
|
1467
|
+
return withPriority(defineParagraphSpec(), Priority.highest);
|
1468
|
+
}
|
1469
|
+
|
1470
|
+
//#endregion
|
1471
|
+
//#region src/extensions/text.ts
|
1472
|
+
/**
|
1473
|
+
* @public
|
1474
|
+
*
|
1475
|
+
* @deprecated Use the following import instead:
|
1476
|
+
*
|
1477
|
+
* ```ts
|
1478
|
+
* import { defineText } from 'prosekit/extensions/text'
|
1479
|
+
* ```
|
1480
|
+
*/
|
1391
1481
|
function defineText() {
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1482
|
+
console.warn("[prosekit] The `defineText` function from `prosekit/core` is deprecated. Use the following import instead: `import { defineText } from \"prosekit/extensions/text\"`.");
|
1483
|
+
return defineNodeSpec({
|
1484
|
+
name: "text",
|
1485
|
+
group: "inline"
|
1486
|
+
});
|
1396
1487
|
}
|
1397
1488
|
|
1398
|
-
|
1489
|
+
//#endregion
|
1490
|
+
//#region src/utils/cache.ts
|
1399
1491
|
function cache(fn) {
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
}
|
1492
|
+
let result = void 0;
|
1493
|
+
return () => {
|
1494
|
+
if (result === void 0) result = fn();
|
1495
|
+
return result;
|
1496
|
+
};
|
1497
|
+
}
|
1498
|
+
|
1499
|
+
//#endregion
|
1500
|
+
//#region src/utils/can-use-regex-lookbehind.ts
|
1501
|
+
const canUseRegexLookbehind = cache(() => {
|
1502
|
+
try {
|
1503
|
+
return "ab".replace(new RegExp("(?<=a)b", "g"), "c") === "ac";
|
1504
|
+
} catch {
|
1505
|
+
return false;
|
1506
|
+
}
|
1416
1507
|
});
|
1417
1508
|
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1509
|
+
//#endregion
|
1510
|
+
//#region src/utils/clsx.ts
|
1511
|
+
/**
|
1512
|
+
* A utility for constructing `className` strings conditionally.
|
1513
|
+
*
|
1514
|
+
* It is a re-export of [clsx/lite](https://www.npmjs.com/package/clsx) with stricter types.
|
1515
|
+
*
|
1516
|
+
* @public
|
1517
|
+
*/
|
1518
|
+
const clsx = clsxLite;
|
1519
|
+
|
1520
|
+
//#endregion
|
1521
|
+
//#region src/utils/collect-children.ts
|
1522
|
+
/**
|
1523
|
+
* Collects all children of a node or a fragment, and returns them as an array.
|
1524
|
+
*
|
1525
|
+
* @public
|
1526
|
+
*/
|
1423
1527
|
function collectChildren(parent) {
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1528
|
+
const children = [];
|
1529
|
+
for (let i = 0; i < parent.childCount; i++) children.push(parent.child(i));
|
1530
|
+
return children;
|
1531
|
+
}
|
1532
|
+
|
1533
|
+
//#endregion
|
1534
|
+
//#region src/utils/collect-nodes.ts
|
1535
|
+
/**
|
1536
|
+
* Collects all nodes from a given content.
|
1537
|
+
*
|
1538
|
+
* @deprecated Use `collectChildren` instead.
|
1539
|
+
*
|
1540
|
+
* @public
|
1541
|
+
*/
|
1436
1542
|
function collectNodes(content) {
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
// src/utils/contains-inline-node.ts
|
1543
|
+
if (Array.isArray(content)) return content.flatMap(collectNodes);
|
1544
|
+
if (content instanceof ProseMirrorNode) return [content];
|
1545
|
+
if (content instanceof ProseMirrorFragment) {
|
1546
|
+
const nodes = [];
|
1547
|
+
for (let i = 0; i < content.childCount; i++) nodes.push(content.child(i));
|
1548
|
+
return nodes;
|
1549
|
+
}
|
1550
|
+
throw new ProseKitError(`Invalid node content: ${typeof content}`);
|
1551
|
+
}
|
1552
|
+
|
1553
|
+
//#endregion
|
1554
|
+
//#region src/utils/contains-inline-node.ts
|
1555
|
+
/**
|
1556
|
+
* @internal
|
1557
|
+
*/
|
1454
1558
|
function containsInlineNode(doc, from, to) {
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
}
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1559
|
+
let found = false;
|
1560
|
+
doc.nodesBetween(from, to, (node) => {
|
1561
|
+
if (found) return false;
|
1562
|
+
if (node.isInline) found = true;
|
1563
|
+
});
|
1564
|
+
return found;
|
1565
|
+
}
|
1566
|
+
|
1567
|
+
//#endregion
|
1568
|
+
//#region src/utils/get-id.ts
|
1569
|
+
let id = 0;
|
1570
|
+
/**
|
1571
|
+
* Returns a unique id in the current process that can be used in various places.
|
1572
|
+
*
|
1573
|
+
* @internal
|
1574
|
+
*/
|
1465
1575
|
function getId() {
|
1466
|
-
|
1467
|
-
|
1576
|
+
id = (id + 1) % Number.MAX_SAFE_INTEGER;
|
1577
|
+
return `id:${id}`;
|
1468
1578
|
}
|
1469
1579
|
|
1470
|
-
|
1580
|
+
//#endregion
|
1581
|
+
//#region src/utils/is-at-block-start.ts
|
1582
|
+
/**
|
1583
|
+
* Whether the selection is an empty text selection at the start of a block.
|
1584
|
+
*
|
1585
|
+
* @internal
|
1586
|
+
*/
|
1471
1587
|
function isAtBlockStart(state, view) {
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
}
|
1476
|
-
return $cursor;
|
1588
|
+
const { $cursor } = state.selection;
|
1589
|
+
if (!$cursor || (view ? !view.endOfTextblock("backward", state) : $cursor.parentOffset > 0)) return null;
|
1590
|
+
return $cursor;
|
1477
1591
|
}
|
1478
1592
|
|
1479
|
-
|
1593
|
+
//#endregion
|
1594
|
+
//#region src/utils/is-in-code-block.ts
|
1480
1595
|
function isCodeBlockType(type) {
|
1481
|
-
|
1596
|
+
return !!(type.spec.code && type.isBlock);
|
1482
1597
|
}
|
1598
|
+
/**
|
1599
|
+
* Check if the selection is in a code block.
|
1600
|
+
*
|
1601
|
+
* @internal
|
1602
|
+
*/
|
1483
1603
|
function isInCodeBlock(selection) {
|
1484
|
-
|
1604
|
+
return isCodeBlockType(selection.$from.parent.type) || isCodeBlockType(selection.$to.parent.type);
|
1485
1605
|
}
|
1486
1606
|
|
1487
|
-
|
1607
|
+
//#endregion
|
1608
|
+
//#region src/utils/maybe-run.ts
|
1609
|
+
/**
|
1610
|
+
* @internal
|
1611
|
+
*/
|
1488
1612
|
function maybeRun(value, ...args) {
|
1489
|
-
|
1613
|
+
return typeof value === "function" ? value(...args) : value;
|
1490
1614
|
}
|
1491
1615
|
|
1492
|
-
|
1493
|
-
|
1616
|
+
//#endregion
|
1617
|
+
//#region src/utils/unicode.ts
|
1618
|
+
/**
|
1619
|
+
* @internal
|
1620
|
+
*/
|
1621
|
+
const OBJECT_REPLACEMENT_CHARACTER = "";
|
1494
1622
|
|
1495
|
-
|
1623
|
+
//#endregion
|
1624
|
+
//#region src/utils/with-skip-code-block.ts
|
1625
|
+
/**
|
1626
|
+
* @internal
|
1627
|
+
*/
|
1496
1628
|
function withSkipCodeBlock(command) {
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
export {
|
1505
|
-
Editor,
|
1506
|
-
EditorNotFoundError,
|
1507
|
-
OBJECT_REPLACEMENT_CHARACTER,
|
1508
|
-
Priority,
|
1509
|
-
ProseKitError,
|
1510
|
-
getId as _getId,
|
1511
|
-
addMark,
|
1512
|
-
assert,
|
1513
|
-
canUseRegexLookbehind,
|
1514
|
-
clsx,
|
1515
|
-
collectChildren,
|
1516
|
-
collectNodes,
|
1517
|
-
containsInlineNode,
|
1518
|
-
createEditor,
|
1519
|
-
defaultBlockAt,
|
1520
|
-
defineBaseCommands,
|
1521
|
-
defineBaseKeymap,
|
1522
|
-
defineClickHandler,
|
1523
|
-
defineClickOnHandler,
|
1524
|
-
defineClipboardSerializer,
|
1525
|
-
defineCommands,
|
1526
|
-
defineDOMEventHandler,
|
1527
|
-
defineDefaultState,
|
1528
|
-
defineDoc,
|
1529
|
-
defineDocChangeHandler,
|
1530
|
-
defineDoubleClickHandler,
|
1531
|
-
defineDoubleClickOnHandler,
|
1532
|
-
defineDropHandler,
|
1533
|
-
defineFacet,
|
1534
|
-
defineFacetPayload,
|
1535
|
-
defineFocusChangeHandler,
|
1536
|
-
defineHistory,
|
1537
|
-
defineKeyDownHandler,
|
1538
|
-
defineKeyPressHandler,
|
1539
|
-
defineKeymap,
|
1540
|
-
defineMarkAttr,
|
1541
|
-
defineMarkSpec,
|
1542
|
-
defineMarkView,
|
1543
|
-
defineMarkViewComponent,
|
1544
|
-
defineMarkViewFactory,
|
1545
|
-
defineMountHandler,
|
1546
|
-
defineNodeAttr,
|
1547
|
-
defineNodeSpec,
|
1548
|
-
defineNodeView,
|
1549
|
-
defineNodeViewComponent,
|
1550
|
-
defineNodeViewFactory,
|
1551
|
-
defineParagraph,
|
1552
|
-
definePasteHandler,
|
1553
|
-
definePlugin,
|
1554
|
-
defineScrollToSelectionHandler,
|
1555
|
-
defineText,
|
1556
|
-
defineTextInputHandler,
|
1557
|
-
defineTripleClickHandler,
|
1558
|
-
defineTripleClickOnHandler,
|
1559
|
-
defineUnmountHandler,
|
1560
|
-
defineUpdateHandler,
|
1561
|
-
editorEventFacet,
|
1562
|
-
elementFromJSON,
|
1563
|
-
elementFromNode,
|
1564
|
-
expandMark,
|
1565
|
-
findParentNode,
|
1566
|
-
findParentNodeOfType,
|
1567
|
-
getMarkType,
|
1568
|
-
getNodeType,
|
1569
|
-
htmlFromJSON,
|
1570
|
-
htmlFromNode,
|
1571
|
-
insertDefaultBlock,
|
1572
|
-
insertNode,
|
1573
|
-
isAllSelection,
|
1574
|
-
isApple,
|
1575
|
-
isAtBlockStart,
|
1576
|
-
isElement,
|
1577
|
-
isFragment,
|
1578
|
-
isInCodeBlock,
|
1579
|
-
isMark,
|
1580
|
-
isMarkAbsent,
|
1581
|
-
isMarkActive,
|
1582
|
-
isNodeSelection,
|
1583
|
-
isProseMirrorNode,
|
1584
|
-
isSelection,
|
1585
|
-
isSlice,
|
1586
|
-
isTextSelection,
|
1587
|
-
jsonFromHTML,
|
1588
|
-
jsonFromNode,
|
1589
|
-
jsonFromState,
|
1590
|
-
keymapFacet,
|
1591
|
-
maybeRun,
|
1592
|
-
nodeFromElement,
|
1593
|
-
nodeFromHTML,
|
1594
|
-
nodeFromJSON,
|
1595
|
-
pluginFacet,
|
1596
|
-
removeMark,
|
1597
|
-
removeNode,
|
1598
|
-
setBlockType,
|
1599
|
-
setNodeAttrs,
|
1600
|
-
setSelectionAround,
|
1601
|
-
stateFromJSON,
|
1602
|
-
toggleMark,
|
1603
|
-
toggleNode,
|
1604
|
-
toggleWrap,
|
1605
|
-
union,
|
1606
|
-
unsetBlockType,
|
1607
|
-
unsetMark,
|
1608
|
-
withPriority,
|
1609
|
-
withSkipCodeBlock,
|
1610
|
-
wrap
|
1611
|
-
};
|
1629
|
+
return (state, dispatch, view) => {
|
1630
|
+
if (isInCodeBlock(state.selection)) return false;
|
1631
|
+
return command(state, dispatch, view);
|
1632
|
+
};
|
1633
|
+
}
|
1634
|
+
|
1635
|
+
//#endregion
|
1636
|
+
export { Editor, EditorNotFoundError, OBJECT_REPLACEMENT_CHARACTER, Priority, ProseKitError, getId as _getId, addMark, assert, canUseRegexLookbehind, clsx, collectChildren, collectNodes, containsInlineNode, createEditor, defaultBlockAt, defineBaseCommands, defineBaseKeymap, defineClickHandler, defineClickOnHandler, defineClipboardSerializer, defineCommands, defineDOMEventHandler, defineDefaultState, defineDoc, defineDocChangeHandler, defineDoubleClickHandler, defineDoubleClickOnHandler, defineDropHandler, defineFacet, defineFacetPayload, defineFocusChangeHandler, defineHistory, defineKeyDownHandler, defineKeyPressHandler, defineKeymap, defineMarkAttr, defineMarkSpec, defineMarkView, defineMarkViewComponent, defineMarkViewFactory, defineMountHandler, defineNodeAttr, defineNodeSpec, defineNodeView, defineNodeViewComponent, defineNodeViewFactory, defineParagraph, definePasteHandler, definePlugin, defineScrollToSelectionHandler, defineText, defineTextInputHandler, defineTripleClickHandler, defineTripleClickOnHandler, defineUnmountHandler, defineUpdateHandler, editorEventFacet, elementFromJSON, elementFromNode, expandMark, findParentNode, findParentNodeOfType, getMarkType, getNodeType, htmlFromJSON, htmlFromNode, insertDefaultBlock, insertNode, isAllSelection, isApple, isAtBlockStart, isFragment, isInCodeBlock, isMark, isMarkAbsent, isMarkActive, isNodeSelection, isProseMirrorNode, isSelection, isSlice, isTextSelection, jsonFromHTML, jsonFromNode, jsonFromState, keymapFacet, maybeRun, nodeFromElement, nodeFromHTML, nodeFromJSON, pluginFacet, removeMark, removeNode, setBlockType, setNodeAttrs, setSelectionAround, stateFromJSON, toggleMark, toggleNode, toggleWrap, union, unsetBlockType, unsetMark, withPriority, withSkipCodeBlock, wrap };
|