@prosekit/core 0.8.3 → 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-DlGlYOp-.js → editor-CizSwUN8.js} +76 -168
- 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 +766 -743
- package/dist/prosekit-core.d.ts.map +1 -0
- package/dist/prosekit-core.js +26 -43
- package/dist/prosekit-core.js.map +1 -0
- package/package.json +12 -9
- 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-OUH5V8BA.d.ts +0 -754
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Attrs,
|
|
3
|
+
MarkType,
|
|
4
|
+
} from '@prosekit/pm/model'
|
|
5
|
+
import type { Command } from '@prosekit/pm/state'
|
|
6
|
+
|
|
7
|
+
import type { CommandCreator } from '../types/extension-command'
|
|
8
|
+
import { getMarkType } from '../utils/get-mark-type'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export interface AddMarkOptions {
|
|
14
|
+
/**
|
|
15
|
+
* The type of the mark to add.
|
|
16
|
+
*/
|
|
17
|
+
type: string | MarkType
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The attributes of the mark to add.
|
|
21
|
+
*/
|
|
22
|
+
attrs?: Attrs | null
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The start position of the document. By default it will be the start position of current selection.
|
|
26
|
+
*/
|
|
27
|
+
from?: number
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The end position of the document. By default it will be the end position of current selection.
|
|
31
|
+
*/
|
|
32
|
+
to?: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns a command that adds the given mark with the given attributes.
|
|
37
|
+
*
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
export function addMark(options: AddMarkOptions): Command {
|
|
41
|
+
return (state, dispatch) => {
|
|
42
|
+
const mark = getMarkType(state.schema, options.type).create(options.attrs)
|
|
43
|
+
const from = options.from ?? state.selection.from
|
|
44
|
+
const to = options.to ?? state.selection.to
|
|
45
|
+
if (from > to) {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
dispatch?.(state.tr.addMark(from, to, mark))
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
addMark satisfies CommandCreator
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Mark,
|
|
3
|
+
MarkType,
|
|
4
|
+
ResolvedPos,
|
|
5
|
+
} from '@prosekit/pm/model'
|
|
6
|
+
import {
|
|
7
|
+
TextSelection,
|
|
8
|
+
type Command,
|
|
9
|
+
} from '@prosekit/pm/state'
|
|
10
|
+
|
|
11
|
+
import { getMarkType } from '../utils/get-mark-type'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export interface ExpandMarkOptions {
|
|
17
|
+
/**
|
|
18
|
+
* The type of the mark to expand.
|
|
19
|
+
*/
|
|
20
|
+
type: string | MarkType
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Expands the selection to include the entire mark at the current position.
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export function expandMark(options: ExpandMarkOptions): Command {
|
|
29
|
+
return (state, dispatch) => {
|
|
30
|
+
const markType = getMarkType(state.schema, options.type)
|
|
31
|
+
const predicate = (mark: Mark) => mark.type === markType
|
|
32
|
+
|
|
33
|
+
const from = expandMarkBefore(state.selection.$from, predicate)
|
|
34
|
+
const to = expandMarkAfter(state.selection.$to, predicate)
|
|
35
|
+
|
|
36
|
+
if (from === state.selection.from && to === state.selection.to) {
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (dispatch) {
|
|
41
|
+
dispatch(state.tr.setSelection(TextSelection.create(state.doc, from, to)))
|
|
42
|
+
}
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function expandMarkBefore(
|
|
48
|
+
$pos: ResolvedPos,
|
|
49
|
+
predicate: (mark: Mark) => boolean,
|
|
50
|
+
): number {
|
|
51
|
+
const { parent } = $pos
|
|
52
|
+
|
|
53
|
+
if (!$pos.marks().some(predicate)) {
|
|
54
|
+
return $pos.pos
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const index = $pos.index()
|
|
58
|
+
let boundaryIndex = index
|
|
59
|
+
|
|
60
|
+
for (let i = index; i >= 0; i--) {
|
|
61
|
+
const node = parent.child(i)
|
|
62
|
+
if (node.marks.some(predicate)) {
|
|
63
|
+
boundaryIndex = i
|
|
64
|
+
} else {
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return $pos.posAtIndex(boundaryIndex)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function expandMarkAfter(
|
|
73
|
+
$pos: ResolvedPos,
|
|
74
|
+
predicate: (mark: Mark) => boolean,
|
|
75
|
+
): number {
|
|
76
|
+
const { parent } = $pos
|
|
77
|
+
|
|
78
|
+
if (!$pos.marks().some(predicate)) {
|
|
79
|
+
return $pos.pos
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const index = Math.max(0, $pos.indexAfter() - 1)
|
|
83
|
+
const childCount = parent.childCount
|
|
84
|
+
let boundaryIndex = index
|
|
85
|
+
|
|
86
|
+
for (let i = index; i < childCount; i++) {
|
|
87
|
+
const node = parent.child(i)
|
|
88
|
+
if (node.marks.some(predicate)) {
|
|
89
|
+
boundaryIndex = i
|
|
90
|
+
} else {
|
|
91
|
+
break
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return $pos.posAtIndex(boundaryIndex) + parent.child(boundaryIndex).nodeSize
|
|
96
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describe,
|
|
3
|
+
expect,
|
|
4
|
+
it,
|
|
5
|
+
} from 'vitest'
|
|
6
|
+
|
|
7
|
+
import { setupTest } from '../testing'
|
|
8
|
+
import { inputText } from '../testing/keyboard'
|
|
9
|
+
|
|
10
|
+
import { insertDefaultBlock } from './insert-default-block'
|
|
11
|
+
|
|
12
|
+
describe('insertDefaultBlock', () => {
|
|
13
|
+
const { editor, n } = setupTest()
|
|
14
|
+
const doc = n.doc(
|
|
15
|
+
/*0*/
|
|
16
|
+
n.p(/*1*/ 'ab' /*3*/),
|
|
17
|
+
/*4*/
|
|
18
|
+
n.p(/*5*/ 'cd' /*7*/),
|
|
19
|
+
/*8*/
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
it('can insert a default block before the first text block', async () => {
|
|
23
|
+
editor.set(doc)
|
|
24
|
+
editor.exec(insertDefaultBlock({ pos: 0 }))
|
|
25
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
26
|
+
n.doc(n.p(), n.p('ab'), n.p('cd')).toJSON(),
|
|
27
|
+
)
|
|
28
|
+
await inputText('x')
|
|
29
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
30
|
+
n.doc(n.p('x'), n.p('ab'), n.p('cd')).toJSON(),
|
|
31
|
+
)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('can insert a default block between two text blocks', async () => {
|
|
35
|
+
editor.set(doc)
|
|
36
|
+
editor.exec(insertDefaultBlock({ pos: 4 }))
|
|
37
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
38
|
+
n.doc(n.p('ab'), n.p(), n.p('cd')).toJSON(),
|
|
39
|
+
)
|
|
40
|
+
await inputText('x')
|
|
41
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
42
|
+
n.doc(n.p('ab'), n.p('x'), n.p('cd')).toJSON(),
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('can insert a default block after the last text block', async () => {
|
|
47
|
+
editor.set(doc)
|
|
48
|
+
editor.exec(insertDefaultBlock({ pos: 8 }))
|
|
49
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
50
|
+
n.doc(n.p('ab'), n.p('cd'), n.p()).toJSON(),
|
|
51
|
+
)
|
|
52
|
+
await inputText('x')
|
|
53
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
54
|
+
n.doc(n.p('ab'), n.p('cd'), n.p('x')).toJSON(),
|
|
55
|
+
)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('can insert a default block after current text block', async () => {
|
|
59
|
+
for (const pos of [1, 2, 3]) {
|
|
60
|
+
editor.set(doc)
|
|
61
|
+
editor.exec(insertDefaultBlock({ pos }))
|
|
62
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
63
|
+
n.doc(n.p('ab'), n.p(), n.p('cd')).toJSON(),
|
|
64
|
+
)
|
|
65
|
+
await inputText('x')
|
|
66
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
67
|
+
n.doc(n.p('ab'), n.p('x'), n.p('cd')).toJSON(),
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const pos of [5, 6, 7]) {
|
|
72
|
+
editor.set(doc)
|
|
73
|
+
editor.exec(insertDefaultBlock({ pos }))
|
|
74
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
75
|
+
n.doc(n.p('ab'), n.p('cd'), n.p()).toJSON(),
|
|
76
|
+
)
|
|
77
|
+
await inputText('x')
|
|
78
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
79
|
+
n.doc(n.p('ab'), n.p('cd'), n.p('x')).toJSON(),
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('can insert a default block after current selection', async () => {
|
|
85
|
+
const doc = n.doc(
|
|
86
|
+
/*0*/
|
|
87
|
+
n.p(/*1*/ 'a<a>b' /*3*/),
|
|
88
|
+
/*4*/
|
|
89
|
+
n.p(/*5*/ 'cd' /*7*/),
|
|
90
|
+
/*8*/
|
|
91
|
+
)
|
|
92
|
+
editor.set(doc)
|
|
93
|
+
editor.exec(insertDefaultBlock())
|
|
94
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
95
|
+
n.doc(n.p('ab'), n.p(), n.p('cd')).toJSON(),
|
|
96
|
+
)
|
|
97
|
+
await inputText('x')
|
|
98
|
+
expect(editor.state.doc.toJSON()).toEqual(
|
|
99
|
+
n.doc(n.p('ab'), n.p('x'), n.p('cd')).toJSON(),
|
|
100
|
+
)
|
|
101
|
+
})
|
|
102
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TextSelection,
|
|
3
|
+
type Command,
|
|
4
|
+
} from '@prosekit/pm/state'
|
|
5
|
+
|
|
6
|
+
import { defaultBlockAt } from '../utils/default-block-at'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export interface InsertDefaultBlockOptions {
|
|
12
|
+
/**
|
|
13
|
+
* The position to insert the node at. By default it will insert after the
|
|
14
|
+
* current selection.
|
|
15
|
+
*/
|
|
16
|
+
pos?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns a command that inserts a default block after current selection or at
|
|
21
|
+
* the given position.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export function insertDefaultBlock(
|
|
26
|
+
options?: InsertDefaultBlockOptions,
|
|
27
|
+
): Command {
|
|
28
|
+
return (state, dispatch) => {
|
|
29
|
+
const $pos = options?.pos == null
|
|
30
|
+
? state.selection.$to
|
|
31
|
+
: state.doc.resolve(options.pos)
|
|
32
|
+
const depth = $pos.parent.isTextblock ? $pos.depth - 1 : $pos.depth
|
|
33
|
+
const parent = $pos.node(depth)
|
|
34
|
+
const index = $pos.indexAfter(depth)
|
|
35
|
+
const type = defaultBlockAt(parent.contentMatchAt(index))
|
|
36
|
+
if (!type) return false
|
|
37
|
+
if (dispatch) {
|
|
38
|
+
const pos = $pos.posAtIndex(index, depth)
|
|
39
|
+
const node = type.createAndFill()
|
|
40
|
+
if (!node) return false
|
|
41
|
+
const tr = state.tr.insert(pos, node)
|
|
42
|
+
const selection = TextSelection.findFrom(tr.doc.resolve(pos), 1)
|
|
43
|
+
if (!selection) return false
|
|
44
|
+
tr.setSelection(selection)
|
|
45
|
+
dispatch(tr.scrollIntoView())
|
|
46
|
+
}
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Attrs,
|
|
3
|
+
NodeType,
|
|
4
|
+
ProseMirrorNode,
|
|
5
|
+
} from '@prosekit/pm/model'
|
|
6
|
+
import type { Command } from '@prosekit/pm/state'
|
|
7
|
+
import { insertPoint } from '@prosekit/pm/transform'
|
|
8
|
+
|
|
9
|
+
import { assert } from '../utils/assert'
|
|
10
|
+
import { getNodeType } from '../utils/get-node-type'
|
|
11
|
+
import { setSelectionAround } from '../utils/set-selection-around'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export interface InsertNodeOptions {
|
|
17
|
+
/**
|
|
18
|
+
* The node to insert. Either this or `type` must be provided.
|
|
19
|
+
*/
|
|
20
|
+
node?: ProseMirrorNode
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The type of the node to insert. Either this or `node` must be provided.
|
|
24
|
+
*/
|
|
25
|
+
type?: string | NodeType
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* When `type` is provided, the attributes of the node to insert.
|
|
29
|
+
*/
|
|
30
|
+
attrs?: Attrs
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The position to insert the node at. By default it will be the anchor
|
|
34
|
+
* position of current selection.
|
|
35
|
+
*/
|
|
36
|
+
pos?: number
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Returns a command that inserts the given node at the current selection or at
|
|
41
|
+
* the given position.
|
|
42
|
+
*
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
function insertNode(options: InsertNodeOptions): Command {
|
|
46
|
+
return (state, dispatch) => {
|
|
47
|
+
const node = options.node
|
|
48
|
+
? options.node
|
|
49
|
+
: options.type
|
|
50
|
+
? getNodeType(state.schema, options.type).createAndFill(options.attrs)
|
|
51
|
+
: null
|
|
52
|
+
|
|
53
|
+
assert(node, 'You must provide either a node or a type')
|
|
54
|
+
|
|
55
|
+
const insertPos = insertPoint(
|
|
56
|
+
state.doc,
|
|
57
|
+
options.pos ?? state.selection.anchor,
|
|
58
|
+
node.type,
|
|
59
|
+
)
|
|
60
|
+
if (insertPos == null) return false
|
|
61
|
+
|
|
62
|
+
if (dispatch) {
|
|
63
|
+
const tr = state.tr.insert(insertPos, node)
|
|
64
|
+
setSelectionAround(tr, insertPos + node.nodeSize)
|
|
65
|
+
dispatch(tr)
|
|
66
|
+
}
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { insertNode }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Command } from '@prosekit/pm/state'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export type InsertTextOptions = {
|
|
7
|
+
text: string
|
|
8
|
+
from?: number
|
|
9
|
+
to?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns a command that inserts the given text.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export function insertText({ text, from, to }: InsertTextOptions): Command {
|
|
18
|
+
return (state, dispatch) => {
|
|
19
|
+
if (text) {
|
|
20
|
+
dispatch?.(state.tr.insertText(text, from, to))
|
|
21
|
+
}
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Attrs,
|
|
3
|
+
MarkType,
|
|
4
|
+
} from '@prosekit/pm/model'
|
|
5
|
+
import type { Command } from '@prosekit/pm/state'
|
|
6
|
+
|
|
7
|
+
import type { CommandCreator } from '../types/extension-command'
|
|
8
|
+
import { getMarkType } from '../utils/get-mark-type'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export interface RemoveMarkOptions {
|
|
14
|
+
/**
|
|
15
|
+
* The type of the mark to remove.
|
|
16
|
+
*/
|
|
17
|
+
type: string | MarkType
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* If attrs is given, remove precisely the mark with the given attrs. Otherwise, remove all marks of the given type.
|
|
21
|
+
*/
|
|
22
|
+
attrs?: Attrs | null
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The start position of the document. By default it will be the start position of current selection.
|
|
26
|
+
*/
|
|
27
|
+
from?: number
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The end position of the document. By default it will be the end position of current selection.
|
|
31
|
+
*/
|
|
32
|
+
to?: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns a command that removes the given mark.
|
|
37
|
+
*
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
export function removeMark(options: RemoveMarkOptions): Command {
|
|
41
|
+
return (state, dispatch) => {
|
|
42
|
+
const markType = getMarkType(state.schema, options.type)
|
|
43
|
+
const mark = options.attrs ? markType.create(options.attrs) : markType
|
|
44
|
+
const from = options.from ?? state.selection.from
|
|
45
|
+
const to = options.to ?? state.selection.to
|
|
46
|
+
if (from > to) {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
dispatch?.(state.tr.removeMark(from, to, mark))
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
removeMark satisfies CommandCreator
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { NodeType } from '@prosekit/pm/model'
|
|
2
|
+
import type { Command } from '@prosekit/pm/state'
|
|
3
|
+
|
|
4
|
+
import type { CommandCreator } from '../types/extension-command'
|
|
5
|
+
import { findParentNodeOfType } from '../utils/find-parent-node-of-type'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export interface RemoveNodeOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The type of the node to remove.
|
|
13
|
+
*/
|
|
14
|
+
type: string | NodeType
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The document position to start searching node. By default it will be the
|
|
18
|
+
* anchor position of current selection.
|
|
19
|
+
*/
|
|
20
|
+
pos?: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns a command to remove the nearest ancestor node of a specific type from the current position.
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export function removeNode(options: RemoveNodeOptions): Command {
|
|
29
|
+
return (state, dispatch) => {
|
|
30
|
+
const $pos = typeof options.pos === 'number'
|
|
31
|
+
? state.doc.resolve(options.pos)
|
|
32
|
+
: state.selection.$anchor
|
|
33
|
+
|
|
34
|
+
const found = findParentNodeOfType(options.type, $pos)
|
|
35
|
+
if (!found) return false
|
|
36
|
+
|
|
37
|
+
const { pos, node } = found
|
|
38
|
+
dispatch?.(state.tr.delete(pos, pos + node.nodeSize))
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
removeNode satisfies CommandCreator
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AllSelection,
|
|
3
|
+
type Command,
|
|
4
|
+
} from '@prosekit/pm/state'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns a command that selects the whole document.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export function selectAll(): Command {
|
|
12
|
+
return (state, dispatch) => {
|
|
13
|
+
dispatch?.(state.tr.setSelection(new AllSelection(state.doc)))
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Attrs,
|
|
3
|
+
NodeType,
|
|
4
|
+
} from '@prosekit/pm/model'
|
|
5
|
+
import type { Command } from '@prosekit/pm/state'
|
|
6
|
+
|
|
7
|
+
import { getCustomSelection } from '../utils/get-custom-selection'
|
|
8
|
+
import { getNodeType } from '../utils/get-node-type'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export interface SetBlockTypeOptions {
|
|
14
|
+
type: NodeType | string
|
|
15
|
+
attrs?: Attrs | null
|
|
16
|
+
from?: number
|
|
17
|
+
to?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns a command that tries to set the selected textblocks to the given node
|
|
22
|
+
* type with the given attributes.
|
|
23
|
+
*
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export function setBlockType(options: SetBlockTypeOptions): Command {
|
|
27
|
+
return (state, dispatch) => {
|
|
28
|
+
const nodeType = getNodeType(state.schema, options.type)
|
|
29
|
+
const selection = getCustomSelection(state, options.from, options.to)
|
|
30
|
+
const attrs = options.attrs
|
|
31
|
+
|
|
32
|
+
let applicable = false
|
|
33
|
+
for (let i = 0; i < selection.ranges.length && !applicable; i++) {
|
|
34
|
+
const {
|
|
35
|
+
$from: { pos: from },
|
|
36
|
+
$to: { pos: to },
|
|
37
|
+
} = selection.ranges[i]
|
|
38
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
39
|
+
if (applicable) return false
|
|
40
|
+
if (!node.isTextblock || node.hasMarkup(nodeType, attrs)) return
|
|
41
|
+
if (node.type == nodeType) {
|
|
42
|
+
applicable = true
|
|
43
|
+
} else {
|
|
44
|
+
const $pos = state.doc.resolve(pos),
|
|
45
|
+
index = $pos.index()
|
|
46
|
+
applicable = $pos.parent.canReplaceWith(index, index + 1, nodeType)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
if (!applicable) return false
|
|
51
|
+
if (dispatch) {
|
|
52
|
+
const tr = state.tr
|
|
53
|
+
for (const range of selection.ranges) {
|
|
54
|
+
const {
|
|
55
|
+
$from: { pos: from },
|
|
56
|
+
$to: { pos: to },
|
|
57
|
+
} = range
|
|
58
|
+
tr.setBlockType(from, to, nodeType, attrs)
|
|
59
|
+
}
|
|
60
|
+
dispatch(tr.scrollIntoView())
|
|
61
|
+
}
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Attrs,
|
|
3
|
+
NodeType,
|
|
4
|
+
} from '@prosekit/pm/model'
|
|
5
|
+
import type { Command } from '@prosekit/pm/state'
|
|
6
|
+
|
|
7
|
+
import { getNodeTypes } from '../utils/get-node-types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export interface SetNodeAttrsOptions {
|
|
13
|
+
/**
|
|
14
|
+
* The type of node to set the attributes of.
|
|
15
|
+
*
|
|
16
|
+
* If current node is not of this type, the command will do nothing.
|
|
17
|
+
*/
|
|
18
|
+
type: string | NodeType | string[] | NodeType[]
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The attributes to set.
|
|
22
|
+
*/
|
|
23
|
+
attrs: Attrs
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The position of the node. Defaults to the position of the wrapping node
|
|
27
|
+
* containing the current selection.
|
|
28
|
+
*/
|
|
29
|
+
pos?: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns a command that set the attributes of the current node.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export function setNodeAttrs(options: SetNodeAttrsOptions): Command {
|
|
38
|
+
return (state, dispatch) => {
|
|
39
|
+
const nodeTypes = getNodeTypes(state.schema, options.type)
|
|
40
|
+
const from = options.pos ?? state.selection.from
|
|
41
|
+
const to = options.pos ?? state.selection.to
|
|
42
|
+
const positions: number[] = []
|
|
43
|
+
|
|
44
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
45
|
+
if (nodeTypes.includes(node.type)) {
|
|
46
|
+
positions.push(pos)
|
|
47
|
+
}
|
|
48
|
+
if (!dispatch && positions.length > 0) {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
if (positions.length === 0) {
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (dispatch) {
|
|
58
|
+
const { tr } = state
|
|
59
|
+
for (const pos of positions) {
|
|
60
|
+
for (const [key, value] of Object.entries(options.attrs)) {
|
|
61
|
+
tr.setNodeAttribute(pos, key, value)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
dispatch(tr)
|
|
65
|
+
}
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
}
|