@prosekit/core 0.8.2 → 0.8.4

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 (168) hide show
  1. package/dist/editor-CfkZ4TNU.d.ts +748 -0
  2. package/dist/editor-CfkZ4TNU.d.ts.map +1 -0
  3. package/dist/{editor-DbMrpnmL.js → editor-CizSwUN8.js} +102 -192
  4. package/dist/editor-CizSwUN8.js.map +1 -0
  5. package/dist/prosekit-core-test.d.ts +20 -19
  6. package/dist/prosekit-core-test.d.ts.map +1 -0
  7. package/dist/prosekit-core-test.js +4 -5
  8. package/dist/prosekit-core-test.js.map +1 -0
  9. package/dist/prosekit-core.d.ts +782 -757
  10. package/dist/prosekit-core.d.ts.map +1 -0
  11. package/dist/prosekit-core.js +30 -45
  12. package/dist/prosekit-core.js.map +1 -0
  13. package/package.json +14 -11
  14. package/src/commands/add-mark.ts +53 -0
  15. package/src/commands/expand-mark.ts +96 -0
  16. package/src/commands/insert-default-block.spec.ts +102 -0
  17. package/src/commands/insert-default-block.ts +49 -0
  18. package/src/commands/insert-node.ts +71 -0
  19. package/src/commands/insert-text.ts +24 -0
  20. package/src/commands/remove-mark.ts +54 -0
  21. package/src/commands/remove-node.ts +43 -0
  22. package/src/commands/select-all.ts +16 -0
  23. package/src/commands/set-block-type.ts +64 -0
  24. package/src/commands/set-node-attrs.ts +68 -0
  25. package/src/commands/toggle-mark.ts +65 -0
  26. package/src/commands/toggle-node.ts +47 -0
  27. package/src/commands/toggle-wrap.spec.ts +35 -0
  28. package/src/commands/toggle-wrap.ts +42 -0
  29. package/src/commands/unset-block-type.spec.ts +49 -0
  30. package/src/commands/unset-block-type.ts +84 -0
  31. package/src/commands/unset-mark.spec.ts +35 -0
  32. package/src/commands/unset-mark.ts +38 -0
  33. package/src/commands/wrap.ts +50 -0
  34. package/src/editor/action.spec.ts +143 -0
  35. package/src/editor/action.ts +248 -0
  36. package/src/editor/editor.spec.ts +186 -0
  37. package/src/editor/editor.ts +563 -0
  38. package/src/editor/union.spec.ts +108 -0
  39. package/src/editor/union.ts +47 -0
  40. package/src/editor/with-priority.ts +25 -0
  41. package/src/error.ts +28 -0
  42. package/src/extensions/clipboard-serializer.ts +107 -0
  43. package/src/extensions/command.ts +121 -0
  44. package/src/extensions/default-state.spec.ts +60 -0
  45. package/src/extensions/default-state.ts +76 -0
  46. package/src/extensions/doc.ts +31 -0
  47. package/src/extensions/events/doc-change.ts +34 -0
  48. package/src/extensions/events/dom-event.spec.ts +70 -0
  49. package/src/extensions/events/dom-event.ts +117 -0
  50. package/src/extensions/events/editor-event.ts +293 -0
  51. package/src/extensions/events/focus.spec.ts +50 -0
  52. package/src/extensions/events/focus.ts +28 -0
  53. package/src/extensions/events/plugin-view.ts +132 -0
  54. package/src/extensions/history.ts +81 -0
  55. package/src/extensions/keymap-base.ts +60 -0
  56. package/src/extensions/keymap.spec.ts +89 -0
  57. package/src/extensions/keymap.ts +96 -0
  58. package/src/extensions/mark-spec.spec.ts +177 -0
  59. package/src/extensions/mark-spec.ts +181 -0
  60. package/src/extensions/mark-view-effect.ts +85 -0
  61. package/src/extensions/mark-view.ts +43 -0
  62. package/src/extensions/node-spec.spec.ts +224 -0
  63. package/src/extensions/node-spec.ts +199 -0
  64. package/src/extensions/node-view-effect.ts +85 -0
  65. package/src/extensions/node-view.ts +43 -0
  66. package/src/extensions/paragraph.ts +61 -0
  67. package/src/extensions/plugin.ts +91 -0
  68. package/src/extensions/text.ts +34 -0
  69. package/src/facets/base-extension.ts +54 -0
  70. package/src/facets/command.ts +21 -0
  71. package/src/facets/facet-extension.spec.ts +173 -0
  72. package/src/facets/facet-extension.ts +53 -0
  73. package/src/facets/facet-node.spec.ts +265 -0
  74. package/src/facets/facet-node.ts +185 -0
  75. package/src/facets/facet-types.ts +9 -0
  76. package/src/facets/facet.spec.ts +76 -0
  77. package/src/facets/facet.ts +84 -0
  78. package/src/facets/root.ts +44 -0
  79. package/src/facets/schema-spec.ts +30 -0
  80. package/src/facets/schema.ts +26 -0
  81. package/src/facets/state.ts +57 -0
  82. package/src/facets/union-extension.ts +41 -0
  83. package/src/index.ts +302 -0
  84. package/src/test/index.ts +4 -0
  85. package/src/test/test-builder.ts +68 -0
  86. package/src/test/test-editor.spec.ts +104 -0
  87. package/src/test/test-editor.ts +113 -0
  88. package/src/testing/index.ts +283 -0
  89. package/src/testing/keyboard.ts +5 -0
  90. package/src/types/any-function.ts +4 -0
  91. package/src/types/assert-type-equal.ts +8 -0
  92. package/src/types/attrs.ts +32 -0
  93. package/src/types/base-node-view-options.ts +33 -0
  94. package/src/types/dom-node.ts +1 -0
  95. package/src/types/extension-command.ts +52 -0
  96. package/src/types/extension-mark.ts +15 -0
  97. package/src/types/extension-node.ts +15 -0
  98. package/src/types/extension.spec.ts +56 -0
  99. package/src/types/extension.ts +168 -0
  100. package/src/types/model.ts +54 -0
  101. package/src/types/object-entries.ts +13 -0
  102. package/src/types/pick-string-literal.spec.ts +10 -0
  103. package/src/types/pick-string-literal.ts +6 -0
  104. package/src/types/pick-sub-type.spec.ts +20 -0
  105. package/src/types/pick-sub-type.ts +6 -0
  106. package/src/types/priority.ts +12 -0
  107. package/src/types/setter.ts +4 -0
  108. package/src/types/simplify-deeper.spec.ts +40 -0
  109. package/src/types/simplify-deeper.ts +6 -0
  110. package/src/types/simplify-union.spec.ts +21 -0
  111. package/src/types/simplify-union.ts +11 -0
  112. package/src/utils/array-grouping.spec.ts +29 -0
  113. package/src/utils/array-grouping.ts +25 -0
  114. package/src/utils/array.ts +21 -0
  115. package/src/utils/assert.ts +13 -0
  116. package/src/utils/attrs-match.ts +20 -0
  117. package/src/utils/can-use-regex-lookbehind.ts +12 -0
  118. package/src/utils/clsx.spec.ts +14 -0
  119. package/src/utils/clsx.ts +12 -0
  120. package/src/utils/collect-children.ts +21 -0
  121. package/src/utils/collect-nodes.ts +37 -0
  122. package/src/utils/combine-event-handlers.spec.ts +27 -0
  123. package/src/utils/combine-event-handlers.ts +27 -0
  124. package/src/utils/contains-inline-node.ts +17 -0
  125. package/src/utils/deep-equals.spec.ts +26 -0
  126. package/src/utils/deep-equals.ts +29 -0
  127. package/src/utils/default-block-at.ts +15 -0
  128. package/src/utils/editor-content.spec.ts +47 -0
  129. package/src/utils/editor-content.ts +77 -0
  130. package/src/utils/env.ts +6 -0
  131. package/src/utils/find-parent-node-of-type.ts +29 -0
  132. package/src/utils/find-parent-node.spec.ts +68 -0
  133. package/src/utils/find-parent-node.ts +55 -0
  134. package/src/utils/get-custom-selection.ts +19 -0
  135. package/src/utils/get-dom-api.ts +56 -0
  136. package/src/utils/get-id.spec.ts +14 -0
  137. package/src/utils/get-id.ts +13 -0
  138. package/src/utils/get-mark-type.ts +20 -0
  139. package/src/utils/get-node-type.ts +20 -0
  140. package/src/utils/get-node-types.ts +19 -0
  141. package/src/utils/includes-mark.ts +18 -0
  142. package/src/utils/is-at-block-start.ts +26 -0
  143. package/src/utils/is-in-code-block.ts +18 -0
  144. package/src/utils/is-mark-absent.spec.ts +53 -0
  145. package/src/utils/is-mark-absent.ts +42 -0
  146. package/src/utils/is-mark-active.ts +27 -0
  147. package/src/utils/is-node-active.ts +25 -0
  148. package/src/utils/is-subset.spec.ts +12 -0
  149. package/src/utils/is-subset.ts +11 -0
  150. package/src/utils/maybe-run.spec.ts +39 -0
  151. package/src/utils/maybe-run.ts +11 -0
  152. package/src/utils/merge-objects.spec.ts +30 -0
  153. package/src/utils/merge-objects.ts +11 -0
  154. package/src/utils/merge-specs.ts +35 -0
  155. package/src/utils/object-equal.spec.ts +26 -0
  156. package/src/utils/object-equal.ts +28 -0
  157. package/src/utils/output-spec.test.ts +95 -0
  158. package/src/utils/output-spec.ts +130 -0
  159. package/src/utils/parse.spec.ts +46 -0
  160. package/src/utils/parse.ts +321 -0
  161. package/src/utils/remove-undefined-values.spec.ts +15 -0
  162. package/src/utils/remove-undefined-values.ts +9 -0
  163. package/src/utils/set-selection-around.ts +11 -0
  164. package/src/utils/type-assertion.ts +91 -0
  165. package/src/utils/unicode.spec.ts +10 -0
  166. package/src/utils/unicode.ts +4 -0
  167. package/src/utils/with-skip-code-block.ts +15 -0
  168. package/dist/editor-CjVyjJqw.d.ts +0 -739
