@prosekit/core 0.8.7 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{editor-M9OimMiI.d.ts → editor-BULC1zqX.d.ts} +31 -71
- package/dist/editor-BULC1zqX.d.ts.map +1 -0
- package/dist/{editor-B0L9BgMi.js → editor-g-Rqn-ZE.js} +119 -136
- package/dist/editor-g-Rqn-ZE.js.map +1 -0
- package/dist/prosekit-core-test.d.ts +1 -2
- package/dist/prosekit-core-test.d.ts.map +1 -1
- package/dist/prosekit-core-test.js +1 -1
- package/dist/prosekit-core-test.js.map +1 -1
- package/dist/prosekit-core.d.ts +182 -202
- package/dist/prosekit-core.d.ts.map +1 -1
- package/dist/prosekit-core.js +543 -549
- package/dist/prosekit-core.js.map +1 -1
- package/package.json +9 -12
- package/src/commands/add-mark.ts +1 -4
- package/src/commands/expand-mark.ts +2 -9
- package/src/commands/insert-default-block.spec.ts +1 -5
- package/src/commands/insert-default-block.ts +1 -4
- package/src/commands/insert-node.ts +4 -8
- package/src/commands/remove-mark.ts +1 -4
- package/src/commands/remove-node.ts +2 -2
- package/src/commands/select-all.ts +3 -8
- package/src/commands/select-block.spec.ts +81 -0
- package/src/commands/select-block.ts +56 -0
- package/src/commands/set-block-type.ts +1 -4
- package/src/commands/set-node-attrs-between.spec.ts +221 -0
- package/src/commands/set-node-attrs-between.ts +77 -0
- package/src/commands/set-node-attrs.spec.ts +129 -0
- package/src/commands/set-node-attrs.ts +25 -26
- package/src/commands/toggle-mark.ts +1 -4
- package/src/commands/toggle-node.ts +2 -5
- package/src/commands/toggle-wrap.spec.ts +1 -5
- package/src/commands/toggle-wrap.ts +1 -4
- package/src/commands/unset-block-type.spec.ts +1 -5
- package/src/commands/unset-block-type.ts +2 -8
- package/src/commands/unset-mark.spec.ts +1 -5
- package/src/commands/wrap.ts +2 -10
- package/src/editor/action.spec.ts +2 -6
- package/src/editor/action.ts +2 -19
- package/src/editor/editor.spec.ts +2 -9
- package/src/editor/editor.ts +31 -77
- package/src/editor/union.spec.ts +1 -5
- package/src/editor/union.ts +1 -4
- package/src/extensions/clipboard-serializer.ts +4 -16
- package/src/extensions/command.ts +20 -48
- package/src/extensions/default-state.spec.ts +1 -9
- package/src/extensions/default-state.ts +6 -32
- package/src/extensions/events/dom-event.spec.ts +1 -6
- package/src/extensions/events/dom-event.ts +5 -20
- package/src/extensions/events/editor-event.ts +5 -17
- package/src/extensions/events/focus.spec.ts +1 -6
- package/src/extensions/events/plugin-view.ts +2 -9
- package/src/extensions/history.ts +3 -10
- package/src/extensions/keymap-base.spec.ts +89 -0
- package/src/extensions/keymap-base.ts +34 -13
- package/src/extensions/keymap.spec.ts +12 -22
- package/src/extensions/keymap.ts +16 -69
- package/src/extensions/mark-spec.spec.ts +5 -20
- package/src/extensions/mark-spec.ts +33 -41
- package/src/extensions/mark-view-effect.ts +3 -9
- package/src/extensions/mark-view.ts +2 -8
- package/src/extensions/node-spec.spec.ts +5 -21
- package/src/extensions/node-spec.ts +31 -33
- package/src/extensions/node-view-effect.ts +3 -9
- package/src/extensions/node-view.ts +2 -8
- package/src/extensions/plugin.spec.ts +3 -16
- package/src/extensions/plugin.ts +4 -14
- package/src/facets/base-extension.ts +1 -4
- package/src/facets/command.ts +10 -10
- package/src/facets/facet-extension.spec.ts +4 -15
- package/src/facets/facet-node.spec.ts +2 -9
- package/src/facets/facet-node.ts +4 -9
- package/src/facets/facet.spec.ts +1 -4
- package/src/facets/schema-spec.ts +2 -9
- package/src/facets/schema.ts +3 -12
- package/src/facets/state.spec.ts +8 -15
- package/src/facets/state.ts +5 -20
- package/src/facets/union-extension.ts +2 -8
- package/src/index.ts +42 -188
- package/src/test/index.ts +1 -4
- package/src/test/test-builder.ts +1 -4
- package/src/test/test-editor.spec.ts +1 -5
- package/src/test/test-editor.ts +5 -24
- package/src/testing/index.ts +18 -14
- package/src/types/extension-command.ts +0 -7
- package/src/types/extension.spec.ts +1 -4
- package/src/types/extension.ts +3 -29
- package/src/types/simplify-union.ts +1 -4
- package/src/utils/array-grouping.spec.ts +2 -15
- package/src/utils/array-grouping.ts +1 -14
- package/src/utils/array.ts +0 -4
- package/src/utils/attrs-match.ts +1 -5
- package/src/utils/clsx.spec.ts +1 -4
- package/src/utils/combine-event-handlers.spec.ts +1 -5
- package/src/utils/combine-event-handlers.ts +4 -6
- package/src/utils/default-block-at.ts +1 -4
- package/src/utils/editor-content.spec.ts +1 -4
- package/src/utils/editor-content.ts +7 -19
- package/src/utils/find-node.ts +65 -0
- package/src/utils/find-parent-node-of-type.ts +6 -12
- package/src/utils/find-parent-node.spec.ts +1 -5
- package/src/utils/find-parent-node.ts +1 -4
- package/src/utils/get-custom-selection.ts +1 -5
- package/src/utils/get-mark-type.ts +1 -4
- package/src/utils/get-node-type.ts +1 -4
- package/src/utils/get-node-types.ts +1 -4
- package/src/utils/includes-mark.ts +1 -5
- package/src/utils/is-at-block-start.ts +1 -4
- package/src/utils/is-mark-absent.spec.ts +1 -4
- package/src/utils/is-mark-absent.ts +1 -5
- package/src/utils/is-mark-active.ts +1 -4
- package/src/utils/is-node-active.spec.ts +109 -0
- package/src/utils/is-node-active.ts +17 -8
- package/src/utils/is-subset.spec.ts +1 -4
- package/src/utils/maybe-run.spec.ts +1 -5
- package/src/utils/merge-objects.spec.ts +1 -4
- package/src/utils/merge-objects.ts +2 -1
- package/src/utils/merge-specs.ts +1 -4
- package/src/utils/object-equal.spec.ts +1 -4
- package/src/utils/output-spec.test.ts +1 -5
- package/src/utils/output-spec.ts +12 -9
- package/src/utils/parse.spec.ts +2 -11
- package/src/utils/parse.ts +12 -24
- package/src/utils/remove-undefined-values.spec.ts +1 -4
- package/src/utils/set-selection-around.ts +1 -4
- package/src/utils/type-assertion.ts +2 -21
- package/src/utils/unicode.spec.ts +1 -4
- package/dist/editor-B0L9BgMi.js.map +0 -1
- package/dist/editor-M9OimMiI.d.ts.map +0 -1
- package/src/extensions/doc.ts +0 -31
- package/src/extensions/paragraph.ts +0 -61
- package/src/extensions/text.ts +0 -34
- package/src/types/base-node-view-options.ts +0 -33
- package/src/types/object-entries.ts +0 -13
- package/src/utils/collect-children.ts +0 -21
- package/src/utils/collect-nodes.ts +0 -37
- package/src/utils/deep-equals.spec.ts +0 -26
- package/src/utils/deep-equals.ts +0 -29
- package/src/utils/get-id.spec.ts +0 -14
- package/src/utils/get-id.ts +0 -13
|
@@ -1,52 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '../commands/
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
type InsertDefaultBlockOptions,
|
|
8
|
-
} from '../commands/insert-default-block'
|
|
9
|
-
import {
|
|
10
|
-
insertNode,
|
|
11
|
-
type InsertNodeOptions,
|
|
12
|
-
} from '../commands/insert-node'
|
|
13
|
-
import {
|
|
14
|
-
insertText,
|
|
15
|
-
type InsertTextOptions,
|
|
16
|
-
} from '../commands/insert-text'
|
|
17
|
-
import {
|
|
18
|
-
removeMark,
|
|
19
|
-
type RemoveMarkOptions,
|
|
20
|
-
} from '../commands/remove-mark'
|
|
21
|
-
import {
|
|
22
|
-
removeNode,
|
|
23
|
-
type RemoveNodeOptions,
|
|
24
|
-
} from '../commands/remove-node'
|
|
1
|
+
import { addMark, type AddMarkOptions } from '../commands/add-mark'
|
|
2
|
+
import { insertDefaultBlock, type InsertDefaultBlockOptions } from '../commands/insert-default-block'
|
|
3
|
+
import { insertNode, type InsertNodeOptions } from '../commands/insert-node'
|
|
4
|
+
import { insertText, type InsertTextOptions } from '../commands/insert-text'
|
|
5
|
+
import { removeMark, type RemoveMarkOptions } from '../commands/remove-mark'
|
|
6
|
+
import { removeNode, type RemoveNodeOptions } from '../commands/remove-node'
|
|
25
7
|
import { selectAll } from '../commands/select-all'
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} from '../commands/set-
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} from '../commands/
|
|
34
|
-
import {
|
|
35
|
-
toggleWrap,
|
|
36
|
-
type ToggleWrapOptions,
|
|
37
|
-
} from '../commands/toggle-wrap'
|
|
38
|
-
import {
|
|
39
|
-
unsetBlockType,
|
|
40
|
-
type UnsetBlockTypeOptions,
|
|
41
|
-
} from '../commands/unset-block-type'
|
|
42
|
-
import {
|
|
43
|
-
unsetMark,
|
|
44
|
-
type UnsetMarkOptions,
|
|
45
|
-
} from '../commands/unset-mark'
|
|
46
|
-
import {
|
|
47
|
-
wrap,
|
|
48
|
-
type WrapOptions,
|
|
49
|
-
} from '../commands/wrap'
|
|
8
|
+
import { selectBlock } from '../commands/select-block'
|
|
9
|
+
import { setBlockType, type SetBlockTypeOptions } from '../commands/set-block-type'
|
|
10
|
+
import { setNodeAttrs, type SetNodeAttrsOptions } from '../commands/set-node-attrs'
|
|
11
|
+
import { setNodeAttrsBetween, type SetNodeAttrsBetweenOptions } from '../commands/set-node-attrs-between'
|
|
12
|
+
import { toggleWrap, type ToggleWrapOptions } from '../commands/toggle-wrap'
|
|
13
|
+
import { unsetBlockType, type UnsetBlockTypeOptions } from '../commands/unset-block-type'
|
|
14
|
+
import { unsetMark, type UnsetMarkOptions } from '../commands/unset-mark'
|
|
15
|
+
import { wrap, type WrapOptions } from '../commands/wrap'
|
|
50
16
|
import { commandFacet } from '../facets/command'
|
|
51
17
|
import { defineFacetPayload } from '../facets/facet-extension'
|
|
52
18
|
import type { Extension } from '../types/extension'
|
|
@@ -76,8 +42,10 @@ export type BaseCommandsExtension = Extension<{
|
|
|
76
42
|
toggleWrap: [options: ToggleWrapOptions]
|
|
77
43
|
setBlockType: [options: SetBlockTypeOptions]
|
|
78
44
|
setNodeAttrs: [options: SetNodeAttrsOptions]
|
|
45
|
+
setNodeAttrsBetween: [options: SetNodeAttrsBetweenOptions]
|
|
79
46
|
insertDefaultBlock: [options?: InsertDefaultBlockOptions]
|
|
80
47
|
selectAll: []
|
|
48
|
+
selectBlock: []
|
|
81
49
|
addMark: [options: AddMarkOptions]
|
|
82
50
|
removeMark: [options: RemoveMarkOptions]
|
|
83
51
|
unsetBlockType: [options?: UnsetBlockTypeOptions]
|
|
@@ -106,10 +74,14 @@ export function defineBaseCommands(): BaseCommandsExtension {
|
|
|
106
74
|
|
|
107
75
|
setNodeAttrs,
|
|
108
76
|
|
|
77
|
+
setNodeAttrsBetween,
|
|
78
|
+
|
|
109
79
|
insertDefaultBlock,
|
|
110
80
|
|
|
111
81
|
selectAll,
|
|
112
82
|
|
|
83
|
+
selectBlock,
|
|
84
|
+
|
|
113
85
|
addMark,
|
|
114
86
|
|
|
115
87
|
removeMark,
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
describe,
|
|
3
|
-
expect,
|
|
4
|
-
it,
|
|
5
|
-
} from 'vitest'
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
6
2
|
|
|
7
3
|
import { createEditor } from '../editor/editor'
|
|
8
4
|
import { defineTestExtension } from '../testing'
|
|
@@ -27,10 +23,6 @@ describe('defineDefaultState', () => {
|
|
|
27
23
|
return editor.state.doc.toString()
|
|
28
24
|
}
|
|
29
25
|
|
|
30
|
-
expect(run({ defaultDoc: docJSON })).toContain('docJSON')
|
|
31
|
-
expect(run({ defaultHTML: docHTMLString })).toContain('docHTMLString')
|
|
32
|
-
expect(run({ defaultHTML: docHTMLElement })).toContain('docHTMLElement')
|
|
33
|
-
|
|
34
26
|
expect(run({ defaultContent: docJSON })).toContain('docJSON')
|
|
35
27
|
expect(run({ defaultContent: docHTMLString })).toContain('docHTMLString')
|
|
36
28
|
expect(run({ defaultContent: docHTMLElement })).toContain('docHTMLElement')
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Selection,
|
|
3
|
-
type EditorStateConfig,
|
|
4
|
-
} from '@prosekit/pm/state'
|
|
1
|
+
import { Selection, type EditorStateConfig } from '@prosekit/pm/state'
|
|
5
2
|
|
|
6
3
|
import { defineFacetPayload } from '../facets/facet-extension'
|
|
7
4
|
import { stateFacet } from '../facets/state'
|
|
8
5
|
import type { PlainExtension } from '../types/extension'
|
|
9
|
-
import type {
|
|
10
|
-
NodeJSON,
|
|
11
|
-
SelectionJSON,
|
|
12
|
-
} from '../types/model'
|
|
6
|
+
import type { NodeJSON, SelectionJSON } from '../types/model'
|
|
13
7
|
import { getEditorContentJSON } from '../utils/editor-content'
|
|
14
8
|
|
|
15
9
|
/**
|
|
@@ -18,25 +12,9 @@ import { getEditorContentJSON } from '../utils/editor-content'
|
|
|
18
12
|
export interface DefaultStateOptions {
|
|
19
13
|
/**
|
|
20
14
|
* The starting document to use when creating the editor. It can be a
|
|
21
|
-
* ProseMirror node JSON object,
|
|
15
|
+
* ProseMirror node JSON object, an HTML string, or a DOM element instance.
|
|
22
16
|
*/
|
|
23
|
-
defaultContent?: NodeJSON | string |
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* A JSON object representing the starting document to use when creating the
|
|
27
|
-
* editor.
|
|
28
|
-
*
|
|
29
|
-
* @deprecated Use `defaultContent` instead.
|
|
30
|
-
*/
|
|
31
|
-
defaultDoc?: NodeJSON
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* A HTML element or a HTML string representing the starting document to use
|
|
35
|
-
* when creating the editor.
|
|
36
|
-
*
|
|
37
|
-
* @deprecated Use `defaultContent` instead.
|
|
38
|
-
*/
|
|
39
|
-
defaultHTML?: string | HTMLElement
|
|
17
|
+
defaultContent?: NodeJSON | string | Element
|
|
40
18
|
|
|
41
19
|
/**
|
|
42
20
|
* A JSON object representing the starting selection to use when creating the
|
|
@@ -55,16 +33,12 @@ export interface DefaultStateOptions {
|
|
|
55
33
|
export function defineDefaultState({
|
|
56
34
|
defaultSelection,
|
|
57
35
|
defaultContent,
|
|
58
|
-
defaultDoc,
|
|
59
|
-
defaultHTML,
|
|
60
36
|
}: DefaultStateOptions): PlainExtension {
|
|
61
|
-
const defaultDocContent = defaultContent || defaultDoc || defaultHTML
|
|
62
|
-
|
|
63
37
|
return defineFacetPayload(stateFacet, [
|
|
64
38
|
({ schema }) => {
|
|
65
39
|
const config: EditorStateConfig = {}
|
|
66
|
-
if (
|
|
67
|
-
const json = getEditorContentJSON(schema,
|
|
40
|
+
if (defaultContent) {
|
|
41
|
+
const json = getEditorContentJSON(schema, defaultContent)
|
|
68
42
|
config.doc = schema.nodeFromJSON(json)
|
|
69
43
|
if (defaultSelection) {
|
|
70
44
|
config.selection = Selection.fromJSON(config.doc, defaultSelection)
|
|
@@ -1,25 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
ProseMirrorPlugin,
|
|
4
|
-
} from '@prosekit/pm/state'
|
|
5
|
-
import type {
|
|
6
|
-
DOMEventMap,
|
|
7
|
-
EditorView,
|
|
8
|
-
} from '@prosekit/pm/view'
|
|
1
|
+
import { PluginKey, ProseMirrorPlugin } from '@prosekit/pm/state'
|
|
2
|
+
import type { DOMEventMap, EditorView } from '@prosekit/pm/view'
|
|
9
3
|
|
|
10
|
-
import {
|
|
11
|
-
defineFacet,
|
|
12
|
-
type Facet,
|
|
13
|
-
} from '../../facets/facet'
|
|
4
|
+
import { defineFacet, type Facet } from '../../facets/facet'
|
|
14
5
|
import { defineFacetPayload } from '../../facets/facet-extension'
|
|
15
6
|
import type { PlainExtension } from '../../types/extension'
|
|
16
7
|
import type { Setter } from '../../types/setter'
|
|
17
8
|
import { groupEntries } from '../../utils/array-grouping'
|
|
18
9
|
import { combineEventHandlers } from '../../utils/combine-event-handlers'
|
|
19
|
-
import {
|
|
20
|
-
pluginFacet,
|
|
21
|
-
type PluginPayload,
|
|
22
|
-
} from '../plugin'
|
|
10
|
+
import { pluginFacet, type PluginPayload } from '../plugin'
|
|
23
11
|
|
|
24
12
|
/**
|
|
25
13
|
* A function to handle the events fired on the editable DOM element. Returns
|
|
@@ -85,10 +73,7 @@ const domEventFacet: Facet<DOMEventPayload, PluginPayload> = defineFacet(
|
|
|
85
73
|
hasNewEvent = true
|
|
86
74
|
const [setHandlers, combinedHandler] = combineEventHandlers<DOMEventHandler>()
|
|
87
75
|
setHandlersMap[event] = setHandlers
|
|
88
|
-
|
|
89
|
-
return combinedHandler(view, eventObject)
|
|
90
|
-
}
|
|
91
|
-
combinedHandlerMap[event] = e
|
|
76
|
+
combinedHandlerMap[event] = combinedHandler
|
|
92
77
|
}
|
|
93
78
|
}
|
|
94
79
|
|
|
@@ -1,26 +1,14 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '@prosekit/pm/model'
|
|
5
|
-
import {
|
|
6
|
-
PluginKey,
|
|
7
|
-
ProseMirrorPlugin,
|
|
8
|
-
} from '@prosekit/pm/state'
|
|
1
|
+
import type { ObjectEntries } from '@ocavue/utils'
|
|
2
|
+
import type { Node, Slice } from '@prosekit/pm/model'
|
|
3
|
+
import { PluginKey, ProseMirrorPlugin } from '@prosekit/pm/state'
|
|
9
4
|
import type { EditorView } from '@prosekit/pm/view'
|
|
10
5
|
|
|
11
|
-
import {
|
|
12
|
-
defineFacet,
|
|
13
|
-
type Facet,
|
|
14
|
-
} from '../../facets/facet'
|
|
6
|
+
import { defineFacet, type Facet } from '../../facets/facet'
|
|
15
7
|
import { defineFacetPayload } from '../../facets/facet-extension'
|
|
16
8
|
import type { PlainExtension } from '../../types/extension'
|
|
17
|
-
import type { ObjectEntries } from '../../types/object-entries'
|
|
18
9
|
import { groupEntries } from '../../utils/array-grouping'
|
|
19
10
|
import { combineEventHandlers } from '../../utils/combine-event-handlers'
|
|
20
|
-
import {
|
|
21
|
-
pluginFacet,
|
|
22
|
-
type PluginPayload,
|
|
23
|
-
} from '../plugin'
|
|
11
|
+
import { pluginFacet, type PluginPayload } from '../plugin'
|
|
24
12
|
|
|
25
13
|
export type KeyDownHandler = (
|
|
26
14
|
view: EditorView,
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
PluginKey,
|
|
3
|
-
ProseMirrorPlugin,
|
|
4
|
-
type EditorState,
|
|
5
|
-
} from '@prosekit/pm/state'
|
|
1
|
+
import { PluginKey, ProseMirrorPlugin, type EditorState } from '@prosekit/pm/state'
|
|
6
2
|
import type { EditorView } from '@prosekit/pm/view'
|
|
7
3
|
|
|
8
4
|
import { defineFacet } from '../../facets/facet'
|
|
9
5
|
import { defineFacetPayload } from '../../facets/facet-extension'
|
|
10
6
|
import type { PlainExtension } from '../../types/extension'
|
|
11
|
-
import {
|
|
12
|
-
pluginFacet,
|
|
13
|
-
type PluginPayload,
|
|
14
|
-
} from '../plugin'
|
|
7
|
+
import { pluginFacet, type PluginPayload } from '../plugin'
|
|
15
8
|
|
|
16
9
|
/**
|
|
17
10
|
* A function that is called when the editor view is mounted.
|
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
history,
|
|
3
|
-
redo,
|
|
4
|
-
undo,
|
|
5
|
-
} from '@prosekit/pm/history'
|
|
1
|
+
import { history, redo, undo } from '@prosekit/pm/history'
|
|
6
2
|
|
|
7
3
|
import { union } from '../editor/union'
|
|
8
4
|
import type { Extension } from '../types/extension'
|
|
9
5
|
import { isApple } from '../utils/env'
|
|
10
6
|
|
|
11
7
|
import { defineCommands } from './command'
|
|
12
|
-
import {
|
|
13
|
-
defineKeymap,
|
|
14
|
-
type Keymap,
|
|
15
|
-
} from './keymap'
|
|
8
|
+
import { defineKeymap, type Keymap } from './keymap'
|
|
16
9
|
import { definePlugin } from './plugin'
|
|
17
10
|
|
|
18
11
|
const keymap: Keymap = {
|
|
19
12
|
'Mod-z': undo,
|
|
20
|
-
'
|
|
13
|
+
'Mod-Z': redo,
|
|
21
14
|
}
|
|
22
15
|
|
|
23
16
|
if (!isApple) {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { keyboard } from 'vitest-browser-commands/playwright'
|
|
3
|
+
|
|
4
|
+
import { union } from '../editor/union'
|
|
5
|
+
import type { TestEditor } from '../test'
|
|
6
|
+
import { defineDoc, defineParagraph, defineText, setupTestFromExtension } from '../testing'
|
|
7
|
+
import type { SelectionJSON } from '../types/model'
|
|
8
|
+
|
|
9
|
+
import { defineBaseKeymap } from './keymap-base'
|
|
10
|
+
|
|
11
|
+
describe('Mod-a', () => {
|
|
12
|
+
it('can select the block for the first Mod-a press', async () => {
|
|
13
|
+
const { editor, n } = setupTestFromExtension(union(
|
|
14
|
+
defineDoc(),
|
|
15
|
+
defineText(),
|
|
16
|
+
defineParagraph(),
|
|
17
|
+
defineBaseKeymap(),
|
|
18
|
+
))
|
|
19
|
+
|
|
20
|
+
editor.set(n.doc(n.paragraph('Fo<a>o<b>'), n.paragraph('Bar')))
|
|
21
|
+
await keyboard.press('ControlOrMeta+a')
|
|
22
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"text: <paragraph("Foo")>"`)
|
|
23
|
+
await keyboard.press('ControlOrMeta+a')
|
|
24
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"all: <paragraph("Foo"), paragraph("Bar")>"`)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('can select the entire document if the current textblock is already selected', async () => {
|
|
28
|
+
const { editor, n } = setupTestFromExtension(union(
|
|
29
|
+
defineDoc(),
|
|
30
|
+
defineText(),
|
|
31
|
+
defineParagraph(),
|
|
32
|
+
defineBaseKeymap(),
|
|
33
|
+
))
|
|
34
|
+
|
|
35
|
+
editor.set(n.doc(n.paragraph('<a>Foo<b>'), n.paragraph('Bar')))
|
|
36
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"text: <paragraph("Foo")>"`)
|
|
37
|
+
await keyboard.press('ControlOrMeta+a')
|
|
38
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"all: <paragraph("Foo"), paragraph("Bar")>"`)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('can select the entire document if multiple textblocks are already selected', async () => {
|
|
42
|
+
const { editor, n } = setupTestFromExtension(union(
|
|
43
|
+
defineDoc(),
|
|
44
|
+
defineText(),
|
|
45
|
+
defineParagraph(),
|
|
46
|
+
defineBaseKeymap(),
|
|
47
|
+
))
|
|
48
|
+
|
|
49
|
+
editor.set(n.doc(n.paragraph('<a>Foo'), n.paragraph('Bar<b>'), n.paragraph('Baz')))
|
|
50
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"text: <paragraph("Foo"), paragraph("Bar")>"`)
|
|
51
|
+
await keyboard.press('ControlOrMeta+a')
|
|
52
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"all: <paragraph("Foo"), paragraph("Bar"), paragraph("Baz")>"`)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('can select the entire document if the current textblock is empty', async () => {
|
|
56
|
+
const { editor, n } = setupTestFromExtension(union(
|
|
57
|
+
defineDoc(),
|
|
58
|
+
defineText(),
|
|
59
|
+
defineParagraph(),
|
|
60
|
+
defineBaseKeymap(),
|
|
61
|
+
))
|
|
62
|
+
|
|
63
|
+
editor.set(n.doc(n.paragraph('Foo'), n.paragraph('<a>'), n.paragraph('Bar')))
|
|
64
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"text: <>"`)
|
|
65
|
+
await keyboard.press('ControlOrMeta+a')
|
|
66
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"all: <paragraph("Foo"), paragraph, paragraph("Bar")>"`)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('can select the entire document directly if `preferBlockSelection` is false', async () => {
|
|
70
|
+
const { editor, n } = setupTestFromExtension(union(
|
|
71
|
+
defineDoc(),
|
|
72
|
+
defineText(),
|
|
73
|
+
defineParagraph(),
|
|
74
|
+
defineBaseKeymap({ preferBlockSelection: false }),
|
|
75
|
+
))
|
|
76
|
+
|
|
77
|
+
editor.set(n.doc(n.paragraph('<a>Foo<b>'), n.paragraph('Bar')))
|
|
78
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"text: <paragraph("Foo")>"`)
|
|
79
|
+
await keyboard.press('ControlOrMeta+a')
|
|
80
|
+
expect(inspectSelection(editor)).toMatchInlineSnapshot(`"all: <paragraph("Foo"), paragraph("Bar")>"`)
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
function inspectSelection(editor: TestEditor) {
|
|
85
|
+
const selection = editor.state.selection
|
|
86
|
+
const text = selection.content().content.toString()
|
|
87
|
+
const json = selection.toJSON() as SelectionJSON
|
|
88
|
+
return `${json.type}: ${text}`
|
|
89
|
+
}
|
|
@@ -6,15 +6,17 @@ import {
|
|
|
6
6
|
joinTextblockBackward,
|
|
7
7
|
liftEmptyBlock,
|
|
8
8
|
newlineInCode,
|
|
9
|
+
selectAll,
|
|
9
10
|
selectNodeBackward,
|
|
10
11
|
} from '@prosekit/pm/commands'
|
|
11
12
|
import { splitSplittableBlock } from 'prosemirror-splittable'
|
|
12
13
|
|
|
14
|
+
import { selectBlockCommand } from '../commands/select-block'
|
|
13
15
|
import { withPriority } from '../editor/with-priority'
|
|
14
16
|
import type { PlainExtension } from '../types/extension'
|
|
15
17
|
import { Priority } from '../types/priority'
|
|
16
18
|
|
|
17
|
-
import { defineKeymap } from './keymap'
|
|
19
|
+
import { defineKeymap, type Keymap } from './keymap'
|
|
18
20
|
|
|
19
21
|
// Replace `splitBlock` with `splitSplittableBlock`
|
|
20
22
|
const customEnter = chainCommands(
|
|
@@ -31,30 +33,49 @@ const customBackspace = chainCommands(
|
|
|
31
33
|
selectNodeBackward,
|
|
32
34
|
)
|
|
33
35
|
|
|
34
|
-
const customBaseKeymap = {
|
|
35
|
-
...baseKeymap,
|
|
36
|
-
Enter: customEnter,
|
|
37
|
-
Backspace: customBackspace,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
36
|
/**
|
|
41
37
|
* @internal
|
|
42
38
|
*/
|
|
43
39
|
export type BaseKeymapExtension = PlainExtension
|
|
44
40
|
|
|
45
41
|
/**
|
|
46
|
-
* Defines some basic key bindings.
|
|
47
|
-
*
|
|
48
42
|
* @public
|
|
49
43
|
*/
|
|
50
|
-
export
|
|
44
|
+
export interface BaseKeymapOptions {
|
|
51
45
|
/**
|
|
52
46
|
* The priority of the keymap.
|
|
53
47
|
*
|
|
54
48
|
* @default Priority.low
|
|
55
49
|
*/
|
|
56
50
|
priority?: Priority
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* If `true`, the first `Mod-a` press selects the current block that the
|
|
54
|
+
* cursor is in, and a second press selects the entire document.
|
|
55
|
+
*
|
|
56
|
+
* If `false`, `Mod-a` immediately selects the entire document.
|
|
57
|
+
*
|
|
58
|
+
* @default true
|
|
59
|
+
*/
|
|
60
|
+
preferBlockSelection?: boolean
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Defines some basic key bindings.
|
|
65
|
+
*
|
|
66
|
+
* @param options
|
|
67
|
+
*
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
export function defineBaseKeymap({
|
|
71
|
+
priority = Priority.low,
|
|
72
|
+
preferBlockSelection = true,
|
|
73
|
+
}: BaseKeymapOptions = {}): BaseKeymapExtension {
|
|
74
|
+
const keymap: Keymap = {
|
|
75
|
+
...baseKeymap,
|
|
76
|
+
'Mod-a': preferBlockSelection ? chainCommands(selectBlockCommand, selectAll) : selectAll,
|
|
77
|
+
'Enter': customEnter,
|
|
78
|
+
'Backspace': customBackspace,
|
|
79
|
+
}
|
|
80
|
+
return withPriority(defineKeymap(keymap), priority)
|
|
60
81
|
}
|
|
@@ -1,27 +1,13 @@
|
|
|
1
1
|
import type { Command } from '@prosekit/pm/state'
|
|
2
|
-
import {
|
|
3
|
-
describe,
|
|
4
|
-
expect,
|
|
5
|
-
it,
|
|
6
|
-
vi,
|
|
7
|
-
} from 'vitest'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
8
3
|
import { keyboard } from 'vitest-browser-commands/playwright'
|
|
9
4
|
|
|
10
5
|
import { union } from '../editor/union'
|
|
11
6
|
import { withPriority } from '../editor/with-priority'
|
|
12
|
-
import {
|
|
13
|
-
defineDoc,
|
|
14
|
-
defineParagraph,
|
|
15
|
-
defineText,
|
|
16
|
-
setupTest,
|
|
17
|
-
setupTestFromExtension,
|
|
18
|
-
} from '../testing'
|
|
7
|
+
import { defineDoc, defineParagraph, defineText, setupTest, setupTestFromExtension } from '../testing'
|
|
19
8
|
import { Priority } from '../types/priority'
|
|
20
9
|
|
|
21
|
-
import {
|
|
22
|
-
defineKeymap,
|
|
23
|
-
type Keymap,
|
|
24
|
-
} from './keymap'
|
|
10
|
+
import { defineKeymap, type Keymap } from './keymap'
|
|
25
11
|
|
|
26
12
|
describe('keymap', () => {
|
|
27
13
|
it('can register and unregister keymap', () => {
|
|
@@ -159,9 +145,13 @@ describe('keymap', () => {
|
|
|
159
145
|
// Do not match
|
|
160
146
|
'ctrl-c',
|
|
161
147
|
]
|
|
162
|
-
const keymap: Keymap = Object.fromEntries(keybindings.map(binding => [binding, record(binding)]))
|
|
163
148
|
|
|
164
|
-
|
|
149
|
+
for (const key of keybindings) {
|
|
150
|
+
const command: Command = record(key)
|
|
151
|
+
const keymap: Keymap = { [key]: command }
|
|
152
|
+
const extension = defineKeymap(keymap)
|
|
153
|
+
editor.use(extension)
|
|
154
|
+
}
|
|
165
155
|
|
|
166
156
|
called.length = 0
|
|
167
157
|
await keyboard.down('Control')
|
|
@@ -170,8 +160,8 @@ describe('keymap', () => {
|
|
|
170
160
|
await keyboard.up('Control')
|
|
171
161
|
expect(called).toMatchInlineSnapshot(`
|
|
172
162
|
[
|
|
173
|
-
"ctrl-b",
|
|
174
163
|
"CTRL-b",
|
|
164
|
+
"ctrl-b",
|
|
175
165
|
]
|
|
176
166
|
`)
|
|
177
167
|
|
|
@@ -184,10 +174,10 @@ describe('keymap', () => {
|
|
|
184
174
|
await keyboard.up('Control')
|
|
185
175
|
expect(called).toMatchInlineSnapshot(`
|
|
186
176
|
[
|
|
187
|
-
"c-s-B",
|
|
188
|
-
"c-B",
|
|
189
177
|
"Ctrl-B",
|
|
178
|
+
"c-s-B",
|
|
190
179
|
"ctrl-shift-b",
|
|
180
|
+
"c-B",
|
|
191
181
|
]
|
|
192
182
|
`)
|
|
193
183
|
})
|
package/src/extensions/keymap.ts
CHANGED
|
@@ -1,26 +1,12 @@
|
|
|
1
|
-
import { chainCommands } from '@prosekit/pm/commands'
|
|
2
1
|
import { keydownHandler } from '@prosekit/pm/keymap'
|
|
3
|
-
import {
|
|
4
|
-
Plugin,
|
|
5
|
-
PluginKey,
|
|
6
|
-
type Command,
|
|
7
|
-
} from '@prosekit/pm/state'
|
|
2
|
+
import { Plugin, PluginKey, type Command } from '@prosekit/pm/state'
|
|
8
3
|
import type { EditorView } from '@prosekit/pm/view'
|
|
9
|
-
import mapValues from 'just-map-values'
|
|
10
4
|
|
|
11
|
-
import {
|
|
12
|
-
defineFacet,
|
|
13
|
-
type Facet,
|
|
14
|
-
} from '../facets/facet'
|
|
5
|
+
import { defineFacet, type Facet } from '../facets/facet'
|
|
15
6
|
import { defineFacetPayload } from '../facets/facet-extension'
|
|
16
7
|
import type { PlainExtension } from '../types/extension'
|
|
17
|
-
import { toReversed } from '../utils/array'
|
|
18
|
-
import { isApple } from '../utils/env'
|
|
19
8
|
|
|
20
|
-
import {
|
|
21
|
-
pluginFacet,
|
|
22
|
-
type PluginPayload,
|
|
23
|
-
} from './plugin'
|
|
9
|
+
import { pluginFacet, type PluginPayload } from './plugin'
|
|
24
10
|
|
|
25
11
|
/**
|
|
26
12
|
* A set of keybindings. Please read the
|
|
@@ -55,27 +41,29 @@ export const keymapFacet: Facet<KeymapPayload, PluginPayload> = defineFacet<
|
|
|
55
41
|
PluginPayload
|
|
56
42
|
>({
|
|
57
43
|
reduce: () => {
|
|
58
|
-
type
|
|
44
|
+
type KeydownHandler = (view: EditorView, event: KeyboardEvent) => boolean
|
|
59
45
|
|
|
60
|
-
|
|
46
|
+
// An array of keymap handlers, ordered from the highest priority to the lowest.
|
|
47
|
+
let subHandlers: KeydownHandler[] = []
|
|
61
48
|
|
|
62
|
-
|
|
63
|
-
|
|
49
|
+
// A root handler that combines all the sub handlers.
|
|
50
|
+
const rootHandler: KeydownHandler = (view, event) => {
|
|
51
|
+
for (const handler of subHandlers) {
|
|
52
|
+
if (handler(view, event)) return true
|
|
53
|
+
}
|
|
64
54
|
return false
|
|
65
55
|
}
|
|
66
56
|
|
|
67
57
|
const plugin = new Plugin({
|
|
68
58
|
key: keymapPluginKey,
|
|
69
|
-
props: { handleKeyDown:
|
|
59
|
+
props: { handleKeyDown: rootHandler },
|
|
70
60
|
})
|
|
71
61
|
|
|
72
62
|
return (keymaps: Keymap[]) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
),
|
|
78
|
-
)
|
|
63
|
+
// The keymap at the end has a higher priority, so we need to reverse the
|
|
64
|
+
// order here.
|
|
65
|
+
subHandlers = keymaps.map(keydownHandler).reverse()
|
|
66
|
+
|
|
79
67
|
return plugin
|
|
80
68
|
}
|
|
81
69
|
},
|
|
@@ -83,45 +71,4 @@ export const keymapFacet: Facet<KeymapPayload, PluginPayload> = defineFacet<
|
|
|
83
71
|
singleton: true,
|
|
84
72
|
})
|
|
85
73
|
|
|
86
|
-
function mergeKeymaps(keymaps: Keymap[]): Keymap {
|
|
87
|
-
const bindings: Record<string, Command[]> = {}
|
|
88
|
-
|
|
89
|
-
for (const keymap of keymaps) {
|
|
90
|
-
for (const [key, command] of Object.entries(keymap)) {
|
|
91
|
-
const normalizedKey = normalizeKeyName(key)
|
|
92
|
-
const commands = bindings[normalizedKey] ||= []
|
|
93
|
-
commands.push(command)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return mapValues(bindings, mergeCommands)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function mergeCommands(commands: Command[]): Command {
|
|
101
|
-
return chainCommands(...commands)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Copied from https://github.com/ProseMirror/prosemirror-keymap/blob/1.2.3/src/keymap.ts#L8
|
|
105
|
-
function normalizeKeyName(name: string) {
|
|
106
|
-
let parts = name.split(/-(?!$)/), result = parts[parts.length - 1]
|
|
107
|
-
if (result == 'Space') result = ' '
|
|
108
|
-
let alt, ctrl, shift, meta
|
|
109
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
110
|
-
let mod = parts[i]
|
|
111
|
-
if (/^(cmd|meta|m)$/i.test(mod)) meta = true
|
|
112
|
-
else if (/^a(lt)?$/i.test(mod)) alt = true
|
|
113
|
-
else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true
|
|
114
|
-
else if (/^s(hift)?$/i.test(mod)) shift = true
|
|
115
|
-
else if (/^mod$/i.test(mod)) {
|
|
116
|
-
if (isApple) meta = true
|
|
117
|
-
else ctrl = true
|
|
118
|
-
} else throw new Error('Unrecognized modifier name: ' + mod)
|
|
119
|
-
}
|
|
120
|
-
if (alt) result = 'Alt-' + result
|
|
121
|
-
if (ctrl) result = 'Ctrl-' + result
|
|
122
|
-
if (meta) result = 'Meta-' + result
|
|
123
|
-
if (shift) result = 'Shift-' + result
|
|
124
|
-
return result
|
|
125
|
-
}
|
|
126
|
-
|
|
127
74
|
const keymapPluginKey = new PluginKey('prosekit-keymap')
|