@prosekit/core 0.8.7 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/dist/{editor-M9OimMiI.d.ts → editor-BULC1zqX.d.ts} +31 -71
  2. package/dist/editor-BULC1zqX.d.ts.map +1 -0
  3. package/dist/{editor-B0L9BgMi.js → editor-g-Rqn-ZE.js} +119 -136
  4. package/dist/editor-g-Rqn-ZE.js.map +1 -0
  5. package/dist/prosekit-core-test.d.ts +1 -2
  6. package/dist/prosekit-core-test.d.ts.map +1 -1
  7. package/dist/prosekit-core-test.js +1 -1
  8. package/dist/prosekit-core-test.js.map +1 -1
  9. package/dist/prosekit-core.d.ts +182 -202
  10. package/dist/prosekit-core.d.ts.map +1 -1
  11. package/dist/prosekit-core.js +543 -549
  12. package/dist/prosekit-core.js.map +1 -1
  13. package/package.json +9 -12
  14. package/src/commands/add-mark.ts +1 -4
  15. package/src/commands/expand-mark.ts +2 -9
  16. package/src/commands/insert-default-block.spec.ts +1 -5
  17. package/src/commands/insert-default-block.ts +1 -4
  18. package/src/commands/insert-node.ts +4 -8
  19. package/src/commands/remove-mark.ts +1 -4
  20. package/src/commands/remove-node.ts +2 -2
  21. package/src/commands/select-all.ts +3 -8
  22. package/src/commands/select-block.spec.ts +81 -0
  23. package/src/commands/select-block.ts +56 -0
  24. package/src/commands/set-block-type.ts +1 -4
  25. package/src/commands/set-node-attrs-between.spec.ts +221 -0
  26. package/src/commands/set-node-attrs-between.ts +77 -0
  27. package/src/commands/set-node-attrs.spec.ts +129 -0
  28. package/src/commands/set-node-attrs.ts +25 -26
  29. package/src/commands/toggle-mark.ts +1 -4
  30. package/src/commands/toggle-node.ts +2 -5
  31. package/src/commands/toggle-wrap.spec.ts +1 -5
  32. package/src/commands/toggle-wrap.ts +1 -4
  33. package/src/commands/unset-block-type.spec.ts +1 -5
  34. package/src/commands/unset-block-type.ts +2 -8
  35. package/src/commands/unset-mark.spec.ts +1 -5
  36. package/src/commands/wrap.ts +2 -10
  37. package/src/editor/action.spec.ts +2 -6
  38. package/src/editor/action.ts +2 -19
  39. package/src/editor/editor.spec.ts +2 -9
  40. package/src/editor/editor.ts +31 -77
  41. package/src/editor/union.spec.ts +1 -5
  42. package/src/editor/union.ts +1 -4
  43. package/src/extensions/clipboard-serializer.ts +4 -16
  44. package/src/extensions/command.ts +20 -48
  45. package/src/extensions/default-state.spec.ts +1 -9
  46. package/src/extensions/default-state.ts +6 -32
  47. package/src/extensions/events/dom-event.spec.ts +1 -6
  48. package/src/extensions/events/dom-event.ts +5 -20
  49. package/src/extensions/events/editor-event.ts +5 -17
  50. package/src/extensions/events/focus.spec.ts +1 -6
  51. package/src/extensions/events/plugin-view.ts +2 -9
  52. package/src/extensions/history.ts +3 -10
  53. package/src/extensions/keymap-base.spec.ts +89 -0
  54. package/src/extensions/keymap-base.ts +34 -13
  55. package/src/extensions/keymap.spec.ts +12 -22
  56. package/src/extensions/keymap.ts +16 -69
  57. package/src/extensions/mark-spec.spec.ts +5 -20
  58. package/src/extensions/mark-spec.ts +33 -41
  59. package/src/extensions/mark-view-effect.ts +3 -9
  60. package/src/extensions/mark-view.ts +2 -8
  61. package/src/extensions/node-spec.spec.ts +5 -21
  62. package/src/extensions/node-spec.ts +31 -33
  63. package/src/extensions/node-view-effect.ts +3 -9
  64. package/src/extensions/node-view.ts +2 -8
  65. package/src/extensions/plugin.spec.ts +3 -16
  66. package/src/extensions/plugin.ts +4 -14
  67. package/src/facets/base-extension.ts +1 -4
  68. package/src/facets/command.ts +10 -10
  69. package/src/facets/facet-extension.spec.ts +4 -15
  70. package/src/facets/facet-node.spec.ts +2 -9
  71. package/src/facets/facet-node.ts +4 -9
  72. package/src/facets/facet.spec.ts +1 -4
  73. package/src/facets/schema-spec.ts +2 -9
  74. package/src/facets/schema.ts +3 -12
  75. package/src/facets/state.spec.ts +8 -15
  76. package/src/facets/state.ts +5 -20
  77. package/src/facets/union-extension.ts +2 -8
  78. package/src/index.ts +42 -188
  79. package/src/test/index.ts +1 -4
  80. package/src/test/test-builder.ts +1 -4
  81. package/src/test/test-editor.spec.ts +1 -5
  82. package/src/test/test-editor.ts +5 -24
  83. package/src/testing/index.ts +18 -14
  84. package/src/types/extension-command.ts +0 -7
  85. package/src/types/extension.spec.ts +1 -4
  86. package/src/types/extension.ts +3 -29
  87. package/src/types/simplify-union.ts +1 -4
  88. package/src/utils/array-grouping.spec.ts +2 -15
  89. package/src/utils/array-grouping.ts +1 -14
  90. package/src/utils/array.ts +0 -4
  91. package/src/utils/attrs-match.ts +1 -5
  92. package/src/utils/clsx.spec.ts +1 -4
  93. package/src/utils/combine-event-handlers.spec.ts +1 -5
  94. package/src/utils/combine-event-handlers.ts +4 -6
  95. package/src/utils/default-block-at.ts +1 -4
  96. package/src/utils/editor-content.spec.ts +1 -4
  97. package/src/utils/editor-content.ts +7 -19
  98. package/src/utils/find-node.ts +65 -0
  99. package/src/utils/find-parent-node-of-type.ts +6 -12
  100. package/src/utils/find-parent-node.spec.ts +1 -5
  101. package/src/utils/find-parent-node.ts +1 -4
  102. package/src/utils/get-custom-selection.ts +1 -5
  103. package/src/utils/get-mark-type.ts +1 -4
  104. package/src/utils/get-node-type.ts +1 -4
  105. package/src/utils/get-node-types.ts +1 -4
  106. package/src/utils/includes-mark.ts +1 -5
  107. package/src/utils/is-at-block-start.ts +1 -4
  108. package/src/utils/is-mark-absent.spec.ts +1 -4
  109. package/src/utils/is-mark-absent.ts +1 -5
  110. package/src/utils/is-mark-active.ts +1 -4
  111. package/src/utils/is-node-active.spec.ts +109 -0
  112. package/src/utils/is-node-active.ts +17 -8
  113. package/src/utils/is-subset.spec.ts +1 -4
  114. package/src/utils/maybe-run.spec.ts +1 -5
  115. package/src/utils/merge-objects.spec.ts +1 -4
  116. package/src/utils/merge-objects.ts +2 -1
  117. package/src/utils/merge-specs.ts +1 -4
  118. package/src/utils/object-equal.spec.ts +1 -4
  119. package/src/utils/output-spec.test.ts +1 -5
  120. package/src/utils/output-spec.ts +12 -9
  121. package/src/utils/parse.spec.ts +2 -11
  122. package/src/utils/parse.ts +12 -24
  123. package/src/utils/remove-undefined-values.spec.ts +1 -4
  124. package/src/utils/set-selection-around.ts +1 -4
  125. package/src/utils/type-assertion.ts +2 -21
  126. package/src/utils/unicode.spec.ts +1 -4
  127. package/dist/editor-B0L9BgMi.js.map +0 -1
  128. package/dist/editor-M9OimMiI.d.ts.map +0 -1
  129. package/src/extensions/doc.ts +0 -31
  130. package/src/extensions/paragraph.ts +0 -61
  131. package/src/extensions/text.ts +0 -34
  132. package/src/types/base-node-view-options.ts +0 -33
  133. package/src/types/object-entries.ts +0 -13
  134. package/src/utils/collect-children.ts +0 -21
  135. package/src/utils/collect-nodes.ts +0 -37
  136. package/src/utils/deep-equals.spec.ts +0 -26
  137. package/src/utils/deep-equals.ts +0 -29
  138. package/src/utils/get-id.spec.ts +0 -14
  139. package/src/utils/get-id.ts +0 -13
