@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,185 @@
1
+ import { Priority } from '../types/priority'
2
+ import {
3
+ arraySubtract,
4
+ uniqPush,
5
+ } from '../utils/array'
6
+ import { assert } from '../utils/assert'
7
+ import { isNotNullish } from '../utils/type-assertion'
8
+
9
+ import type { Facet } from './facet'
10
+ import type {
11
+ FacetReducer,
12
+ Tuple5,
13
+ } from './facet-types'
14
+ import type { RootOutput } from './root'
15
+
16
+ function zip5<T>(
17
+ a: Tuple5<T>,
18
+ b: Tuple5<T>,
19
+ mapper: (a: T, b: T) => T,
20
+ ): Tuple5<T> {
21
+ return [
22
+ mapper(a[0], b[0]),
23
+ mapper(a[1], b[1]),
24
+ mapper(a[2], b[2]),
25
+ mapper(a[3], b[3]),
26
+ mapper(a[4], b[4]),
27
+ ]
28
+ }
29
+
30
+ function unionInput<T>(a: T[] | null, b: T[] | null): T[] | null {
31
+ if (!a && !b) return null
32
+ return uniqPush(a ?? [], b ?? [])
33
+ }
34
+
35
+ function subtractInput<T>(a: T[] | null, b: T[] | null): T[] | null {
36
+ if (!a) return null
37
+ if (!b) return [...a]
38
+ return arraySubtract(a, b)
39
+ }
40
+
41
+ function unionChildren(
42
+ a: Map<number, FacetNode>,
43
+ b: Map<number, FacetNode>,
44
+ ): Map<number, FacetNode> {
45
+ const merged = new Map(a)
46
+ for (const [key, valueB] of b.entries()) {
47
+ const valueA = a.get(key)
48
+ merged.set(key, valueA ? unionFacetNode(valueA, valueB) : valueB)
49
+ }
50
+ return merged
51
+ }
52
+
53
+ function subtractChildren(
54
+ a: Map<number, FacetNode>,
55
+ b: Map<number, FacetNode>,
56
+ ): Map<number, FacetNode> {
57
+ const merged = new Map(a)
58
+ for (const [key, valueB] of b.entries()) {
59
+ const valueA = a.get(key)
60
+ if (valueA) {
61
+ merged.set(key, subtractFacetNode(valueA, valueB))
62
+ }
63
+ }
64
+ return merged
65
+ }
66
+
67
+ /**
68
+ * Takes two facet nodes and returns a new facet node containing inputs and
69
+ * children from both nodes.
70
+ *
71
+ * The reducers of the first facet node will be reused.
72
+ *
73
+ * @internal
74
+ */
75
+ export function unionFacetNode<I, O>(
76
+ a: FacetNode<I, O>,
77
+ b: FacetNode<I, O>,
78
+ ): FacetNode<I, O> {
79
+ assert(a.facet === b.facet)
80
+ return new FacetNode(
81
+ a.facet,
82
+ zip5(a.inputs, b.inputs, unionInput),
83
+ unionChildren(a.children, b.children),
84
+ a.reducers,
85
+ )
86
+ }
87
+
88
+ /**
89
+ * Takes two facet nodes and returns a new facet node containing inputs and
90
+ * children from the first node but not the second.
91
+ *
92
+ * The reducers of the first facet node will be reused.
93
+ *
94
+ * @internal
95
+ */
96
+ export function subtractFacetNode<I, O>(
97
+ a: FacetNode<I, O>,
98
+ b: FacetNode<I, O>,
99
+ ): FacetNode<I, O> {
100
+ assert(a.facet === b.facet)
101
+ return new FacetNode(
102
+ a.facet,
103
+ zip5(a.inputs, b.inputs, subtractInput),
104
+ subtractChildren(a.children, b.children),
105
+ a.reducers,
106
+ )
107
+ }
108
+
109
+ export class FacetNode<I = any, O = any> {
110
+ output: Tuple5<O | null> | null = null
111
+
112
+ constructor(
113
+ readonly facet: Facet<I, O>,
114
+ readonly inputs: Tuple5<I[] | null> = [null, null, null, null, null],
115
+ readonly children: Map<number, FacetNode> = new Map(),
116
+ readonly reducers: Tuple5<FacetReducer<I, O> | null> = [
117
+ null,
118
+ null,
119
+ null,
120
+ null,
121
+ null,
122
+ ],
123
+ ) {}
124
+
125
+ private calcOutput(): Tuple5<O | null> {
126
+ const inputs: Tuple5<I[] | null> = [null, null, null, null, null]
127
+ const output: Tuple5<O | null> = [null, null, null, null, null]
128
+
129
+ for (let pri = 0; pri < 5; pri++) {
130
+ const input = this.inputs[pri]
131
+ if (input) {
132
+ inputs[pri] = [...input]
133
+ }
134
+ }
135
+
136
+ for (const child of this.children.values()) {
137
+ const childOutput = child.getOutput()
138
+ for (let pri = 0; pri < 5; pri++) {
139
+ if (childOutput[pri]) {
140
+ const input = (inputs[pri] ||= [])
141
+ input.push(childOutput[pri] as I)
142
+ }
143
+ }
144
+ }
145
+
146
+ if (this.facet.singleton) {
147
+ const reducer = (this.reducers[Priority.default] ||= this.facet.reducer)
148
+ const input: I[] = inputs.filter(isNotNullish).flat()
149
+ output[Priority.default] = reducer(input)
150
+ } else {
151
+ for (let pri = 0; pri < 5; pri++) {
152
+ const input = inputs[pri]
153
+ if (input) {
154
+ const reducer = (this.reducers[pri] ||= this.facet.reducer)
155
+ output[pri] = reducer(input)
156
+ }
157
+ }
158
+ }
159
+
160
+ return output
161
+ }
162
+
163
+ getOutput(): Tuple5<O | null> {
164
+ if (!this.output) {
165
+ this.output = this.calcOutput()
166
+ }
167
+ return this.output
168
+ }
169
+
170
+ getSingletonOutput(): O | null {
171
+ assert(this.facet.singleton)
172
+ return this.getOutput()[Priority.default]
173
+ }
174
+
175
+ getRootOutput(): RootOutput {
176
+ assert(this.isRoot())
177
+ const output = this.getSingletonOutput()
178
+ assert(output)
179
+ return output
180
+ }
181
+
182
+ isRoot(): boolean {
183
+ return !this.facet.parent
184
+ }
185
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ export type Tuple5<T> = [T, T, T, T, T]
5
+
6
+ /**
7
+ * @internal
8
+ */
9
+ export type FacetReducer<Input, Output> = (input: Input[]) => Output
@@ -0,0 +1,76 @@
1
+ import {
2
+ expect,
3
+ test,
4
+ } from 'vitest'
5
+
6
+ import { Facet } from './facet'
7
+
8
+ type RootInput = {
9
+ count: number
10
+ }
11
+ type RootOutput = {
12
+ count: number
13
+ id: symbol
14
+ }
15
+ const id = Symbol('root-facet')
16
+ const rootFacet = new Facet<RootInput, RootOutput>(null, true, (input) => {
17
+ const count = input
18
+ .map(({ count }) => count)
19
+ .reduce((acc, cur) => {
20
+ return acc + cur
21
+ }, 0)
22
+
23
+ return {
24
+ count,
25
+ id,
26
+ }
27
+ })
28
+
29
+ const doubleFacet = new Facet<RootOutput, RootOutput>(
30
+ rootFacet,
31
+ false,
32
+ (input) => {
33
+ const id = input[0].id
34
+ const count = input
35
+ .map(({ count }) => count)
36
+ .reduce((acc, cur) => {
37
+ return acc + cur
38
+ }, 0)
39
+
40
+ return {
41
+ count: count * 2,
42
+ id,
43
+ }
44
+ },
45
+ )
46
+
47
+ test('Root Facet', () => {
48
+ expect(rootFacet.parent).toBe(null)
49
+ expect(rootFacet.singleton).toBe(true)
50
+ expect(rootFacet.path).toEqual([])
51
+ expect(
52
+ rootFacet.reducer([
53
+ { count: 1 },
54
+ { count: 2 },
55
+ { count: 3 },
56
+ { count: 4 },
57
+ { count: 0 },
58
+ ]),
59
+ ).toEqual({ count: 10, id })
60
+ })
61
+
62
+ test('Child Facet', () => {
63
+ expect(doubleFacet.parent).toBe(rootFacet)
64
+ expect(doubleFacet.singleton).toBe(false)
65
+ expect(doubleFacet.path).toEqual([1])
66
+ expect(
67
+ doubleFacet.reducer([
68
+ { count: 1, id },
69
+ { count: 2, id },
70
+ { count: 3, id },
71
+ ]),
72
+ ).toEqual({
73
+ count: 12,
74
+ id,
75
+ })
76
+ })
@@ -0,0 +1,84 @@
1
+ import { assert } from '../utils/assert'
2
+
3
+ import type { FacetReducer } from './facet-types'
4
+
5
+ let facetCount = 0
6
+
7
+ /**
8
+ * @internal
9
+ */
10
+ export class Facet<Input, Output> {
11
+ /**
12
+ * @internal
13
+ */
14
+ readonly index: number = facetCount++
15
+ /**
16
+ * @internal
17
+ */
18
+ readonly parent: Facet<Output, any> | null
19
+ /**
20
+ * @internal
21
+ */
22
+ readonly singleton: boolean
23
+ /**
24
+ * A index path to retrieve the current facet in a tree from the root.
25
+ *
26
+ * @internal
27
+ */
28
+ readonly path: number[]
29
+
30
+ /**
31
+ * @internal
32
+ */
33
+ constructor(
34
+ parent: Facet<Output, any> | null,
35
+ singleton: boolean,
36
+ private _reducer?: FacetReducer<Input, Output> | undefined,
37
+ private _reduce?: () => FacetReducer<Input, Output>,
38
+ ) {
39
+ // Only one of _reducer or _reduce can be defined
40
+ assert((_reduce || _reducer) && !(_reduce && _reducer))
41
+
42
+ this.parent = parent
43
+ this.singleton = singleton
44
+ this.path = parent ? [...parent.path, this.index] : []
45
+ }
46
+
47
+ get reducer(): FacetReducer<Input, Output> {
48
+ return (this._reducer ?? this._reduce?.())!
49
+ }
50
+ }
51
+
52
+ /**
53
+ * @internal
54
+ */
55
+ export function defineFacet<Input, Output>(options: {
56
+ /**
57
+ * The parent facet in the tree.
58
+ */
59
+ parent: Facet<Output, any>
60
+
61
+ /**
62
+ * Set this to true if you only want to keep one facet payload. For example,
63
+ * this facet corresponds to a ProseMirror plugin with a key.
64
+ */
65
+ singleton?: boolean
66
+
67
+ /**
68
+ * A reducer is a function that accepts an array of input and produce a single
69
+ * output.
70
+ */
71
+ reducer?: FacetReducer<Input, Output>
72
+ /**
73
+ * A callback function that returns a reducer. This is useful if you want to
74
+ * store something in the closure.
75
+ */
76
+ reduce?: () => FacetReducer<Input, Output>
77
+ }): Facet<Input, Output> {
78
+ return new Facet(
79
+ options.parent,
80
+ options.singleton ?? false,
81
+ options.reducer,
82
+ options.reduce,
83
+ )
84
+ }
@@ -0,0 +1,44 @@
1
+ import type { Schema } from '@prosekit/pm/model'
2
+ import type { EditorStateConfig } from '@prosekit/pm/state'
3
+ import type { DirectEditorProps } from '@prosekit/pm/view'
4
+
5
+ import type { CommandCreators } from '../types/extension-command'
6
+
7
+ import { Facet } from './facet'
8
+
9
+ export type RootPayload = {
10
+ schema?: Schema | null
11
+ commands?: CommandCreators
12
+ state?: (ctx: { schema: Schema }) => EditorStateConfig
13
+ view?: Omit<DirectEditorProps, 'state'>
14
+ }
15
+
16
+ export type RootOutput = {
17
+ schema?: Schema | null
18
+ commands?: CommandCreators
19
+ state?: EditorStateConfig
20
+ view?: Omit<DirectEditorProps, 'state'>
21
+ }
22
+
23
+ function rootReducer(inputs: RootPayload[]): RootOutput {
24
+ let schema: Schema | undefined
25
+ let commands: CommandCreators | undefined
26
+ let stateFunc: ((ctx: { schema: Schema }) => EditorStateConfig) | undefined
27
+ let view: Omit<DirectEditorProps, 'state'> | undefined
28
+
29
+ for (const input of inputs) {
30
+ schema = input.schema || schema
31
+ commands = input.commands || commands
32
+ stateFunc = input.state || stateFunc
33
+ view = input.view || view
34
+ }
35
+
36
+ const state = schema && (stateFunc?.({ schema }) ?? { schema })
37
+
38
+ return { schema, state, commands, view }
39
+ }
40
+
41
+ export const rootFacet: Facet<RootPayload, RootOutput> = new Facet<
42
+ RootPayload,
43
+ RootOutput
44
+ >(null, true, rootReducer)
@@ -0,0 +1,30 @@
1
+ import type {
2
+ MarkSpec,
3
+ NodeSpec,
4
+ SchemaSpec,
5
+ } from '@prosekit/pm/model'
6
+ import OrderedMap from 'orderedmap'
7
+
8
+ import {
9
+ defineFacet,
10
+ type Facet,
11
+ } from './facet'
12
+ import { schemaFacet } from './schema'
13
+
14
+ export const schemaSpecFacet: Facet<SchemaSpec, SchemaSpec> = defineFacet({
15
+ reducer: (specs): SchemaSpec => {
16
+ let nodes = OrderedMap.from<NodeSpec>({})
17
+ let marks = OrderedMap.from<MarkSpec>({})
18
+ let topNode: string | undefined = undefined
19
+
20
+ for (const spec of specs) {
21
+ nodes = nodes.append(spec.nodes)
22
+ marks = marks.append(spec.marks ?? {})
23
+ topNode = topNode ?? spec.topNode
24
+ }
25
+
26
+ return { nodes, marks, topNode }
27
+ },
28
+ parent: schemaFacet,
29
+ singleton: true,
30
+ })
@@ -0,0 +1,26 @@
1
+ import {
2
+ Schema,
3
+ type SchemaSpec,
4
+ } from '@prosekit/pm/model'
5
+
6
+ import { assert } from '../utils/assert'
7
+
8
+ import {
9
+ defineFacet,
10
+ type Facet,
11
+ } from './facet'
12
+ import {
13
+ rootFacet,
14
+ type RootPayload,
15
+ } from './root'
16
+
17
+ export const schemaFacet: Facet<SchemaSpec, RootPayload> = defineFacet({
18
+ reducer: (specs) => {
19
+ assert(specs.length <= 1)
20
+ const spec = specs[0]
21
+ const schema = spec ? new Schema(spec) : null
22
+ return { schema }
23
+ },
24
+ parent: rootFacet,
25
+ singleton: true,
26
+ })
@@ -0,0 +1,57 @@
1
+ import type { Schema } from '@prosekit/pm/model'
2
+ import type { EditorStateConfig } from '@prosekit/pm/state'
3
+
4
+ import { uniqPush } from '../utils/array'
5
+ import { assert } from '../utils/assert'
6
+
7
+ import {
8
+ defineFacet,
9
+ type Facet,
10
+ } from './facet'
11
+ import {
12
+ rootFacet,
13
+ type RootPayload,
14
+ } from './root'
15
+
16
+ export type StatePayload = (ctx: { schema: Schema }) => EditorStateConfig
17
+
18
+ export const stateFacet: Facet<StatePayload, RootPayload> = defineFacet({
19
+ reduce: () => {
20
+ let callbacks: StatePayload[] = []
21
+
22
+ const state: StatePayload = (ctx) => {
23
+ const configs = callbacks.map((cb) => cb(ctx))
24
+ const config: EditorStateConfig = {
25
+ schema: ctx.schema,
26
+ storedMarks: [],
27
+ plugins: [],
28
+ }
29
+
30
+ for (const c of configs) {
31
+ config.schema = config.schema ?? c.schema
32
+ config.doc = config.doc ?? c.doc
33
+ config.selection = config.selection ?? c.selection
34
+ config.storedMarks = [...config.storedMarks!, ...(c.storedMarks ?? [])]
35
+ config.plugins = uniqPush(config.plugins ?? [], c.plugins ?? [])
36
+ }
37
+
38
+ assert(
39
+ config.doc || config.schema,
40
+ "Can't create state without a schema nor a document",
41
+ )
42
+
43
+ if (config.doc) {
44
+ config.schema = undefined
45
+ }
46
+
47
+ return config
48
+ }
49
+
50
+ return function reducer(inputs) {
51
+ callbacks = inputs
52
+ return { state }
53
+ }
54
+ },
55
+ singleton: true,
56
+ parent: rootFacet,
57
+ })
@@ -0,0 +1,41 @@
1
+ import type {
2
+ Extension,
3
+ ExtensionTyping,
4
+ } from '../types/extension'
5
+ import type { Priority } from '../types/priority'
6
+ import { assert } from '../utils/assert'
7
+
8
+ import { BaseExtension } from './base-extension'
9
+ import {
10
+ unionFacetNode,
11
+ type FacetNode,
12
+ } from './facet-node'
13
+
14
+ export class UnionExtensionImpl<T extends ExtensionTyping = ExtensionTyping> extends BaseExtension<T> implements Extension<T> {
15
+ /**
16
+ * @internal
17
+ */
18
+ constructor(public extension: BaseExtension[] = []) {
19
+ super()
20
+ }
21
+
22
+ /**
23
+ * @internal
24
+ */
25
+ createTree(priority: Priority): FacetNode {
26
+ const pri = this.priority ?? priority
27
+
28
+ const extensions = [...this.extension]
29
+ extensions.sort((a, b) => (a.priority ?? pri) - (b.priority ?? pri))
30
+
31
+ const children: FacetNode[] = extensions.map((ext) => ext.getTree(pri))
32
+
33
+ assert(children.length > 0)
34
+
35
+ let node = children[0]
36
+ for (let i = 1; i < children.length; i++) {
37
+ node = unionFacetNode(node, children[i])
38
+ }
39
+ return node
40
+ }
41
+ }