@prosekit/core 0.8.0 → 0.8.2
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-CjVyjJqw.d.ts +739 -0
- package/dist/editor-DbMrpnmL.js +1319 -0
- package/dist/prosekit-core-test.d.ts +33 -2
- package/dist/prosekit-core-test.js +94 -116
- package/dist/prosekit-core.d.ts +1451 -203
- package/dist/prosekit-core.js +1441 -1438
- package/package.json +21 -11
- package/dist/_tsup-dts-rollup.d.ts +0 -3063
- package/dist/chunk-B3WEP4DD.js +0 -1188
package/dist/prosekit-core.js
CHANGED
@@ -1,1620 +1,1623 @@
|
|
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-DbMrpnmL.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, once } 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 its 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
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
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
|
+
*/
|
822
857
|
function defineMountHandler(handler) {
|
823
|
-
|
858
|
+
return definePluginViewFacetPayload(["mount", handler]);
|
824
859
|
}
|
860
|
+
/**
|
861
|
+
* Registers a event handler that is called when the editor state is updated.
|
862
|
+
*
|
863
|
+
* @public
|
864
|
+
*/
|
825
865
|
function defineUpdateHandler(handler) {
|
826
|
-
|
866
|
+
return definePluginViewFacetPayload(["update", handler]);
|
827
867
|
}
|
868
|
+
/**
|
869
|
+
* Registers a event handler that is called when the editor view is unmounted.
|
870
|
+
*
|
871
|
+
* @public
|
872
|
+
*/
|
828
873
|
function defineUnmountHandler(handler) {
|
829
|
-
|
874
|
+
return definePluginViewFacetPayload(["unmount", handler]);
|
830
875
|
}
|
831
876
|
function definePluginViewFacetPayload(input) {
|
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
|
-
|
874
|
-
|
875
|
-
|
876
|
-
parent: pluginFacet,
|
877
|
-
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
|
878
921
|
});
|
879
|
-
|
880
|
-
|
881
|
-
|
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
|
+
*/
|
882
931
|
function defineDocChangeHandler(handler) {
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
}
|
887
|
-
});
|
932
|
+
return defineUpdateHandler((view, prevState) => {
|
933
|
+
if (!view.state.doc.eq(prevState.doc)) handler(view, prevState);
|
934
|
+
});
|
888
935
|
}
|
889
936
|
|
890
|
-
|
891
|
-
|
892
|
-
PluginKey as PluginKey3,
|
893
|
-
ProseMirrorPlugin as ProseMirrorPlugin3
|
894
|
-
} from "@prosekit/pm/state";
|
895
|
-
|
896
|
-
// src/utils/combine-event-handlers.ts
|
937
|
+
//#endregion
|
938
|
+
//#region src/utils/combine-event-handlers.ts
|
897
939
|
function combineEventHandlers() {
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
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
|
+
*/
|
914
956
|
function defineDomEventFacetPayload(...payloads) {
|
915
|
-
|
916
|
-
domEventFacet,
|
917
|
-
payloads
|
918
|
-
);
|
957
|
+
return defineFacetPayload(domEventFacet, payloads);
|
919
958
|
}
|
959
|
+
/**
|
960
|
+
* Register a new event handler for the given event type.
|
961
|
+
*
|
962
|
+
* @public
|
963
|
+
*/
|
920
964
|
function defineDOMEventHandler(event, handler) {
|
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
|
-
|
961
|
-
|
962
|
-
|
963
|
-
singleton: true
|
964
|
-
}
|
965
|
-
);
|
966
|
-
|
967
|
-
// src/extensions/events/editor-event.ts
|
968
|
-
import {
|
969
|
-
PluginKey as PluginKey4,
|
970
|
-
ProseMirrorPlugin as ProseMirrorPlugin4
|
971
|
-
} 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
|
972
1007
|
function defineEventFacetPayload(payload) {
|
973
|
-
|
1008
|
+
return defineFacetPayload(editorEventFacet, [payload]);
|
974
1009
|
}
|
1010
|
+
/**
|
1011
|
+
* @public
|
1012
|
+
*
|
1013
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleKeyDown}
|
1014
|
+
*/
|
975
1015
|
function defineKeyDownHandler(handler) {
|
976
|
-
|
1016
|
+
return defineEventFacetPayload(["keyDown", handler]);
|
977
1017
|
}
|
1018
|
+
/**
|
1019
|
+
* @public
|
1020
|
+
*
|
1021
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleKeyPress}
|
1022
|
+
*/
|
978
1023
|
function defineKeyPressHandler(handler) {
|
979
|
-
|
1024
|
+
return defineEventFacetPayload(["keyPress", handler]);
|
980
1025
|
}
|
1026
|
+
/**
|
1027
|
+
* @public
|
1028
|
+
*
|
1029
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleTextInput}
|
1030
|
+
*/
|
981
1031
|
function defineTextInputHandler(handler) {
|
982
|
-
|
1032
|
+
return defineEventFacetPayload(["textInput", handler]);
|
983
1033
|
}
|
1034
|
+
/**
|
1035
|
+
* @public
|
1036
|
+
*
|
1037
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleClickOn}
|
1038
|
+
*/
|
984
1039
|
function defineClickOnHandler(handler) {
|
985
|
-
|
1040
|
+
return defineEventFacetPayload(["clickOn", handler]);
|
986
1041
|
}
|
1042
|
+
/**
|
1043
|
+
* @public
|
1044
|
+
*
|
1045
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleClick}
|
1046
|
+
*/
|
987
1047
|
function defineClickHandler(handler) {
|
988
|
-
|
1048
|
+
return defineEventFacetPayload(["click", handler]);
|
989
1049
|
}
|
1050
|
+
/**
|
1051
|
+
* @public
|
1052
|
+
*
|
1053
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleDoubleClickOn}
|
1054
|
+
*/
|
990
1055
|
function defineDoubleClickOnHandler(handler) {
|
991
|
-
|
1056
|
+
return defineEventFacetPayload(["doubleClickOn", handler]);
|
992
1057
|
}
|
1058
|
+
/**
|
1059
|
+
* @public
|
1060
|
+
*
|
1061
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleDoubleClick}
|
1062
|
+
*/
|
993
1063
|
function defineDoubleClickHandler(handler) {
|
994
|
-
|
1064
|
+
return defineEventFacetPayload(["doubleClick", handler]);
|
995
1065
|
}
|
1066
|
+
/**
|
1067
|
+
* @public
|
1068
|
+
*
|
1069
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleTripleClickOn}
|
1070
|
+
*/
|
996
1071
|
function defineTripleClickOnHandler(handler) {
|
997
|
-
|
1072
|
+
return defineEventFacetPayload(["tripleClickOn", handler]);
|
998
1073
|
}
|
1074
|
+
/**
|
1075
|
+
* @public
|
1076
|
+
*
|
1077
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleTripleClick}
|
1078
|
+
*/
|
999
1079
|
function defineTripleClickHandler(handler) {
|
1000
|
-
|
1080
|
+
return defineEventFacetPayload(["tripleClick", handler]);
|
1001
1081
|
}
|
1082
|
+
/**
|
1083
|
+
* @public
|
1084
|
+
*
|
1085
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handlePaste}
|
1086
|
+
*/
|
1002
1087
|
function definePasteHandler(handler) {
|
1003
|
-
|
1088
|
+
return defineEventFacetPayload(["paste", handler]);
|
1004
1089
|
}
|
1090
|
+
/**
|
1091
|
+
* @public
|
1092
|
+
*
|
1093
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleDrop}
|
1094
|
+
*/
|
1005
1095
|
function defineDropHandler(handler) {
|
1006
|
-
|
1096
|
+
return defineEventFacetPayload(["drop", handler]);
|
1007
1097
|
}
|
1098
|
+
/**
|
1099
|
+
* @public
|
1100
|
+
*
|
1101
|
+
* See {@link https://prosemirror.net/docs/ref/#view.EditorProps.handleScrollToSelection}
|
1102
|
+
*/
|
1008
1103
|
function defineScrollToSelectionHandler(handler) {
|
1009
|
-
|
1010
|
-
}
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
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
|
1021
1119
|
});
|
1022
1120
|
function setupEditorEventPlugin() {
|
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
|
-
|
1068
|
-
}
|
1069
|
-
|
1070
|
-
|
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
|
+
*/
|
1071
1175
|
function defineFocusChangeHandler(handler) {
|
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
|
-
handler = keydownHandler(
|
1114
|
-
mergeKeymaps(
|
1115
|
-
// The keymap at the end have a higher priority.
|
1116
|
-
toReversed(keymaps)
|
1117
|
-
)
|
1118
|
-
);
|
1119
|
-
return plugin;
|
1120
|
-
};
|
1121
|
-
},
|
1122
|
-
parent: pluginFacet,
|
1123
|
-
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(toReversed(keymaps)));
|
1212
|
+
return plugin;
|
1213
|
+
};
|
1214
|
+
},
|
1215
|
+
parent: pluginFacet,
|
1216
|
+
singleton: true
|
1124
1217
|
});
|
1125
1218
|
function mergeKeymaps(keymaps) {
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
"Shift-Mod-z": redo
|
1219
|
+
const bindings = {};
|
1220
|
+
for (const keymap$1 of keymaps) for (const [key, command] of Object.entries(keymap$1)) {
|
1221
|
+
const commands$1 = bindings[key] || (bindings[key] = []);
|
1222
|
+
commands$1.push(command);
|
1223
|
+
}
|
1224
|
+
return mapValues(bindings, mergeCommands);
|
1225
|
+
}
|
1226
|
+
function mergeCommands(commands$1) {
|
1227
|
+
return chainCommands(...commands$1);
|
1228
|
+
}
|
1229
|
+
const keymapPluginKey = new PluginKey("prosekit-keymap");
|
1230
|
+
|
1231
|
+
//#endregion
|
1232
|
+
//#region src/extensions/history.ts
|
1233
|
+
const keymap = {
|
1234
|
+
"Mod-z": undo,
|
1235
|
+
"Shift-Mod-z": redo
|
1144
1236
|
};
|
1145
|
-
if (!isApple)
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
undo: () => undo,
|
1150
|
-
redo: () => redo
|
1237
|
+
if (!isApple) keymap["Mod-y"] = redo;
|
1238
|
+
const commands = {
|
1239
|
+
undo: () => undo,
|
1240
|
+
redo: () => redo
|
1151
1241
|
};
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
import { splitSplittableBlock } from "prosemirror-splittable";
|
1175
|
-
var customEnter = chainCommands2(
|
1176
|
-
newlineInCode,
|
1177
|
-
createParagraphNear,
|
1178
|
-
liftEmptyBlock,
|
1179
|
-
splitSplittableBlock
|
1180
|
-
);
|
1181
|
-
var customBackspace = chainCommands2(
|
1182
|
-
deleteSelection,
|
1183
|
-
joinTextblockBackward,
|
1184
|
-
selectNodeBackward
|
1185
|
-
);
|
1186
|
-
var customBaseKeymap = {
|
1187
|
-
...baseKeymap,
|
1188
|
-
Enter: customEnter,
|
1189
|
-
Backspace: customBackspace
|
1242
|
+
/**
|
1243
|
+
* Add undo/redo history to the editor.
|
1244
|
+
*
|
1245
|
+
* @param options
|
1246
|
+
*
|
1247
|
+
* @public
|
1248
|
+
*/
|
1249
|
+
function defineHistory({ depth = 200, newGroupDelay = 250 } = {}) {
|
1250
|
+
return union(definePlugin(history({
|
1251
|
+
depth,
|
1252
|
+
newGroupDelay
|
1253
|
+
})), defineKeymap(keymap), defineCommands(commands));
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
//#endregion
|
1257
|
+
//#region src/extensions/keymap-base.ts
|
1258
|
+
const customEnter = chainCommands(newlineInCode, createParagraphNear, liftEmptyBlock, splitSplittableBlock);
|
1259
|
+
const customBackspace = chainCommands(deleteSelection, joinTextblockBackward, selectNodeBackward);
|
1260
|
+
const customBaseKeymap = {
|
1261
|
+
...baseKeymap,
|
1262
|
+
Enter: customEnter,
|
1263
|
+
Backspace: customBackspace
|
1190
1264
|
};
|
1265
|
+
/**
|
1266
|
+
* Defines some basic key bindings.
|
1267
|
+
*
|
1268
|
+
* @public
|
1269
|
+
*/
|
1191
1270
|
function defineBaseKeymap(options) {
|
1192
|
-
|
1193
|
-
|
1271
|
+
const priority = options?.priority ?? Priority.low;
|
1272
|
+
return withPriority(defineKeymap(customBaseKeymap), priority);
|
1194
1273
|
}
|
1195
1274
|
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1275
|
+
//#endregion
|
1276
|
+
//#region src/extensions/mark-spec.ts
|
1277
|
+
/**
|
1278
|
+
* @public
|
1279
|
+
*/
|
1199
1280
|
function defineMarkSpec(options) {
|
1200
|
-
|
1201
|
-
|
1281
|
+
const payload = [options, void 0];
|
1282
|
+
return defineFacetPayload(markSpecFacet, [payload]);
|
1202
1283
|
}
|
1284
|
+
/**
|
1285
|
+
* @public
|
1286
|
+
*/
|
1203
1287
|
function defineMarkAttr(options) {
|
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
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
spec.parseDOM = spec.parseDOM.map((rule) => wrapParseRuleAttrs(rule, attrs));
|
1240
|
-
}
|
1241
|
-
specs = specs.update(type, spec);
|
1242
|
-
}
|
1243
|
-
return { marks: specs, nodes: {} };
|
1244
|
-
},
|
1245
|
-
parent: schemaSpecFacet,
|
1246
|
-
singleton: true
|
1288
|
+
const payload = [void 0, options];
|
1289
|
+
return defineFacetPayload(markSpecFacet, [payload]);
|
1290
|
+
}
|
1291
|
+
const markSpecFacet = defineFacet({
|
1292
|
+
reducer: (payloads) => {
|
1293
|
+
let specs = OrderedMap.from({});
|
1294
|
+
const specPayloads = payloads.map((input) => input[0]).filter(isNotNullish);
|
1295
|
+
const attrPayloads = payloads.map((input) => input[1]).filter(isNotNullish);
|
1296
|
+
for (const { name,...spec } of specPayloads) {
|
1297
|
+
const prevSpec = specs.get(name);
|
1298
|
+
if (prevSpec) specs = specs.update(name, mergeSpecs(prevSpec, spec));
|
1299
|
+
else specs = specs.addToStart(name, spec);
|
1300
|
+
}
|
1301
|
+
const groupedAttrs = groupBy(attrPayloads, (payload) => payload.type);
|
1302
|
+
for (const [type, attrs] of Object.entries(groupedAttrs)) {
|
1303
|
+
if (!attrs) continue;
|
1304
|
+
const maybeSpec = specs.get(type);
|
1305
|
+
assert(maybeSpec, `Mark type ${type} must be defined`);
|
1306
|
+
const spec = clone(maybeSpec);
|
1307
|
+
if (!spec.attrs) spec.attrs = {};
|
1308
|
+
for (const attr of attrs) spec.attrs[attr.attr] = {
|
1309
|
+
default: attr.default,
|
1310
|
+
validate: attr.validate
|
1311
|
+
};
|
1312
|
+
if (spec.toDOM) spec.toDOM = wrapOutputSpecAttrs(spec.toDOM, attrs);
|
1313
|
+
if (spec.parseDOM) spec.parseDOM = spec.parseDOM.map((rule) => wrapParseRuleAttrs(rule, attrs));
|
1314
|
+
specs = specs.update(type, spec);
|
1315
|
+
}
|
1316
|
+
return {
|
1317
|
+
marks: specs,
|
1318
|
+
nodes: {}
|
1319
|
+
};
|
1320
|
+
},
|
1321
|
+
parent: schemaSpecFacet,
|
1322
|
+
singleton: true
|
1247
1323
|
});
|
1248
1324
|
function wrapParseRuleAttrs(rule, attrs) {
|
1249
|
-
|
1250
|
-
|
1251
|
-
}
|
1252
|
-
return rule;
|
1325
|
+
if (rule.tag) return wrapTagParseRuleAttrs(rule, attrs);
|
1326
|
+
return rule;
|
1253
1327
|
}
|
1254
1328
|
|
1255
|
-
|
1256
|
-
|
1257
|
-
PluginKey as PluginKey6,
|
1258
|
-
ProseMirrorPlugin as ProseMirrorPlugin5
|
1259
|
-
} from "@prosekit/pm/state";
|
1329
|
+
//#endregion
|
1330
|
+
//#region src/extensions/mark-view.ts
|
1260
1331
|
function defineMarkView(options) {
|
1261
|
-
|
1262
|
-
}
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
key: new PluginKey6("prosekit-mark-view"),
|
1274
|
-
props: { markViews }
|
1275
|
-
})
|
1276
|
-
];
|
1277
|
-
},
|
1278
|
-
parent: pluginFacet
|
1332
|
+
return defineFacetPayload(markViewFacet, [options]);
|
1333
|
+
}
|
1334
|
+
const markViewFacet = defineFacet({
|
1335
|
+
reducer: (inputs) => {
|
1336
|
+
const markViews = {};
|
1337
|
+
for (const input of inputs) if (!markViews[input.name]) markViews[input.name] = input.constructor;
|
1338
|
+
return () => [new ProseMirrorPlugin({
|
1339
|
+
key: new PluginKey("prosekit-mark-view"),
|
1340
|
+
props: { markViews }
|
1341
|
+
})];
|
1342
|
+
},
|
1343
|
+
parent: pluginFacet
|
1279
1344
|
});
|
1280
1345
|
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1346
|
+
//#endregion
|
1347
|
+
//#region src/extensions/mark-view-effect.ts
|
1348
|
+
/**
|
1349
|
+
* @internal
|
1350
|
+
*/
|
1286
1351
|
function defineMarkViewFactory(options) {
|
1287
|
-
|
1288
|
-
|
1352
|
+
const input = [options, null];
|
1353
|
+
return defineFacetPayload(markViewFactoryFacet, [input]);
|
1289
1354
|
}
|
1355
|
+
/**
|
1356
|
+
* @internal
|
1357
|
+
*/
|
1290
1358
|
function defineMarkViewComponent(options) {
|
1291
|
-
|
1292
|
-
|
1293
|
-
}
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
},
|
1313
|
-
parent: pluginFacet
|
1359
|
+
const input = [null, options];
|
1360
|
+
return defineFacetPayload(markViewFactoryFacet, [input]);
|
1361
|
+
}
|
1362
|
+
const isServer$1 = typeof window === "undefined";
|
1363
|
+
const markViewFactoryFacet = defineFacet({
|
1364
|
+
reducer: (inputs) => {
|
1365
|
+
if (isServer$1) return [];
|
1366
|
+
const markViews = {};
|
1367
|
+
const factories = inputs.map((x) => x[0]).filter(isNotNullish);
|
1368
|
+
const options = inputs.map((x) => x[1]).filter(isNotNullish);
|
1369
|
+
for (const { group, name, args } of options) {
|
1370
|
+
const factory = factories.find((factory$1) => factory$1.group === group);
|
1371
|
+
if (!factory) continue;
|
1372
|
+
markViews[name] = factory.factory(args);
|
1373
|
+
}
|
1374
|
+
return () => [new ProseMirrorPlugin({
|
1375
|
+
key: new PluginKey("prosekit-mark-view-effect"),
|
1376
|
+
props: { markViews }
|
1377
|
+
})];
|
1378
|
+
},
|
1379
|
+
parent: pluginFacet
|
1314
1380
|
});
|
1315
1381
|
|
1316
|
-
|
1317
|
-
|
1318
|
-
PluginKey as PluginKey8,
|
1319
|
-
ProseMirrorPlugin as ProseMirrorPlugin7
|
1320
|
-
} from "@prosekit/pm/state";
|
1382
|
+
//#endregion
|
1383
|
+
//#region src/extensions/node-view.ts
|
1321
1384
|
function defineNodeView(options) {
|
1322
|
-
|
1323
|
-
}
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
key: new PluginKey8("prosekit-node-view"),
|
1335
|
-
props: { nodeViews }
|
1336
|
-
})
|
1337
|
-
];
|
1338
|
-
},
|
1339
|
-
parent: pluginFacet
|
1385
|
+
return defineFacetPayload(nodeViewFacet, [options]);
|
1386
|
+
}
|
1387
|
+
const nodeViewFacet = defineFacet({
|
1388
|
+
reducer: (inputs) => {
|
1389
|
+
const nodeViews = {};
|
1390
|
+
for (const input of inputs) if (!nodeViews[input.name]) nodeViews[input.name] = input.constructor;
|
1391
|
+
return () => [new ProseMirrorPlugin({
|
1392
|
+
key: new PluginKey("prosekit-node-view"),
|
1393
|
+
props: { nodeViews }
|
1394
|
+
})];
|
1395
|
+
},
|
1396
|
+
parent: pluginFacet
|
1340
1397
|
});
|
1341
1398
|
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1399
|
+
//#endregion
|
1400
|
+
//#region src/extensions/node-view-effect.ts
|
1401
|
+
/**
|
1402
|
+
* @internal
|
1403
|
+
*/
|
1347
1404
|
function defineNodeViewFactory(options) {
|
1348
|
-
|
1349
|
-
|
1405
|
+
const input = [options, null];
|
1406
|
+
return defineFacetPayload(nodeViewFactoryFacet, [input]);
|
1350
1407
|
}
|
1408
|
+
/**
|
1409
|
+
* @internal
|
1410
|
+
*/
|
1351
1411
|
function defineNodeViewComponent(options) {
|
1352
|
-
|
1353
|
-
|
1354
|
-
}
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
},
|
1374
|
-
parent: pluginFacet
|
1412
|
+
const input = [null, options];
|
1413
|
+
return defineFacetPayload(nodeViewFactoryFacet, [input]);
|
1414
|
+
}
|
1415
|
+
const isServer = typeof window === "undefined";
|
1416
|
+
const nodeViewFactoryFacet = defineFacet({
|
1417
|
+
reducer: (inputs) => {
|
1418
|
+
if (isServer) return [];
|
1419
|
+
const nodeViews = {};
|
1420
|
+
const factories = inputs.map((x) => x[0]).filter(isNotNullish);
|
1421
|
+
const options = inputs.map((x) => x[1]).filter(isNotNullish);
|
1422
|
+
for (const { group, name, args } of options) {
|
1423
|
+
const factory = factories.find((factory$1) => factory$1.group === group);
|
1424
|
+
if (!factory) continue;
|
1425
|
+
nodeViews[name] = factory.factory(args);
|
1426
|
+
}
|
1427
|
+
return () => [new ProseMirrorPlugin({
|
1428
|
+
key: new PluginKey("prosekit-node-view-effect"),
|
1429
|
+
props: { nodeViews }
|
1430
|
+
})];
|
1431
|
+
},
|
1432
|
+
parent: pluginFacet
|
1375
1433
|
});
|
1376
1434
|
|
1377
|
-
|
1435
|
+
//#endregion
|
1436
|
+
//#region src/extensions/paragraph.ts
|
1437
|
+
/**
|
1438
|
+
* Defines a paragraph node spec.
|
1439
|
+
*/
|
1378
1440
|
function defineParagraphSpec() {
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
}
|
1441
|
+
return defineNodeSpec({
|
1442
|
+
name: "paragraph",
|
1443
|
+
content: "inline*",
|
1444
|
+
group: "block",
|
1445
|
+
parseDOM: [{ tag: "p" }],
|
1446
|
+
toDOM() {
|
1447
|
+
return ["p", 0];
|
1448
|
+
}
|
1449
|
+
});
|
1450
|
+
}
|
1451
|
+
/**
|
1452
|
+
* @public
|
1453
|
+
*
|
1454
|
+
* Defines a paragraph node spec as the highest priority, because it should be the default block node for most cases.
|
1455
|
+
*
|
1456
|
+
* @deprecated Use the following import instead:
|
1457
|
+
*
|
1458
|
+
* ```ts
|
1459
|
+
* import { defineParagraph } from 'prosekit/extensions/paragraph'
|
1460
|
+
* ```
|
1461
|
+
*/
|
1389
1462
|
function defineParagraph() {
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1463
|
+
console.warn("[prosekit] The `defineParagraph` function from `prosekit/core` is deprecated. Use the following import instead: `import { defineParagraph } from \"prosekit/extensions/paragraph\"`.");
|
1464
|
+
return withPriority(defineParagraphSpec(), Priority.highest);
|
1465
|
+
}
|
1466
|
+
|
1467
|
+
//#endregion
|
1468
|
+
//#region src/extensions/text.ts
|
1469
|
+
/**
|
1470
|
+
* @public
|
1471
|
+
*
|
1472
|
+
* @deprecated Use the following import instead:
|
1473
|
+
*
|
1474
|
+
* ```ts
|
1475
|
+
* import { defineText } from 'prosekit/extensions/text'
|
1476
|
+
* ```
|
1477
|
+
*/
|
1397
1478
|
function defineText() {
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
}
|
1414
|
-
return result;
|
1415
|
-
};
|
1416
|
-
}
|
1417
|
-
|
1418
|
-
// src/utils/can-use-regex-lookbehind.ts
|
1419
|
-
var canUseRegexLookbehind = cache(() => {
|
1420
|
-
try {
|
1421
|
-
return "ab".replace(new RegExp("(?<=a)b", "g"), "c") === "ac";
|
1422
|
-
} catch {
|
1423
|
-
return false;
|
1424
|
-
}
|
1479
|
+
console.warn("[prosekit] The `defineText` function from `prosekit/core` is deprecated. Use the following import instead: `import { defineText } from \"prosekit/extensions/text\"`.");
|
1480
|
+
return defineNodeSpec({
|
1481
|
+
name: "text",
|
1482
|
+
group: "inline"
|
1483
|
+
});
|
1484
|
+
}
|
1485
|
+
|
1486
|
+
//#endregion
|
1487
|
+
//#region src/utils/can-use-regex-lookbehind.ts
|
1488
|
+
const canUseRegexLookbehind = once(() => {
|
1489
|
+
try {
|
1490
|
+
return "ab".replace(new RegExp("(?<=a)b", "g"), "c") === "ac";
|
1491
|
+
} catch {
|
1492
|
+
return false;
|
1493
|
+
}
|
1425
1494
|
});
|
1426
1495
|
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1496
|
+
//#endregion
|
1497
|
+
//#region src/utils/clsx.ts
|
1498
|
+
/**
|
1499
|
+
* A utility for constructing `className` strings conditionally.
|
1500
|
+
*
|
1501
|
+
* It is a re-export of [clsx/lite](https://www.npmjs.com/package/clsx) with stricter types.
|
1502
|
+
*
|
1503
|
+
* @public
|
1504
|
+
*/
|
1505
|
+
const clsx = clsxLite;
|
1506
|
+
|
1507
|
+
//#endregion
|
1508
|
+
//#region src/utils/collect-children.ts
|
1509
|
+
/**
|
1510
|
+
* Collects all children of a node or a fragment, and returns them as an array.
|
1511
|
+
*
|
1512
|
+
* @public
|
1513
|
+
*/
|
1432
1514
|
function collectChildren(parent) {
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1515
|
+
const children = [];
|
1516
|
+
for (let i = 0; i < parent.childCount; i++) children.push(parent.child(i));
|
1517
|
+
return children;
|
1518
|
+
}
|
1519
|
+
|
1520
|
+
//#endregion
|
1521
|
+
//#region src/utils/collect-nodes.ts
|
1522
|
+
/**
|
1523
|
+
* Collects all nodes from a given content.
|
1524
|
+
*
|
1525
|
+
* @deprecated Use `collectChildren` instead.
|
1526
|
+
*
|
1527
|
+
* @public
|
1528
|
+
*/
|
1445
1529
|
function collectNodes(content) {
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
// src/utils/contains-inline-node.ts
|
1530
|
+
if (Array.isArray(content)) return content.flatMap(collectNodes);
|
1531
|
+
if (content instanceof ProseMirrorNode) return [content];
|
1532
|
+
if (content instanceof ProseMirrorFragment) {
|
1533
|
+
const nodes = [];
|
1534
|
+
for (let i = 0; i < content.childCount; i++) nodes.push(content.child(i));
|
1535
|
+
return nodes;
|
1536
|
+
}
|
1537
|
+
throw new ProseKitError(`Invalid node content: ${typeof content}`);
|
1538
|
+
}
|
1539
|
+
|
1540
|
+
//#endregion
|
1541
|
+
//#region src/utils/contains-inline-node.ts
|
1542
|
+
/**
|
1543
|
+
* @internal
|
1544
|
+
*/
|
1463
1545
|
function containsInlineNode(doc, from, to) {
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
}
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1546
|
+
let found = false;
|
1547
|
+
doc.nodesBetween(from, to, (node) => {
|
1548
|
+
if (found) return false;
|
1549
|
+
if (node.isInline) found = true;
|
1550
|
+
});
|
1551
|
+
return found;
|
1552
|
+
}
|
1553
|
+
|
1554
|
+
//#endregion
|
1555
|
+
//#region src/utils/get-id.ts
|
1556
|
+
let id = 0;
|
1557
|
+
/**
|
1558
|
+
* Returns a unique id in the current process that can be used in various places.
|
1559
|
+
*
|
1560
|
+
* @internal
|
1561
|
+
*/
|
1474
1562
|
function getId() {
|
1475
|
-
|
1476
|
-
|
1563
|
+
id = (id + 1) % Number.MAX_SAFE_INTEGER;
|
1564
|
+
return `id:${id}`;
|
1477
1565
|
}
|
1478
1566
|
|
1479
|
-
|
1567
|
+
//#endregion
|
1568
|
+
//#region src/utils/is-at-block-start.ts
|
1569
|
+
/**
|
1570
|
+
* Whether the selection is an empty text selection at the start of a block.
|
1571
|
+
*
|
1572
|
+
* @internal
|
1573
|
+
*/
|
1480
1574
|
function isAtBlockStart(state, view) {
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
}
|
1485
|
-
return $cursor;
|
1575
|
+
const { $cursor } = state.selection;
|
1576
|
+
if (!$cursor || (view ? !view.endOfTextblock("backward", state) : $cursor.parentOffset > 0)) return null;
|
1577
|
+
return $cursor;
|
1486
1578
|
}
|
1487
1579
|
|
1488
|
-
|
1580
|
+
//#endregion
|
1581
|
+
//#region src/utils/is-in-code-block.ts
|
1489
1582
|
function isCodeBlockType(type) {
|
1490
|
-
|
1583
|
+
return !!(type.spec.code && type.isBlock);
|
1491
1584
|
}
|
1585
|
+
/**
|
1586
|
+
* Check if the selection is in a code block.
|
1587
|
+
*
|
1588
|
+
* @internal
|
1589
|
+
*/
|
1492
1590
|
function isInCodeBlock(selection) {
|
1493
|
-
|
1591
|
+
return isCodeBlockType(selection.$from.parent.type) || isCodeBlockType(selection.$to.parent.type);
|
1494
1592
|
}
|
1495
1593
|
|
1496
|
-
|
1594
|
+
//#endregion
|
1595
|
+
//#region src/utils/maybe-run.ts
|
1596
|
+
/**
|
1597
|
+
* @internal
|
1598
|
+
*/
|
1497
1599
|
function maybeRun(value, ...args) {
|
1498
|
-
|
1600
|
+
return typeof value === "function" ? value(...args) : value;
|
1499
1601
|
}
|
1500
1602
|
|
1501
|
-
|
1502
|
-
|
1603
|
+
//#endregion
|
1604
|
+
//#region src/utils/unicode.ts
|
1605
|
+
/**
|
1606
|
+
* @internal
|
1607
|
+
*/
|
1608
|
+
const OBJECT_REPLACEMENT_CHARACTER = "";
|
1503
1609
|
|
1504
|
-
|
1610
|
+
//#endregion
|
1611
|
+
//#region src/utils/with-skip-code-block.ts
|
1612
|
+
/**
|
1613
|
+
* @internal
|
1614
|
+
*/
|
1505
1615
|
function withSkipCodeBlock(command) {
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
export {
|
1514
|
-
Editor,
|
1515
|
-
EditorNotFoundError,
|
1516
|
-
OBJECT_REPLACEMENT_CHARACTER,
|
1517
|
-
Priority,
|
1518
|
-
ProseKitError,
|
1519
|
-
getId as _getId,
|
1520
|
-
addMark,
|
1521
|
-
assert,
|
1522
|
-
canUseRegexLookbehind,
|
1523
|
-
clsx,
|
1524
|
-
collectChildren,
|
1525
|
-
collectNodes,
|
1526
|
-
containsInlineNode,
|
1527
|
-
createEditor,
|
1528
|
-
defaultBlockAt,
|
1529
|
-
defineBaseCommands,
|
1530
|
-
defineBaseKeymap,
|
1531
|
-
defineClickHandler,
|
1532
|
-
defineClickOnHandler,
|
1533
|
-
defineClipboardSerializer,
|
1534
|
-
defineCommands,
|
1535
|
-
defineDOMEventHandler,
|
1536
|
-
defineDefaultState,
|
1537
|
-
defineDoc,
|
1538
|
-
defineDocChangeHandler,
|
1539
|
-
defineDoubleClickHandler,
|
1540
|
-
defineDoubleClickOnHandler,
|
1541
|
-
defineDropHandler,
|
1542
|
-
defineFacet,
|
1543
|
-
defineFacetPayload,
|
1544
|
-
defineFocusChangeHandler,
|
1545
|
-
defineHistory,
|
1546
|
-
defineKeyDownHandler,
|
1547
|
-
defineKeyPressHandler,
|
1548
|
-
defineKeymap,
|
1549
|
-
defineMarkAttr,
|
1550
|
-
defineMarkSpec,
|
1551
|
-
defineMarkView,
|
1552
|
-
defineMarkViewComponent,
|
1553
|
-
defineMarkViewFactory,
|
1554
|
-
defineMountHandler,
|
1555
|
-
defineNodeAttr,
|
1556
|
-
defineNodeSpec,
|
1557
|
-
defineNodeView,
|
1558
|
-
defineNodeViewComponent,
|
1559
|
-
defineNodeViewFactory,
|
1560
|
-
defineParagraph,
|
1561
|
-
definePasteHandler,
|
1562
|
-
definePlugin,
|
1563
|
-
defineScrollToSelectionHandler,
|
1564
|
-
defineText,
|
1565
|
-
defineTextInputHandler,
|
1566
|
-
defineTripleClickHandler,
|
1567
|
-
defineTripleClickOnHandler,
|
1568
|
-
defineUnmountHandler,
|
1569
|
-
defineUpdateHandler,
|
1570
|
-
editorEventFacet,
|
1571
|
-
elementFromJSON,
|
1572
|
-
elementFromNode,
|
1573
|
-
expandMark,
|
1574
|
-
findParentNode,
|
1575
|
-
findParentNodeOfType,
|
1576
|
-
getMarkType,
|
1577
|
-
getNodeType,
|
1578
|
-
htmlFromJSON,
|
1579
|
-
htmlFromNode,
|
1580
|
-
insertDefaultBlock,
|
1581
|
-
insertNode,
|
1582
|
-
isAllSelection,
|
1583
|
-
isApple,
|
1584
|
-
isAtBlockStart,
|
1585
|
-
isElement,
|
1586
|
-
isFragment,
|
1587
|
-
isInCodeBlock,
|
1588
|
-
isMark,
|
1589
|
-
isMarkAbsent,
|
1590
|
-
isMarkActive,
|
1591
|
-
isNodeSelection,
|
1592
|
-
isProseMirrorNode,
|
1593
|
-
isSelection,
|
1594
|
-
isSlice,
|
1595
|
-
isTextSelection,
|
1596
|
-
jsonFromHTML,
|
1597
|
-
jsonFromNode,
|
1598
|
-
jsonFromState,
|
1599
|
-
keymapFacet,
|
1600
|
-
maybeRun,
|
1601
|
-
nodeFromElement,
|
1602
|
-
nodeFromHTML,
|
1603
|
-
nodeFromJSON,
|
1604
|
-
pluginFacet,
|
1605
|
-
removeMark,
|
1606
|
-
removeNode,
|
1607
|
-
setBlockType,
|
1608
|
-
setNodeAttrs,
|
1609
|
-
setSelectionAround,
|
1610
|
-
stateFromJSON,
|
1611
|
-
toggleMark,
|
1612
|
-
toggleNode,
|
1613
|
-
toggleWrap,
|
1614
|
-
union,
|
1615
|
-
unsetBlockType,
|
1616
|
-
unsetMark,
|
1617
|
-
withPriority,
|
1618
|
-
withSkipCodeBlock,
|
1619
|
-
wrap
|
1620
|
-
};
|
1616
|
+
return (state, dispatch, view) => {
|
1617
|
+
if (isInCodeBlock(state.selection)) return false;
|
1618
|
+
return command(state, dispatch, view);
|
1619
|
+
};
|
1620
|
+
}
|
1621
|
+
|
1622
|
+
//#endregion
|
1623
|
+
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 };
|