@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
@@ -0,0 +1,129 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { setupTest } from '../testing'
4
+
5
+ import { setNodeAttrs } from './set-node-attrs'
6
+
7
+ describe('setNodeAttrs', () => {
8
+ it('should set attributes on a code block node', () => {
9
+ const { editor, n } = setupTest()
10
+
11
+ editor.set(n.doc(n.codeBlock('const x = 1')))
12
+
13
+ // Get the initial language attribute (should be empty string by default)
14
+ expect(editor.state.doc.firstChild?.attrs.language).toBe('')
15
+
16
+ // Set the language attribute
17
+ const command = setNodeAttrs({
18
+ type: 'codeBlock',
19
+ attrs: { language: 'typescript' },
20
+ })
21
+
22
+ expect(editor.exec(command)).toBe(true)
23
+ expect(editor.state.doc.firstChild?.attrs.language).toBe('typescript')
24
+ })
25
+
26
+ it('should set multiple attributes at once', () => {
27
+ const { editor, n } = setupTest()
28
+
29
+ editor.set(n.doc(n.codeBlock('const x = 1')))
30
+
31
+ // Verify initial state
32
+ expect(editor.state.doc.firstChild?.attrs).toMatchObject({
33
+ language: '',
34
+ lineNumbers: false,
35
+ })
36
+
37
+ // Set multiple attributes at once
38
+ const command = setNodeAttrs({
39
+ type: 'codeBlock',
40
+ attrs: { language: 'javascript', lineNumbers: true },
41
+ })
42
+
43
+ editor.exec(command)
44
+
45
+ // Verify both attributes were set
46
+ expect(editor.state.doc.firstChild?.attrs).toMatchObject({
47
+ language: 'javascript',
48
+ lineNumbers: true,
49
+ })
50
+ })
51
+
52
+ it('should return false when node type does not match', () => {
53
+ const { editor, n } = setupTest()
54
+
55
+ editor.set(n.doc(n.paragraph('Hello world')))
56
+
57
+ const command = setNodeAttrs({
58
+ type: 'codeBlock',
59
+ attrs: { language: 'typescript' },
60
+ })
61
+
62
+ // Should return false because paragraph is not a codeBlock
63
+ expect(editor.exec(command)).toBe(false)
64
+ })
65
+
66
+ it('should set attributes at a specific position', () => {
67
+ const { editor, n } = setupTest()
68
+
69
+ editor.set(
70
+ n.doc(
71
+ /*0*/
72
+ n.codeBlock(/*1*/ 'A' /*2*/),
73
+ /*3*/
74
+ n.codeBlock(/*4*/ 'B' /*5*/),
75
+ /*6*/
76
+ ),
77
+ )
78
+
79
+ // Set attribute on the second code block (position after first block)
80
+ const command = setNodeAttrs({
81
+ type: 'codeBlock',
82
+ attrs: { language: 'python' },
83
+ pos: 3, // Position of second code block
84
+ })
85
+
86
+ editor.exec(command)
87
+
88
+ // First block should still have default language
89
+ expect(editor.state.doc.child(0).attrs.language).toBe('')
90
+ // Second block should have the new language
91
+ expect(editor.state.doc.child(1).attrs.language).toBe('python')
92
+ })
93
+
94
+ it('should handle cursor inside a node', () => {
95
+ const { editor, n } = setupTest()
96
+
97
+ editor.set(n.doc(n.codeBlock('const<a> x = 1')))
98
+
99
+ const command = setNodeAttrs({
100
+ type: 'codeBlock',
101
+ attrs: { language: 'typescript' },
102
+ })
103
+
104
+ editor.exec(command)
105
+
106
+ expect(editor.state.doc.firstChild?.attrs.language).toBe('typescript')
107
+ })
108
+
109
+ it('should set attrs on wrapping node containing selection', () => {
110
+ const { editor, n } = setupTest()
111
+
112
+ editor.set(
113
+ n.doc(
114
+ n.blockquote(
115
+ n.paragraph('Hello<a> world<b>'),
116
+ ),
117
+ ),
118
+ )
119
+
120
+ const command = setNodeAttrs({
121
+ type: 'blockquote',
122
+ attrs: { variant: 'fancy' },
123
+ })
124
+
125
+ // Should find the blockquote wrapping the selection
126
+ expect(editor.exec(command)).toBe(true)
127
+ expect(editor.state.doc.firstChild?.attrs.variant).toBe('fancy')
128
+ })
129
+ })
@@ -1,9 +1,7 @@
1
- import type {
2
- Attrs,
3
- NodeType,
4
- } from '@prosekit/pm/model'
1
+ import type { Attrs, NodeType } from '@prosekit/pm/model'
5
2
  import type { Command } from '@prosekit/pm/state'
