@jvs-milkdown/prose 1.0.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/LICENSE +21 -0
- package/README.md +11 -0
- package/lib/changeset.d.ts +2 -0
- package/lib/changeset.d.ts.map +1 -0
- package/lib/changeset.js +2 -0
- package/lib/changeset.js.map +1 -0
- package/lib/commands.d.ts +2 -0
- package/lib/commands.d.ts.map +1 -0
- package/lib/commands.js +2 -0
- package/lib/commands.js.map +1 -0
- package/lib/dropcursor.d.ts +2 -0
- package/lib/dropcursor.d.ts.map +1 -0
- package/lib/dropcursor.js +2 -0
- package/lib/dropcursor.js.map +1 -0
- package/lib/gapcursor.d.ts +2 -0
- package/lib/gapcursor.d.ts.map +1 -0
- package/lib/gapcursor.js +2 -0
- package/lib/gapcursor.js.map +1 -0
- package/lib/history.d.ts +2 -0
- package/lib/history.d.ts.map +1 -0
- package/lib/history.js +2 -0
- package/lib/history.js.map +1 -0
- package/lib/index.d.ts +132 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +402 -0
- package/lib/index.js.map +1 -0
- package/lib/inputrules.d.ts +2 -0
- package/lib/inputrules.d.ts.map +1 -0
- package/lib/inputrules.js +2 -0
- package/lib/inputrules.js.map +1 -0
- package/lib/keymap.d.ts +2 -0
- package/lib/keymap.d.ts.map +1 -0
- package/lib/keymap.js +2 -0
- package/lib/keymap.js.map +1 -0
- package/lib/model.d.ts +2 -0
- package/lib/model.d.ts.map +1 -0
- package/lib/model.js +2 -0
- package/lib/model.js.map +1 -0
- package/lib/schema-list.d.ts +2 -0
- package/lib/schema-list.d.ts.map +1 -0
- package/lib/schema-list.js +2 -0
- package/lib/schema-list.js.map +1 -0
- package/lib/state.d.ts +2 -0
- package/lib/state.d.ts.map +1 -0
- package/lib/state.js +2 -0
- package/lib/state.js.map +1 -0
- package/lib/style/gapcursor.css +25 -0
- package/lib/style/prosemirror.css +54 -0
- package/lib/style/tables.css +48 -0
- package/lib/tables.d.ts +2 -0
- package/lib/tables.d.ts.map +1 -0
- package/lib/tables.js +2 -0
- package/lib/tables.js.map +1 -0
- package/lib/toolkit/browser.d.ts +17 -0
- package/lib/toolkit/browser.d.ts.map +1 -0
- package/lib/toolkit/index.d.ts +5 -0
- package/lib/toolkit/index.d.ts.map +1 -0
- package/lib/toolkit/input-rules/common.d.ts +20 -0
- package/lib/toolkit/input-rules/common.d.ts.map +1 -0
- package/lib/toolkit/input-rules/custom-input-rules.d.ts +7 -0
- package/lib/toolkit/input-rules/custom-input-rules.d.ts.map +1 -0
- package/lib/toolkit/input-rules/index.d.ts +5 -0
- package/lib/toolkit/input-rules/index.d.ts.map +1 -0
- package/lib/toolkit/input-rules/mark-rule.d.ts +5 -0
- package/lib/toolkit/input-rules/mark-rule.d.ts.map +1 -0
- package/lib/toolkit/input-rules/node-rule.d.ts +5 -0
- package/lib/toolkit/input-rules/node-rule.d.ts.map +1 -0
- package/lib/toolkit/position/index.d.ts +13 -0
- package/lib/toolkit/position/index.d.ts.map +1 -0
- package/lib/toolkit/prose/helper.d.ts +6 -0
- package/lib/toolkit/prose/helper.d.ts.map +1 -0
- package/lib/toolkit/prose/index.d.ts +5 -0
- package/lib/toolkit/prose/index.d.ts.map +1 -0
- package/lib/toolkit/prose/node.d.ts +17 -0
- package/lib/toolkit/prose/node.d.ts.map +1 -0
- package/lib/toolkit/prose/schema.d.ts +4 -0
- package/lib/toolkit/prose/schema.d.ts.map +1 -0
- package/lib/toolkit/prose/selection.d.ts +19 -0
- package/lib/toolkit/prose/selection.d.ts.map +1 -0
- package/lib/toolkit/prose/types.d.ts +3 -0
- package/lib/toolkit/prose/types.d.ts.map +1 -0
- package/lib/transform.d.ts +2 -0
- package/lib/transform.d.ts.map +1 -0
- package/lib/transform.js +2 -0
- package/lib/transform.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/view.d.ts +2 -0
- package/lib/view.d.ts.map +1 -0
- package/lib/view.js +2 -0
- package/lib/view.js.map +1 -0
- package/package.json +141 -0
- package/src/changeset.ts +1 -0
- package/src/commands.ts +1 -0
- package/src/dropcursor.ts +1 -0
- package/src/gapcursor.ts +1 -0
- package/src/history.ts +1 -0
- package/src/index.ts +1 -0
- package/src/inputrules.ts +1 -0
- package/src/keymap.ts +1 -0
- package/src/model.ts +1 -0
- package/src/schema-list.ts +1 -0
- package/src/state.ts +1 -0
- package/src/tables.ts +1 -0
- package/src/toolkit/browser.ts +39 -0
- package/src/toolkit/index.ts +4 -0
- package/src/toolkit/input-rules/common.ts +25 -0
- package/src/toolkit/input-rules/custom-input-rules.ts +113 -0
- package/src/toolkit/input-rules/index.ts +4 -0
- package/src/toolkit/input-rules/mark-rule.ts +60 -0
- package/src/toolkit/input-rules/node-rule.ts +51 -0
- package/src/toolkit/position/index.ts +108 -0
- package/src/toolkit/prose/helper.ts +30 -0
- package/src/toolkit/prose/index.ts +4 -0
- package/src/toolkit/prose/node.ts +63 -0
- package/src/toolkit/prose/schema.ts +19 -0
- package/src/toolkit/prose/selection.ts +97 -0
- package/src/toolkit/prose/types.ts +3 -0
- package/src/transform.ts +1 -0
- package/src/view.ts +1 -0
package/lib/view.js
ADDED
package/lib/view.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jvs-milkdown/prose",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/Milkdown/milkdown.git",
|
|
8
|
+
"directory": "packages/prose"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"lib",
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"main": "./lib/index.js",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./lib/index.d.ts",
|
|
20
|
+
"import": "./lib/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./changeset": {
|
|
23
|
+
"types": "./lib/changeset.d.ts",
|
|
24
|
+
"import": "./lib/changeset.js"
|
|
25
|
+
},
|
|
26
|
+
"./commands": {
|
|
27
|
+
"types": "./lib/commands.d.ts",
|
|
28
|
+
"import": "./lib/commands.js"
|
|
29
|
+
},
|
|
30
|
+
"./dropcursor": {
|
|
31
|
+
"types": "./lib/dropcursor.d.ts",
|
|
32
|
+
"import": "./lib/dropcursor.js"
|
|
33
|
+
},
|
|
34
|
+
"./gapcursor": {
|
|
35
|
+
"types": "./lib/gapcursor.d.ts",
|
|
36
|
+
"import": "./lib/gapcursor.js"
|
|
37
|
+
},
|
|
38
|
+
"./history": {
|
|
39
|
+
"types": "./lib/history.d.ts",
|
|
40
|
+
"import": "./lib/history.js"
|
|
41
|
+
},
|
|
42
|
+
"./inputrules": {
|
|
43
|
+
"types": "./lib/inputrules.d.ts",
|
|
44
|
+
"import": "./lib/inputrules.js"
|
|
45
|
+
},
|
|
46
|
+
"./keymap": {
|
|
47
|
+
"types": "./lib/keymap.d.ts",
|
|
48
|
+
"import": "./lib/keymap.js"
|
|
49
|
+
},
|
|
50
|
+
"./model": {
|
|
51
|
+
"types": "./lib/model.d.ts",
|
|
52
|
+
"import": "./lib/model.js"
|
|
53
|
+
},
|
|
54
|
+
"./schema-list": {
|
|
55
|
+
"types": "./lib/schema-list.d.ts",
|
|
56
|
+
"import": "./lib/schema-list.js"
|
|
57
|
+
},
|
|
58
|
+
"./state": {
|
|
59
|
+
"types": "./lib/state.d.ts",
|
|
60
|
+
"import": "./lib/state.js"
|
|
61
|
+
},
|
|
62
|
+
"./transform": {
|
|
63
|
+
"types": "./lib/transform.d.ts",
|
|
64
|
+
"import": "./lib/transform.js"
|
|
65
|
+
},
|
|
66
|
+
"./view": {
|
|
67
|
+
"types": "./lib/view.d.ts",
|
|
68
|
+
"import": "./lib/view.js"
|
|
69
|
+
},
|
|
70
|
+
"./tables": {
|
|
71
|
+
"types": "./lib/tables.d.ts",
|
|
72
|
+
"import": "./lib/tables.js"
|
|
73
|
+
},
|
|
74
|
+
"./view/style/prosemirror.css": "./lib/style/prosemirror.css",
|
|
75
|
+
"./tables/style/tables.css": "./lib/style/tables.css",
|
|
76
|
+
"./gapcursor/style/gapcursor.css": "./lib/style/gapcursor.css"
|
|
77
|
+
},
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"prosemirror-changeset": "^2.3.1",
|
|
80
|
+
"prosemirror-commands": "^1.7.1",
|
|
81
|
+
"prosemirror-dropcursor": "^1.8.2",
|
|
82
|
+
"prosemirror-gapcursor": "^1.4.0",
|
|
83
|
+
"prosemirror-history": "^1.5.0",
|
|
84
|
+
"prosemirror-inputrules": "^1.5.1",
|
|
85
|
+
"prosemirror-keymap": "^1.2.3",
|
|
86
|
+
"prosemirror-model": "^1.25.4",
|
|
87
|
+
"prosemirror-schema-list": "^1.5.1",
|
|
88
|
+
"prosemirror-state": "^1.4.4",
|
|
89
|
+
"prosemirror-tables": "^1.8.1",
|
|
90
|
+
"prosemirror-transform": "^1.10.5",
|
|
91
|
+
"prosemirror-view": "^1.41.3",
|
|
92
|
+
"@jvs-milkdown/exception": "1.0.0"
|
|
93
|
+
},
|
|
94
|
+
"scripts": {
|
|
95
|
+
"build": "rollup -c"
|
|
96
|
+
},
|
|
97
|
+
"types": "./lib/index.d.ts",
|
|
98
|
+
"typesVersions": {
|
|
99
|
+
"*": {
|
|
100
|
+
"changeset": [
|
|
101
|
+
"lib/changeset.d.ts"
|
|
102
|
+
],
|
|
103
|
+
"commands": [
|
|
104
|
+
"lib/commands.d.ts"
|
|
105
|
+
],
|
|
106
|
+
"dropcursor": [
|
|
107
|
+
"lib/dropcursor.d.ts"
|
|
108
|
+
],
|
|
109
|
+
"gapcursor": [
|
|
110
|
+
"lib/gapcursor.d.ts"
|
|
111
|
+
],
|
|
112
|
+
"history": [
|
|
113
|
+
"lib/history.d.ts"
|
|
114
|
+
],
|
|
115
|
+
"inputrules": [
|
|
116
|
+
"lib/inputrules.d.ts"
|
|
117
|
+
],
|
|
118
|
+
"keymap": [
|
|
119
|
+
"lib/keymap.d.ts"
|
|
120
|
+
],
|
|
121
|
+
"model": [
|
|
122
|
+
"lib/model.d.ts"
|
|
123
|
+
],
|
|
124
|
+
"schema-list": [
|
|
125
|
+
"lib/schema-list.d.ts"
|
|
126
|
+
],
|
|
127
|
+
"state": [
|
|
128
|
+
"lib/state.d.ts"
|
|
129
|
+
],
|
|
130
|
+
"transform": [
|
|
131
|
+
"lib/transform.d.ts"
|
|
132
|
+
],
|
|
133
|
+
"view": [
|
|
134
|
+
"lib/view.d.ts"
|
|
135
|
+
],
|
|
136
|
+
"tables": [
|
|
137
|
+
"lib/tables.d.ts"
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/changeset.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-changeset'
|
package/src/commands.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-commands'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-dropcursor'
|
package/src/gapcursor.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-gapcursor'
|
package/src/history.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-history'
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './toolkit'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-inputrules'
|
package/src/keymap.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-keymap'
|
package/src/model.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-model'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-schema-list'
|
package/src/state.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-state'
|
package/src/tables.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'prosemirror-tables'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy paste from:
|
|
3
|
+
* https://github.com/ProseMirror/prosemirror-view/blob/master/src/browser.ts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const nav = typeof navigator != 'undefined' ? navigator : null
|
|
7
|
+
const doc = typeof document != 'undefined' ? document : null
|
|
8
|
+
const agent = (nav && nav.userAgent) || ''
|
|
9
|
+
|
|
10
|
+
const ie_edge = /Edge\/(\d+)/.exec(agent)
|
|
11
|
+
const ie_upto10 = /MSIE \d/.exec(agent)
|
|
12
|
+
const ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(agent)
|
|
13
|
+
|
|
14
|
+
export const ie = !!(ie_upto10 || ie_11up || ie_edge)
|
|
15
|
+
export const ie_version = ie_upto10
|
|
16
|
+
? (document as typeof document & { documentMode: unknown }).documentMode
|
|
17
|
+
: ie_11up
|
|
18
|
+
? +ie_11up[1]!
|
|
19
|
+
: ie_edge
|
|
20
|
+
? +ie_edge[1]!
|
|
21
|
+
: 0
|
|
22
|
+
export const gecko = !ie && /gecko\/(\d+)/i.test(agent)
|
|
23
|
+
export const gecko_version =
|
|
24
|
+
gecko && +(/Firefox\/(\d+)/.exec(agent) || [0, 0])[1]
|
|
25
|
+
|
|
26
|
+
const _chrome = !ie && /Chrome\/(\d+)/.exec(agent)
|
|
27
|
+
export const chrome = !!_chrome
|
|
28
|
+
export const chrome_version = _chrome ? +_chrome[1]! : 0
|
|
29
|
+
export const safari = !ie && !!nav && /Apple Computer/.test(nav.vendor)
|
|
30
|
+
// Is true for both iOS and iPadOS for convenience
|
|
31
|
+
export const ios =
|
|
32
|
+
safari && (/Mobile\/\w+/.test(agent) || (!!nav && nav.maxTouchPoints > 2))
|
|
33
|
+
export const mac = ios || (nav ? /Mac/.test(nav.platform) : false)
|
|
34
|
+
export const android = /Android \d/.test(agent)
|
|
35
|
+
export const webkit =
|
|
36
|
+
!!doc && 'webkitFontSmoothing' in doc.documentElement.style
|
|
37
|
+
export const webkit_version = webkit
|
|
38
|
+
? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1]
|
|
39
|
+
: 0
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Attrs } from '../../model'
|
|
2
|
+
import type { Transaction } from '../../state'
|
|
3
|
+
|
|
4
|
+
/// @internal
|
|
5
|
+
export interface Captured {
|
|
6
|
+
group: string | undefined
|
|
7
|
+
fullMatch: string
|
|
8
|
+
start: number
|
|
9
|
+
end: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/// @internal
|
|
13
|
+
export interface BeforeDispatch {
|
|
14
|
+
match: string[]
|
|
15
|
+
start: number
|
|
16
|
+
end: number
|
|
17
|
+
tr: Transaction
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// @internal
|
|
21
|
+
export interface Options {
|
|
22
|
+
getAttr?: (match: RegExpMatchArray) => Attrs
|
|
23
|
+
updateCaptured?: (captured: Captured) => Partial<Captured>
|
|
24
|
+
beforeDispatch?: (options: BeforeDispatch) => void
|
|
25
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { InputRule } from '../../inputrules'
|
|
2
|
+
import type { EditorState, TextSelection, Transaction } from '../../state'
|
|
3
|
+
import type { EditorView } from '../../view'
|
|
4
|
+
|
|
5
|
+
import { Plugin, PluginKey } from '../../state'
|
|
6
|
+
import { android, chrome } from '../browser'
|
|
7
|
+
|
|
8
|
+
function run(
|
|
9
|
+
view: EditorView,
|
|
10
|
+
from: number,
|
|
11
|
+
to: number,
|
|
12
|
+
text: string,
|
|
13
|
+
rules: InputRule[],
|
|
14
|
+
plugin: Plugin
|
|
15
|
+
) {
|
|
16
|
+
if (view.composing) return false
|
|
17
|
+
const state = view.state
|
|
18
|
+
const $from = state.doc.resolve(from)
|
|
19
|
+
if ($from.parent.type.spec.code) return false
|
|
20
|
+
const textBefore =
|
|
21
|
+
$from.parent.textBetween(
|
|
22
|
+
Math.max(0, $from.parentOffset - 500),
|
|
23
|
+
$from.parentOffset,
|
|
24
|
+
undefined,
|
|
25
|
+
'\uFFFC'
|
|
26
|
+
) + text
|
|
27
|
+
for (let _matcher of rules) {
|
|
28
|
+
const matcher = _matcher as unknown as {
|
|
29
|
+
match: RegExp
|
|
30
|
+
handler: (
|
|
31
|
+
state: EditorState,
|
|
32
|
+
match: string[],
|
|
33
|
+
from: number,
|
|
34
|
+
to: number
|
|
35
|
+
) => Transaction
|
|
36
|
+
undoable?: boolean
|
|
37
|
+
}
|
|
38
|
+
const match = matcher.match.exec(textBefore)
|
|
39
|
+
const tr =
|
|
40
|
+
match &&
|
|
41
|
+
match[0] &&
|
|
42
|
+
matcher.handler(state, match, from - (match[0].length - text.length), to)
|
|
43
|
+
if (!tr) continue
|
|
44
|
+
if (matcher.undoable !== false)
|
|
45
|
+
tr.setMeta(plugin, { transform: tr, from, to, text })
|
|
46
|
+
view.dispatch(tr)
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const customInputRulesKey = new PluginKey('MILKDOWN_CUSTOM_INPUTRULES')
|
|
53
|
+
export function customInputRules({ rules }: { rules: InputRule[] }): Plugin {
|
|
54
|
+
const plugin: Plugin = new Plugin({
|
|
55
|
+
key: customInputRulesKey,
|
|
56
|
+
isInputRules: true,
|
|
57
|
+
|
|
58
|
+
state: {
|
|
59
|
+
init() {
|
|
60
|
+
return null
|
|
61
|
+
},
|
|
62
|
+
apply(this: typeof plugin, tr, prev) {
|
|
63
|
+
const stored = tr.getMeta(this)
|
|
64
|
+
if (stored) return stored
|
|
65
|
+
return tr.selectionSet || tr.docChanged ? null : prev
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
props: {
|
|
69
|
+
handleTextInput(view, from, to, text) {
|
|
70
|
+
return run(view, from, to, text, rules, plugin)
|
|
71
|
+
},
|
|
72
|
+
handleDOMEvents: {
|
|
73
|
+
compositionend: (view) => {
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
const { $cursor } = view.state.selection as TextSelection
|
|
76
|
+
if ($cursor) run(view, $cursor.pos, $cursor.pos, '', rules, plugin)
|
|
77
|
+
})
|
|
78
|
+
return false
|
|
79
|
+
},
|
|
80
|
+
keydown: (view, event) => {
|
|
81
|
+
// On Chrome Android, prosemirror-view suppresses Enter keydown events
|
|
82
|
+
// to avoid input corruption during composition. It then relies on DOM
|
|
83
|
+
// mutation detection to retroactively handle Enter. However, this
|
|
84
|
+
// fallback fails with custom node views (e.g. list-item-block) whose
|
|
85
|
+
// wrapper DOM structure prevents the Enter detection heuristics from
|
|
86
|
+
// recognizing the mutation. We intercept Enter here — before
|
|
87
|
+
// prosemirror-view's suppression — and manually route it through
|
|
88
|
+
// handleKeyDown so that keymaps (splitListItem, etc.) work correctly.
|
|
89
|
+
if (!(android && chrome && (event as KeyboardEvent).key === 'Enter'))
|
|
90
|
+
return false
|
|
91
|
+
if (view.composing) return false
|
|
92
|
+
if (
|
|
93
|
+
view.someProp('handleKeyDown', (f) =>
|
|
94
|
+
f(view, event as KeyboardEvent)
|
|
95
|
+
)
|
|
96
|
+
) {
|
|
97
|
+
event.preventDefault()
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
100
|
+
return false
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
handleKeyDown(view, event) {
|
|
104
|
+
if (event.key !== 'Enter') return false
|
|
105
|
+
const { $cursor } = view.state.selection as TextSelection
|
|
106
|
+
if ($cursor)
|
|
107
|
+
return run(view, $cursor.pos, $cursor.pos, '\n', rules, plugin)
|
|
108
|
+
return false
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
return plugin
|
|
113
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Mark, MarkType } from '../../model'
|
|
2
|
+
import type { Captured, Options } from './common'
|
|
3
|
+
|
|
4
|
+
import { InputRule } from '../../inputrules'
|
|
5
|
+
|
|
6
|
+
/// Create an input rule for a mark.
|
|
7
|
+
export function markRule(
|
|
8
|
+
regexp: RegExp,
|
|
9
|
+
markType: MarkType,
|
|
10
|
+
options: Options = {}
|
|
11
|
+
): InputRule {
|
|
12
|
+
return new InputRule(regexp, (state, match, start, end) => {
|
|
13
|
+
const { tr } = state
|
|
14
|
+
const matchLength = match.length
|
|
15
|
+
|
|
16
|
+
let group = match[matchLength - 1]
|
|
17
|
+
let fullMatch = match[0]
|
|
18
|
+
let initialStoredMarks: readonly Mark[] = []
|
|
19
|
+
|
|
20
|
+
let markEnd: number
|
|
21
|
+
|
|
22
|
+
const captured: Captured = {
|
|
23
|
+
group,
|
|
24
|
+
fullMatch,
|
|
25
|
+
start,
|
|
26
|
+
end,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const result = options.updateCaptured?.(captured)
|
|
30
|
+
Object.assign(captured, result)
|
|
31
|
+
;({ group, fullMatch, start, end } = captured)
|
|
32
|
+
|
|
33
|
+
if (fullMatch === null) return null
|
|
34
|
+
|
|
35
|
+
if (group?.trim() === '') return null
|
|
36
|
+
|
|
37
|
+
if (group) {
|
|
38
|
+
const startSpaces = fullMatch.search(/\S/)
|
|
39
|
+
const textStart = start + fullMatch.indexOf(group)
|
|
40
|
+
const textEnd = textStart + group.length
|
|
41
|
+
|
|
42
|
+
initialStoredMarks = tr.storedMarks ?? []
|
|
43
|
+
|
|
44
|
+
if (textEnd < end) tr.delete(textEnd, end)
|
|
45
|
+
|
|
46
|
+
if (textStart > start) tr.delete(start + startSpaces, textStart)
|
|
47
|
+
|
|
48
|
+
markEnd = start + startSpaces + group.length
|
|
49
|
+
|
|
50
|
+
const attrs = options.getAttr?.(match)
|
|
51
|
+
|
|
52
|
+
tr.addMark(start, markEnd, markType.create(attrs))
|
|
53
|
+
tr.setStoredMarks(initialStoredMarks)
|
|
54
|
+
|
|
55
|
+
options.beforeDispatch?.({ match, start, end, tr })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return tr
|
|
59
|
+
})
|
|
60
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { NodeType } from '../../model'
|
|
2
|
+
import type { Captured, Options } from './common'
|
|
3
|
+
|
|
4
|
+
import { InputRule } from '../../inputrules'
|
|
5
|
+
|
|
6
|
+
/// Create an input rule for a node.
|
|
7
|
+
export function nodeRule(
|
|
8
|
+
regexp: RegExp,
|
|
9
|
+
nodeType: NodeType,
|
|
10
|
+
options: Options = {}
|
|
11
|
+
): InputRule {
|
|
12
|
+
return new InputRule(regexp, (state, match, start, end) => {
|
|
13
|
+
const { tr } = state
|
|
14
|
+
let group: string | undefined = match[1]
|
|
15
|
+
let fullMatch = match[0]
|
|
16
|
+
|
|
17
|
+
const captured: Captured = {
|
|
18
|
+
group,
|
|
19
|
+
fullMatch,
|
|
20
|
+
start,
|
|
21
|
+
end,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const result = options.updateCaptured?.(captured)
|
|
25
|
+
Object.assign(captured, result)
|
|
26
|
+
;({ group, fullMatch, start, end } = captured)
|
|
27
|
+
|
|
28
|
+
if (fullMatch === null) return null
|
|
29
|
+
|
|
30
|
+
if (!group || group.trim() === '') return null
|
|
31
|
+
|
|
32
|
+
const attrs = options.getAttr?.(match)
|
|
33
|
+
const node = nodeType.createAndFill(attrs)
|
|
34
|
+
|
|
35
|
+
if (node) {
|
|
36
|
+
tr.replaceRangeWith(
|
|
37
|
+
nodeType.isBlock ? tr.doc.resolve(start).before() : start,
|
|
38
|
+
end,
|
|
39
|
+
node
|
|
40
|
+
)
|
|
41
|
+
options.beforeDispatch?.({
|
|
42
|
+
match: [fullMatch, group],
|
|
43
|
+
start,
|
|
44
|
+
end,
|
|
45
|
+
tr,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return tr
|
|
50
|
+
})
|
|
51
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { expectDomTypeError, missingRootElement } from '@jvs-milkdown/exception'
|
|
2
|
+
|
|
3
|
+
import type { EditorView } from '../../view'
|
|
4
|
+
|
|
5
|
+
type Point = [top: number, left: number]
|
|
6
|
+
|
|
7
|
+
export function calculateNodePosition(
|
|
8
|
+
view: EditorView,
|
|
9
|
+
target: HTMLElement,
|
|
10
|
+
handler: (
|
|
11
|
+
selectedRect: DOMRect,
|
|
12
|
+
targetRect: DOMRect,
|
|
13
|
+
parentRect: DOMRect
|
|
14
|
+
) => Point
|
|
15
|
+
) {
|
|
16
|
+
const state = view.state
|
|
17
|
+
const { from } = state.selection
|
|
18
|
+
|
|
19
|
+
const { node } = view.domAtPos(from)
|
|
20
|
+
const element = node instanceof Text ? node.parentElement : node
|
|
21
|
+
if (!(element instanceof HTMLElement)) throw expectDomTypeError(element)
|
|
22
|
+
|
|
23
|
+
const selectedNodeRect = element.getBoundingClientRect()
|
|
24
|
+
const targetNodeRect = target.getBoundingClientRect()
|
|
25
|
+
const parent = target.parentElement
|
|
26
|
+
if (!parent) throw expectDomTypeError(parent)
|
|
27
|
+
|
|
28
|
+
const parentNodeRect = parent.getBoundingClientRect()
|
|
29
|
+
|
|
30
|
+
const [top, left] = handler(selectedNodeRect, targetNodeRect, parentNodeRect)
|
|
31
|
+
|
|
32
|
+
target.style.top = `${top}px`
|
|
33
|
+
target.style.left = `${left}px`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface Rect {
|
|
37
|
+
left: number
|
|
38
|
+
right: number
|
|
39
|
+
top: number
|
|
40
|
+
bottom: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function calculateTextPosition(
|
|
44
|
+
view: EditorView,
|
|
45
|
+
target: HTMLElement,
|
|
46
|
+
handler: (
|
|
47
|
+
start: Rect,
|
|
48
|
+
end: Rect,
|
|
49
|
+
targetRect: DOMRect,
|
|
50
|
+
parentRect: DOMRect
|
|
51
|
+
) => Point
|
|
52
|
+
) {
|
|
53
|
+
const state = view.state
|
|
54
|
+
const { from, to } = state.selection
|
|
55
|
+
const start = view.coordsAtPos(from)
|
|
56
|
+
const end = view.coordsAtPos(to)
|
|
57
|
+
|
|
58
|
+
const targetNodeRect = target.getBoundingClientRect()
|
|
59
|
+
const parent = target.parentElement
|
|
60
|
+
if (!parent) throw missingRootElement()
|
|
61
|
+
|
|
62
|
+
const parentNodeRect = parent.getBoundingClientRect()
|
|
63
|
+
|
|
64
|
+
const [top, left] = handler(start, end, targetNodeRect, parentNodeRect)
|
|
65
|
+
|
|
66
|
+
target.style.top = `${top}px`
|
|
67
|
+
target.style.left = `${left}px`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function minMax(value = 0, min = 0, max = 0): number {
|
|
71
|
+
return Math.min(Math.max(value, min), max)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function posToDOMRect(
|
|
75
|
+
view: EditorView,
|
|
76
|
+
from: number,
|
|
77
|
+
to: number
|
|
78
|
+
): DOMRect {
|
|
79
|
+
const minPos = 0
|
|
80
|
+
const maxPos = view.state.doc.content.size
|
|
81
|
+
const resolvedFrom = minMax(from, minPos, maxPos)
|
|
82
|
+
const resolvedEnd = minMax(to, minPos, maxPos)
|
|
83
|
+
const start = view.coordsAtPos(resolvedFrom)
|
|
84
|
+
const end = view.coordsAtPos(resolvedEnd, -1)
|
|
85
|
+
const top = Math.min(start.top, end.top)
|
|
86
|
+
const bottom = Math.max(start.bottom, end.bottom)
|
|
87
|
+
const left = Math.min(start.left, end.left)
|
|
88
|
+
const right = Math.max(start.right, end.right)
|
|
89
|
+
const width = right - left
|
|
90
|
+
const height = bottom - top
|
|
91
|
+
const x = left
|
|
92
|
+
const y = top
|
|
93
|
+
const data = {
|
|
94
|
+
top,
|
|
95
|
+
bottom,
|
|
96
|
+
left,
|
|
97
|
+
right,
|
|
98
|
+
width,
|
|
99
|
+
height,
|
|
100
|
+
x,
|
|
101
|
+
y,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
...data,
|
|
106
|
+
toJSON: () => data,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Slice, NodeType, Node as ProseNode } from '../../model'
|
|
2
|
+
import type { Transaction } from '../../state'
|
|
3
|
+
|
|
4
|
+
export function cloneTr(tr: Transaction): Transaction {
|
|
5
|
+
return Object.assign(Object.create(tr), tr).setTime(Date.now())
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function equalNodeType(
|
|
9
|
+
nodeType: NodeType | NodeType[],
|
|
10
|
+
node: ProseNode
|
|
11
|
+
) {
|
|
12
|
+
return (
|
|
13
|
+
(Array.isArray(nodeType) && nodeType.includes(node.type)) ||
|
|
14
|
+
node.type === nodeType
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isTextOnlySlice(slice: Slice): ProseNode | false {
|
|
19
|
+
if (slice.content.childCount === 1) {
|
|
20
|
+
const node = slice.content.firstChild
|
|
21
|
+
if (node?.type.name === 'text' && node.marks.length === 0) return node
|
|
22
|
+
|
|
23
|
+
if (node?.type.name === 'paragraph' && node.childCount === 1) {
|
|
24
|
+
const _node = node.firstChild
|
|
25
|
+
if (_node?.type.name === 'text' && _node.marks.length === 0) return _node
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return false
|
|
30
|
+
}
|