@prosekit/core 0.9.0 → 0.11.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 (143) hide show
  1. package/dist/{editor-4lgGc3CY.d.ts → editor.d.ts} +58 -18
  2. package/dist/editor.d.ts.map +1 -0
  3. package/dist/{editor-DGNUXn-u.js → editor.js} +40 -81
  4. package/dist/editor.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 +2 -4
  8. package/dist/prosekit-core-test.js.map +1 -1
  9. package/dist/prosekit-core.d.ts +148 -68
  10. package/dist/prosekit-core.d.ts.map +1 -1
  11. package/dist/prosekit-core.js +184 -138
  12. package/dist/prosekit-core.js.map +1 -1
  13. package/package.json +9 -9
  14. package/src/commands/add-mark.ts +3 -6
  15. package/src/commands/expand-mark.ts +4 -11
  16. package/src/commands/insert-default-block.spec.ts +4 -8
  17. package/src/commands/insert-default-block.ts +2 -5
  18. package/src/commands/insert-node.ts +7 -11
  19. package/src/commands/remove-mark.ts +3 -6
  20. package/src/commands/remove-node.ts +4 -4
  21. package/src/commands/select-block.spec.ts +6 -8
  22. package/src/commands/select-block.ts +2 -5
  23. package/src/commands/set-block-type.ts +3 -6
  24. package/src/commands/set-node-attrs-between.spec.ts +221 -0
  25. package/src/commands/set-node-attrs-between.ts +77 -0
  26. package/src/commands/set-node-attrs.spec.ts +129 -0
  27. package/src/commands/set-node-attrs.ts +26 -27
  28. package/src/commands/toggle-mark.ts +3 -6
  29. package/src/commands/toggle-node.ts +4 -7
  30. package/src/commands/toggle-wrap.spec.ts +2 -6
  31. package/src/commands/toggle-wrap.ts +3 -6
  32. package/src/commands/unset-block-type.spec.ts +2 -6
  33. package/src/commands/unset-block-type.ts +3 -9
  34. package/src/commands/unset-mark.spec.ts +2 -6
  35. package/src/commands/unset-mark.ts +1 -1
  36. package/src/commands/wrap.ts +2 -5
  37. package/src/editor/action.spec.ts +5 -9
  38. package/src/editor/action.ts +7 -14
  39. package/src/editor/editor.spec.ts +8 -15
  40. package/src/editor/editor.ts +18 -52
  41. package/src/editor/union.spec.ts +8 -12
  42. package/src/editor/union.ts +4 -7
  43. package/src/editor/with-priority.ts +3 -3
  44. package/src/error.ts +8 -1
  45. package/src/extensions/clipboard-serializer.ts +22 -26
  46. package/src/extensions/command.ts +22 -54
  47. package/src/extensions/default-state.spec.ts +4 -8
  48. package/src/extensions/default-state.ts +6 -12
  49. package/src/extensions/events/doc-change.ts +2 -2
  50. package/src/extensions/events/dom-event.spec.ts +4 -9
  51. package/src/extensions/events/dom-event.ts +9 -21
  52. package/src/extensions/events/editor-event.ts +8 -20
  53. package/src/extensions/events/focus.spec.ts +7 -12
  54. package/src/extensions/events/focus.ts +2 -2
  55. package/src/extensions/events/plugin-view.ts +5 -12
  56. package/src/extensions/history.ts +7 -14
  57. package/src/extensions/keymap-base.spec.ts +6 -15
  58. package/src/extensions/keymap-base.ts +6 -9
  59. package/src/extensions/keymap.spec.ts +10 -24
  60. package/src/extensions/keymap.ts +5 -15
  61. package/src/extensions/mark-spec.spec.ts +6 -21
  62. package/src/extensions/mark-spec.ts +10 -21
  63. package/src/extensions/mark-view-effect.ts +6 -12
  64. package/src/extensions/mark-view.ts +5 -11
  65. package/src/extensions/node-spec.spec.ts +10 -26
  66. package/src/extensions/node-spec.ts +10 -21
  67. package/src/extensions/node-view-effect.ts +6 -12
  68. package/src/extensions/node-view.ts +5 -11
  69. package/src/extensions/plugin.spec.ts +9 -22
  70. package/src/extensions/plugin.ts +6 -15
  71. package/src/facets/base-extension.ts +7 -10
  72. package/src/facets/command.ts +3 -9
  73. package/src/facets/facet-extension.spec.ts +10 -21
  74. package/src/facets/facet-extension.ts +12 -8
  75. package/src/facets/facet-node.spec.ts +4 -11
  76. package/src/facets/facet-node.ts +27 -22
  77. package/src/facets/facet.spec.ts +2 -5
  78. package/src/facets/facet.ts +14 -7
  79. package/src/facets/root.ts +2 -2
  80. package/src/facets/schema-spec.ts +3 -10
  81. package/src/facets/schema.ts +4 -13
  82. package/src/facets/state.spec.ts +8 -15
  83. package/src/facets/state.ts +5 -19
  84. package/src/facets/union-extension.ts +10 -13
  85. package/src/index.ts +74 -200
  86. package/src/test/index.ts +1 -4
  87. package/src/test/test-builder.ts +2 -5
  88. package/src/test/test-editor.spec.ts +2 -6
  89. package/src/test/test-editor.ts +7 -26
  90. package/src/testing/index.ts +26 -22
  91. package/src/types/extension-mark.ts +1 -1
  92. package/src/types/extension-node.ts +1 -1
  93. package/src/types/extension.spec.ts +2 -5
  94. package/src/types/extension.ts +8 -18
  95. package/src/types/pick-string-literal.spec.ts +2 -2
  96. package/src/types/pick-string-literal.ts +1 -1
  97. package/src/types/pick-sub-type.spec.ts +2 -2
  98. package/src/types/priority.ts +50 -7
  99. package/src/types/simplify-deeper.spec.ts +2 -2
  100. package/src/types/simplify-union.spec.ts +2 -2
  101. package/src/types/simplify-union.ts +1 -4
  102. package/src/utils/array-grouping.spec.ts +2 -5
  103. package/src/utils/assert.ts +1 -1
  104. package/src/utils/attrs-match.ts +1 -5
  105. package/src/utils/can-use-regex-lookbehind.ts +2 -8
  106. package/src/utils/clsx.spec.ts +2 -5
  107. package/src/utils/combine-event-handlers.spec.ts +2 -6
  108. package/src/utils/default-block-at.ts +1 -4
  109. package/src/utils/editor-content.spec.ts +3 -6
  110. package/src/utils/editor-content.ts +5 -17
  111. package/src/utils/find-node.ts +65 -0
  112. package/src/utils/find-parent-node-of-type.ts +6 -12
  113. package/src/utils/find-parent-node.spec.ts +3 -7
  114. package/src/utils/find-parent-node.ts +1 -4
  115. package/src/utils/get-custom-selection.ts +1 -5
  116. package/src/utils/get-dom-api.ts +1 -1
  117. package/src/utils/get-mark-type.ts +2 -5
  118. package/src/utils/get-node-type.ts +2 -5
  119. package/src/utils/get-node-types.ts +2 -5
  120. package/src/utils/includes-mark.ts +2 -6
  121. package/src/utils/is-at-block-start.ts +1 -4
  122. package/src/utils/is-mark-absent.spec.ts +3 -6
  123. package/src/utils/is-mark-absent.ts +2 -6
  124. package/src/utils/is-mark-active.ts +4 -7
  125. package/src/utils/is-node-active.spec.ts +109 -0
  126. package/src/utils/is-node-active.ts +19 -10
  127. package/src/utils/is-subset.spec.ts +2 -5
  128. package/src/utils/maybe-run.spec.ts +2 -6
  129. package/src/utils/merge-objects.spec.ts +2 -5
  130. package/src/utils/merge-objects.ts +3 -2
  131. package/src/utils/merge-specs.ts +2 -5
  132. package/src/utils/object-equal.spec.ts +2 -5
  133. package/src/utils/output-spec.test.ts +2 -6
  134. package/src/utils/output-spec.ts +2 -10
  135. package/src/utils/parse.spec.ts +6 -15
  136. package/src/utils/parse.ts +4 -16
  137. package/src/utils/remove-undefined-values.spec.ts +2 -5
  138. package/src/utils/set-selection-around.ts +1 -4
  139. package/src/utils/type-assertion.ts +2 -21
  140. package/src/utils/unicode.spec.ts +2 -5
  141. package/src/utils/with-skip-code-block.ts +1 -1
  142. package/dist/editor-4lgGc3CY.d.ts.map +0 -1
  143. package/dist/editor-DGNUXn-u.js.map +0 -1