6
3
 
4
+ import { findParentNodeOfType } from '../utils/find-parent-node-of-type'
7
5
  import { getNodeTypes } from '../utils/get-node-types'
8
6
 
9
7
  /**
@@ -12,8 +10,6 @@ import { getNodeTypes } from '../utils/get-node-types'
12
10
  export interface SetNodeAttrsOptions {
13
11
  /**
14
12
  * The type of node to set the attributes of.
15
- *
16
- * If current node is not of this type, the command will do nothing.
17
13
  */
18
14
  type: string | NodeType | string[] | NodeType[]
19
15
 
@@ -23,43 +19,46 @@ export interface SetNodeAttrsOptions {
23
19
  attrs: Attrs
24
20
 
25
21
  /**
26
- * The position of the node. Defaults to the position of the wrapping node
27
- * containing the current selection.
22
+ * The document position of the node to update. If not provided, the command
23
+ * will find the closest ancestor node that matches the type based on the
24
+ * anchor position of the selection.
28
25
  */
29
26
  pos?: number
30
27
  }
31
28
 
32
29
  /**
33
- * Returns a command that set the attributes of the current node.
30
+ * Returns a command that sets the attributes of the current node.
31
+ *
32
+ * @param options
34
33
  *
35
34
  * @public
36
35
  */