@@ -0,0 +1,54 @@
1
+ /**
2
+ * A JSON representation of the prosemirror node.
3
+ *
4
+ * @public
5
+ */
6
+ export interface NodeJSON {
7
+ type: string
8
+ marks?: Array<{ type: string; attrs?: Record<string, any> }>
9
+ text?: string
10
+ content?: NodeJSON[]
11
+ attrs?: Record<string, any>
12
+ }
13
+
14
+ /**
15
+ * A JSON representation of the prosemirror selection.
16
+ *
17
+ * @public
18
+ */
19
+ export interface SelectionJSON {
20
+ anchor: number
21
+ head: number
22
+ type: string
23
+ }
24
+
25
+ /**
26
+ * A JSON representation of the prosemirror state.
27
+ *
28
+ * @public
29
+ */
30
+ export interface StateJSON {
31
+ /**
32
+ * The main `ProseMirror` doc.
33
+ */
34
+ doc: NodeJSON
35
+
36
+ /**
37
+ * The current selection.
38
+ */
39
+ selection: SelectionJSON
40
+ }
41
+
42
+ /**
43
+ * A JSON representation of the prosemirror step.
44
+ *
45
+ * @public
46
+ */
47
+ export interface StepJSON {
48
+ /**
49
+ * The type of the step.
50
+ */
51
+ stepType: string
52
+
53
+ [x: string]: unknown
54
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @internal
3
+ *
4
+ * @example
5
+ *
6
+ * ```
7
+ * type MyObject = { a: 1; b: 'B' }
8
+ * type MyEntries = ObjectEntries<MyObject>
9
+ * // ^ ["a", 1] | ["b", "B"]
10
+ */
11
+ export type ObjectEntries<T extends Record<string, any>> = {
12
+ [K in keyof T]: [K, T[K]]
13
+ }[keyof T]
@@ -0,0 +1,10 @@
1
+ import { test } from 'vitest'
2
+
3
+ import { assertTypeEqual } from './assert-type-equal'
4
+ import type { PickStringLiteral } from './pick-string-literal'
5
+
6
+ test('PickStringLiteral', () => {
7
+ assertTypeEqual<PickStringLiteral<'foo'>, 'foo'>(true)
8
+ assertTypeEqual<PickStringLiteral<'foo' | 'bar'>, 'foo' | 'bar'>(true)
9
+ assertTypeEqual<PickStringLiteral<string>, never>(true)
10
+ })
@@ -0,0 +1,6 @@
1
+ import type { PickSubType } from './pick-sub-type'
2
+
3
+ /**
4
+ * @internal
5
+ */
6
+ export type PickStringLiteral<T> = PickSubType<T, string>
@@ -0,0 +1,20 @@
1
+ import { test } from 'vitest'
2
+
3
+ import { assertTypeEqual } from './assert-type-equal'
4
+ import type { PickSubType } from './pick-sub-type'
5
+
6
+ test('PickSubType', () => {
7
+ assertTypeEqual<PickSubType<'abc', string>, 'abc'>(true)
8
+ assertTypeEqual<PickSubType<'abc' | 'def', string>, 'abc' | 'def'>(true)
9
+ assertTypeEqual<PickSubType<123, string>, never>(true)
10
+ assertTypeEqual<PickSubType<string, string>, never>(true)
11
+
12
+ assertTypeEqual<PickSubType<{ foo: 1 }, Record<string, number>>, { foo: 1 }>(
13
+ true,
14
+ )
15
+ assertTypeEqual<PickSubType<{ foo: 1 }, Record<string, string>>, never>(true)
16
+ assertTypeEqual<
17
+ PickSubType<Record<string, number>, Record<string, string>>,
18
+ never
19
+ >(true)
20
+ })
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ export type PickSubType<Type, ParentType> = Type extends ParentType ? [ParentType] extends [Type] ? never
5
+ : Type
6
+ : never
@@ -0,0 +1,12 @@
1
+ /**
2
+ * ProseKit extension priority.
3
+ *
4
+ * @public
5
+ */
6
+ export enum Priority {
7
+ lowest = 0,
8
+ low = 1,
9
+ default = 2,
10
+ high = 3,
11
+ highest = 4,
12
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ export type Setter<T> = (value: T) => void
@@ -0,0 +1,40 @@
1
+ import type { Simplify } from 'type-fest'
2
+ import { test } from 'vitest'
3
+
4
+ import { assertTypeEqual } from './assert-type-equal'
5
+ import type { SimplifyDeeper } from './simplify-deeper'
6
+
7
+ test('SimplifyDeeper', () => {
8
+ type T1 = {
9
+ nodeA: {
10
+ attr1: string
11
+ } & {
12
+ attr2: string
13
+ }
14
+ } & {
15
+ nodeB: {
16
+ attr3: string
17
+ } & {
18
+ attr4: string
19
+ }
20
+ }
21
+ interface T2 {
22
+ nodeA: {
23
+ attr1: string
24
+ attr2: string
25
+ }
26
+ nodeB: {
27
+ attr3: string
28
+ attr4: string
29
+ }
30
+ }
31
+
32
+ assertTypeEqual<T1, T1>(true)
33
+ assertTypeEqual<T1, T2>(false)
34
+
35
+ assertTypeEqual<T1, SimplifyDeeper<T1>>(false)
36
+ assertTypeEqual<T2, SimplifyDeeper<T1>>(true)
37
+
38
+ assertTypeEqual<T1, Simplify<T1>>(false)
39
+ assertTypeEqual<T2, Simplify<T1>>(false)
40
+ })
@@ -0,0 +1,6 @@
1
+ import type { Simplify } from 'type-fest'
2
+
3
+ /**
4
+ * @internal
5
+ */
6
+ export type SimplifyDeeper<T> = { [KeyType in keyof T]: Simplify<T[KeyType]> }
@@ -0,0 +1,21 @@
1
+ import { test } from 'vitest'
2
+
3
+ import { assertTypeEqual } from './assert-type-equal'
4
+ import type { SimplifyUnion } from './simplify-union'
5
+
6
+ test('SimplifyUnion', () => {
7
+ type T1 = { a: string } | { b: string }
8
+
9
+ interface T2 {
10
+ a: string
11
+ b: string
12
+ }
13
+
14
+ type T3 = SimplifyUnion<T1>
15
+
16
+ assertTypeEqual<T1, T1>(true)
17
+ assertTypeEqual<T1, T2>(false)
18
+
19
+ assertTypeEqual<T1, T3>(false)
20
+ assertTypeEqual<T2, T3>(true)
21
+ })
@@ -0,0 +1,11 @@
1
+ import type {
2
+ Simplify,
3
+ UnionToIntersection,
4
+ } from 'type-fest'
5
+
6
+ /**
7
+ * @internal
8
+ */
9
+ export type SimplifyUnion<T> = Simplify<
10
+ UnionToIntersection<T extends undefined ? never : T>
11
+ >
@@ -0,0 +1,29 @@
1
+ import {
2
+ expect,
3
+ test,
4
+ } from 'vitest'
5
+
6
+ import {
7
+ groupBy,
8
+ groupEntries,
9
+ } from './array-grouping'
10
+
11
+ test('groupEntries', () => {
12
+ expect(
13
+ groupEntries([
14
+ ['a', 1],
15
+ ['b', 2],
16
+ ['a', 3],
17
+ ]),
18
+ ).toEqual({
19
+ a: [1, 3],
20
+ b: [2],
21
+ })
22
+ })
23
+
24
+ test('groupBy', () => {
25
+ expect(groupBy([1, 2, 3, 4, 5], (n) => n % 2)).toEqual({
26
+ 0: [2, 4],
27
+ 1: [1, 3, 5],
28
+ })
29
+ })
@@ -0,0 +1,25 @@
1
+ import type { ObjectEntries } from '../types/object-entries'
2
+
3
+ export function groupBy<K extends PropertyKey, T>(
4
+ items: Iterable<T>,
5
+ keySelector: (item: T) => K,
6
+ ): Partial<Record<K, T[]>> {
7
+ const result: Partial<Record<K, T[]>> = {}
8
+ for (const item of items) {
9
+ const key = keySelector(item)
10
+ const values = (result[key] ||= [])
11
+ values.push(item)
12
+ }
13
+ return result
14
+ }
15
+
16
+ export function groupEntries<T extends Record<string, any>>(
17
+ entries: ObjectEntries<T>[],
18
+ ): { [K in keyof T]?: T[K][] } {
19
+ const result: { [K in keyof T]?: T[K][] } = {}
20
+ for (const [key, value] of entries) {
21
+ const values = (result[key] ||= [])
22
+ values.push(value)
23
+ }
24
+ return result
25
+ }
@@ -0,0 +1,21 @@
1
+ export function uniqPush<T>(prev: readonly T[], next: readonly T[]): T[] {
2
+ const result = [...prev]
3
+
4
+ for (const item of next) {
5
+ if (!result.includes(item)) {
6
+ result.push(item)
7
+ }
8
+ }
9
+ return result
10
+ }
11
+
12
+ /**
13
+ * @internal
14
+ */
15
+ export function arraySubtract<T>(a: T[], b: T[]): T[] {
16
+ return a.filter((x) => !b.includes(x))
17
+ }
18
+
19
+ export function toReversed<T>(arr: T[]): T[] {
20
+ return arr.toReversed?.() ?? [...arr].reverse()
21
+ }
@@ -0,0 +1,13 @@
1
+ import { ProseKitError } from '../error'
2
+
3
+ /**
4
+ * @internal
5
+ */
6
+ export function assert(
7
+ condition: unknown,
8
+ message = 'Assertion failed',
9
+ ): asserts condition {
10
+ if (!condition) {
11
+ throw new ProseKitError(message)
12
+ }
13
+ }
@@ -0,0 +1,20 @@
1
+ import type {
2
+ Attrs,
3
+ Mark,
4
+ ProseMirrorNode,
5
+ } from '@prosekit/pm/model'
6
+
7
+ export function attrsMatch(
8
+ nodeOrMark: ProseMirrorNode | Mark,
9
+ attrs: Attrs,
10
+ ): boolean {
11
+ const currentAttrs = nodeOrMark.attrs
12
+
13
+ for (const [key, value] of Object.entries(attrs)) {
14
+ if (currentAttrs[key] !== value) {
15
+ return false
16
+ }
17
+ }
18
+
19
+ return true
20
+ }
@@ -0,0 +1,12 @@
1
+ import { once } from '@ocavue/utils'
2
+
3
+ /**
4
+ * Checks if the browser supports [regex lookbehind assertion](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Lookbehind_assertion).
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
+ })
@@ -0,0 +1,14 @@
1
+ import {
2
+ expect,
3
+ test,
4
+ } from 'vitest'
5
+
6
+ import { clsx } from './clsx'
7
+
8
+ test('joins class names', () => {
9
+ expect(clsx('foo', 'bar')).toBe('foo bar')
10
+ })
11
+
12
+ test('filters falsy values', () => {
13
+ expect(clsx('foo', false, null, undefined, 'baz')).toBe('foo baz')
14
+ })
@@ -0,0 +1,12 @@
1
+ import clsxLite from 'clsx/lite'
2
+
3
+ /**
4
+ * A utility for constructing `className` strings conditionally.
5
+ *
6
+ * It is a re-export of [clsx/lite](https://www.npmjs.com/package/clsx) with stricter types.
7
+ *
8
+ * @public
9
+ */
10
+ export const clsx: (
11
+ ...args: Array<string | boolean | null | undefined>
12
+ ) => string = clsxLite
@@ -0,0 +1,21 @@
1
+ import type {
2
+ Fragment,
3
+ ProseMirrorNode,
4
+ } from '@prosekit/pm/model'
5
+
6
+ /**
7
+ * Collects all children of a node or a fragment, and returns them as an array.
8
+ *
9
+ * @deprecated Use `node.children` or `fragment.content` instead.
10
+ *
11
+ * @hidden
12
+ */
13
+ export function collectChildren(
14
+ parent: ProseMirrorNode | Fragment,
15
+ ): ProseMirrorNode[] {
16
+ const children: ProseMirrorNode[] = []
17
+ for (let i = 0; i < parent.childCount; i++) {
18
+ children.push(parent.child(i))
19
+ }
20
+ return children
21
+ }
@@ -0,0 +1,37 @@
1
+ import {
2
+ ProseMirrorFragment,
3
+ ProseMirrorNode,
4
+ } from '@prosekit/pm/model'
5
+
6
+ import { ProseKitError } from '../error'
7
+
8
+ /**
9
+ * @hidden
10
+ *
11
+ * @deprecated
12
+ */
13
+ export type NodeContent = ProseMirrorNode | ProseMirrorFragment | NodeContent[]
14
+
15
+ /**
16
+ * Collects all nodes from a given content.
17
+ *
18
+ * @deprecated Use `collectChildren` instead.
19
+ *
20
+ * @hidden
21
+ */
22
+ export function collectNodes(content: NodeContent): ProseMirrorNode[] {
23
+ if (Array.isArray(content)) {
24
+ return content.flatMap(collectNodes)
25
+ }
26
+ if (content instanceof ProseMirrorNode) {
27
+ return [content]
28
+ }
29
+ if (content instanceof ProseMirrorFragment) {
30
+ const nodes: ProseMirrorNode[] = []
31
+ for (let i = 0; i < content.childCount; i++) {
32
+ nodes.push(content.child(i))
33
+ }
34
+ return nodes
35
+ }
36
+ throw new ProseKitError(`Invalid node content: ${typeof content}`)
37
+ }
@@ -0,0 +1,27 @@
1
+ import {
2
+ expect,
3
+ test,
4
+ vi,
5
+ } from 'vitest'
6
+
7
+ import { combineEventHandlers } from './combine-event-handlers'
8
+
9
+ test('runs handlers in reverse order and stops on true', () => {
10
+ const handler1 = vi.fn(() => false)
11
+ const handler2 = vi.fn(() => true)
12
+ const handler3 = vi.fn(() => false)
13
+ const [setHandlers, run] = combineEventHandlers<() => boolean>()
14
+ setHandlers([handler1, handler2, handler3])
15
+ expect(run()).toBe(true)
16
+ expect(handler3).toHaveBeenCalled()
17
+ expect(handler2).toHaveBeenCalled()
18
+ expect(handler1).not.toHaveBeenCalled()
19
+ })
20
+
21
+ test('returns false when all handlers return false', () => {
22
+ const handler1 = vi.fn(() => false)
23
+ const handler2 = vi.fn(() => false)
24
+ const [setHandlers, run] = combineEventHandlers<() => boolean>()
25
+ setHandlers([handler1, handler2])
26
+ expect(run()).toBe(false)
27
+ })
@@ -0,0 +1,27 @@
1
+ import { toReversed } from './array'
2
+
3
+ export function combineEventHandlers<
4
+ Handler extends (...args: any[]) => boolean | void,
5
+ Args extends Parameters<Handler> = Parameters<Handler>,
6
+ >(): [
7
+ setHandlers: (eventHandlers: Handler[]) => void,
8
+ combinedEventHandler: (...args: Args) => boolean,
9
+ ] {
10
+ let handlers: Handler[] = []
11
+
12
+ function setHandlers(eventHandlers: Handler[]): void {
13
+ // The handlers at the end have a higher priority.
14
+ handlers = toReversed(eventHandlers)
15
+ }
16
+
17
+ function combinedEventHandler(...args: Args): boolean {
18
+ for (const handler of handlers) {
19
+ if (handler(...args)) {
20
+ return true
21
+ }
22
+ }
23
+ return false
24
+ }
25
+
26
+ return [setHandlers, combinedEventHandler] as const
27
+ }
@@ -0,0 +1,17 @@
1
+ import type { ProseMirrorNode } from '@prosekit/pm/model'
2
+
3
+ /**
4
+ * @internal
5
+ */
6
+ export function containsInlineNode(
7
+ doc: ProseMirrorNode,
8
+ from: number,
9
+ to: number,
10
+ ): boolean {
11
+ let found = false
12
+ doc.nodesBetween(from, to, (node) => {
13
+ if (found) return false
14
+ if (node.isInline) found = true
15
+ })
16
+ return found
17
+ }
@@ -0,0 +1,26 @@
1
+ import OrderedMap from 'orderedmap'
2
+ import {
3
+ expect,
4
+ test,
5
+ } from 'vitest'
6
+
7
+ import { deepEquals } from './deep-equals'
8
+
9
+ test('arrays with equal values are equal', () => {
10
+ expect(deepEquals([1, 2], [1, 2])).toBe(true)
11
+ expect(deepEquals([1, 2], [2, 1])).toBe(false)
12
+ })
13
+
14
+ test('nested objects', () => {
15
+ const a = { a: 1, b: { c: 3 } }
16
+ const b = { a: 1, b: { c: 3 } }
17
+ const c = { a: 1, b: { c: 4 } }
18
+ expect(deepEquals(a, b)).toBe(true)
19
+ expect(deepEquals(a, c)).toBe(false)
20
+ })
21
+
22
+ test('ordered maps', () => {
23
+ const map1 = OrderedMap.from({ a: 1, b: 2 })
24
+ const map2 = OrderedMap.from({ a: 1, b: 2 })
25
+ expect(deepEquals(map1, map2)).toBe(true)
26
+ })
@@ -0,0 +1,29 @@
1
+ import OrderedMap from 'orderedmap'
2
+
3
+ export function deepEquals<T>(a: T, b: T): boolean {
4
+ if (a === b) {
5
+ return true
6
+ }
7
+
8
+ if (!a || !b) {
9
+ return false
10
+ }
11
+
12
+ if (Array.isArray(a) && Array.isArray(b)) {
13
+ return a.length === b.length && a.every((x, i) => deepEquals(x, b[i]))
14
+ }
15
+
16
+ if (a instanceof OrderedMap && b instanceof OrderedMap) {
17
+ return a.size === b.size && deepEquals(a.toObject(), b.toObject())
18
+ }
19
+
20
+ if (typeof a === 'object' && typeof b === 'object') {
21
+ const aKeys = Object.keys(a)
22
+ const bKeys = Object.keys(b)
23
+ return (
24
+ aKeys.length === bKeys.length
25
+ && aKeys.every((key) => deepEquals(a[key as keyof T], b[key as keyof T]))
26
+ )
27
+ }
28
+ return false
29
+ }
@@ -0,0 +1,15 @@
1
+ import type {
2
+ ContentMatch,
3
+ NodeType,
4
+ } from '@prosekit/pm/model'
5
+
6
+ /**
7
+ * @internal
8
+ */
9
+ export function defaultBlockAt(match: ContentMatch): NodeType | null {
10
+ for (let i = 0; i < match.edgeCount; i++) {
11
+ const { type } = match.edge(i)
12
+ if (type.isTextblock && !type.hasRequiredAttrs()) return type
13
+ }
14
+ return null
15
+ }
@@ -0,0 +1,47 @@
1
+ import { AllSelection } from '@prosekit/pm/state'
2
+ import {
3
+ expect,
4
+ test,
5
+ } from 'vitest'
6
+
7
+ import { setupTest } from '../testing'
8
+
9
+ import { getEditorSelection } from './editor-content'
10
+
11
+ test('getEditorSelection', () => {
12
+ const { n } = setupTest()
13
+ const doc = n.doc(n.paragraph('hello world'))
14
+ expect(getEditorSelection(doc, 'start').toJSON()).toMatchInlineSnapshot(`
15
+ {
16
+ "anchor": 1,
17
+ "head": 1,
18
+ "type": "text",
19
+ }
20
+ `)
21
+ expect(getEditorSelection(doc, 'end').toJSON()).toMatchInlineSnapshot(`
22
+ {
23
+ "anchor": 12,
24
+ "head": 12,
25
+ "type": "text",
26
+ }
27
+ `)
28
+ expect(
29
+ getEditorSelection(doc, {
30
+ anchor: 4,
31
+ head: 8,
32
+ type: 'text',
33
+ }).toJSON(),
34
+ ).toMatchInlineSnapshot(`
35
+ {
36
+ "anchor": 4,
37
+ "head": 8,
38
+ "type": "text",
39
+ }
40
+ `)
41
+ expect(getEditorSelection(doc, new AllSelection(doc)).toJSON())
42
+ .toMatchInlineSnapshot(`
43
+ {
44
+ "type": "all",
45
+ }
46
+ `)
47
+ })