@@ -1,9 +1,6 @@
1
- import {
2
- expect,
3
- test,
4
- } from 'vitest'
1
+ import { expect, test } from 'vitest'
5
2
 
6
- import { groupEntries } from './array-grouping'
3
+ import { groupEntries } from './array-grouping.ts'
7
4
 
8
5
  test('groupEntries', () => {
9
6
  expect(
@@ -1,4 +1,4 @@
1
- import { ProseKitError } from '../error'
1
+ import { ProseKitError } from '../error.ts'
2
2
 
3
3
  /**
4
4
  * @internal
@@ -1,8 +1,4 @@
1
- import type {
2
- Attrs,
3
- Mark,
4
- ProseMirrorNode,
5
- } from '@prosekit/pm/model'
1
+ import type { Attrs, Mark, ProseMirrorNode } from '@prosekit/pm/model'
6
2
 
7
3
  export function attrsMatch(
8
4
  nodeOrMark: ProseMirrorNode | Mark,
@@ -1,12 +1,6 @@
1
- import { once } from '@ocavue/utils'
1
+ import { supportsRegexLookbehind } from '@ocavue/utils'
2
2
 
3
3
  /**
4
4
  * Checks if the browser supports [regex lookbehind assertion](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Lookbehind_assertion).
5
5
  */
6
- export const canUseRegexLookbehind: () => boolean = once(() => {
7
- try {
8
- return 'ab'.replace(new RegExp('(?<=a)b', 'g'), 'c') === 'ac'
9
- } catch {
10
- return false
11
- }
12
- })
6
+ export const canUseRegexLookbehind: () => boolean = supportsRegexLookbehind
@@ -1,9 +1,6 @@
1
- import {
2
- expect,
3
- test,
4
- } from 'vitest'
1
+ import { expect, test } from 'vitest'
5
2
 
6
- import { clsx } from './clsx'
3
+ import { clsx } from './clsx.ts'
7
4
 
8
5
  test('joins class names', () => {
9
6
  expect(clsx('foo', 'bar')).toBe('foo bar')
@@ -1,10 +1,6 @@
1
- import {
2
- expect,
3
- test,
4
- vi,
5
- } from 'vitest'
1
+ import { expect, test, vi } from 'vitest'
6
2
 
7
- import { combineEventHandlers } from './combine-event-handlers'
3
+ import { combineEventHandlers } from './combine-event-handlers.ts'
8
4
 
9
5
  test('runs handlers in reverse order and stops on true', () => {
10
6
  const handler1 = vi.fn(() => false)
@@ -1,7 +1,4 @@
1
- import type {
2
- ContentMatch,
3
- NodeType,
4
- } from '@prosekit/pm/model'
1
+ import type { ContentMatch, NodeType } from '@prosekit/pm/model'
5
2
 
6
3
  /**
7
4
  * @internal
@@ -1,12 +1,9 @@
1
1
  import { AllSelection } from '@prosekit/pm/state'
2
- import {
3
- expect,
4
- test,
5
- } from 'vitest'
2
+ import { expect, test } from 'vitest'
6
3
 
7
- import { setupTest } from '../testing'
4
+ import { setupTest } from '../testing/index.ts'
8
5
 
9
- import { getEditorSelection } from './editor-content'
6
+ import { getEditorSelection } from './editor-content.ts'
10
7
 
11
8
  test('getEditorSelection', () => {
12
9
  const { n } = setupTest()
@@ -1,24 +1,12 @@
1
1
  import { isElementLike } from '@ocavue/utils'
2
- import type {
3
- ProseMirrorNode,
4
- Schema,
5
- } from '@prosekit/pm/model'
2
+ import type { ProseMirrorNode, Schema } from '@prosekit/pm/model'
6
3
  import { Selection } from '@prosekit/pm/state'
7
4
 
8
- import type {
9
- NodeJSON,
10
- SelectionJSON,
11
- } from '../types/model'
5
+ import type { NodeJSON, SelectionJSON } from '../types/model.ts'
12
6
 
13
- import { assert } from './assert'
14
- import {
15
- jsonFromElement,
16
- jsonFromHTML,
17
- } from './parse'
18
- import {
19
- isProseMirrorNode,
20
- isSelection,
21
- } from './type-assertion'
7
+ import { assert } from './assert.ts'
8
+ import { jsonFromElement, jsonFromHTML } from './parse.ts'
9
+ import { isProseMirrorNode, isSelection } from './type-assertion.ts'
22
10
 
23
11
  export function getEditorContentJSON(
24
12
  schema: Schema,
@@ -0,0 +1,65 @@
1
+ import type { ProseMirrorNode } from '@prosekit/pm/model'
2
+
3
+ /**
4
+ * Finds the first node that satisfies the predicate from the given document.
5
+ *
6
+ * @internal
7
+ */
8
+ export function findNode(
9
+ doc: ProseMirrorNode,
10
+ predicate: (node: ProseMirrorNode) => boolean,
11
+ ): FindNodeResult | undefined {
12
+ let found: FindNodeResult | undefined
13
+ doc.descendants((node, pos, parent, index) => {
14
+ if (found) {
15
+ return false
16
+ }
17
+ if (predicate(node)) {
18
+ found = { node, pos, parent, index }
19
+ return false
20
+ }
21
+ })
22
+ return found
23
+ }
24
+
25
+ /**
26
+ * Finds all nodes that satisfy the predicate from the given document.
27
+ *
28
+ * @internal
29
+ */
30
+ export function findNodes(
31
+ doc: ProseMirrorNode,
32
+ predicate: (node: ProseMirrorNode) => boolean,
33
+ ): FindNodeResult[] {
34
+ const results: FindNodeResult[] = []
35
+ doc.descendants((node, pos, parent, index) => {
36
+ if (predicate(node)) {
37
+ results.push({ node, pos, parent, index })
38
+ }
39
+ })
40
+ return results
41
+ }
42
+
43
+ /**
44
+ * The result of the {@link findNode} function.
45
+ *
46
+ * @internal
47
+ */
48
+ export interface FindNodeResult {
49
+ /**
50
+ * The node that satisfies the predicate.
51
+ */
52
+ node: ProseMirrorNode
53
+ /**
54
+ * The position of the node.
55
+ */
56
+ pos: number
57
+ /**
58
+ * The parent of the node.
59
+ */
60
+ parent: ProseMirrorNode | null
61
+ /**
62
+ * The index of the node in the parent.
63
+ */
64
+ index: number
65
+ }
@@ -1,13 +1,7 @@
1
- import type {
2
- NodeType,
3
- ResolvedPos,
4
- } from '@prosekit/pm/model'
1
+ import type { NodeType, ResolvedPos } from '@prosekit/pm/model'
5
2
 
6
- import {
7
- findParentNode,
8
- type FindParentNodeResult,
9
- } from './find-parent-node'
10
- import { getNodeType } from './get-node-type'
3
+ import { findParentNode, type FindParentNodeResult } from './find-parent-node.ts'
4
+ import { getNodeTypes } from './get-node-types.ts'
11
5
 
12
6
  /**
13
7
  * Finds the closest parent node that matches the given node type.
@@ -18,12 +12,12 @@ export function findParentNodeOfType(
18
12
  /**
19
13
  * The type of the node to find.
20
14
  */
21
- type: NodeType | string,
15
+ type: string | NodeType | string[] | NodeType[],
22
16
  /**
23
17
  * The position to start searching from.
24
18
  */
25
19
  $pos: ResolvedPos,
26
20
  ): FindParentNodeResult | undefined {
27
- const nodeType = getNodeType($pos.doc.type.schema, type)
28
- return findParentNode((node) => node.type === nodeType, $pos)
21
+ const nodeTypes = getNodeTypes($pos.doc.type.schema, type)
22
+ return findParentNode((node) => nodeTypes.includes(node.type), $pos)
29
23
  }
@@ -1,12 +1,8 @@
1
- import {
2
- describe,
3
- expect,
4
- it,
5
- } from 'vitest'
1
+ import { describe, expect, it } from 'vitest'
6
2
 
7
- import { setupTest } from '../testing'
3
+ import { setupTest } from '../testing/index.ts'
8
4
 
9
- import { findParentNode } from './find-parent-node'
5
+ import { findParentNode } from './find-parent-node.ts'
10
6
 
11
7
  describe('findParentNode', () => {
12
8
  it('finds parent node with cursor directly inside', () => {
@@ -1,7 +1,4 @@
1
- import type {
2
- ProseMirrorNode,
3
- ResolvedPos,
4
- } from '@prosekit/pm/model'
1
+ import type { ProseMirrorNode, ResolvedPos } from '@prosekit/pm/model'
5
2
 
6
3
  /**
7
4
  * @public
@@ -1,8 +1,4 @@
1
- import {
2
- TextSelection,
3
- type EditorState,
4
- type Selection,
5
- } from '@prosekit/pm/state'
1
+ import { TextSelection, type EditorState, type Selection } from '@prosekit/pm/state'
6
2
 
7
3
  export function getCustomSelection(
8
4
  state: EditorState,
@@ -1,4 +1,4 @@
1
- import { DOMDocumentNotFoundError } from '../error'
1
+ import { DOMDocumentNotFoundError } from '../error.ts'
2
2
 
3
3
  function findGlobalBrowserDocument() {
4
4
  if (typeof document !== 'undefined') {
@@ -1,9 +1,6 @@
1
- import type {
2
- MarkType,
3
- Schema,
4
- } from '@prosekit/pm/model'
1
+ import type { MarkType, Schema } from '@prosekit/pm/model'
5
2
 
6
- import { ProseKitError } from '../error'
3
+ import { ProseKitError } from '../error.ts'
7
4
 
8
5
  /**
9
6
  * @internal
@@ -1,9 +1,6 @@
1
- import type {
2
- NodeType,
3
- Schema,
4
- } from '@prosekit/pm/model'
1
+ import type { NodeType, Schema } from '@prosekit/pm/model'
5
2
 
6
- import { ProseKitError } from '../error'
3
+ import { ProseKitError } from '../error.ts'
7
4
 
8
5
  /**
9
6
  * @internal
@@ -1,9 +1,6 @@
1
- import type {
2
- NodeType,
3
- Schema,
4
- } from '@prosekit/pm/model'
1
+ import type { NodeType, Schema } from '@prosekit/pm/model'
5
2
 
6
- import { getNodeType } from './get-node-type'
3
+ import { getNodeType } from './get-node-type.ts'
7
4
 
8
5
  /**
9
6
  * @internal
@@ -1,10 +1,6 @@
1
- import type {
2
- Attrs,
3
- Mark,
4
- MarkType,
5
- } from '@prosekit/pm/model'
1
+ import type { Attrs, Mark, MarkType } from '@prosekit/pm/model'
6
2
 
7
- import { isSubset } from './is-subset'
3
+ import { isSubset } from './is-subset.ts'
8
4
 
9
5
  export function includesMark(
10
6
  marks: readonly Mark[],
@@ -1,8 +1,5 @@
1
1
  import type { ResolvedPos } from '@prosekit/pm/model'
2
- import type {
3
- EditorState,
4
- TextSelection,
5
- } from '@prosekit/pm/state'
2
+ import type { EditorState, TextSelection } from '@prosekit/pm/state'
6
3
  import type { EditorView } from '@prosekit/pm/view'
7
4
 
8
5
  /**
@@ -1,11 +1,8 @@
1
- import {
2
- expect,
3
- test,
4
- } from 'vitest'
1
+ import { expect, test } from 'vitest'
5
2
 
6
- import { setupTest } from '../testing'
3
+ import { setupTest } from '../testing/index.ts'
7
4
 
8
- import { isMarkAbsent } from './is-mark-absent'
5
+ import { isMarkAbsent } from './is-mark-absent.ts'
9
6
 
10
7
  test('isMarkAbsent', () => {
11
8
  const { editor, m, n } = setupTest()
@@ -1,10 +1,6 @@
1
- import type {
2
- Attrs,
3
- MarkType,
4
- ProseMirrorNode,
5
- } from '@prosekit/pm/model'
1
+ import type { Attrs, MarkType, ProseMirrorNode } from '@prosekit/pm/model'
6
2
 
7
- import { includesMark } from './includes-mark'
3
+ import { includesMark } from './includes-mark.ts'
8
4
 
9
5
  /**
10
6
  * Returns true if the given mark is missing in some part of the range.
@@ -1,12 +1,9 @@
1
- import type {
2
- Attrs,
3
- MarkType,
4
- } from '@prosekit/pm/model'
1
+ import type { Attrs, MarkType } from '@prosekit/pm/model'
5
2
  import type { EditorState } from '@prosekit/pm/state'
6
3
 
7
- import { getMarkType } from './get-mark-type'
8
- import { includesMark } from './includes-mark'
9
- import { isMarkAbsent } from './is-mark-absent'
4
+ import { getMarkType } from './get-mark-type.ts'
5
+ import { includesMark } from './includes-mark.ts'
6
+ import { isMarkAbsent } from './is-mark-absent.ts'
10
7
 
11
8
  /**
12
9
  * @internal
@@ -0,0 +1,109 @@
1
+ import { NodeSelection } from '@prosekit/pm/state'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import { setupTest } from '../testing/index.ts'
5
+
6
+ import { isNodeActive } from './is-node-active.ts'
7
+
8
+ describe('isNodeActive', () => {
9
+ it('should return true when cursor is in a node of the specified type', () => {
10
+ const { editor, n } = setupTest()
11
+ editor.set(n.doc(n.p('Hello <a>world')))
12
+
13
+ expect(isNodeActive(editor.state, 'paragraph')).toBe(true)
14
+ })
15
+
16
+ it('should return false when cursor is not in a node of the specified type', () => {
17
+ const { editor, n } = setupTest()
18
+ editor.set(n.doc(n.p('Hello <a>world')))
19
+
20
+ expect(isNodeActive(editor.state, 'codeBlock')).toBe(false)
21
+ })
22
+
23
+ it('should return true when cursor is in a nested node of the specified type', () => {
24
+ const { editor, n } = setupTest()
25
+ editor.set(n.doc(n.blockquote(n.p('Hello <a>world'))))
26
+
27
+ expect(isNodeActive(editor.state, 'blockquote')).toBe(true)
28
+ })
29
+
30
+ it('should return true when cursor is in a node with matching attributes', () => {
31
+ const { editor, n } = setupTest()
32
+ editor.set(n.doc(n.codeBlock({ language: 'typescript' }, '<a>code')))
33
+
34
+ expect(isNodeActive(editor.state, 'codeBlock', { language: 'typescript' })).toBe(true)
35
+ })
36
+
37
+ it('should return false when cursor is in a node with non-matching attributes', () => {
38
+ const { editor, n } = setupTest()
39
+ editor.set(n.doc(n.codeBlock({ language: 'typescript' }, '<a>code')))
40
+
41
+ expect(isNodeActive(editor.state, 'codeBlock', { language: 'javascript' })).toBe(false)
42
+ })
43
+
44
+ it('should return true when using NodeSelection with matching type', () => {
45
+ const { editor, n } = setupTest()
46
+ editor.set(n.doc(n.p('Hello world')))
47
+
48
+ const $pos = editor.state.doc.resolve(0)
49
+ const state = editor.state.apply(
50
+ editor.state.tr.setSelection(NodeSelection.create(editor.state.doc, $pos.pos)),
51
+ )
52
+
53
+ expect(isNodeActive(state, 'paragraph')).toBe(true)
54
+ })
55
+
56
+ it('should return true when using NodeSelection with matching type and attributes', () => {
57
+ const { editor, n } = setupTest()
58
+ editor.set(n.doc(n.codeBlock({ language: 'python' }, 'code')))
59
+
60
+ const $pos = editor.state.doc.resolve(0)
61
+ const state = editor.state.apply(
62
+ editor.state.tr.setSelection(NodeSelection.create(editor.state.doc, $pos.pos)),
63
+ )
64
+
65
+ expect(isNodeActive(state, 'codeBlock', { language: 'python' })).toBe(true)
66
+ })
67
+
68
+ it('should return false when using NodeSelection with non-matching attributes', () => {
69
+ const { editor, n } = setupTest()
70
+ editor.set(n.doc(n.codeBlock({ language: 'python' }, 'code')))
71
+
72
+ const $pos = editor.state.doc.resolve(0)
73
+ const state = editor.state.apply(
74
+ editor.state.tr.setSelection(NodeSelection.create(editor.state.doc, $pos.pos)),
75
+ )
76
+
77
+ expect(isNodeActive(state, 'codeBlock', { language: 'javascript' })).toBe(false)
78
+ })
79
+
80
+ it('should work with NodeType instead of string', () => {
81
+ const { editor, n } = setupTest()
82
+ editor.set(n.doc(n.p('Hello <a>world')))
83
+
84
+ expect(isNodeActive(editor.state, editor.state.schema.nodes.paragraph)).toBe(true)
85
+ })
86
+
87
+ it('should return true when node is at any depth in the hierarchy', () => {
88
+ const { editor, n } = setupTest()
89
+ editor.set(n.doc(n.blockquote(n.p('Hello <a>world'))))
90
+
91
+ expect(isNodeActive(editor.state, 'paragraph')).toBe(true)
92
+ expect(isNodeActive(editor.state, 'blockquote')).toBe(true)
93
+ expect(isNodeActive(editor.state, 'doc')).toBe(true)
94
+ })
95
+
96
+ it('should return true when attributes is null', () => {
97
+ const { editor, n } = setupTest()
98
+ editor.set(n.doc(n.codeBlock({ language: 'typescript' }, '<a>code')))
99
+
100
+ expect(isNodeActive(editor.state, 'codeBlock', null)).toBe(true)
101
+ })
102
+
103
+ it('should match partial attributes', () => {
104
+ const { editor, n } = setupTest()
105
+ editor.set(n.doc(n.codeBlock({ language: 'typescript', lineNumbers: true }, '<a>code')))
106
+
107
+ expect(isNodeActive(editor.state, 'codeBlock', { language: 'typescript' })).toBe(true)
108
+ })
109
+ })
@@ -1,25 +1,34 @@
1
- import type {
2
- Attrs,
3
- NodeType,
4
- } from '@prosekit/pm/model'
1
+ import type { Attrs, NodeType, ProseMirrorNode } from '@prosekit/pm/model'
5
2
  import type { EditorState } from '@prosekit/pm/state'
6
3
 
7
- import { attrsMatch } from './attrs-match'
8
- import { getNodeType } from './get-node-type'
4
+ import { attrsMatch } from './attrs-match.ts'
5
+ import { getNodeType } from './get-node-type.ts'
6
+ import { isNodeSelection } from './type-assertion.ts'
9
7
 
8
+ /**
9
+ * @internal
10
+ */
10
11
  export function isNodeActive(
11
12
  state: EditorState,
12
13
  type: string | NodeType,
13
14
  attrs?: Attrs | null,
14
15
  ): boolean {
15
- const $pos = state.selection.$from
16
- const nodeType = getNodeType(state.schema, type)
16
+ const { selection, schema } = state
17
+ const $pos = selection.$from
18
+ const nodeType = getNodeType(schema, type)
19
+
20
+ if (isNodeSelection(selection) && checkNode(selection.node, nodeType, attrs)) {
21
+ return true
22
+ }
17
23
 
18
24
  for (let depth = $pos.depth; depth >= 0; depth--) {
19
- const node = $pos.node(depth)
20
- if (node.type === nodeType && (!attrs || attrsMatch(node, attrs))) {
25
+ if (checkNode($pos.node(depth), nodeType, attrs)) {
21
26
  return true
22
27
  }
23
28
  }
24
29
  return false
25
30
  }
31
+
32
+ function checkNode(node: ProseMirrorNode, nodeType: NodeType, attrs?: Attrs | null): boolean {
33
+ return node.type === nodeType && (!attrs || attrsMatch(node, attrs))
34
+ }
@@ -1,9 +1,6 @@
1
- import {
2
- expect,
3
- test,
4
- } from 'vitest'
1
+ import { expect, test } from 'vitest'
5
2
 
6
- import { isSubset } from './is-subset'
3
+ import { isSubset } from './is-subset.ts'
7
4
 
8
5
  test('isSubset', () => {
9
6
  expect(isSubset({ a: 1, b: 2 }, { a: 1, b: 2, c: 3 })).toBe(true)
@@ -1,10 +1,6 @@
1
- import {
2
- expect,
3
- test,
4
- vi,
5
- } from 'vitest'
1
+ import { expect, test, vi } from 'vitest'
6
2
 
7
- import { maybeRun } from './maybe-run'
3
+ import { maybeRun } from './maybe-run.ts'
8
4
 
9
5
  test('executes function argument', () => {
10
6
  const fn = vi.fn((x: number) => x + 1)
@@ -1,9 +1,6 @@
1
- import {
2
- expect,
3
- test,
4
- } from 'vitest'
1
+ import { expect, test } from 'vitest'
5
2
 
6
- import { mergeObjects } from './merge-objects'
3
+ import { mergeObjects } from './merge-objects.ts'
7
4
 
8
5
  // basic merge
9
6
  test('merge simple objects', () => {
@@ -1,5 +1,6 @@
1
- import { removeUndefinedValues } from './remove-undefined-values'
2
- import { isNotNullish } from './type-assertion'
1
+ import { isNotNullish } from '@ocavue/utils'
2
+
3
+ import { removeUndefinedValues } from './remove-undefined-values.ts'
3
4
 
4
5
  export function mergeObjects<T extends object>(
5
6
  ...objects: Array<Partial<T> | null | undefined>
@@ -1,9 +1,6 @@
1
- import type {
2
- MarkSpec,
3
- NodeSpec,
4
- } from '@prosekit/pm/model'
1
+ import type { MarkSpec, NodeSpec } from '@prosekit/pm/model'
5
2
 
6
- import { mergeObjects } from './merge-objects'
3
+ import { mergeObjects } from './merge-objects.ts'
7
4
 
8
5
  function mergeSpecs(a: NodeSpec, b: NodeSpec): NodeSpec
9
6
  function mergeSpecs(a: MarkSpec, b: MarkSpec): MarkSpec
@@ -1,9 +1,6 @@
1
- import {
2
- expect,
3
- test,
4
- } from 'vitest'
1
+ import { expect, test } from 'vitest'
5
2
 
6
- import { objectEqual } from './object-equal'
3
+ import { objectEqual } from './object-equal.ts'
7
4
 
8
5
  test('objects with same keys and values are equal', () => {
9
6
  expect(objectEqual({ a: 1, b: 2 }, { a: 1, b: 2 })).toBe(true)
@@ -1,11 +1,7 @@
1
1
  import type { DOMOutputSpec } from '@prosekit/pm/model'
2
- import {
3
- describe,
4
- expect,
5
- it,
6
- } from 'vitest'
2
+ import { describe, expect, it } from 'vitest'
7
3
 
8
- import { insertOutputSpecAttrs } from './output-spec'
4
+ import { insertOutputSpecAttrs } from './output-spec.ts'
9
5
 
10
6
  describe('insertOutputSpecAttrs', () => {
11
7
  it('should insert attrs into an array without attributes', () => {
@@ -1,13 +1,5 @@
1
- import { isElementLike } from '@ocavue/utils'
2
- import type {
3
- DOMOutputSpec,
4
- Mark,
5
- ParseRule,
6
- ProseMirrorNode,
7
- TagParseRule,
8
- } from '@prosekit/pm/model'
9
-
10
- import { isNotNullish } from './type-assertion'
1
+ import { isElementLike, isNotNullish } from '@ocavue/utils'
2
+ import type { DOMOutputSpec, Mark, ParseRule, ProseMirrorNode, TagParseRule } from '@prosekit/pm/model'
11
3
 
12
4
  interface AttrOptions {
13
5
  attr: string