@@ -1,15 +1,13 @@
1
- import { A as isSelection, B as assert, C as defineFacetPayload, D as isNodeSelection, E as isMark, F as rootFacet, H as EditorNotFoundError, I as defineFacet, L as Priority, M as isTextSelection, N as toReversed, O as isNotNullish, P as schemaFacet, R as isNodeActive, S as stateFacet, T as isFragment, U as ProseKitError, V as getMarkType, _ as jsonFromState, a as union, b as nodeFromJSON, c as isMarkActive, d as elementFromJSON, f as elementFromNode, g as jsonFromNode, h as jsonFromHTML, j as isSlice, k as isProseMirrorNode, l as isMarkAbsent, m as htmlFromNode, p as htmlFromJSON, r as createEditor, t as Editor, u as defineDefaultState, v as nodeFromElement, w as isAllSelection, x as stateFromJSON, y as nodeFromHTML, z as getNodeType } from "./editor-B0L9BgMi.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";
1
+ import { A as isFragment, B as EditorNotFoundError, C as defineFacetPayload, D as Priority, E as defineFacet, F as isSlice, I as isTextSelection, L as getNodeType, M as isNodeSelection, N as isProseMirrorNode, O as isNodeActive, P as isSelection, R as assert, S as stateFacet, T as rootFacet, V as ProseKitError, _ as jsonFromState, a as union, b as nodeFromJSON, c as isMarkActive, d as elementFromJSON, f as elementFromNode, g as jsonFromNode, h as jsonFromHTML, j as isMark, k as isAllSelection, l as isMarkAbsent, m as htmlFromNode, p as htmlFromJSON, r as createEditor, t as Editor, u as defineDefaultState, v as nodeFromElement, w as schemaFacet, x as stateFromJSON, y as nodeFromHTML, z as getMarkType } from "./editor-g-Rqn-ZE.js";
2
+ import { Plugin, PluginKey, ProseMirrorPlugin, TextSelection } from "@prosekit/pm/state";
3
+ import { DOMSerializer, Fragment, Slice } from "@prosekit/pm/model";
4
+ import { ReplaceAroundStep, dropPoint, findWrapping } from "@prosekit/pm/transform";
5
+ import { baseKeymap, chainCommands, createParagraphNear, deleteSelection, joinTextblockBackward, lift, liftEmptyBlock, newlineInCode, selectAll as selectAll$1, selectNodeBackward, setBlockType as setBlockType$1, toggleMark as toggleMark$1 } from "@prosekit/pm/commands";
6
+ import { isElementLike, isNotNullish, mapGroupBy, once } from "@ocavue/utils";
10
7
  import { history, redo, undo } from "@prosekit/pm/history";
11
8
  import { keydownHandler } from "@prosekit/pm/keymap";
12
9
  import { splitSplittableBlock } from "prosemirror-splittable";
10
+ import OrderedMap from "orderedmap";
13
11
  import clsxLite from "clsx/lite";
14
12
 
15
13
  //#region src/commands/add-mark.ts
