@milkdown/preset-commonmark 6.5.4 → 7.0.0-next.1
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/lib/composed/commands.d.ts +3 -0
- package/lib/composed/commands.d.ts.map +1 -0
- package/lib/composed/index.d.ts +6 -0
- package/lib/composed/index.d.ts.map +1 -0
- package/lib/composed/inputrules.d.ts +3 -0
- package/lib/composed/inputrules.d.ts.map +1 -0
- package/lib/composed/keymap.d.ts +3 -0
- package/lib/composed/keymap.d.ts.map +1 -0
- package/lib/composed/plugins.d.ts +3 -0
- package/lib/composed/plugins.d.ts.map +1 -0
- package/lib/composed/schema.d.ts +3 -0
- package/lib/composed/schema.d.ts.map +1 -0
- package/lib/index.d.ts +4 -29
- package/lib/index.d.ts.map +1 -1
- package/lib/index.es.js +1099 -1386
- package/lib/index.es.js.map +1 -1
- package/lib/mark/emphasis.d.ts +5 -0
- package/lib/mark/emphasis.d.ts.map +1 -0
- package/lib/mark/index.d.ts +3 -4
- package/lib/mark/index.d.ts.map +1 -1
- package/lib/mark/inline-code.d.ts +5 -0
- package/lib/mark/inline-code.d.ts.map +1 -0
- package/lib/mark/link.d.ts +8 -10
- package/lib/mark/link.d.ts.map +1 -1
- package/lib/mark/strong.d.ts +4 -2
- package/lib/mark/strong.d.ts.map +1 -1
- package/lib/node/blockquote.d.ts +6 -2
- package/lib/node/blockquote.d.ts.map +1 -1
- package/lib/node/bullet-list.d.ts +5 -2
- package/lib/node/bullet-list.d.ts.map +1 -1
- package/lib/node/code-block.d.ts +10 -0
- package/lib/node/code-block.d.ts.map +1 -0
- package/lib/node/doc.d.ts +1 -1
- package/lib/node/doc.d.ts.map +1 -1
- package/lib/node/hardbreak.d.ts +4 -6
- package/lib/node/hardbreak.d.ts.map +1 -1
- package/lib/node/heading.d.ts +7 -12
- package/lib/node/heading.d.ts.map +1 -1
- package/lib/node/hr.d.ts +4 -2
- package/lib/node/hr.d.ts.map +1 -1
- package/lib/node/image.d.ts +10 -11
- package/lib/node/image.d.ts.map +1 -1
- package/lib/node/index.d.ts +6 -8
- package/lib/node/index.d.ts.map +1 -1
- package/lib/node/list-item.d.ts +6 -7
- package/lib/node/list-item.d.ts.map +1 -1
- package/lib/node/ordered-list.d.ts +5 -2
- package/lib/node/ordered-list.d.ts.map +1 -1
- package/lib/node/paragraph.d.ts +4 -2
- package/lib/node/paragraph.d.ts.map +1 -1
- package/lib/node/text.d.ts +1 -1
- package/lib/node/text.d.ts.map +1 -1
- package/lib/plugin/hardbreak-clear-mark-plugin.d.ts +2 -0
- package/lib/plugin/hardbreak-clear-mark-plugin.d.ts.map +1 -0
- package/lib/plugin/hardbreak-filter-plugin.d.ts +3 -0
- package/lib/plugin/hardbreak-filter-plugin.d.ts.map +1 -0
- package/lib/plugin/index.d.ts +9 -3
- package/lib/plugin/index.d.ts.map +1 -1
- package/lib/plugin/inline-nodes-cursor-plugin.d.ts +2 -0
- package/lib/plugin/inline-nodes-cursor-plugin.d.ts.map +1 -0
- package/lib/plugin/{inline-sync → inline-sync-plugin}/config.d.ts +2 -2
- package/lib/plugin/inline-sync-plugin/config.d.ts.map +1 -0
- package/lib/plugin/{inline-sync → inline-sync-plugin}/context.d.ts +1 -2
- package/lib/plugin/inline-sync-plugin/context.d.ts.map +1 -0
- package/lib/plugin/inline-sync-plugin/index.d.ts +3 -0
- package/lib/plugin/inline-sync-plugin/index.d.ts.map +1 -0
- package/lib/plugin/inline-sync-plugin/inline-sync-plugin.d.ts +2 -0
- package/lib/plugin/inline-sync-plugin/inline-sync-plugin.d.ts.map +1 -0
- package/lib/plugin/{inline-sync → inline-sync-plugin}/regexp.d.ts +0 -0
- package/lib/plugin/inline-sync-plugin/regexp.d.ts.map +1 -0
- package/lib/plugin/{inline-sync → inline-sync-plugin}/replacer.d.ts +1 -1
- package/lib/plugin/inline-sync-plugin/replacer.d.ts.map +1 -0
- package/lib/plugin/{inline-sync → inline-sync-plugin}/utils.d.ts +0 -0
- package/lib/plugin/inline-sync-plugin/utils.d.ts.map +1 -0
- package/lib/plugin/remark-add-order-in-list-plugin.d.ts +2 -0
- package/lib/plugin/remark-add-order-in-list-plugin.d.ts.map +1 -0
- package/lib/plugin/remark-inline-link-plugin.d.ts +2 -0
- package/lib/plugin/remark-inline-link-plugin.d.ts.map +1 -0
- package/lib/plugin/remark-line-break.d.ts +2 -0
- package/lib/plugin/remark-line-break.d.ts.map +1 -0
- package/lib/plugin/sync-heading-id-plugin.d.ts +2 -0
- package/lib/plugin/sync-heading-id-plugin.d.ts.map +1 -0
- package/lib/plugin/sync-list-order-plugin.d.ts +2 -0
- package/lib/plugin/sync-list-order-plugin.d.ts.map +1 -0
- package/package.json +9 -7
- package/src/composed/commands.ts +31 -0
- package/src/composed/index.ts +6 -0
- package/src/composed/inputrules.ts +13 -0
- package/src/composed/keymap.ts +20 -0
- package/src/composed/plugins.ts +23 -0
- package/src/composed/schema.ts +55 -0
- package/src/index.ts +6 -52
- package/src/mark/emphasis.ts +47 -0
- package/src/mark/index.ts +3 -10
- package/src/mark/inline-code.ts +70 -0
- package/src/mark/link.ts +96 -247
- package/src/mark/strong.ts +41 -36
- package/src/node/blockquote.ts +39 -33
- package/src/node/bullet-list.ts +62 -55
- package/src/node/code-block.ts +103 -0
- package/src/node/doc.ts +18 -22
- package/src/node/hardbreak.ts +68 -117
- package/src/node/heading.ts +175 -284
- package/src/node/hr.ts +57 -57
- package/src/node/image.ts +113 -209
- package/src/node/index.ts +6 -35
- package/src/node/list-item.ts +125 -141
- package/src/node/ordered-list.ts +70 -65
- package/src/node/paragraph.ts +54 -50
- package/src/node/text.ts +14 -16
- package/src/plugin/hardbreak-clear-mark-plugin.ts +45 -0
- package/src/plugin/hardbreak-filter-plugin.ts +33 -0
- package/src/plugin/index.ts +12 -14
- package/src/plugin/{inline-nodes-cursor.ts → inline-nodes-cursor-plugin.ts} +5 -8
- package/src/plugin/{inline-sync → inline-sync-plugin}/config.ts +18 -3
- package/src/plugin/{inline-sync → inline-sync-plugin}/context.ts +4 -6
- package/src/plugin/inline-sync-plugin/index.ts +4 -0
- package/src/plugin/{inline-sync/index.ts → inline-sync-plugin/inline-sync-plugin.ts} +14 -11
- package/src/plugin/{inline-sync → inline-sync-plugin}/regexp.ts +0 -0
- package/src/plugin/{inline-sync → inline-sync-plugin}/replacer.ts +3 -3
- package/src/plugin/{inline-sync → inline-sync-plugin}/utils.ts +0 -0
- package/src/plugin/remark-add-order-in-list-plugin.ts +16 -0
- package/src/plugin/remark-inline-link-plugin.ts +6 -0
- package/src/plugin/remark-line-break.ts +44 -0
- package/src/plugin/sync-heading-id-plugin.ts +55 -0
- package/src/plugin/sync-list-order-plugin.ts +57 -0
- package/lib/mark/code-inline.d.ts +0 -3
- package/lib/mark/code-inline.d.ts.map +0 -1
- package/lib/mark/em.d.ts +0 -3
- package/lib/mark/em.d.ts.map +0 -1
- package/lib/node/code-fence.d.ts +0 -7
- package/lib/node/code-fence.d.ts.map +0 -1
- package/lib/plugin/add-order-in-list.d.ts +0 -3
- package/lib/plugin/add-order-in-list.d.ts.map +0 -1
- package/lib/plugin/filter-html.d.ts +0 -3
- package/lib/plugin/filter-html.d.ts.map +0 -1
- package/lib/plugin/inline-nodes-cursor.d.ts +0 -7
- package/lib/plugin/inline-nodes-cursor.d.ts.map +0 -1
- package/lib/plugin/inline-sync/config.d.ts.map +0 -1
- package/lib/plugin/inline-sync/context.d.ts.map +0 -1
- package/lib/plugin/inline-sync/index.d.ts +0 -6
- package/lib/plugin/inline-sync/index.d.ts.map +0 -1
- package/lib/plugin/inline-sync/regexp.d.ts.map +0 -1
- package/lib/plugin/inline-sync/replacer.d.ts.map +0 -1
- package/lib/plugin/inline-sync/utils.d.ts.map +0 -1
- package/lib/supported-keys.d.ts +0 -23
- package/lib/supported-keys.d.ts.map +0 -1
- package/src/mark/code-inline.ts +0 -66
- package/src/mark/em.ts +0 -42
- package/src/node/code-fence.ts +0 -245
- package/src/plugin/add-order-in-list.ts +0 -19
- package/src/plugin/filter-html.ts +0 -43
- package/src/supported-keys.ts +0 -25
- package/src/types.d.ts +0 -5
package/src/node/heading.ts
CHANGED
|
@@ -1,315 +1,206 @@
|
|
|
1
1
|
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
import
|
|
3
|
-
import { createCmd, createCmdKey, editorViewCtx, getPalette, schemaCtx } from '@milkdown/core'
|
|
2
|
+
import { commandsCtx, editorViewCtx } from '@milkdown/core'
|
|
4
3
|
import { expectDomTypeError } from '@milkdown/exception'
|
|
5
|
-
import { cloneTr } from '@milkdown/prose'
|
|
6
4
|
import { setBlockType } from '@milkdown/prose/commands'
|
|
7
5
|
import { textblockTypeInputRule } from '@milkdown/prose/inputrules'
|
|
8
|
-
import type { Node
|
|
6
|
+
import type { Node } from '@milkdown/prose/model'
|
|
9
7
|
import { Fragment } from '@milkdown/prose/model'
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import { Decoration, DecorationSet } from '@milkdown/prose/view'
|
|
13
|
-
import type { ThemeUtils } from '@milkdown/utils'
|
|
14
|
-
import { createNode, createShortcut } from '@milkdown/utils'
|
|
15
|
-
|
|
16
|
-
import { SupportedKeys } from '../supported-keys'
|
|
8
|
+
import { $command, $ctx, $inputRule, $nodeAttr, $nodeSchema, $useKeymap } from '@milkdown/utils'
|
|
9
|
+
import { paragraphSchema } from './paragraph'
|
|
17
10
|
|
|
18
11
|
const headingIndex = Array(6)
|
|
19
12
|
.fill(0)
|
|
20
13
|
.map((_, i) => i + 1)
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
| SupportedKeys['H1']
|
|
24
|
-
| SupportedKeys['H2']
|
|
25
|
-
| SupportedKeys['H3']
|
|
26
|
-
| SupportedKeys['H4']
|
|
27
|
-
| SupportedKeys['H5']
|
|
28
|
-
| SupportedKeys['H6']
|
|
29
|
-
| SupportedKeys['DowngradeHeading']
|
|
30
|
-
|
|
31
|
-
export const TurnIntoHeading = createCmdKey<number>('TurnIntoHeading')
|
|
32
|
-
export const DowngradeHeading = createCmdKey('DowngradeHeading')
|
|
33
|
-
|
|
34
|
-
export const headingIdPluginKey = new PluginKey('MILKDOWN_HEADING_ID')
|
|
35
|
-
export const headingHashPluginKey = new PluginKey('MILKDOWN_HEADING_HASH')
|
|
36
|
-
|
|
37
|
-
const createId = (node: Node) =>
|
|
15
|
+
const defaultHeadingIdGenerator = (node: Node) =>
|
|
38
16
|
node.textContent
|
|
39
17
|
.replace(/[\p{P}\p{S}]/gu, '')
|
|
40
18
|
.replace(/\s/g, '-')
|
|
41
19
|
.toLowerCase()
|
|
42
20
|
.trim()
|
|
43
21
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
id,
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
if (found)
|
|
67
|
-
callback(tr)
|
|
68
|
-
}
|
|
69
|
-
return new Plugin({
|
|
70
|
-
key: headingIdPluginKey,
|
|
71
|
-
props: {
|
|
72
|
-
handleDOMEvents: {
|
|
73
|
-
compositionstart: () => {
|
|
74
|
-
lock = true
|
|
75
|
-
return false
|
|
76
|
-
},
|
|
77
|
-
compositionend: () => {
|
|
78
|
-
lock = false
|
|
79
|
-
const view = ctx.get(editorViewCtx)
|
|
80
|
-
setTimeout(() => {
|
|
81
|
-
walkThrough(view.state, tr => view.dispatch(tr))
|
|
82
|
-
}, 0)
|
|
83
|
-
return false
|
|
84
|
-
},
|
|
22
|
+
/// This is a slice contains a function to generate heading id.
|
|
23
|
+
/// You can configure it to generate id in your own way.
|
|
24
|
+
export const headingIdGenerator = $ctx(defaultHeadingIdGenerator, 'headingIdGenerator')
|
|
25
|
+
|
|
26
|
+
/// HTML attributes for heading node.
|
|
27
|
+
export const headingAttr = $nodeAttr('heading')
|
|
28
|
+
|
|
29
|
+
/// Schema for heading node.
|
|
30
|
+
export const headingSchema = $nodeSchema('heading', (ctx) => {
|
|
31
|
+
const getId = ctx.get(headingIdGenerator.key)
|
|
32
|
+
return {
|
|
33
|
+
content: 'inline*',
|
|
34
|
+
group: 'block',
|
|
35
|
+
defining: true,
|
|
36
|
+
attrs: {
|
|
37
|
+
id: {
|
|
38
|
+
default: '',
|
|
85
39
|
},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
let tr: Transaction | null = null
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
transactions.every(transaction => !transaction.getMeta(headingIdPluginKey))
|
|
92
|
-
&& transactions.some(transaction => transaction.docChanged)
|
|
93
|
-
) {
|
|
94
|
-
walkThrough(nextState, (t) => {
|
|
95
|
-
tr = t
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return tr
|
|
100
|
-
},
|
|
101
|
-
view: (view) => {
|
|
102
|
-
const doc = view.state.doc
|
|
103
|
-
let tr = view.state.tr.setMeta('addToHistory', false)
|
|
104
|
-
doc.descendants((node, pos) => {
|
|
105
|
-
if (node.type.name === 'heading' && node.attrs.level) {
|
|
106
|
-
if (!node.attrs.id) {
|
|
107
|
-
tr = tr.setNodeMarkup(pos, undefined, {
|
|
108
|
-
...node.attrs,
|
|
109
|
-
id: getId(node),
|
|
110
|
-
})
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
view.dispatch(tr)
|
|
115
|
-
return {}
|
|
116
|
-
},
|
|
117
|
-
})
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const headingHashPlugin = (ctx: Ctx, type: NodeType, utils: ThemeUtils): Plugin => {
|
|
121
|
-
return new Plugin({
|
|
122
|
-
key: headingHashPluginKey,
|
|
123
|
-
state: {
|
|
124
|
-
init: () => {
|
|
125
|
-
return DecorationSet.empty
|
|
40
|
+
level: {
|
|
41
|
+
default: 1,
|
|
126
42
|
},
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const node = $from.node()
|
|
134
|
-
if (node.type !== type)
|
|
135
|
-
return DecorationSet.empty
|
|
136
|
-
|
|
137
|
-
const level = node.attrs.level
|
|
138
|
-
const getHashes = (level: number) => {
|
|
139
|
-
return Array(level)
|
|
140
|
-
.fill(0)
|
|
141
|
-
.map(_ => '#')
|
|
142
|
-
.join('')
|
|
143
|
-
}
|
|
144
|
-
const widget = document.createElement('span')
|
|
145
|
-
widget.textContent = getHashes(level)
|
|
146
|
-
widget.contentEditable = 'false'
|
|
147
|
-
utils.themeManager.onFlush(() => {
|
|
148
|
-
const style = utils.getStyle(({ css }) => {
|
|
149
|
-
const palette = getPalette(utils.themeManager)
|
|
150
|
-
return css`
|
|
151
|
-
margin-right: 4px;
|
|
152
|
-
color: ${palette('primary')};
|
|
153
|
-
`
|
|
154
|
-
})
|
|
155
|
-
if (style)
|
|
156
|
-
widget.className = style
|
|
157
|
-
})
|
|
43
|
+
},
|
|
44
|
+
parseDOM: headingIndex.map(x => ({
|
|
45
|
+
tag: `h${x}`,
|
|
46
|
+
getAttrs: (node) => {
|
|
47
|
+
if (!(node instanceof HTMLElement))
|
|
48
|
+
throw expectDomTypeError(node)
|
|
158
49
|
|
|
159
|
-
|
|
160
|
-
return DecorationSet.create(tr.doc, [deco])
|
|
50
|
+
return { level: x, id: node.id }
|
|
161
51
|
},
|
|
162
|
-
},
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
52
|
+
})),
|
|
53
|
+
toDOM: (node) => {
|
|
54
|
+
return [
|
|
55
|
+
`h${node.attrs.level}`,
|
|
56
|
+
{
|
|
57
|
+
...ctx.get(headingAttr.key)(node),
|
|
58
|
+
id: node.attrs.id || getId(node),
|
|
169
59
|
},
|
|
60
|
+
0,
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
parseMarkdown: {
|
|
64
|
+
match: ({ type }) => type === 'heading',
|
|
65
|
+
runner: (state, node, type) => {
|
|
66
|
+
const depth = node.depth as number
|
|
67
|
+
state.openNode(type, { level: depth })
|
|
68
|
+
state.next(node.children)
|
|
69
|
+
state.closeNode()
|
|
170
70
|
},
|
|
171
|
-
|
|
172
|
-
|
|
71
|
+
},
|
|
72
|
+
toMarkdown: {
|
|
73
|
+
match: node => node.type.name === 'heading',
|
|
74
|
+
runner: (state, node) => {
|
|
75
|
+
state.openNode('heading', undefined, { depth: node.attrs.level })
|
|
76
|
+
const lastIsHardbreak = node.childCount >= 1 && node.lastChild?.type.name === 'hardbreak'
|
|
77
|
+
if (lastIsHardbreak) {
|
|
78
|
+
const contentArr: Node[] = []
|
|
79
|
+
node.content.forEach((n, _, i) => {
|
|
80
|
+
if (i === node.childCount - 1)
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
contentArr.push(n)
|
|
84
|
+
})
|
|
85
|
+
state.next(Fragment.fromArray(contentArr))
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
state.next(node.content)
|
|
89
|
+
}
|
|
90
|
+
state.closeNode()
|
|
173
91
|
},
|
|
174
92
|
},
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
/// This input rule can turn the selected block into heading.
|
|
97
|
+
/// You can input numbers of `#` and a `space` to create heading.
|
|
98
|
+
export const wrapInHeadingInputRule = $inputRule((ctx) => {
|
|
99
|
+
return textblockTypeInputRule(/^(?<hashes>#+)\s$/, headingSchema.type(), (match) => {
|
|
100
|
+
const x = match.groups?.hashes?.length || 0
|
|
101
|
+
|
|
102
|
+
const view = ctx.get(editorViewCtx)
|
|
103
|
+
const { $from } = view.state.selection
|
|
104
|
+
const node = $from.node()
|
|
105
|
+
if (node.type.name === 'heading') {
|
|
106
|
+
let level = Number(node.attrs.level) + Number(x)
|
|
107
|
+
if (level > 6)
|
|
108
|
+
level = 6
|
|
109
|
+
|
|
110
|
+
return { level }
|
|
111
|
+
}
|
|
112
|
+
return { level: x }
|
|
175
113
|
})
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export const heading = createNode<Keys, { getId: (node: Node) => string; displayHashtag: boolean }>(
|
|
179
|
-
(utils, options) => {
|
|
180
|
-
const id = 'heading'
|
|
181
|
-
|
|
182
|
-
const getId = options?.getId ?? createId
|
|
183
|
-
const displayHashtag = options?.displayHashtag ?? true
|
|
114
|
+
})
|
|
184
115
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
attrs: {
|
|
192
|
-
id: {
|
|
193
|
-
default: '',
|
|
194
|
-
},
|
|
195
|
-
level: {
|
|
196
|
-
default: 1,
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
parseDOM: headingIndex.map(x => ({
|
|
200
|
-
tag: `h${x}`,
|
|
201
|
-
getAttrs: (node) => {
|
|
202
|
-
if (!(node instanceof HTMLElement))
|
|
203
|
-
throw expectDomTypeError(node)
|
|
116
|
+
/// This command can turn the selected block into heading.
|
|
117
|
+
/// You can pass the level of heading to this command.
|
|
118
|
+
/// By default, the level is 1, which means it will create a `h1` element.
|
|
119
|
+
export const wrapInHeadingCommand = $command('WrapInHeading', () => {
|
|
120
|
+
return (level?: number) => {
|
|
121
|
+
level ??= 1
|
|
204
122
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
})),
|
|
208
|
-
toDOM: (node) => {
|
|
209
|
-
return [
|
|
210
|
-
`h${node.attrs.level}`,
|
|
211
|
-
{
|
|
212
|
-
id: node.attrs.id || getId(node),
|
|
213
|
-
class: utils.getClassName(node.attrs, `heading h${node.attrs.level}`),
|
|
214
|
-
},
|
|
215
|
-
0,
|
|
216
|
-
]
|
|
217
|
-
},
|
|
218
|
-
parseMarkdown: {
|
|
219
|
-
match: ({ type }) => type === id,
|
|
220
|
-
runner: (state, node, type) => {
|
|
221
|
-
const depth = node.depth as number
|
|
222
|
-
state.openNode(type, { level: depth })
|
|
223
|
-
state.next(node.children)
|
|
224
|
-
state.closeNode()
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
toMarkdown: {
|
|
228
|
-
match: node => node.type.name === id,
|
|
229
|
-
runner: (state, node) => {
|
|
230
|
-
state.openNode('heading', undefined, { depth: node.attrs.level })
|
|
231
|
-
const lastIsHardbreak = node.childCount >= 1 && node.lastChild?.type.name === 'hardbreak'
|
|
232
|
-
if (lastIsHardbreak) {
|
|
233
|
-
const contentArr: Node[] = []
|
|
234
|
-
node.content.forEach((n, _, i) => {
|
|
235
|
-
if (i === node.childCount - 1)
|
|
236
|
-
return
|
|
123
|
+
if (level < 1)
|
|
124
|
+
return setBlockType(paragraphSchema.type())
|
|
237
125
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
126
|
+
return setBlockType(headingSchema.type(), { level })
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
/// This command can downgrade the selected heading.
|
|
131
|
+
/// For example, if you have a `h2` element, and you call this command, you will get a `h1` element.
|
|
132
|
+
/// If the element is already a `h1` element, it will turn it into a `p` element.
|
|
133
|
+
export const downgradeHeadingCommand = $command('DowngradeHeading', () => () =>
|
|
134
|
+
(state, dispatch, view) => {
|
|
135
|
+
const { $from } = state.selection
|
|
136
|
+
const node = $from.node()
|
|
137
|
+
if (node.type !== headingSchema.type() || !state.selection.empty || $from.parentOffset !== 0)
|
|
138
|
+
return false
|
|
139
|
+
|
|
140
|
+
const level = node.attrs.level - 1
|
|
141
|
+
if (!level)
|
|
142
|
+
return setBlockType(paragraphSchema.type())(state, dispatch, view)
|
|
143
|
+
|
|
144
|
+
dispatch?.(
|
|
145
|
+
state.tr.setNodeMarkup(state.selection.$from.before(), undefined, {
|
|
146
|
+
...node.attrs,
|
|
147
|
+
level,
|
|
248
148
|
}),
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const view = ctx.get(editorViewCtx)
|
|
253
|
-
const { $from } = view.state.selection
|
|
254
|
-
const node = $from.node()
|
|
255
|
-
if (node.type.name === 'heading') {
|
|
256
|
-
let level = Number(node.attrs.level) + Number(x)
|
|
257
|
-
if (level > 6)
|
|
258
|
-
level = 6
|
|
259
|
-
|
|
260
|
-
return {
|
|
261
|
-
level,
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return {
|
|
265
|
-
level: x,
|
|
266
|
-
}
|
|
267
|
-
}),
|
|
268
|
-
),
|
|
269
|
-
commands: (type, ctx) => [
|
|
270
|
-
createCmd(TurnIntoHeading, (level = 1) => {
|
|
271
|
-
if (level < 1)
|
|
272
|
-
return setBlockType(level === 0 ? ctx.get(schemaCtx).nodes.paragraph || type : type)
|
|
273
|
-
|
|
274
|
-
return setBlockType(level === 0 ? ctx.get(schemaCtx).nodes.paragraph || type : type, { level })
|
|
275
|
-
}),
|
|
276
|
-
createCmd(DowngradeHeading, () => {
|
|
277
|
-
return (state, dispatch, view) => {
|
|
278
|
-
const { $from } = state.selection
|
|
279
|
-
const node = $from.node()
|
|
280
|
-
if (node.type !== type || !state.selection.empty || $from.parentOffset !== 0)
|
|
281
|
-
return false
|
|
282
|
-
|
|
283
|
-
const level = node.attrs.level - 1
|
|
284
|
-
if (!level)
|
|
285
|
-
return setBlockType(ctx.get(schemaCtx).nodes.paragraph || type)(state, dispatch, view)
|
|
286
|
-
|
|
287
|
-
dispatch?.(
|
|
288
|
-
state.tr.setNodeMarkup(state.selection.$from.before(), undefined, {
|
|
289
|
-
...node.attrs,
|
|
290
|
-
level,
|
|
291
|
-
}),
|
|
292
|
-
)
|
|
293
|
-
return true
|
|
294
|
-
}
|
|
295
|
-
}),
|
|
296
|
-
],
|
|
297
|
-
shortcuts: {
|
|
298
|
-
[SupportedKeys.H1]: createShortcut(TurnIntoHeading, 'Mod-Alt-1', 1),
|
|
299
|
-
[SupportedKeys.H2]: createShortcut(TurnIntoHeading, 'Mod-Alt-2', 2),
|
|
300
|
-
[SupportedKeys.H3]: createShortcut(TurnIntoHeading, 'Mod-Alt-3', 3),
|
|
301
|
-
[SupportedKeys.H4]: createShortcut(TurnIntoHeading, 'Mod-Alt-4', 4),
|
|
302
|
-
[SupportedKeys.H5]: createShortcut(TurnIntoHeading, 'Mod-Alt-5', 5),
|
|
303
|
-
[SupportedKeys.H6]: createShortcut(TurnIntoHeading, 'Mod-Alt-6', 6),
|
|
304
|
-
[SupportedKeys.DowngradeHeading]: createShortcut(DowngradeHeading, ['Backspace', 'Delete']),
|
|
305
|
-
},
|
|
306
|
-
prosePlugins: (type, ctx) => {
|
|
307
|
-
const plugins = [headingIdPlugin(ctx, type, getId)]
|
|
308
|
-
if (displayHashtag)
|
|
309
|
-
plugins.push(headingHashPlugin(ctx, type, utils))
|
|
149
|
+
)
|
|
150
|
+
return true
|
|
151
|
+
})
|
|
310
152
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
153
|
+
/// Keymap for heading node.
|
|
154
|
+
/// - `<Mod-Alt-{1-6}>`: Turn the selected block into `h{1-6}` element.
|
|
155
|
+
/// - `<Delete>/<Backspace>`: Downgrade the selected heading.
|
|
156
|
+
export const headingKeymap = $useKeymap('headingKeymap', {
|
|
157
|
+
TurnIntoH1: {
|
|
158
|
+
shortcuts: 'Mod-Alt-1',
|
|
159
|
+
command: (ctx) => {
|
|
160
|
+
const commands = ctx.get(commandsCtx)
|
|
161
|
+
return () => commands.call(wrapInHeadingCommand.key, 1)
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
TurnIntoH2: {
|
|
165
|
+
shortcuts: 'Mod-Alt-2',
|
|
166
|
+
command: (ctx) => {
|
|
167
|
+
const commands = ctx.get(commandsCtx)
|
|
168
|
+
return () => commands.call(wrapInHeadingCommand.key, 2)
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
TurnIntoH3: {
|
|
172
|
+
shortcuts: 'Mod-Alt-3',
|
|
173
|
+
command: (ctx) => {
|
|
174
|
+
const commands = ctx.get(commandsCtx)
|
|
175
|
+
return () => commands.call(wrapInHeadingCommand.key, 3)
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
TurnIntoH4: {
|
|
179
|
+
shortcuts: 'Mod-Alt-4',
|
|
180
|
+
command: (ctx) => {
|
|
181
|
+
const commands = ctx.get(commandsCtx)
|
|
182
|
+
return () => commands.call(wrapInHeadingCommand.key, 3)
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
TurnIntoH5: {
|
|
186
|
+
shortcuts: 'Mod-Alt-5',
|
|
187
|
+
command: (ctx) => {
|
|
188
|
+
const commands = ctx.get(commandsCtx)
|
|
189
|
+
return () => commands.call(wrapInHeadingCommand.key, 3)
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
TurnIntoH6: {
|
|
193
|
+
shortcuts: 'Mod-Alt-6',
|
|
194
|
+
command: (ctx) => {
|
|
195
|
+
const commands = ctx.get(commandsCtx)
|
|
196
|
+
return () => commands.call(wrapInHeadingCommand.key, 3)
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
DowngradeHeading: {
|
|
200
|
+
shortcuts: ['Delete', 'Backspace'],
|
|
201
|
+
command: (ctx) => {
|
|
202
|
+
const commands = ctx.get(commandsCtx)
|
|
203
|
+
return () => commands.call(downgradeHeadingCommand.key)
|
|
204
|
+
},
|
|
314
205
|
},
|
|
315
|
-
)
|
|
206
|
+
})
|
package/src/node/hr.ts
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
1
|
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
import { createCmd, createCmdKey, schemaCtx } from '@milkdown/core'
|
|
3
2
|
import { InputRule } from '@milkdown/prose/inputrules'
|
|
4
3
|
import { Selection } from '@milkdown/prose/state'
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
4
|
+
import { $command, $inputRule, $nodeAttr, $nodeSchema } from '@milkdown/utils'
|
|
5
|
+
import { paragraphSchema } from './paragraph'
|
|
6
|
+
|
|
7
|
+
/// HTML attributes for the hr node.
|
|
8
|
+
export const hrAttr = $nodeAttr('hr')
|
|
9
|
+
|
|
10
|
+
/// Hr node schema.
|
|
11
|
+
export const hrSchema = $nodeSchema('hr', ctx => ({
|
|
12
|
+
group: 'block',
|
|
13
|
+
parseDOM: [{ tag: 'hr' }],
|
|
14
|
+
toDOM: node => ['hr', ctx.get(hrAttr.key)(node)],
|
|
15
|
+
parseMarkdown: {
|
|
16
|
+
match: ({ type }) => type === 'thematicBreak',
|
|
17
|
+
runner: (state, _, type) => {
|
|
18
|
+
state.addNode(type)
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
toMarkdown: {
|
|
22
|
+
match: node => node.type.name === 'hr',
|
|
23
|
+
runner: (state) => {
|
|
24
|
+
state.addNode('thematicBreak')
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
/// Input rule to insert a hr.
|
|
30
|
+
/// For example, `---` will be converted to a hr.
|
|
31
|
+
export const insertHrInputRule = $inputRule(() => new InputRule(
|
|
32
|
+
/^(?:---|___\s|\*\*\*\s)$/, (state, match, start, end) => {
|
|
33
|
+
const { tr } = state
|
|
34
|
+
|
|
35
|
+
if (match[0])
|
|
36
|
+
tr.replaceWith(start - 1, end, hrSchema.type().create())
|
|
37
|
+
|
|
38
|
+
return tr
|
|
39
|
+
},
|
|
40
|
+
))
|
|
41
|
+
|
|
42
|
+
/// Command to insert a hr.
|
|
43
|
+
export const insertHrCommand = $command('InsertHr', () => () => (state, dispatch) => {
|
|
44
|
+
if (!dispatch)
|
|
45
|
+
return true
|
|
46
|
+
|
|
47
|
+
const paragraph = paragraphSchema.node.type().create()
|
|
48
|
+
const { tr, selection } = state
|
|
49
|
+
const { from } = selection
|
|
50
|
+
const node = hrSchema.type().create()
|
|
51
|
+
if (!node)
|
|
52
|
+
return true
|
|
53
|
+
|
|
54
|
+
const _tr = tr.replaceSelectionWith(node).insert(from, paragraph)
|
|
55
|
+
const sel = Selection.findFrom(_tr.doc.resolve(from), 1, true)
|
|
56
|
+
if (!sel)
|
|
57
|
+
return true
|
|
58
|
+
|
|
59
|
+
dispatch(_tr.setSelection(sel).scrollIntoView())
|
|
60
|
+
return true
|
|
61
61
|
})
|