@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.
- package/dist/editor-CfkZ4TNU.d.ts +748 -0
- package/dist/editor-CfkZ4TNU.d.ts.map +1 -0
- package/dist/{editor-DbMrpnmL.js → editor-CizSwUN8.js} +102 -192
- package/dist/editor-CizSwUN8.js.map +1 -0
- package/dist/prosekit-core-test.d.ts +20 -19
- package/dist/prosekit-core-test.d.ts.map +1 -0
- package/dist/prosekit-core-test.js +4 -5
- package/dist/prosekit-core-test.js.map +1 -0
- package/dist/prosekit-core.d.ts +782 -757
- package/dist/prosekit-core.d.ts.map +1 -0
- package/dist/prosekit-core.js +30 -45
- package/dist/prosekit-core.js.map +1 -0
- package/package.json +14 -11
- package/src/commands/add-mark.ts +53 -0
- package/src/commands/expand-mark.ts +96 -0
- package/src/commands/insert-default-block.spec.ts +102 -0
- package/src/commands/insert-default-block.ts +49 -0
- package/src/commands/insert-node.ts +71 -0
- package/src/commands/insert-text.ts +24 -0
- package/src/commands/remove-mark.ts +54 -0
- package/src/commands/remove-node.ts +43 -0
- package/src/commands/select-all.ts +16 -0
- package/src/commands/set-block-type.ts +64 -0
- package/src/commands/set-node-attrs.ts +68 -0
- package/src/commands/toggle-mark.ts +65 -0
- package/src/commands/toggle-node.ts +47 -0
- package/src/commands/toggle-wrap.spec.ts +35 -0
- package/src/commands/toggle-wrap.ts +42 -0
- package/src/commands/unset-block-type.spec.ts +49 -0
- package/src/commands/unset-block-type.ts +84 -0
- package/src/commands/unset-mark.spec.ts +35 -0
- package/src/commands/unset-mark.ts +38 -0
- package/src/commands/wrap.ts +50 -0
- package/src/editor/action.spec.ts +143 -0
- package/src/editor/action.ts +248 -0
- package/src/editor/editor.spec.ts +186 -0
- package/src/editor/editor.ts +563 -0
- package/src/editor/union.spec.ts +108 -0
- package/src/editor/union.ts +47 -0
- package/src/editor/with-priority.ts +25 -0
- package/src/error.ts +28 -0
- package/src/extensions/clipboard-serializer.ts +107 -0
- package/src/extensions/command.ts +121 -0
- package/src/extensions/default-state.spec.ts +60 -0
- package/src/extensions/default-state.ts +76 -0
- package/src/extensions/doc.ts +31 -0
- package/src/extensions/events/doc-change.ts +34 -0
- package/src/extensions/events/dom-event.spec.ts +70 -0
- package/src/extensions/events/dom-event.ts +117 -0
- package/src/extensions/events/editor-event.ts +293 -0
- package/src/extensions/events/focus.spec.ts +50 -0
- package/src/extensions/events/focus.ts +28 -0
- package/src/extensions/events/plugin-view.ts +132 -0
- package/src/extensions/history.ts +81 -0
- package/src/extensions/keymap-base.ts +60 -0
- package/src/extensions/keymap.spec.ts +89 -0
- package/src/extensions/keymap.ts +96 -0
- package/src/extensions/mark-spec.spec.ts +177 -0
- package/src/extensions/mark-spec.ts +181 -0
- package/src/extensions/mark-view-effect.ts +85 -0
- package/src/extensions/mark-view.ts +43 -0
- package/src/extensions/node-spec.spec.ts +224 -0
- package/src/extensions/node-spec.ts +199 -0
- package/src/extensions/node-view-effect.ts +85 -0
- package/src/extensions/node-view.ts +43 -0
- package/src/extensions/paragraph.ts +61 -0
- package/src/extensions/plugin.ts +91 -0
- package/src/extensions/text.ts +34 -0
- package/src/facets/base-extension.ts +54 -0
- package/src/facets/command.ts +21 -0
- package/src/facets/facet-extension.spec.ts +173 -0
- package/src/facets/facet-extension.ts +53 -0
- package/src/facets/facet-node.spec.ts +265 -0
- package/src/facets/facet-node.ts +185 -0
- package/src/facets/facet-types.ts +9 -0
- package/src/facets/facet.spec.ts +76 -0
- package/src/facets/facet.ts +84 -0
- package/src/facets/root.ts +44 -0
- package/src/facets/schema-spec.ts +30 -0
- package/src/facets/schema.ts +26 -0
- package/src/facets/state.ts +57 -0
- package/src/facets/union-extension.ts +41 -0
- package/src/index.ts +302 -0
- package/src/test/index.ts +4 -0
- package/src/test/test-builder.ts +68 -0
- package/src/test/test-editor.spec.ts +104 -0
- package/src/test/test-editor.ts +113 -0
- package/src/testing/index.ts +283 -0
- package/src/testing/keyboard.ts +5 -0
- package/src/types/any-function.ts +4 -0
- package/src/types/assert-type-equal.ts +8 -0
- package/src/types/attrs.ts +32 -0
- package/src/types/base-node-view-options.ts +33 -0
- package/src/types/dom-node.ts +1 -0
- package/src/types/extension-command.ts +52 -0
- package/src/types/extension-mark.ts +15 -0
- package/src/types/extension-node.ts +15 -0
- package/src/types/extension.spec.ts +56 -0
- package/src/types/extension.ts +168 -0
- package/src/types/model.ts +54 -0
- package/src/types/object-entries.ts +13 -0
- package/src/types/pick-string-literal.spec.ts +10 -0
- package/src/types/pick-string-literal.ts +6 -0
- package/src/types/pick-sub-type.spec.ts +20 -0
- package/src/types/pick-sub-type.ts +6 -0
- package/src/types/priority.ts +12 -0
- package/src/types/setter.ts +4 -0
- package/src/types/simplify-deeper.spec.ts +40 -0
- package/src/types/simplify-deeper.ts +6 -0
- package/src/types/simplify-union.spec.ts +21 -0
- package/src/types/simplify-union.ts +11 -0
- package/src/utils/array-grouping.spec.ts +29 -0
- package/src/utils/array-grouping.ts +25 -0
- package/src/utils/array.ts +21 -0
- package/src/utils/assert.ts +13 -0
- package/src/utils/attrs-match.ts +20 -0
- package/src/utils/can-use-regex-lookbehind.ts +12 -0
- package/src/utils/clsx.spec.ts +14 -0
- package/src/utils/clsx.ts +12 -0
- package/src/utils/collect-children.ts +21 -0
- package/src/utils/collect-nodes.ts +37 -0
- package/src/utils/combine-event-handlers.spec.ts +27 -0
- package/src/utils/combine-event-handlers.ts +27 -0
- package/src/utils/contains-inline-node.ts +17 -0
- package/src/utils/deep-equals.spec.ts +26 -0
- package/src/utils/deep-equals.ts +29 -0
- package/src/utils/default-block-at.ts +15 -0
- package/src/utils/editor-content.spec.ts +47 -0
- package/src/utils/editor-content.ts +77 -0
- package/src/utils/env.ts +6 -0
- package/src/utils/find-parent-node-of-type.ts +29 -0
- package/src/utils/find-parent-node.spec.ts +68 -0
- package/src/utils/find-parent-node.ts +55 -0
- package/src/utils/get-custom-selection.ts +19 -0
- package/src/utils/get-dom-api.ts +56 -0
- package/src/utils/get-id.spec.ts +14 -0
- package/src/utils/get-id.ts +13 -0
- package/src/utils/get-mark-type.ts +20 -0
- package/src/utils/get-node-type.ts +20 -0
- package/src/utils/get-node-types.ts +19 -0
- package/src/utils/includes-mark.ts +18 -0
- package/src/utils/is-at-block-start.ts +26 -0
- package/src/utils/is-in-code-block.ts +18 -0
- package/src/utils/is-mark-absent.spec.ts +53 -0
- package/src/utils/is-mark-absent.ts +42 -0
- package/src/utils/is-mark-active.ts +27 -0
- package/src/utils/is-node-active.ts +25 -0
- package/src/utils/is-subset.spec.ts +12 -0
- package/src/utils/is-subset.ts +11 -0
- package/src/utils/maybe-run.spec.ts +39 -0
- package/src/utils/maybe-run.ts +11 -0
- package/src/utils/merge-objects.spec.ts +30 -0
- package/src/utils/merge-objects.ts +11 -0
- package/src/utils/merge-specs.ts +35 -0
- package/src/utils/object-equal.spec.ts +26 -0
- package/src/utils/object-equal.ts +28 -0
- package/src/utils/output-spec.test.ts +95 -0
- package/src/utils/output-spec.ts +130 -0
- package/src/utils/parse.spec.ts +46 -0
- package/src/utils/parse.ts +321 -0
- package/src/utils/remove-undefined-values.spec.ts +15 -0
- package/src/utils/remove-undefined-values.ts +9 -0
- package/src/utils/set-selection-around.ts +11 -0
- package/src/utils/type-assertion.ts +91 -0
- package/src/utils/unicode.spec.ts +10 -0
- package/src/utils/unicode.ts +4 -0
- package/src/utils/with-skip-code-block.ts +15 -0
- 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,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
|
+
}
|