@@ -131,7 +129,7 @@ function insertNode(options) {
131
129
  return (state, dispatch) => {
132
130
  const node = options.node ? options.node : options.type ? getNodeType(state.schema, options.type).createAndFill(options.attrs) : null;
133
131
  assert(node, "You must provide either a node or a type");
134
- const insertPos = insertPoint(state.doc, options.pos ?? state.selection.anchor, node.type);
132
+ const insertPos = dropPoint(state.doc, options.pos ?? state.selection.anchor, new Slice(Fragment.from([node]), 0, 0));
135
133
  if (insertPos == null) return false;
136
134
  if (dispatch) {
137
135
  const tr = state.tr.insert(insertPos, node);
@@ -180,6 +178,16 @@ function findParentNode(predicate, $pos) {
180
178
  }
181
179
  }
182
180
 
181
+ //#endregion
182
+ //#region src/utils/get-node-types.ts
183
+ /**
184
+ * @internal
185
+ */
186
+ function getNodeTypes(schema, types) {
187
+ if (Array.isArray(types)) return types.map((type) => getNodeType(schema, type));
188
+ return [getNodeType(schema, types)];
189
+ }
190
+
183
191
  //#endregion
184
192
  //#region src/utils/find-parent-node-of-type.ts
185
193
  /**
@@ -188,8 +196,8 @@ function findParentNode(predicate, $pos) {
188
196
  * @public
189
197
  */
190
198
  function findParentNodeOfType(type, $pos) {
191
- const nodeType = getNodeType($pos.doc.type.schema, type);
192
- return findParentNode((node) => node.type === nodeType, $pos);
199
+ const nodeTypes = getNodeTypes($pos.doc.type.schema, type);
200
+ return findParentNode((node) => nodeTypes.includes(node.type), $pos);
193
201
  }
194
202
 
195
203
  //#endregion
@@ -210,6 +218,56 @@ function removeNode(options) {
210
218
  };
211
219
  }
212
220
 
221
+ //#endregion
222
+ //#region src/commands/select-all.ts
223
+ /**
224
+ * Returns a command that selects the whole document.
225
+ *
226
+ * @public
227
+ */
228
+ function selectAll() {
229
+ return selectAll$1;
230
+ }
231
+
232
+ //#endregion
233
+ //#region src/commands/select-block.ts
234
+ function getTextblockEndpoint(selection, side) {
235
+ const $pos = side < 0 ? selection.$from : selection.$to;
236
+ let depth = $pos.depth;
237
+ while ($pos.node(depth).isInline) {
238
+ if (!depth) return;
239
+ depth--;
240
+ }
241
+ if (!$pos.node(depth).isTextblock) return;
242
+ return side < 0 ? $pos.start(depth) : $pos.end(depth);
243
+ }
244
+ /**
245
+ * @internal
246
+ */
247
+ const selectBlockCommand = (state, dispatch) => {
248
+ const { selection } = state;
249
+ if (!isTextSelection(selection)) return false;
250
+ const expectedFrom = getTextblockEndpoint(selection, -1);
251
+ const expectedTo = getTextblockEndpoint(selection, 1);
252
+ if (expectedFrom == null || expectedTo == null) return false;
253
+ if (selection.from <= expectedFrom && selection.to >= expectedTo) return false;
254
+ if (dispatch) {
255
+ const newSelection = TextSelection.create(state.doc, expectedFrom, expectedTo);
256
+ dispatch(state.tr.setSelection(newSelection));
257
+ }
258
+ return true;
259
+ };
260
+ /**
261
+ * Returns a command to expand the text selection to cover the current block
262
+ * node. If the text selection spans multiple blocks, it will select all
263
+ * blocks in the selection.
264
+ *
265
+ * @public
266
+ */
267
+ function selectBlock() {
268
+ return selectBlockCommand;
269
+ }
270
+
213
271
  //#endregion
214
272
  //#region src/utils/get-custom-selection.ts
215
273
  function getCustomSelection(state, from, to) {
@@ -262,36 +320,65 @@ function setBlockType(options) {
262
320
  }
263
321
 
264
322
  //#endregion
265
- //#region src/utils/get-node-types.ts
323
+ //#region src/commands/set-node-attrs.ts
266
324
  /**
267
- * @internal
325
+ * Returns a command that sets the attributes of the current node.
326
+ *
327
+ * @param options
328
+ *
329
+ * @public
268
330
  */
269
- function getNodeTypes(schema, types) {
270
- if (Array.isArray(types)) return types.map((type) => getNodeType(schema, type));
271
- return [getNodeType(schema, types)];
331
+ function setNodeAttrs({ type, attrs, pos }) {
332
+ return (state, dispatch) => {
333
+ let updatePos;
334
+ if (pos == null) {
335
+ const found = findParentNodeOfType(type, state.selection.$anchor);
336
+ if (!found) return false;
337
+ updatePos = found.pos;
338
+ } else {
339
+ const found = state.doc.nodeAt(pos);
340
+ if (!found) return false;
341
+ if (!getNodeTypes(state.schema, type).includes(found.type)) return false;
342
+ updatePos = pos;
343
+ }
344
+ if (dispatch) {
345
+ const { tr } = state;
346
+ for (const [key, value] of Object.entries(attrs)) tr.setNodeAttribute(updatePos, key, value);
347
+ dispatch(tr);
348
+ }
349
+ return true;
350
+ };
272
351
  }
273
352
 
274
353
  //#endregion
275
- //#region src/commands/set-node-attrs.ts
354
+ //#region src/commands/set-node-attrs-between.ts
276
355
  /**
277
- * Returns a command that set the attributes of the current node.
356
+ * Returns a command that sets the attributes of all matching nodes between the
357
+ * `from` and `to` positions.
358
+ *
359
+ * @param options
278
360
  *
279
361
  * @public
280
362
  */
281
- function setNodeAttrs(options) {
363
+ function setNodeAttrsBetween(options) {
282
364
  return (state, dispatch) => {
365
+ const from = options.from ?? state.selection.from;
366
+ const to = options.to ?? state.selection.to;
367
+ if (from > to) return false;
283
368
  const nodeTypes = getNodeTypes(state.schema, options.type);
284
- const from = options.pos ?? state.selection.from;
285
- const to = options.pos ?? state.selection.to;
286
369
  const positions = [];
370
+ let found = false;
287
371
  state.doc.nodesBetween(from, to, (node, pos) => {
288
- if (nodeTypes.includes(node.type)) positions.push(pos);
289
- if (!dispatch && positions.length > 0) return false;
372
+ if (nodeTypes.includes(node.type)) {
373
+ positions.push(pos);
374
+ found = true;
375
+ }
376
+ if (!dispatch && found) return false;
290
377
  });
291
- if (positions.length === 0) return false;
378
+ if (!found) return false;
292
379
  if (dispatch) {
293
380
  const { tr } = state;
294
- for (const pos of positions) for (const [key, value] of Object.entries(options.attrs)) tr.setNodeAttribute(pos, key, value);
381
+ for (const [key, value] of Object.entries(options.attrs)) for (const pos of positions) tr.setNodeAttribute(pos, key, value);
295
382
  dispatch(tr);
296
383
  }
297
384
  return true;
@@ -319,7 +406,7 @@ function toggleMark({ type, attrs, removeWhenPresent = false, enterInlineAtoms =
319
406
  //#endregion
320
407
  //#region src/commands/toggle-node.ts
321
408
  /**
322
- * Returns a command that set the selected textblocks to the given node type
409
+ * Returns a command that sets the selected textblocks to the given node type
323
410
  * with the given attributes.
324
411
  *
325
412
  * @param options
@@ -350,7 +437,7 @@ function wrap(options) {
350
437
  const { $from, $to } = state.selection;
351
438
  const range = $from.blockRange($to);
352
439
  if (!range) return false;
353
- const wrapping = findWrapping(range, getNodeType(state.schema, options.nodeType || options.type), options.attrs);
440
+ const wrapping = findWrapping(range, getNodeType(state.schema, options.type), options.attrs);
354
441
  if (!wrapping) return false;
355
442
  dispatch?.(state.tr.wrap(range, wrapping));
356
443
  return true;
@@ -480,7 +567,7 @@ const pluginFacet = defineFacet({
480
567
  else if (Array.isArray(payload) && payload.every((p) => p instanceof Plugin)) plugins.push(...payload);
481
568
  else if (typeof payload === "function") plugins.push(...[payload({ schema })].flat());
482
569
  else throw new ProseKitError("Invalid plugin");
483
- return { plugins: toReversed(plugins) };
570
+ return { plugins: [...plugins].reverse() };
484
571
  };
485
572
  },
486
573
  parent: stateFacet
@@ -489,7 +576,7 @@ const pluginFacet = defineFacet({
489
576
  //#endregion
490
577
  //#region src/extensions/clipboard-serializer.ts
491
578
  function mergeWrappers(wrappers) {
492
- return (fn) => wrappers.filter(isNotNullish).reduce((fn$1, wrapper) => wrapper(fn$1), fn);
579
+ return (fn) => wrappers.filter(isNotNullish).reduce((fn, wrapper) => wrapper(fn), fn);
493
580
  }
494
581
  function wrapFunction(fn, wrapper) {
495
582
  return wrapper ? wrapper(fn) : fn;
@@ -501,11 +588,11 @@ var CustomDOMSerializer = class extends DOMSerializer {
501
588
  this.serializeNodeWrapper = serializeNodeWrapper;
502
589
  }
503
590
  serializeFragment(...args) {
504
- const fn = (...args$1) => super.serializeFragment(...args$1);
591
+ const fn = (...args) => super.serializeFragment(...args);
505
592
  return wrapFunction(fn, this.serializeFragmentWrapper)(...args);
506
593
  }
507
594
  serializeNode(...args) {
508
- const fn = (...args$1) => super.serializeNode(...args$1);
595
+ const fn = (...args) => super.serializeNode(...args);
509
596
  return wrapFunction(fn, this.serializeNodeWrapper)(...args);
510
597
  }
511
598
  };
@@ -554,25 +641,15 @@ function insertText({ text, from, to }) {
554
641
  };
555
642
  }
556
643
 
557
- //#endregion
558
- //#region src/commands/select-all.ts
559
- /**
560
- * Returns a command that selects the whole document.
561
- *
562
- * @public
563
- */
564
- function selectAll() {
565
- return (state, dispatch) => {
566
- dispatch?.(state.tr.setSelection(new AllSelection(state.doc)));
567
- return true;
568
- };
569
- }
570
-
571
644
  //#endregion
572
645
  //#region src/facets/command.ts
573
646
  const commandFacet = defineFacet({
574
647
  reducer: (inputs) => {
575
- return { commands: Object.assign({}, ...inputs) };
648
+ switch (inputs.length) {
649
+ case 0: return { commands: {} };
650
+ case 1: return { commands: inputs[0] };
651
+ default: return { commands: Object.assign({}, ...inputs) };
652
+ }
576
653
  },
577
654
  parent: rootFacet,
578
655
  singleton: true
@@ -580,8 +657,8 @@ const commandFacet = defineFacet({
580
657
 
581
658
  //#endregion
582
659
  //#region src/extensions/command.ts
583
- function defineCommands(commands$1) {
584
- return defineFacetPayload(commandFacet, [commands$1]);
660
+ function defineCommands(commands) {
661
+ return defineFacetPayload(commandFacet, [commands]);
585
662
  }
586
663
  /**
587
664
  * Add some base commands
@@ -597,8 +674,10 @@ function defineBaseCommands() {
597
674
  toggleWrap,
598
675
  setBlockType,
599
676
  setNodeAttrs,
677
+ setNodeAttrsBetween,
600
678
  insertDefaultBlock,
601
679
  selectAll,
680
+ selectBlock,
602
681
  addMark,
603
682
  removeMark,
604
683
  unsetBlockType,
@@ -607,37 +686,94 @@ function defineBaseCommands() {
607
686
  }
608
687
 
609
688
  //#endregion
610
- //#region src/facets/schema-spec.ts
611
- const schemaSpecFacet = defineFacet({
612
- reducer: (specs) => {
613
- let nodes = OrderedMap.from({});
614
- let marks = OrderedMap.from({});
615
- let topNode = void 0;
616
- for (const spec of specs) {
617
- nodes = nodes.append(spec.nodes);
618
- marks = marks.append(spec.marks ?? {});
619
- topNode = topNode ?? spec.topNode;
620
- }
621
- return {
622
- nodes,
623
- marks,
624
- topNode
689
+ //#region src/extensions/events/plugin-view.ts
690
+ /**
691
+ * Registers a event handler that is called when the editor view is mounted.
692
+ *
693
+ * @public
694
+ */
695
+ function defineMountHandler(handler) {
696
+ return definePluginViewFacetPayload(["mount", handler]);
697
+ }
698
+ /**
699
+ * Registers a event handler that is called when the editor state is updated.
700
+ *
701
+ * @public
702
+ */
703
+ function defineUpdateHandler(handler) {
704
+ return definePluginViewFacetPayload(["update", handler]);
705
+ }
706
+ /**
707
+ * Registers a event handler that is called when the editor view is unmounted.
708
+ *
709
+ * @public
710
+ */
711
+ function defineUnmountHandler(handler) {
712
+ return definePluginViewFacetPayload(["unmount", handler]);
713
+ }
714
+ function definePluginViewFacetPayload(input) {
715
+ return defineFacetPayload(pluginViewFacet, [input]);
716
+ }
717
+ const pluginViewFacet = defineFacet({
718
+ reduce: () => {
719
+ let mountHandlers = [];
720
+ let updateHandlers = [];
721
+ let unmountHandlers = [];
722
+ const plugin = new ProseMirrorPlugin({
723
+ key: pluginKey,
724
+ view: (view) => {
725
+ mountHandlers.forEach((fn) => fn(view));
726
+ return {
727
+ update: (view, prevState) => {
728
+ updateHandlers.forEach((fn) => fn(view, prevState));
729
+ },
730
+ destroy: () => {
731
+ unmountHandlers.forEach((fn) => fn());
732
+ }
733
+ };
734
+ }
735
+ });
736
+ const register = (input) => {
737
+ mountHandlers = [];
738
+ updateHandlers = [];
739
+ unmountHandlers = [];
740
+ for (const args of input) switch (args[0]) {
741
+ case "mount":
742
+ mountHandlers.push(args[1]);
743
+ break;
744
+ case "update":
745
+ updateHandlers.push(args[1]);
746
+ break;
747
+ case "unmount":
748
+ unmountHandlers.push(args[1]);
749
+ break;
750
+ }
751
+ };
752
+ return function reducer(input) {
753
+ register(input);
754
+ return plugin;
625
755
  };
626
756
  },
627
- parent: schemaFacet,
757
+ parent: pluginFacet,
628
758
  singleton: true
629
759
  });
760
+ const pluginKey = new PluginKey("prosekit-plugin-view-handler");
630
761
 
631
762
  //#endregion
632
- //#region src/utils/array-grouping.ts
633
- function groupBy(items, keySelector) {
634
- const result = {};
635
- for (const item of items) {
636
- const key = keySelector(item);
637
- (result[key] ||= []).push(item);
638
- }
639
- return result;
763
+ //#region src/extensions/events/doc-change.ts
764
+ /**
765
+ * Registers a event handler that is called when the editor document is changed.
766
+ *
767
+ * @public
768
+ */
769
+ function defineDocChangeHandler(handler) {
770
+ return defineUpdateHandler((view, prevState) => {
771
+ if (!view.state.doc.eq(prevState.doc)) handler(view, prevState);
772
+ });
640
773
  }
774
+
775
+ //#endregion
776
+ //#region src/utils/array-grouping.ts
641
777
  function groupEntries(entries) {
642
778
  const result = {};
643
779
  for (const [key, value] of entries) (result[key] ||= []).push(value);
@@ -645,329 +781,66 @@ function groupEntries(entries) {
645
781
  }
646
782
 
647
783
  //#endregion
648
- //#region src/utils/remove-undefined-values.ts
649
- function removeUndefinedValues(obj) {
650
- const result = {};
651
- for (const [key, value] of Object.entries(obj)) if (value !== void 0) result[key] = value;
652
- return result;
784
+ //#region src/utils/combine-event-handlers.ts
785
+ function combineEventHandlers() {
786
+ let handlers = [];
787
+ function setHandlers(eventHandlers) {
788
+ handlers = eventHandlers;
789
+ }
790
+ function combinedEventHandler(...args) {
791
+ for (let i = handlers.length - 1; i >= 0; i--) if (handlers[i](...args)) return true;
792
+ return false;
793
+ }
794
+ return [setHandlers, combinedEventHandler];
653
795
  }
654
796
 
655
797
  //#endregion
656
- //#region src/utils/merge-objects.ts
657
- function mergeObjects(...objects) {
658
- const filteredObjects = objects.filter(isNotNullish).map(removeUndefinedValues);
659
- return Object.assign({}, ...filteredObjects);
798
+ //#region src/extensions/events/dom-event.ts
799
+ /**
800
+ * @internal
801
+ */
802
+ function defineDomEventFacetPayload(...payloads) {
803
+ return defineFacetPayload(domEventFacet, payloads);
660
804
  }
661
-
662
- //#endregion
663
- //#region src/utils/merge-specs.ts
664
- function mergeSpecs(a, b) {
665
- const attrs = {};
666
- const attrNames = new Set([...Object.keys(a.attrs ?? {}), ...Object.keys(b.attrs ?? {})]);
667
- for (const name of attrNames) {
668
- const attrSpecA = a.attrs?.[name];
669
- const attrSpecB = b.attrs?.[name];
670
- const attrSpecMerged = mergeObjects(attrSpecA, attrSpecB);
671
- if (attrSpecMerged) attrs[name] = attrSpecMerged;
672
- }
673
- return mergeObjects(a, b, {
674
- attrs,
675
- parseDOM: [...a.parseDOM ?? [], ...b.parseDOM ?? []]
676
- });
805
+ /**
806
+ * Register a new event handler for the given event type.
807
+ *
808
+ * @public
809
+ */
810
+ function defineDOMEventHandler(event, handler) {
811
+ return defineDomEventFacetPayload([event, handler]);
677
812
  }
678
-
679
- //#endregion
680
- //#region src/utils/output-spec.ts
681
- function wrapOutputSpecAttrs(toDOM, options) {
682
- return (node, ...args) => {
683
- return insertOutputSpecAttrs(toDOM(node, ...args), options.map((option) => option.toDOM?.(node.attrs[option.attr])).filter(isNotNullish));
684
- };
685
- }
686
- function wrapTagParseRuleAttrs(rule, options) {
687
- const existingGetAttrs = rule.getAttrs;
688
- const existingAttrs = rule.attrs;
689
- return {
690
- ...rule,
691
- getAttrs: (dom) => {
692
- const baseAttrs = existingGetAttrs?.(dom) ?? existingAttrs ?? {};
693
- if (baseAttrs === false || !dom || !isElementLike(dom)) return baseAttrs ?? null;
694
- const insertedAttrs = {};
695
- for (const option of options) if (option.parseDOM) insertedAttrs[option.attr] = option.parseDOM(dom);
696
- return {
697
- ...baseAttrs,
698
- ...insertedAttrs
699
- };
700
- }
701
- };
702
- }
703
- function insertOutputSpecAttrs(dom, attrs) {
704
- if (!dom) return dom;
705
- if (Array.isArray(dom)) {
706
- const rest = dom.slice(1);
707
- let oldAttrs;
708
- if (rest.length > 0 && (rest[0] == null || typeof rest[0] === "object")) oldAttrs = rest.shift();
709
- else oldAttrs = {};
710
- const newAttrs = setObjectAttributes(oldAttrs, attrs);
711
- return [
712
- dom[0],
713
- newAttrs,
714
- ...rest
715
- ];
716
- }
717
- if (isElementLike(dom)) return setElementAttributes(dom, attrs);
718
- if (typeof dom === "object" && "dom" in dom && isElementLike(dom.dom)) return {
719
- ...dom,
720
- dom: setElementAttributes(dom.dom, attrs)
721
- };
722
- return dom;
723
- }
724
- function setObjectAttributes(obj, attrs) {
725
- obj = { ...obj };
726
- for (const [key, value] of attrs) {
727
- const oldValue = obj[key];
728
- obj[key] = key === "style" ? joinStyles(value, typeof oldValue === "string" ? oldValue : "") : value;
729
- }
730
- return obj;
731
- }
732
- function setElementAttributes(element, attrs) {
733
- element = element.cloneNode(true);
734
- for (const [key, value] of attrs) {
735
- const oldValue = element.getAttribute(key);
736
- const newValue = key === "style" ? joinStyles(value, typeof oldValue === "string" ? oldValue : "") : value;
737
- element.setAttribute(key, newValue);
738
- }
739
- return element;
740
- }
741
- function joinStyles(...styles) {
742
- return styles.map((style) => style.trim().replace(/;$/, "")).filter(Boolean).join("; ");
743
- }
744
-
745
- //#endregion
746
- //#region src/extensions/node-spec.ts
747
- /**
748
- * Defines a node type.
749
- *
750
- * @public
751
- */
752
- function defineNodeSpec(options) {
753
- return defineFacetPayload(nodeSpecFacet, [[options, void 0]]);
754
- }
755
- /**
756
- * Defines an attribute for a node type.
757
- *
758
- * @public
759
- */
760
- function defineNodeAttr(options) {
761
- return defineFacetPayload(nodeSpecFacet, [[void 0, options]]);
762
- }
763
- const nodeSpecFacet = defineFacet({
764
- reducer: (payloads) => {
765
- let specs = OrderedMap.from({});
766
- let topNodeName = void 0;
767
- const specPayloads = payloads.map((input) => input[0]).filter(isNotNullish);
768
- const attrPayloads = payloads.map((input) => input[1]).filter(isNotNullish);
769
- for (const { name, topNode, ...spec } of specPayloads) {
770
- if (topNode) topNodeName = name;
771
- const prevSpec = specs.get(name);
772
- if (prevSpec) specs = specs.update(name, mergeSpecs(prevSpec, spec));
773
- else specs = specs.addToStart(name, spec);
774
- }
775
- const groupedAttrs = groupBy(attrPayloads, (payload) => payload.type);
776
- for (const [type, attrs] of Object.entries(groupedAttrs)) {
777
- if (!attrs) continue;
778
- const maybeSpec = specs.get(type);
779
- assert(maybeSpec, `Node type ${type} must be defined`);
780
- const spec = clone(maybeSpec);
781
- if (!spec.attrs) spec.attrs = {};
782
- for (const attr of attrs) spec.attrs[attr.attr] = {
783
- default: attr.default,
784
- validate: attr.validate,
785
- splittable: attr.splittable
786
- };
787
- if (spec.toDOM) spec.toDOM = wrapOutputSpecAttrs(spec.toDOM, attrs);
788
- if (spec.parseDOM) spec.parseDOM = spec.parseDOM.map((rule) => wrapTagParseRuleAttrs(rule, attrs));
789
- specs = specs.update(type, spec);
790
- }
791
- return {
792
- nodes: specs,
793
- topNode: topNodeName
794
- };
795
- },
796
- parent: schemaSpecFacet,
797
- singleton: true
798
- });
799
-
800
- //#endregion
801
- //#region src/extensions/doc.ts
802
- /**
803
- * @public
804
- *
805
- * @deprecated Use the following import instead:
806
- *
807
- * ```ts
808
- * import { defineDoc } from 'prosekit/extensions/doc'
809
- * ```
810
- */
811
- function defineDoc() {
812
- console.warn("[prosekit] The `defineDoc` function from `prosekit/core` is deprecated. Use the following import instead: `import { defineDoc } from \"prosekit/extensions/doc\"`.");
813
- return defineNodeSpec({
814
- name: "doc",
815
- content: "block+",
816
- topNode: true
817
- });
818
- }
819
-
820
- //#endregion
821
- //#region src/extensions/events/plugin-view.ts
822
- /**
823
- * Registers a event handler that is called when the editor view is mounted.
824
- *
825
- * @public
826
- */
827
- function defineMountHandler(handler) {
828
- return definePluginViewFacetPayload(["mount", handler]);
829
- }
830
- /**
831
- * Registers a event handler that is called when the editor state is updated.
832
- *
833
- * @public
834
- */
835
- function defineUpdateHandler(handler) {
836
- return definePluginViewFacetPayload(["update", handler]);
837
- }
838
- /**
839
- * Registers a event handler that is called when the editor view is unmounted.
840
- *
841
- * @public
842
- */
843
- function defineUnmountHandler(handler) {
844
- return definePluginViewFacetPayload(["unmount", handler]);
845
- }
846
- function definePluginViewFacetPayload(input) {
847
- return defineFacetPayload(pluginViewFacet, [input]);
848
- }
849
- const pluginViewFacet = defineFacet({
850
- reduce: () => {
851
- let mountHandlers = [];
852
- let updateHandlers = [];
853
- let unmountHandlers = [];
854
- const plugin = new ProseMirrorPlugin({
855
- key: pluginKey,
856
- view: (view) => {
857
- mountHandlers.forEach((fn) => fn(view));
858
- return {
859
- update: (view$1, prevState) => {
860
- updateHandlers.forEach((fn) => fn(view$1, prevState));
861
- },
862
- destroy: () => {
863
- unmountHandlers.forEach((fn) => fn());
864
- }
865
- };
866
- }
867
- });
868
- const register = (input) => {
869
- mountHandlers = [];
870
- updateHandlers = [];
871
- unmountHandlers = [];
872
- for (const args of input) switch (args[0]) {
873
- case "mount":
874
- mountHandlers.push(args[1]);
875
- break;
876
- case "update":
877
- updateHandlers.push(args[1]);
878
- break;
879
- case "unmount":
880
- unmountHandlers.push(args[1]);
881
- break;
882
- }
883
- };
884
- return function reducer(input) {
885
- register(input);
886
- return plugin;
887
- };
888
- },
889
- parent: pluginFacet,
890
- singleton: true
891
- });
892
- const pluginKey = new PluginKey("prosekit-plugin-view-handler");
893
-
894
- //#endregion
895
- //#region src/extensions/events/doc-change.ts
896
- /**
897
- * Registers a event handler that is called when the editor document is changed.
898
- *
899
- * @public
900
- */
901
- function defineDocChangeHandler(handler) {
902
- return defineUpdateHandler((view, prevState) => {
903
- if (!view.state.doc.eq(prevState.doc)) handler(view, prevState);
904
- });
905
- }
906
-
907
- //#endregion
908
- //#region src/utils/combine-event-handlers.ts
909
- function combineEventHandlers() {
910
- let handlers = [];
911
- function setHandlers(eventHandlers) {
912
- handlers = toReversed(eventHandlers);
913
- }
914
- function combinedEventHandler(...args) {
915
- for (const handler of handlers) if (handler(...args)) return true;
916
- return false;
917
- }
918
- return [setHandlers, combinedEventHandler];
919
- }
920
-
921
- //#endregion
922
- //#region src/extensions/events/dom-event.ts
923
- /**
924
- * @internal
925
- */
926
- function defineDomEventFacetPayload(...payloads) {
927
- return defineFacetPayload(domEventFacet, payloads);
928
- }
929
- /**
930
- * Register a new event handler for the given event type.
931
- *
932
- * @public
933
- */
934
- function defineDOMEventHandler(event, handler) {
935
- return defineDomEventFacetPayload([event, handler]);
936
- }
937
- /**
938
- * @internal
939
- */
940
- const domEventFacet = defineFacet({
941
- reduce: () => {
942
- const setHandlersMap = {};
943
- const combinedHandlerMap = {};
944
- let plugin;
945
- const update = (payloads) => {
946
- let hasNewEvent = false;
947
- for (const [event] of payloads) if (!setHandlersMap[event]) {
948
- hasNewEvent = true;
949
- const [setHandlers, combinedHandler] = combineEventHandlers();
950
- setHandlersMap[event] = setHandlers;
951
- const e = (view, eventObject) => {
952
- return combinedHandler(view, eventObject);
953
- };
954
- combinedHandlerMap[event] = e;
955
- }
956
- const map = groupEntries(payloads);
957
- for (const [event, setHandlers] of Object.entries(setHandlersMap)) setHandlers(map[event] ?? []);
958
- if (hasNewEvent) plugin = new ProseMirrorPlugin({
959
- key: new PluginKey("prosekit-dom-event-handler"),
960
- props: { handleDOMEvents: combinedHandlerMap }
961
- });
962
- };
963
- return function reducer(inputs) {
964
- update(inputs);
965
- return plugin ?? [];
966
- };
967
- },
968
- parent: pluginFacet,
969
- singleton: true
970
- });
813
+ /**
814
+ * @internal
815
+ */
816
+ const domEventFacet = defineFacet({
817
+ reduce: () => {
818
+ const setHandlersMap = {};
819
+ const combinedHandlerMap = {};
820
+ let plugin;
821
+ const update = (payloads) => {
822
+ let hasNewEvent = false;
823
+ for (const [event] of payloads) if (!setHandlersMap[event]) {
824
+ hasNewEvent = true;
825
+ const [setHandlers, combinedHandler] = combineEventHandlers();
826
+ setHandlersMap[event] = setHandlers;
827
+ combinedHandlerMap[event] = combinedHandler;
828
+ }
829
+ const map = groupEntries(payloads);
830
+ for (const [event, setHandlers] of Object.entries(setHandlersMap)) setHandlers(map[event] ?? []);
831
+ if (hasNewEvent) plugin = new ProseMirrorPlugin({
832
+ key: new PluginKey("prosekit-dom-event-handler"),
833
+ props: { handleDOMEvents: combinedHandlerMap }
834
+ });
835
+ };
836
+ return function reducer(inputs) {
837
+ update(inputs);
838
+ return plugin ?? [];
839
+ };
840
+ },
841
+ parent: pluginFacet,
842
+ singleton: true
843
+ });
971
844
 
972
845
  //#endregion
973
846
  //#region src/extensions/events/editor-event.ts
@@ -1161,69 +1034,38 @@ const isApple = typeof navigator !== "undefined" ? /Mac|iP(hone|[ao]d)/.test(nav
1161
1034
  *
1162
1035
  * @public
1163
1036
  */
1164
- function defineKeymap(keymap$1) {
1165
- return defineFacetPayload(keymapFacet, [keymap$1]);
1037
+ function defineKeymap(keymap) {
1038
+ return defineFacetPayload(keymapFacet, [keymap]);
1166
1039
  }
1167
1040
  /**
1168
1041
  * @internal
1169
1042
  */
1170
1043
  const keymapFacet = defineFacet({
1171
1044
  reduce: () => {
1172
- let handler;
1173
- const handlerWrapper = (view, event) => {
1174
- if (handler) return handler(view, event);
1045
+ let subHandlers = [];
1046
+ const rootHandler = (view, event) => {
1047
+ for (const handler of subHandlers) if (handler(view, event)) return true;
1175
1048
  return false;
1176
1049
  };
1177
1050
  const plugin = new Plugin({
1178
1051
  key: keymapPluginKey,
1179
- props: { handleKeyDown: handlerWrapper }
1052
+ props: { handleKeyDown: rootHandler }
1180
1053
  });
1181
1054
  return (keymaps) => {
1182
- handler = keydownHandler(mergeKeymaps(toReversed(keymaps)));
1055
+ subHandlers = keymaps.map(keydownHandler).reverse();
1183
1056
  return plugin;
1184
1057
  };
1185
1058
  },
1186
1059
  parent: pluginFacet,
1187
1060
  singleton: true
1188
1061
  });
1189
- function mergeKeymaps(keymaps) {
1190
- const bindings = {};
1191
- for (const keymap$1 of keymaps) for (const [key, command] of Object.entries(keymap$1)) {
1192
- const normalizedKey = normalizeKeyName(key);
1193
- (bindings[normalizedKey] ||= []).push(command);
1194
- }
1195
- return mapValues(bindings, mergeCommands);
1196
- }
1197
- function mergeCommands(commands$1) {
1198
- return chainCommands(...commands$1);
1199
- }
1200
- function normalizeKeyName(name) {
1201
- let parts = name.split(/-(?!$)/), result = parts[parts.length - 1];
1202
- if (result == "Space") result = " ";
1203
- let alt, ctrl, shift, meta;
1204
- for (let i = 0; i < parts.length - 1; i++) {
1205
- let mod = parts[i];
1206
- if (/^(cmd|meta|m)$/i.test(mod)) meta = true;
1207
- else if (/^a(lt)?$/i.test(mod)) alt = true;
1208
- else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
1209
- else if (/^s(hift)?$/i.test(mod)) shift = true;
1210
- else if (/^mod$/i.test(mod)) if (isApple) meta = true;
1211
- else ctrl = true;
1212
- else throw new Error("Unrecognized modifier name: " + mod);
1213
- }
1214
- if (alt) result = "Alt-" + result;
1215
- if (ctrl) result = "Ctrl-" + result;
1216
- if (meta) result = "Meta-" + result;
1217
- if (shift) result = "Shift-" + result;
1218
- return result;
1219
- }
1220
1062
  const keymapPluginKey = new PluginKey("prosekit-keymap");
1221
1063
 
1222
1064
  //#endregion
1223
1065
  //#region src/extensions/history.ts
1224
1066
  const keymap = {
1225
1067
  "Mod-z": undo,
1226
- "Shift-Mod-z": redo
1068
+ "Mod-Z": redo
1227
1069
  };
1228
1070
  if (!isApple) keymap["Mod-y"] = redo;
1229
1071
  const commands = {
@@ -1248,25 +1090,168 @@ function defineHistory({ depth = 200, newGroupDelay = 250 } = {}) {
1248
1090
  //#region src/extensions/keymap-base.ts
1249
1091
  const customEnter = chainCommands(newlineInCode, createParagraphNear, liftEmptyBlock, splitSplittableBlock);
1250
1092
  const customBackspace = chainCommands(deleteSelection, joinTextblockBackward, selectNodeBackward);
1251
- const customBaseKeymap = {
1252
- ...baseKeymap,
1253
- Enter: customEnter,
1254
- Backspace: customBackspace
1255
- };
1256
1093
  /**
1257
1094
  * Defines some basic key bindings.
1258
1095
  *
1096
+ * @param options
1097
+ *
1259
1098
  * @public
1260
1099
  */
1261
- function defineBaseKeymap(options) {
1262
- const priority = options?.priority ?? Priority.low;
1263
- return withPriority(defineKeymap(customBaseKeymap), priority);
1100
+ function defineBaseKeymap({ priority = Priority.low, preferBlockSelection = true } = {}) {
1101
+ return withPriority(defineKeymap({
1102
+ ...baseKeymap,
1103
+ "Mod-a": preferBlockSelection ? chainCommands(selectBlockCommand, selectAll$1) : selectAll$1,
1104
+ "Enter": customEnter,
1105
+ "Backspace": customBackspace
1106
+ }), priority);
1107
+ }
1108
+
1109
+ //#endregion
1110
+ //#region src/facets/schema-spec.ts
1111
+ const schemaSpecFacet = defineFacet({
1112
+ reducer: (specs) => {
1113
+ let nodes = OrderedMap.from({});
1114
+ let marks = OrderedMap.from({});
1115
+ let topNode = void 0;
1116
+ for (const spec of specs) {
1117
+ nodes = nodes.append(spec.nodes);
1118
+ marks = marks.append(spec.marks ?? {});
1119
+ topNode = topNode ?? spec.topNode;
1120
+ }
1121
+ return {
1122
+ nodes,
1123
+ marks,
1124
+ topNode
1125
+ };
1126
+ },
1127
+ parent: schemaFacet,
1128
+ singleton: true
1129
+ });
1130
+
1131
+ //#endregion
1132
+ //#region src/utils/remove-undefined-values.ts
1133
+ function removeUndefinedValues(obj) {
1134
+ const result = {};
1135
+ for (const [key, value] of Object.entries(obj)) if (value !== void 0) result[key] = value;
1136
+ return result;
1137
+ }
1138
+
1139
+ //#endregion
1140
+ //#region src/utils/merge-objects.ts
1141
+ function mergeObjects(...objects) {
1142
+ const filteredObjects = objects.filter(isNotNullish).map(removeUndefinedValues);
1143
+ return Object.assign({}, ...filteredObjects);
1144
+ }
1145
+
1146
+ //#endregion
1147
+ //#region src/utils/merge-specs.ts
1148
+ function mergeSpecs(a, b) {
1149
+ const attrs = {};
1150
+ const attrNames = new Set([...Object.keys(a.attrs ?? {}), ...Object.keys(b.attrs ?? {})]);
1151
+ for (const name of attrNames) {
1152
+ const attrSpecA = a.attrs?.[name];
1153
+ const attrSpecB = b.attrs?.[name];
1154
+ const attrSpecMerged = mergeObjects(attrSpecA, attrSpecB);
1155
+ if (attrSpecMerged) attrs[name] = attrSpecMerged;
1156
+ }
1157
+ return mergeObjects(a, b, {
1158
+ attrs,
1159
+ parseDOM: [...a.parseDOM ?? [], ...b.parseDOM ?? []]
1160
+ });
1161
+ }
1162
+
1163
+ //#endregion
1164
+ //#region src/utils/output-spec.ts
1165
+ function wrapOutputSpecAttrs(toDOM, options) {
1166
+ return (node, ...args) => {
1167
+ return insertOutputSpecAttrs(toDOM(node, ...args), options.map((option) => option.toDOM?.(node.attrs[option.attr])).filter(isNotNullish));
1168
+ };
1169
+ }
1170
+ function wrapTagParseRuleAttrs(rule, options) {
1171
+ const existingGetAttrs = rule.getAttrs;
1172
+ const existingAttrs = rule.attrs;
1173
+ return {
1174
+ ...rule,
1175
+ getAttrs: (dom) => {
1176
+ const baseAttrs = existingGetAttrs?.(dom) ?? existingAttrs ?? {};
1177
+ if (baseAttrs === false || !dom || !isElementLike(dom)) return baseAttrs ?? null;
1178
+ const insertedAttrs = {};
1179
+ for (const option of options) if (option.parseDOM) insertedAttrs[option.attr] = option.parseDOM(dom);
1180
+ return {
1181
+ ...baseAttrs,
1182
+ ...insertedAttrs
1183
+ };
1184
+ }
1185
+ };
1186
+ }
1187
+ function wrapParseRuleAttrs(rule, attrs) {
1188
+ if (rule.tag) return wrapTagParseRuleAttrs(rule, attrs);
1189
+ return rule;
1190
+ }
1191
+ function insertOutputSpecAttrs(dom, attrs) {
1192
+ if (!dom) return dom;
1193
+ if (Array.isArray(dom)) {
1194
+ const rest = dom.slice(1);
1195
+ let oldAttrs;
1196
+ if (rest.length > 0 && (rest[0] == null || typeof rest[0] === "object")) oldAttrs = rest.shift();
1197
+ else oldAttrs = {};
1198
+ const newAttrs = setObjectAttributes(oldAttrs, attrs);
1199
+ return [
1200
+ dom[0],
1201
+ newAttrs,
1202
+ ...rest
1203
+ ];
1204
+ }
1205
+ if (isElementLike(dom)) return setElementAttributes(dom, attrs);
1206
+ if (typeof dom === "object" && "dom" in dom && isElementLike(dom.dom)) return {
1207
+ ...dom,
1208
+ dom: setElementAttributes(dom.dom, attrs)
1209
+ };
1210
+ return dom;
1211
+ }
1212
+ function setObjectAttributes(obj, attrs) {
1213
+ obj = { ...obj };
1214
+ for (const [key, value] of attrs) {
1215
+ const oldValue = obj[key];
1216
+ const newValue = key === "style" ? joinStyles(value, typeof oldValue === "string" ? oldValue : "") : value;
1217
+ obj[key] = newValue;
1218
+ }
1219
+ return obj;
1220
+ }
1221
+ function setElementAttributes(element, attrs) {
1222
+ element = element.cloneNode(true);
1223
+ for (const [key, value] of attrs) {
1224
+ const oldValue = element.getAttribute(key);
1225
+ const newValue = key === "style" ? joinStyles(value, typeof oldValue === "string" ? oldValue : "") : value;
1226
+ element.setAttribute(key, newValue);
1227
+ }
1228
+ return element;
1229
+ }
1230
+ function joinStyles(...styles) {
1231
+ return styles.map((style) => style.trim().replace(/;$/, "")).filter(Boolean).join("; ");
1264
1232
  }
1265
1233
 
1266
1234
  //#endregion
1267
1235
  //#region src/extensions/mark-spec.ts
1268
1236
  /**
1237
+ * Defines a mark type into the editor schema.
1238
+ *
1269
1239
  * @public
1240
+ *
1241
+ * @example
1242
+ *
1243
+ * ```ts
1244
+ * const extension = defineMarkSpec({
1245
+ * name: 'bold',
1246
+ * parseDOM: [
1247
+ * { tag: 'strong' },
1248
+ * { tag: 'b' },
1249
+ * ],
1250
+ * toDOM() {
1251
+ * return ['strong', 0]
1252
+ * },
1253
+ * })
1254
+ * ```
1270
1255
  */
1271
1256
  function defineMarkSpec(options) {
1272
1257
  return defineFacetPayload(markSpecFacet, [[options, void 0]]);
@@ -1287,20 +1272,22 @@ const markSpecFacet = defineFacet({
1287
1272
  if (prevSpec) specs = specs.update(name, mergeSpecs(prevSpec, spec));
1288
1273
  else specs = specs.addToStart(name, spec);
1289
1274
  }
1290
- const groupedAttrs = groupBy(attrPayloads, (payload) => payload.type);
1291
- for (const [type, attrs] of Object.entries(groupedAttrs)) {
1275
+ const groupedAttrs = mapGroupBy(attrPayloads, (payload) => payload.type);
1276
+ for (const [type, attrs] of groupedAttrs.entries()) {
1292
1277
  if (!attrs) continue;
1293
- const maybeSpec = specs.get(type);
1294
- assert(maybeSpec, `Mark type ${type} must be defined`);
1295
- const spec = clone(maybeSpec);
1296
- if (!spec.attrs) spec.attrs = {};
1297
- for (const attr of attrs) spec.attrs[attr.attr] = {
1278
+ const oldSpec = specs.get(type);
1279
+ assert(oldSpec, `Mark type ${type} must be defined`);
1280
+ const newSpec = {
1281
+ ...oldSpec,
1282
+ attrs: { ...oldSpec.attrs }
1283
+ };
1284
+ for (const attr of attrs) newSpec.attrs[attr.attr] = {
1298
1285
  default: attr.default,
1299
1286
  validate: attr.validate
1300
1287
  };
1301
- if (spec.toDOM) spec.toDOM = wrapOutputSpecAttrs(spec.toDOM, attrs);
1302
- if (spec.parseDOM) spec.parseDOM = spec.parseDOM.map((rule) => wrapParseRuleAttrs(rule, attrs));
1303
- specs = specs.update(type, spec);
1288
+ if (oldSpec.toDOM) newSpec.toDOM = wrapOutputSpecAttrs(oldSpec.toDOM, attrs);
1289
+ if (oldSpec.parseDOM) newSpec.parseDOM = oldSpec.parseDOM.map((rule) => wrapParseRuleAttrs(rule, attrs));
1290
+ specs = specs.update(type, newSpec);
1304
1291
  }
1305
1292
  return {
1306
1293
  marks: specs,
@@ -1310,10 +1297,6 @@ const markSpecFacet = defineFacet({
1310
1297
  parent: schemaSpecFacet,
1311
1298
  singleton: true
1312
1299
  });
1313
- function wrapParseRuleAttrs(rule, attrs) {
1314
- if (rule.tag) return wrapTagParseRuleAttrs(rule, attrs);
1315
- return rule;
1316
- }
1317
1300
 
1318
1301
  //#endregion
1319
1302
  //#region src/extensions/mark-view.ts
@@ -1354,7 +1337,7 @@ const markViewFactoryFacet = defineFacet({
1354
1337
  const factories = inputs.map((x) => x[0]).filter(isNotNullish);
1355
1338
  const options = inputs.map((x) => x[1]).filter(isNotNullish);
1356
1339
  for (const { group, name, args } of options) {
1357
- const factory = factories.find((factory$1) => factory$1.group === group);
1340
+ const factory = factories.find((factory) => factory.group === group);
1358
1341
  if (!factory) continue;
1359
1342
  markViews[name] = factory.factory(args);
1360
1343
  }
@@ -1366,6 +1349,77 @@ const markViewFactoryFacet = defineFacet({
1366
1349
  parent: pluginFacet
1367
1350
  });
1368
1351
 
1352
+ //#endregion
1353
+ //#region src/extensions/node-spec.ts
1354
+ /**
1355
+ * Defines a node type into the editor schema.
1356
+ *
1357
+ * @public
1358
+ *
1359
+ * @example
1360
+ *
1361
+ * ```ts
1362
+ * const extension = defineNodeSpec({
1363
+ * name: 'fancyParagraph',
1364
+ * content: 'inline*',
1365
+ * group: 'block',
1366
+ * parseDOM: [{ tag: 'p.fancy' }],
1367
+ * toDOM() {
1368
+ * return ['p', { 'class': 'fancy' }, 0]
1369
+ * },
1370
+ * })
1371
+ * ```
1372
+ */
1373
+ function defineNodeSpec(options) {
1374
+ return defineFacetPayload(nodeSpecFacet, [[options, void 0]]);
1375
+ }
1376
+ /**
1377
+ * Defines an attribute for a node type.
1378
+ *
1379
+ * @public
1380
+ */
1381
+ function defineNodeAttr(options) {
1382
+ return defineFacetPayload(nodeSpecFacet, [[void 0, options]]);
1383
+ }
1384
+ const nodeSpecFacet = defineFacet({
1385
+ reducer: (payloads) => {
1386
+ let specs = OrderedMap.from({});
1387
+ let topNodeName = void 0;
1388
+ const specPayloads = payloads.map((input) => input[0]).filter(isNotNullish);
1389
+ const attrPayloads = payloads.map((input) => input[1]).filter(isNotNullish);
1390
+ for (const { name, topNode, ...spec } of specPayloads) {
1391
+ if (topNode) topNodeName = name;
1392
+ const prevSpec = specs.get(name);
1393
+ if (prevSpec) specs = specs.update(name, mergeSpecs(prevSpec, spec));
1394
+ else specs = specs.addToStart(name, spec);
1395
+ }
1396
+ const groupedAttrs = mapGroupBy(attrPayloads, (payload) => payload.type);
1397
+ for (const [type, attrs] of groupedAttrs.entries()) {
1398
+ if (!attrs) continue;
1399
+ const oldSpec = specs.get(type);
1400
+ assert(oldSpec, `Node type ${type} must be defined`);
1401
+ const newSpec = {
1402
+ ...oldSpec,
1403
+ attrs: { ...oldSpec.attrs }
1404
+ };
1405
+ for (const attr of attrs) newSpec.attrs[attr.attr] = {
1406
+ default: attr.default,
1407
+ validate: attr.validate,
1408
+ splittable: attr.splittable
1409
+ };
1410
+ if (oldSpec.toDOM) newSpec.toDOM = wrapOutputSpecAttrs(oldSpec.toDOM, attrs);
1411
+ if (oldSpec.parseDOM) newSpec.parseDOM = oldSpec.parseDOM.map((rule) => wrapTagParseRuleAttrs(rule, attrs));
1412
+ specs = specs.update(type, newSpec);
1413
+ }
1414
+ return {
1415
+ nodes: specs,
1416
+ topNode: topNodeName
1417
+ };
1418
+ },
1419
+ parent: schemaSpecFacet,
1420
+ singleton: true
1421
+ });
1422
+
1369
1423
  //#endregion
1370
1424
  //#region src/extensions/node-view.ts
1371
1425
  function defineNodeView(options) {
@@ -1405,7 +1459,7 @@ const nodeViewFactoryFacet = defineFacet({
1405
1459
  const factories = inputs.map((x) => x[0]).filter(isNotNullish);
1406
1460
  const options = inputs.map((x) => x[1]).filter(isNotNullish);
1407
1461
  for (const { group, name, args } of options) {
1408
- const factory = factories.find((factory$1) => factory$1.group === group);
1462
+ const factory = factories.find((factory) => factory.group === group);
1409
1463
  if (!factory) continue;
1410
1464
  nodeViews[name] = factory.factory(args);
1411
1465
  }
@@ -1417,57 +1471,6 @@ const nodeViewFactoryFacet = defineFacet({
1417
1471
  parent: pluginFacet
1418
1472
  });
1419
1473
 
1420
- //#endregion
1421
- //#region src/extensions/paragraph.ts
1422
- /**
1423
- * Defines a paragraph node spec.
1424
- */
1425
- function defineParagraphSpec() {
1426
- return defineNodeSpec({
1427
- name: "paragraph",
1428
- content: "inline*",
1429
- group: "block",
1430
- parseDOM: [{ tag: "p" }],
1431
- toDOM() {
1432
- return ["p", 0];
1433
- }
1434
- });
1435
- }
1436
- /**
1437
- * @public
1438
- *
1439
- * Defines a paragraph node spec as the highest priority, because it should be the default block node for most cases.
1440
- *
1441
- * @deprecated Use the following import instead:
1442
- *
1443
- * ```ts
1444
- * import { defineParagraph } from 'prosekit/extensions/paragraph'
1445
- * ```
1446
- */
1447
- function defineParagraph() {
1448
- console.warn("[prosekit] The `defineParagraph` function from `prosekit/core` is deprecated. Use the following import instead: `import { defineParagraph } from \"prosekit/extensions/paragraph\"`.");
1449
- return withPriority(defineParagraphSpec(), Priority.highest);
1450
- }
1451
-
1452
- //#endregion
1453
- //#region src/extensions/text.ts
1454
- /**
1455
- * @public
1456
- *
1457
- * @deprecated Use the following import instead:
1458
- *
1459
- * ```ts
1460
- * import { defineText } from 'prosekit/extensions/text'
1461
- * ```
1462
- */
1463
- function defineText() {
1464
- console.warn("[prosekit] The `defineText` function from `prosekit/core` is deprecated. Use the following import instead: `import { defineText } from \"prosekit/extensions/text\"`.");
1465
- return defineNodeSpec({
1466
- name: "text",
1467
- group: "inline"
1468
- });
1469
- }
1470
-
1471
1474
  //#endregion
1472
1475
  //#region src/utils/can-use-regex-lookbehind.ts
1473
1476
  /**
@@ -1475,7 +1478,7 @@ function defineText() {
1475
1478
  */
1476
1479
  const canUseRegexLookbehind = once(() => {
1477
1480
  try {
1478
- return "ab".replace(new RegExp("(?<=a)b", "g"), "c") === "ac";
1481
+ return "ab".replace(/* @__PURE__ */ new RegExp("(?<=a)b", "g"), "c") === "ac";
1479
1482
  } catch {
1480
1483
  return false;
1481
1484
  }
@@ -1492,41 +1495,6 @@ const canUseRegexLookbehind = once(() => {
1492
1495
  */
1493
1496
  const clsx = clsxLite;
1494
1497
 
1495
- //#endregion
1496
- //#region src/utils/collect-children.ts
1497
- /**
1498
- * Collects all children of a node or a fragment, and returns them as an array.
1499
- *
1500
- * @deprecated Use `node.children` or `fragment.content` instead.
1501
- *
1502
- * @hidden
1503
- */
1504
- function collectChildren(parent) {
1505
- const children = [];
1506
- for (let i = 0; i < parent.childCount; i++) children.push(parent.child(i));
1507
- return children;
1508
- }
1509
-
1510
- //#endregion
1511
- //#region src/utils/collect-nodes.ts
1512
- /**
1513
- * Collects all nodes from a given content.
1514
- *
1515
- * @deprecated Use `collectChildren` instead.
1516
- *
1517
- * @hidden
1518
- */
1519
- function collectNodes(content) {
1520
- if (Array.isArray(content)) return content.flatMap(collectNodes);
1521
- if (content instanceof ProseMirrorNode) return [content];
1522
- if (content instanceof ProseMirrorFragment) {
1523
- const nodes = [];
1524
- for (let i = 0; i < content.childCount; i++) nodes.push(content.child(i));
1525
- return nodes;
1526
- }
1527
- throw new ProseKitError(`Invalid node content: ${typeof content}`);
1528
- }
1529
-
1530
1498
  //#endregion
1531
1499
  //#region src/utils/contains-inline-node.ts
1532
1500
  /**
@@ -1542,18 +1510,44 @@ function containsInlineNode(doc, from, to) {
1542
1510
  }
1543
1511
 
1544
1512
  //#endregion
1545
- //#region src/utils/get-id.ts
1546
- let id = 0;
1513
+ //#region src/utils/find-node.ts
1547
1514
  /**
1548
- * Returns a unique id in the current process that can be used in various places.
1515
+ * Finds the first node that satisfies the predicate from the given document.
1549
1516
  *
1550
1517
  * @internal
1518
+ */
1519
+ function findNode(doc, predicate) {
1520
+ let found;
1521
+ doc.descendants((node, pos, parent, index) => {
1522
+ if (found) return false;
1523
+ if (predicate(node)) {
1524
+ found = {
1525
+ node,
1526
+ pos,
1527
+ parent,
1528
+ index
1529
+ };
1530
+ return false;
1531
+ }
1532
+ });
1533
+ return found;
1534
+ }
1535
+ /**
1536
+ * Finds all nodes that satisfy the predicate from the given document.
1551
1537
  *
1552
- * @deprecated Import `getId` from `@ocavue/utils` package instead. Remove it in a future version.
1538
+ * @internal
1553
1539
  */
1554
- function getId() {
1555
- id = (id + 1) % Number.MAX_SAFE_INTEGER;
1556
- return `id:${id}`;
1540
+ function findNodes(doc, predicate) {
1541
+ const results = [];
1542
+ doc.descendants((node, pos, parent, index) => {
1543
+ if (predicate(node)) results.push({
1544
+ node,
1545
+ pos,
1546
+ parent,
1547
+ index
1548
+ });
1549
+ });
1550
+ return results;
1557
1551
  }
1558
1552
 
1559
1553
  //#endregion
@@ -1612,5 +1606,5 @@ function withSkipCodeBlock(command) {
1612
1606
  }
1613
1607
 
1614
1608
  //#endregion
1615
- 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 };
1609
+ export { Editor, EditorNotFoundError, OBJECT_REPLACEMENT_CHARACTER, Priority, ProseKitError, addMark, assert, canUseRegexLookbehind, clsx, containsInlineNode, createEditor, defaultBlockAt, defineBaseCommands, defineBaseKeymap, defineClickHandler, defineClickOnHandler, defineClipboardSerializer, defineCommands, defineDOMEventHandler, defineDefaultState, defineDocChangeHandler, defineDoubleClickHandler, defineDoubleClickOnHandler, defineDropHandler, defineFacet, defineFacetPayload, defineFocusChangeHandler, defineHistory, defineKeyDownHandler, defineKeyPressHandler, defineKeymap, defineMarkAttr, defineMarkSpec, defineMarkView, defineMarkViewComponent, defineMarkViewFactory, defineMountHandler, defineNodeAttr, defineNodeSpec, defineNodeView, defineNodeViewComponent, defineNodeViewFactory, definePasteHandler, definePlugin, defineScrollToSelectionHandler, defineTextInputHandler, defineTripleClickHandler, defineTripleClickOnHandler, defineUnmountHandler, defineUpdateHandler, editorEventFacet, elementFromJSON, elementFromNode, expandMark, findNode, findNodes, 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, selectAll, selectBlock, setBlockType, setNodeAttrs, setNodeAttrsBetween, setSelectionAround, stateFromJSON, toggleMark, toggleNode, toggleWrap, union, unsetBlockType, unsetMark, withPriority, withSkipCodeBlock, wrap };
1616
1610
  //# sourceMappingURL=prosekit-core.js.map