37
- export function setNodeAttrs(options: SetNodeAttrsOptions): Command {
36
+ export function setNodeAttrs({ type, attrs, pos }: SetNodeAttrsOptions): Command {
38
37
  return (state, dispatch) => {
39
- const nodeTypes = getNodeTypes(state.schema, options.type)
40
- const from = options.pos ?? state.selection.from
41
- const to = options.pos ?? state.selection.to
42
- const positions: number[] = []
38
+ let updatePos: number
43
39
 
44
- state.doc.nodesBetween(from, to, (node, pos) => {
45
- if (nodeTypes.includes(node.type)) {
46
- positions.push(pos)
40
+ if (pos == null) {
41
+ const found = findParentNodeOfType(type, state.selection.$anchor)
42
+ if (!found) {
43
+ return false
47
44
  }
48
- if (!dispatch && positions.length > 0) {
45
+ updatePos = found.pos
46
+ } else {
47
+ const found = state.doc.nodeAt(pos)
48
+ if (!found) {
49
49
  return false
50
50
  }
51
- })
52
-
53
- if (positions.length === 0) {
54
- return false
51
+ const nodeTypes = getNodeTypes(state.schema, type)
52
+ if (!nodeTypes.includes(found.type)) {
53
+ return false
54
+ }
55
+ updatePos = pos
55
56
  }
56
57
 
57
58
  if (dispatch) {
58
59
  const { tr } = state
59
- for (const pos of positions) {
60
- for (const [key, value] of Object.entries(options.attrs)) {
61
- tr.setNodeAttribute(pos, key, value)
62
- }
60
+ for (const [key, value] of Object.entries(attrs)) {
61
+ tr.setNodeAttribute(updatePos, key, value)
63
62
  }
64
63
  dispatch(tr)
65
64
  }
@@ -1,8 +1,5 @@
1
1
  import { toggleMark as baseToggleMark } from '@prosekit/pm/commands'
2
- import type {
3
- Attrs,
4
- MarkType,
5
- } from '@prosekit/pm/model'
2
+ import type { Attrs, MarkType } from '@prosekit/pm/model'
6
3
  import type { Command } from '@prosekit/pm/state'
7
4
 
8
5
  import type { CommandCreator } from '../types/extension-command'
@@ -1,8 +1,5 @@
1
1
  import { setBlockType } from '@prosekit/pm/commands'
2
- import type {
3
- Attrs,
4
- NodeType,
5
- } from '@prosekit/pm/model'
2
+ import type { Attrs, NodeType } from '@prosekit/pm/model'
6
3
  import type { Command } from '@prosekit/pm/state'
7
4
 
8
5
  import { getNodeType } from '../utils/get-node-type'
@@ -24,7 +21,7 @@ export interface ToggleNodeOptions {
24
21
  }
25
22
 
26
23
  /**
27
- * Returns a command that set the selected textblocks to the given node type
24
+ * Returns a command that sets the selected textblocks to the given node type
28
25
  * with the given attributes.
29
26
  *
30
27
  * @param options
@@ -1,8 +1,4 @@
1
- import {
2
- describe,
3
- expect,
4
- it,
5
- } from 'vitest'
1
+ import { describe, expect, it } from 'vitest'
6
2
 
7
3
  import { setupTest } from '../testing'
8
4
 
@@ -1,8 +1,5 @@
1
1
  import { lift } from '@prosekit/pm/commands'
2
- import type {
3
- Attrs,
4
- NodeType,
5
- } from '@prosekit/pm/model'
2
+ import type { Attrs, NodeType } from '@prosekit/pm/model'
6
3
  import type { Command } from '@prosekit/pm/state'
7
4
 
8
5
  import { isNodeActive } from '../utils/is-node-active'
@@ -1,8 +1,4 @@
1
- import {
2
- describe,
3
- expect,
4
- it,
5
- } from 'vitest'
1
+ import { describe, expect, it } from 'vitest'
6
2
 
7
3
  import { setupTest } from '../testing'
8
4
 
@@ -1,11 +1,5 @@
1
- import {
2
- Fragment,
3
- Slice,
4
- } from '@prosekit/pm/model'
5
- import type {
6
- Command,
7
- Transaction,
8
- } from '@prosekit/pm/state'
1
+ import { Fragment, Slice } from '@prosekit/pm/model'
2
+ import type { Command, Transaction } from '@prosekit/pm/state'
9
3
  import { ReplaceAroundStep } from '@prosekit/pm/transform'
10
4
 
11
5
  import type { CommandCreator } from '../types/extension-command'
@@ -1,8 +1,4 @@
1
- import {
2
- describe,
3
- expect,
4
- it,
5
- } from 'vitest'
1
+ import { describe, expect, it } from 'vitest'
6
2
 
7
3
  import { setupTest } from '../testing'
8
4
 
@@ -1,7 +1,4 @@
1
- import type {
2
- Attrs,
3
- NodeType,
4
- } from '@prosekit/pm/model'
1
+ import type { Attrs, NodeType } from '@prosekit/pm/model'
5
2
  import type { Command } from '@prosekit/pm/state'
6
3
  import { findWrapping } from '@prosekit/pm/transform'
7
4
 
@@ -16,11 +13,6 @@ export interface WrapOptions {
16
13
  */
17
14
  type: NodeType | string
18
15
 
19
- /**
20
- * @deprecated Use `nodeSpec` instead.
21
- */
22
- nodeType?: NodeType
23
-
24
16
  /**
25
17
  * Optional attributes to apply to the node.
26
18
  */
@@ -40,7 +32,7 @@ export function wrap(options: WrapOptions): Command {
40
32
  const range = $from.blockRange($to)
41
33
  if (!range) return false
42
34
 
43
- const nodeType = getNodeType(state.schema, options.nodeType || options.type)
35
+ const nodeType = getNodeType(state.schema, options.type)
44
36
  const wrapping = findWrapping(range, nodeType, options.attrs)
45
37
  if (!wrapping) return false
46
38
 
@@ -1,8 +1,4 @@
1
- import {
2
- describe,
3
- expect,
4
- it,
5
- } from 'vitest'
1
+ import { describe, expect, it } from 'vitest'
6
2
 
7
3
  import { setupTest } from '../testing'
8
4
  import { jsonFromNode } from '../utils/parse'
@@ -22,7 +18,7 @@ describe('NodeAction', () => {
22
18
  it('can apply node with attrs', () => {
23
19
  expect(n.codeBlock({ language: 'javascript' }, 'foo').toJSON()).toEqual({
24
20
  type: 'codeBlock',
25
- attrs: { language: 'javascript' },
21
+ attrs: { language: 'javascript', lineNumbers: false },
26
22
  content: [{ text: 'foo', type: 'text' }],
27
23
  })
28
24
  })
@@ -1,13 +1,6 @@
1
- import type {
2
- Attrs,
3
- Mark,
4
- MarkType,
5
- NodeType,
6
- ProseMirrorNode,
7
- Schema,
8
- } from '@prosekit/pm/model'
1
+ import { mapValues } from '@ocavue/utils'
2
+ import type { Attrs, Mark, MarkType, NodeType, ProseMirrorNode, Schema } from '@prosekit/pm/model'
9
3
  import type { EditorState } from '@prosekit/pm/state'
10
- import mapValues from 'just-map-values'
11
4
 
12
5
  import { ProseKitError } from '../error'
13
6
  import type { AnyAttrs } from '../types/attrs'
@@ -79,16 +72,6 @@ export interface MarkAction<Attrs extends AnyAttrs = AnyAttrs> {
79
72
  isActive: (attrs?: Attrs) => boolean
80
73
  }
81
74
 
82
- /**
83
- * @deprecated Use type {@link NodeAction} instead.
84
- */
85
- export type NodeBuilder = NodeAction
86
-
87
- /**
88
- * @deprecated Use type {@link MarkAction} instead.
89
- */
90
- export type MarkBuilder = MarkAction
91
-
92
75
  /**
93
76
  * @internal
94
77
  */
@@ -1,15 +1,8 @@
1
- import {
2
- describe,
3
- expect,
4
- it,
5
- } from 'vitest'
1
+ import { describe, expect, it } from 'vitest'
6
2
 
7
3
  import { insertText } from '../commands/insert-text'
8
4
  import { wrap } from '../commands/wrap'
9
- import {
10
- defineTestExtension,
11
- setupTest,
12
- } from '../testing'
5
+ import { defineTestExtension, setupTest } from '../testing'
13
6
  import type { NodeJSON } from '../types/model'
14
7
 
15
8
  import { createEditor } from './editor'
@@ -1,28 +1,12 @@
1
- import type {
2
- ProseMirrorNode,
3
- Schema,
4
- } from '@prosekit/pm/model'
5
- import {
6
- EditorState,
7
- type Command,
8
- type Plugin,
9
- type Selection,
10
- type Transaction,
11
- } from '@prosekit/pm/state'
12
- import {
13
- EditorView,
14
- type DirectEditorProps,
15
- type EditorProps,
16
- } from '@prosekit/pm/view'
1
+ import { isDeepEqual } from '@ocavue/utils'
2
+ import type { ProseMirrorNode, Schema } from '@prosekit/pm/model'
3
+ import { EditorState, type Command, type Plugin, type Selection, type Transaction } from '@prosekit/pm/state'
4
+ import { EditorView, type DirectEditorProps, type EditorProps } from '@prosekit/pm/view'
17
5
 
18
6
  import { ProseKitError } from '../error'
19
7
  import { defineDefaultState } from '../extensions/default-state'
20
8
  import type { BaseExtension } from '../facets/base-extension'
21
- import {
22
- subtractFacetNode,
23
- unionFacetNode,
24
- type FacetNode,
25
- } from '../facets/facet-node'
9
+ import { subtractFacetNode, unionFacetNode, type FacetNode } from '../facets/facet-node'
26
10
  import type {
27
11
  Extension,
28
12
  ExtractCommandActions,
@@ -31,32 +15,13 @@ import type {
31
15
  ExtractNodeActions,
32
16
  ExtractNodeNames,
33
17
  } from '../types/extension'
34
- import type {
35
- CommandAction,
36
- CommandCreator,
37
- } from '../types/extension-command'
38
- import type {
39
- NodeJSON,
40
- SelectionJSON,
41
- } from '../types/model'
18
+ import type { CommandAction, CommandCreator } from '../types/extension-command'
19
+ import type { NodeJSON, SelectionJSON } from '../types/model'
42
20
  import { assert } from '../utils/assert'
43
- import { deepEquals } from '../utils/deep-equals'
44
- import {
45
- getEditorContentDoc,
46
- getEditorSelection,
47
- } from '../utils/editor-content'
48
- import {
49
- htmlFromNode,
50
- jsonFromNode,
51
- type DOMDocumentOptions,
52
- } from '../utils/parse'
53
-
54
- import {
55
- createMarkActions,
56
- createNodeActions,
57
- type MarkAction,
58
- type NodeAction,
59
- } from './action'
21
+ import { getEditorContentDoc, getEditorSelection } from '../utils/editor-content'
22
+ import { htmlFromNode, jsonFromNode, type DOMDocumentOptions } from '../utils/parse'
23
+
24
+ import { createMarkActions, createNodeActions, type MarkAction, type NodeAction } from './action'
60
25
  import { union } from './union'
61
26
 
62
27
  /**
@@ -70,25 +35,9 @@ export interface EditorOptions<E extends Extension> {
70
35
 
71
36
  /**
72
37
  * The starting document to use when creating the editor. It can be a
73
- * ProseMirror node JSON object, a HTML string, or a HTML element instance.
74
- */
75
- defaultContent?: NodeJSON | string | HTMLElement
76
-
77
- /**
78
- * A JSON object representing the starting document to use when creating the
79
- * editor.
80
- *
81
- * @deprecated Use `defaultContent` instead.
82
- */
83
- defaultDoc?: NodeJSON
84
-
85
- /**
86
- * A HTML element or a HTML string representing the starting document to use
87
- * when creating the editor.
88
- *
89
- * @deprecated Use `defaultContent` instead.
38
+ * ProseMirror node JSON object, an HTML string, or a DOM element instance.
90
39
  */
91
- defaultHTML?: string | HTMLElement
40
+ defaultContent?: NodeJSON | string | Element
92
41
 
93
42
  /**
94
43
  * A JSON object representing the starting selection to use when creating the
@@ -108,7 +57,7 @@ export interface getDocHTMLOptions extends DOMDocumentOptions {}
108
57
  export function setupEditorExtension<E extends Extension>(
109
58
  options: EditorOptions<E>,
110
59
  ): E {
111
- if (options.defaultContent || options.defaultDoc || options.defaultHTML) {
60
+ if (options.defaultContent) {
112
61
  return union(
113
62
  options.extension,
114
63
  defineDefaultState(options),
@@ -176,7 +125,7 @@ export class EditorInstance {
176
125
  return this.getState().doc
177
126
  }
178
127
 
179
- private getProp<PropName extends keyof EditorProps>(propName: PropName): EditorProps[PropName] | undefined {
128
+ private getProp<PropName extends keyof EditorProps>(propName: PropName): Partial<EditorProps>[PropName] {
180
129
  return this.view?.someProp(propName) ?? this.directEditorProps[propName]
181
130
  }
182
131
 
@@ -197,7 +146,7 @@ export class EditorInstance {
197
146
  }
198
147
 
199
148
  public setContent(
200
- content: NodeJSON | string | HTMLElement | ProseMirrorNode,
149
+ content: NodeJSON | string | Element | ProseMirrorNode,
201
150
  selection?: SelectionJSON | Selection | 'start' | 'end',
202
151
  ): void {
203
152
  const doc = getEditorContentDoc(this.schema, content)
@@ -226,7 +175,7 @@ export class EditorInstance {
226
175
  }
227
176
 
228
177
  /**
229
- * Return a HTML string representing the editor's current document.
178
+ * Return an HTML string representing the editor's current document.
230
179
  */
231
180
  public getDocHTML = (options?: getDocHTMLOptions): string => {
232
181
  const serializer = this.getProp('clipboardSerializer')
@@ -264,14 +213,14 @@ export class EditorInstance {
264
213
  const newPayload = this.tree.getRootOutput()
265
214
  const newPlugins = [...(newPayload?.state?.plugins ?? [])]
266
215
 
267
- if (!deepEquals(oldPlugins, newPlugins)) {
216
+ if (!isDeepEqual(oldPlugins, newPlugins)) {
268
217
  const state = view.state.reconfigure({ plugins: newPlugins })
269
218
  view.updateState(state)
270
219
  }
271
220
 
272
221
  if (
273
222
  newPayload?.commands
274
- && !deepEquals(oldPayload?.commands, newPayload?.commands)
223
+ && !isDeepEqual(oldPayload?.commands, newPayload?.commands)
275
224
  ) {
276
225
  const commands = newPayload.commands
277
226
  const names = Object.keys(commands)
@@ -306,10 +255,14 @@ export class EditorInstance {
306
255
 
307
256
  public mount(place: HTMLElement): void {
308
257
  if (this.view) {
258
+ // If the editor is already mounted to the same DOM element, do nothing
259
+ if (this.view.dom === place) return
260
+ // If the editor is already mounted to a different element, throw an error
309
261
  throw new ProseKitError('Editor is already mounted')
310
262
  }
311
263
  this.view = new EditorView({ mount: place }, this.directEditorProps)
312
264
  this.afterMounted.forEach((callback) => callback())
265
+ this.afterMounted.length = 0
313
266
  }
314
267
 
315
268
  public unmount(): void {
@@ -374,7 +327,6 @@ export class EditorInstance {
374
327
  return this.canExec(command)
375
328
  }
376
329
 
377
- action.canApply = canExec
378
330
  action.canExec = canExec
379
331
 
380
332
  this.commands[name] = action as CommandAction
@@ -437,12 +389,14 @@ export class Editor<E extends Extension = any> {
437
389
  }
438
390
 
439
391
  /**
440
- * Mount the editor to the given HTML element.
441
- * Pass `null` or `undefined` to unmount the editor.
392
+ * Mount the editor to the given HTML element. Pass `null` or `undefined` to
393
+ * unmount the editor. When an element is passed, this method returns a
394
+ * function to unmount the editor.
442
395
  */
443
- mount = (place: HTMLElement | null | undefined): void => {
396
+ mount = (place: HTMLElement | null | undefined): void | VoidFunction => {
444
397
  if (place) {
445
398
  this.instance.mount(place)
399
+ return this.unmount
446
400
  } else {
447
401
  this.instance.unmount()
448
402
  }
@@ -496,7 +450,7 @@ export class Editor<E extends Extension = any> {
496
450
  * - A ProseMirror node instance
497
451
  * - A ProseMirror node JSON object
498
452
  * - An HTML string
499
- * - An HTML element instance
453
+ * - A DOM element instance
500
454
  * @param selection - Optional. Specifies the new selection. It can be one of the following:
501
455
  * - A ProseMirror selection instance
502
456
  * - A ProseMirror selection JSON object
@@ -504,7 +458,7 @@ export class Editor<E extends Extension = any> {
504
458
  * - The string "end" (to set selection at the end)
505
459
  */
506
460
  setContent = (
507
- content: ProseMirrorNode | NodeJSON | string | HTMLElement,
461
+ content: ProseMirrorNode | NodeJSON | string | Element,
508
462
  selection?: SelectionJSON | Selection | 'start' | 'end',
509
463
  ): void => {
510
464
  return this.instance.setContent(content, selection)
@@ -518,7 +472,7 @@ export class Editor<E extends Extension = any> {
518
472
  }
519
473
 
520
474
  /**
521
- * Return a HTML string representing the editor's current document.
475
+ * Return an HTML string representing the editor's current document.
522
476
  */
523
477
  public getDocHTML = (options?: getDocHTMLOptions): string => {
524
478
  return this.instance.getDocHTML(options)
@@ -1,10 +1,6 @@
1
1
  import type { Attrs } from '@prosekit/pm/model'
2
2
  import type { Command } from '@prosekit/pm/state'
3
- import {
4
- describe,
5
- expectTypeOf,
6
- it,
7
- } from 'vitest'
3
+ import { describe, expectTypeOf, it } from 'vitest'
8
4
 
9
5
  import { defineCommands } from '../extensions/command'
10
6
  import { defineMarkSpec } from '../extensions/mark-spec'
@@ -1,9 +1,6 @@
1
1
  import type { BaseExtension } from '../facets/base-extension'
2
2
  import { UnionExtensionImpl } from '../facets/union-extension'
3
- import type {
4
- Extension,
5
- Union,
6
- } from '../types/extension'
3
+ import type { Extension, Union } from '../types/extension'
7
4
  import { assert } from '../utils/assert'
8
5
 
9
6
  /**
@@ -1,25 +1,13 @@
1
- import {
2
- DOMSerializer,
3
- type DOMOutputSpec,
4
- type Mark,
5
- type ProseMirrorNode,
6
- type Schema,
7
- } from '@prosekit/pm/model'
8
- import {
9
- PluginKey,
10
- ProseMirrorPlugin,
11
- } from '@prosekit/pm/state'
1
+ import { isNotNullish } from '@ocavue/utils'
2
+ import { DOMSerializer, type DOMOutputSpec, type Mark, type ProseMirrorNode, type Schema } from '@prosekit/pm/model'
3
+ import { PluginKey, ProseMirrorPlugin } from '@prosekit/pm/state'
12
4
 
13
5
  import { defineFacet } from '../facets/facet'
14
6
  import { defineFacetPayload } from '../facets/facet-extension'
15
7
  import type { AnyFunction } from '../types/any-function'
16
8
  import type { PlainExtension } from '../types/extension'
17
- import { isNotNullish } from '../utils/type-assertion'
18
9
 
19
- import {
20
- pluginFacet,
21
- type PluginPayload,
22
- } from './plugin'
10
+ import { pluginFacet, type PluginPayload } from './plugin'
23
11
 
24
12
  type SerializeFragmentFunction = typeof DOMSerializer.prototype.serializeFragment
25
13
  type SerializeNodeFunction = typeof DOMSerializer.prototype.serializeNode