@milkdown/preset-commonmark 6.5.4 → 7.0.0-next.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.
Files changed (153) hide show
  1. package/lib/composed/commands.d.ts +3 -0
  2. package/lib/composed/commands.d.ts.map +1 -0
  3. package/lib/composed/index.d.ts +6 -0
  4. package/lib/composed/index.d.ts.map +1 -0
  5. package/lib/composed/inputrules.d.ts +3 -0
  6. package/lib/composed/inputrules.d.ts.map +1 -0
  7. package/lib/composed/keymap.d.ts +3 -0
  8. package/lib/composed/keymap.d.ts.map +1 -0
  9. package/lib/composed/plugins.d.ts +2 -0
  10. package/lib/composed/plugins.d.ts.map +1 -0
  11. package/lib/composed/schema.d.ts +3 -0
  12. package/lib/composed/schema.d.ts.map +1 -0
  13. package/lib/index.d.ts +4 -29
  14. package/lib/index.d.ts.map +1 -1
  15. package/lib/index.es.js +1101 -1386
  16. package/lib/index.es.js.map +1 -1
  17. package/lib/mark/emphasis.d.ts +5 -0
  18. package/lib/mark/emphasis.d.ts.map +1 -0
  19. package/lib/mark/index.d.ts +3 -4
  20. package/lib/mark/index.d.ts.map +1 -1
  21. package/lib/mark/inline-code.d.ts +5 -0
  22. package/lib/mark/inline-code.d.ts.map +1 -0
  23. package/lib/mark/link.d.ts +8 -10
  24. package/lib/mark/link.d.ts.map +1 -1
  25. package/lib/mark/strong.d.ts +4 -2
  26. package/lib/mark/strong.d.ts.map +1 -1
  27. package/lib/node/blockquote.d.ts +6 -2
  28. package/lib/node/blockquote.d.ts.map +1 -1
  29. package/lib/node/bullet-list.d.ts +5 -2
  30. package/lib/node/bullet-list.d.ts.map +1 -1
  31. package/lib/node/code-block.d.ts +10 -0
  32. package/lib/node/code-block.d.ts.map +1 -0
  33. package/lib/node/doc.d.ts +1 -1
  34. package/lib/node/doc.d.ts.map +1 -1
  35. package/lib/node/hardbreak.d.ts +4 -6
  36. package/lib/node/hardbreak.d.ts.map +1 -1
  37. package/lib/node/heading.d.ts +7 -12
  38. package/lib/node/heading.d.ts.map +1 -1
  39. package/lib/node/hr.d.ts +4 -2
  40. package/lib/node/hr.d.ts.map +1 -1
  41. package/lib/node/image.d.ts +10 -11
  42. package/lib/node/image.d.ts.map +1 -1
  43. package/lib/node/index.d.ts +6 -8
  44. package/lib/node/index.d.ts.map +1 -1
  45. package/lib/node/list-item.d.ts +6 -7
  46. package/lib/node/list-item.d.ts.map +1 -1
  47. package/lib/node/ordered-list.d.ts +5 -2
  48. package/lib/node/ordered-list.d.ts.map +1 -1
  49. package/lib/node/paragraph.d.ts +4 -2
  50. package/lib/node/paragraph.d.ts.map +1 -1
  51. package/lib/node/text.d.ts +1 -1
  52. package/lib/node/text.d.ts.map +1 -1
  53. package/lib/plugin/hardbreak-clear-mark-plugin.d.ts +2 -0
  54. package/lib/plugin/hardbreak-clear-mark-plugin.d.ts.map +1 -0
  55. package/lib/plugin/hardbreak-filter-plugin.d.ts +3 -0
  56. package/lib/plugin/hardbreak-filter-plugin.d.ts.map +1 -0
  57. package/lib/plugin/index.d.ts +9 -3
  58. package/lib/plugin/index.d.ts.map +1 -1
  59. package/lib/plugin/{inline-nodes-cursor.d.ts → inline-nodes-cursor-plugin.d.ts} +2 -3
  60. package/lib/plugin/inline-nodes-cursor-plugin.d.ts.map +1 -0
  61. package/lib/plugin/{inline-sync → inline-sync-plugin}/config.d.ts +2 -2
  62. package/lib/plugin/inline-sync-plugin/config.d.ts.map +1 -0
  63. package/lib/plugin/{inline-sync → inline-sync-plugin}/context.d.ts +1 -2
  64. package/lib/plugin/inline-sync-plugin/context.d.ts.map +1 -0
  65. package/lib/plugin/inline-sync-plugin/index.d.ts +3 -0
  66. package/lib/plugin/inline-sync-plugin/index.d.ts.map +1 -0
  67. package/lib/plugin/inline-sync-plugin/inline-sync-plugin.d.ts +2 -0
  68. package/lib/plugin/inline-sync-plugin/inline-sync-plugin.d.ts.map +1 -0
  69. package/lib/plugin/{inline-sync → inline-sync-plugin}/regexp.d.ts +0 -0
  70. package/lib/plugin/inline-sync-plugin/regexp.d.ts.map +1 -0
  71. package/lib/plugin/{inline-sync → inline-sync-plugin}/replacer.d.ts +1 -1
  72. package/lib/plugin/inline-sync-plugin/replacer.d.ts.map +1 -0
  73. package/lib/plugin/{inline-sync → inline-sync-plugin}/utils.d.ts +0 -0
  74. package/lib/plugin/inline-sync-plugin/utils.d.ts.map +1 -0
  75. package/lib/plugin/remark-add-order-in-list-plugin.d.ts +2 -0
  76. package/lib/plugin/remark-add-order-in-list-plugin.d.ts.map +1 -0
  77. package/lib/plugin/remark-inline-link-plugin.d.ts +2 -0
  78. package/lib/plugin/remark-inline-link-plugin.d.ts.map +1 -0
  79. package/lib/plugin/remark-line-break.d.ts +2 -0
  80. package/lib/plugin/remark-line-break.d.ts.map +1 -0
  81. package/lib/plugin/sync-heading-id-plugin.d.ts +2 -0
  82. package/lib/plugin/sync-heading-id-plugin.d.ts.map +1 -0
  83. package/lib/plugin/sync-list-order-plugin.d.ts +2 -0
  84. package/lib/plugin/sync-list-order-plugin.d.ts.map +1 -0
  85. package/package.json +9 -7
  86. package/src/composed/commands.ts +30 -0
  87. package/src/composed/index.ts +6 -0
  88. package/src/composed/inputrules.ts +12 -0
  89. package/src/composed/keymap.ts +19 -0
  90. package/src/composed/plugins.ts +21 -0
  91. package/src/composed/schema.ts +54 -0
  92. package/src/index.ts +5 -52
  93. package/src/mark/emphasis.ts +42 -0
  94. package/src/mark/index.ts +3 -10
  95. package/src/mark/inline-code.ts +64 -0
  96. package/src/mark/link.ts +87 -247
  97. package/src/mark/strong.ts +35 -36
  98. package/src/node/blockquote.ts +32 -33
  99. package/src/node/bullet-list.ts +56 -56
  100. package/src/node/code-block.ts +93 -0
  101. package/src/node/doc.ts +17 -22
  102. package/src/node/hardbreak.ts +60 -117
  103. package/src/node/heading.ts +158 -281
  104. package/src/node/hr.ts +51 -57
  105. package/src/node/image.ts +102 -209
  106. package/src/node/index.ts +6 -35
  107. package/src/node/list-item.ts +81 -142
  108. package/src/node/ordered-list.ts +64 -65
  109. package/src/node/paragraph.ts +48 -50
  110. package/src/node/text.ts +13 -16
  111. package/src/plugin/hardbreak-clear-mark-plugin.ts +44 -0
  112. package/src/plugin/hardbreak-filter-plugin.ts +31 -0
  113. package/src/plugin/index.ts +12 -14
  114. package/src/plugin/{inline-nodes-cursor.ts → inline-nodes-cursor-plugin.ts} +4 -4
  115. package/src/plugin/{inline-sync → inline-sync-plugin}/config.ts +4 -3
  116. package/src/plugin/{inline-sync → inline-sync-plugin}/context.ts +4 -6
  117. package/src/plugin/inline-sync-plugin/index.ts +4 -0
  118. package/src/plugin/{inline-sync/index.ts → inline-sync-plugin/inline-sync-plugin.ts} +9 -11
  119. package/src/plugin/{inline-sync → inline-sync-plugin}/regexp.ts +0 -0
  120. package/src/plugin/{inline-sync → inline-sync-plugin}/replacer.ts +3 -3
  121. package/src/plugin/{inline-sync → inline-sync-plugin}/utils.ts +0 -0
  122. package/src/plugin/remark-add-order-in-list-plugin.ts +15 -0
  123. package/src/plugin/remark-inline-link-plugin.ts +5 -0
  124. package/src/plugin/remark-line-break.ts +41 -0
  125. package/src/plugin/sync-heading-id-plugin.ts +53 -0
  126. package/src/plugin/sync-list-order-plugin.ts +57 -0
  127. package/lib/mark/code-inline.d.ts +0 -3
  128. package/lib/mark/code-inline.d.ts.map +0 -1
  129. package/lib/mark/em.d.ts +0 -3
  130. package/lib/mark/em.d.ts.map +0 -1
  131. package/lib/node/code-fence.d.ts +0 -7
  132. package/lib/node/code-fence.d.ts.map +0 -1
  133. package/lib/plugin/add-order-in-list.d.ts +0 -3
  134. package/lib/plugin/add-order-in-list.d.ts.map +0 -1
  135. package/lib/plugin/filter-html.d.ts +0 -3
  136. package/lib/plugin/filter-html.d.ts.map +0 -1
  137. package/lib/plugin/inline-nodes-cursor.d.ts.map +0 -1
  138. package/lib/plugin/inline-sync/config.d.ts.map +0 -1
  139. package/lib/plugin/inline-sync/context.d.ts.map +0 -1
  140. package/lib/plugin/inline-sync/index.d.ts +0 -6
  141. package/lib/plugin/inline-sync/index.d.ts.map +0 -1
  142. package/lib/plugin/inline-sync/regexp.d.ts.map +0 -1
  143. package/lib/plugin/inline-sync/replacer.d.ts.map +0 -1
  144. package/lib/plugin/inline-sync/utils.d.ts.map +0 -1
  145. package/lib/supported-keys.d.ts +0 -23
  146. package/lib/supported-keys.d.ts.map +0 -1
  147. package/src/mark/code-inline.ts +0 -66
  148. package/src/mark/em.ts +0 -42
  149. package/src/node/code-fence.ts +0 -245
  150. package/src/plugin/add-order-in-list.ts +0 -19
  151. package/src/plugin/filter-html.ts +0 -43
  152. package/src/supported-keys.ts +0 -25
  153. package/src/types.d.ts +0 -5
@@ -1,315 +1,192 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import type { Ctx } from '@milkdown/core'
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, NodeType } from '@milkdown/prose/model'
6
+ import type { Node } from '@milkdown/prose/model'
9
7
  import { Fragment } from '@milkdown/prose/model'
10
- import type { EditorState, Transaction } from '@milkdown/prose/state'
11
- import { Plugin, PluginKey } from '@milkdown/prose/state'
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
- type Keys =
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
- const headingIdPlugin = (ctx: Ctx, type: NodeType, getId: (node: Node) => string): Plugin => {
45
- let lock = false
46
- const walkThrough = (state: EditorState, callback: (tr: Transaction) => void) => {
47
- const tr = state.tr.setMeta('addToHistory', false)
48
- let found = false
49
- state.doc.descendants((node, pos) => {
50
- if (node.type === type && !lock) {
51
- if (node.textContent.trim().length === 0)
52
- return
22
+ export const headingIdGenerator = $ctx(defaultHeadingIdGenerator, 'headingIdGenerator')
53
23
 
54
- const attrs = node.attrs
55
- const id = getId(node)
24
+ export const headingAttr = $nodeAttr('heading')
56
25
 
57
- if (attrs.id !== id) {
58
- found = true
59
- tr.setMeta(headingIdPluginKey, true).setNodeMarkup(pos, undefined, {
60
- ...attrs,
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
- },
26
+ export const headingSchema = $nodeSchema('heading', (ctx) => {
27
+ const getId = ctx.get(headingIdGenerator.key)
28
+ return {
29
+ content: 'inline*',
30
+ group: 'block',
31
+ defining: true,
32
+ attrs: {
33
+ id: {
34
+ default: '',
85
35
  },
86
- },
87
- appendTransaction: (transactions, _, nextState) => {
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
36
+ level: {
37
+ default: 1,
126
38
  },
127
- apply: (tr) => {
128
- const view = ctx.get(editorViewCtx)
129
- if (!view.hasFocus?.() || !view.editable)
130
- return DecorationSet.empty
131
-
132
- const { $from } = tr.selection
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
- })
39
+ },
40
+ parseDOM: headingIndex.map(x => ({
41
+ tag: `h${x}`,
42
+ getAttrs: (node) => {
43
+ if (!(node instanceof HTMLElement))
44
+ throw expectDomTypeError(node)
158
45
 
159
- const deco = Decoration.widget($from.before() + 1, widget, { side: -1 })
160
- return DecorationSet.create(tr.doc, [deco])
46
+ return { level: x, id: node.id }
161
47
  },
162
- },
163
- props: {
164
- handleDOMEvents: {
165
- focus: (view) => {
166
- const tr = cloneTr(view.state.tr)
167
- view.dispatch(tr)
168
- return false
48
+ })),
49
+ toDOM: (node) => {
50
+ return [
51
+ `h${node.attrs.level}`,
52
+ {
53
+ ...ctx.get(headingAttr.key)(node),
54
+ id: node.attrs.id || getId(node),
169
55
  },
56
+ 0,
57
+ ]
58
+ },
59
+ parseMarkdown: {
60
+ match: ({ type }) => type === 'heading',
61
+ runner: (state, node, type) => {
62
+ const depth = node.depth as number
63
+ state.openNode(type, { level: depth })
64
+ state.next(node.children)
65
+ state.closeNode()
170
66
  },
171
- decorations(this: Plugin, state) {
172
- return this.getState(state)
67
+ },
68
+ toMarkdown: {
69
+ match: node => node.type.name === 'heading',
70
+ runner: (state, node) => {
71
+ state.openNode('heading', undefined, { depth: node.attrs.level })
72
+ const lastIsHardbreak = node.childCount >= 1 && node.lastChild?.type.name === 'hardbreak'
73
+ if (lastIsHardbreak) {
74
+ const contentArr: Node[] = []
75
+ node.content.forEach((n, _, i) => {
76
+ if (i === node.childCount - 1)
77
+ return
78
+
79
+ contentArr.push(n)
80
+ })
81
+ state.next(Fragment.fromArray(contentArr))
82
+ }
83
+ else {
84
+ state.next(node.content)
85
+ }
86
+ state.closeNode()
173
87
  },
174
88
  },
175
- })
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
184
-
185
- return {
186
- id,
187
- schema: () => ({
188
- content: 'inline*',
189
- group: 'block',
190
- defining: true,
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)
89
+ }
90
+ })
204
91
 
205
- return { level: x, id: node.id }
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
92
+ export const wrapInHeadingInputRule = $inputRule((ctx) => {
93
+ return textblockTypeInputRule(/^(?<hashes>#+)\s$/, headingSchema.type(), (match) => {
94
+ const x = match.groups?.hashes?.length || 0
237
95
 
238
- contentArr.push(n)
239
- })
240
- state.next(Fragment.fromArray(contentArr))
241
- }
242
- else {
243
- state.next(node.content)
244
- }
245
- state.closeNode()
246
- },
247
- },
248
- }),
249
- inputRules: (type, ctx) =>
250
- headingIndex.map(x =>
251
- textblockTypeInputRule(new RegExp(`^(#{1,${x}})\\s$`), type, () => {
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
96
+ const view = ctx.get(editorViewCtx)
97
+ const { $from } = view.state.selection
98
+ const node = $from.node()
99
+ if (node.type.name === 'heading') {
100
+ let level = Number(node.attrs.level) + Number(x)
101
+ if (level > 6)
102
+ level = 6
259
103
 
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)
104
+ return { level }
105
+ }
106
+ return { level: x }
107
+ })
108
+ },
109
+ )
273
110
 
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
111
+ export const wrapInHeadingCommand = $command('WrapInHeading', () => {
112
+ return (level?: number) => {
113
+ level ??= 1
282
114
 
283
- const level = node.attrs.level - 1
284
- if (!level)
285
- return setBlockType(ctx.get(schemaCtx).nodes.paragraph || type)(state, dispatch, view)
115
+ if (level < 1)
116
+ return setBlockType(paragraphSchema.type())
286
117
 
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))
118
+ return setBlockType(headingSchema.type(), { level })
119
+ }
120
+ })
121
+
122
+ export const downgradeHeadingCommand = $command('DowngradeHeading', () => () =>
123
+ (state, dispatch, view) => {
124
+ const { $from } = state.selection
125
+ const node = $from.node()
126
+ if (node.type !== headingSchema.type() || !state.selection.empty || $from.parentOffset !== 0)
127
+ return false
128
+
129
+ const level = node.attrs.level - 1
130
+ if (!level)
131
+ return setBlockType(paragraphSchema.type())(state, dispatch, view)
132
+
133
+ dispatch?.(
134
+ state.tr.setNodeMarkup(state.selection.$from.before(), undefined, {
135
+ ...node.attrs,
136
+ level,
137
+ }),
138
+ )
139
+ return true
140
+ })
310
141
 
311
- return plugins
312
- },
313
- }
142
+ export const headingKeymap = $useKeymap('headingKeymap', {
143
+ TurnIntoH1: {
144
+ shortcuts: 'Mod-Alt-1',
145
+ command: (ctx) => {
146
+ const commands = ctx.get(commandsCtx)
147
+ return () => commands.call(wrapInHeadingCommand.key, 1)
148
+ },
314
149
  },
315
- )
150
+ TurnIntoH2: {
151
+ shortcuts: 'Mod-Alt-2',
152
+ command: (ctx) => {
153
+ const commands = ctx.get(commandsCtx)
154
+ return () => commands.call(wrapInHeadingCommand.key, 2)
155
+ },
156
+ },
157
+ TurnIntoH3: {
158
+ shortcuts: 'Mod-Alt-3',
159
+ command: (ctx) => {
160
+ const commands = ctx.get(commandsCtx)
161
+ return () => commands.call(wrapInHeadingCommand.key, 3)
162
+ },
163
+ },
164
+ TurnIntoH4: {
165
+ shortcuts: 'Mod-Alt-4',
166
+ command: (ctx) => {
167
+ const commands = ctx.get(commandsCtx)
168
+ return () => commands.call(wrapInHeadingCommand.key, 3)
169
+ },
170
+ },
171
+ TurnIntoH5: {
172
+ shortcuts: 'Mod-Alt-5',
173
+ command: (ctx) => {
174
+ const commands = ctx.get(commandsCtx)
175
+ return () => commands.call(wrapInHeadingCommand.key, 3)
176
+ },
177
+ },
178
+ TurnIntoH6: {
179
+ shortcuts: 'Mod-Alt-6',
180
+ command: (ctx) => {
181
+ const commands = ctx.get(commandsCtx)
182
+ return () => commands.call(wrapInHeadingCommand.key, 3)
183
+ },
184
+ },
185
+ DowngradeHeading: {
186
+ shortcuts: ['Delete', 'Backspace'],
187
+ command: (ctx) => {
188
+ const commands = ctx.get(commandsCtx)
189
+ return () => commands.call(downgradeHeadingCommand.key)
190
+ },
191
+ },
192
+ })
package/src/node/hr.ts CHANGED
@@ -1,61 +1,55 @@
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 { createNode } from '@milkdown/utils'
6
-
7
- const id = 'hr'
8
- export const InsertHr = createCmdKey<string>('InsertHr')
9
- export const hr = createNode((utils) => {
10
- return {
11
- id,
12
- schema: () => ({
13
- group: 'block',
14
- parseDOM: [{ tag: 'hr' }],
15
- toDOM: node => ['hr', { class: utils.getClassName(node.attrs, id) }],
16
- parseMarkdown: {
17
- match: ({ type }) => type === 'thematicBreak',
18
- runner: (state, _, type) => {
19
- state.addNode(type)
20
- },
21
- },
22
- toMarkdown: {
23
- match: node => node.type.name === id,
24
- runner: (state) => {
25
- state.addNode('thematicBreak')
26
- },
27
- },
28
- }),
29
- inputRules: type => [
30
- new InputRule(/^(?:---|___\s|\*\*\*\s)$/, (state, match, start, end) => {
31
- const { tr } = state
32
-
33
- if (match[0])
34
- tr.replaceWith(start - 1, end, type.create())
35
-
36
- return tr
37
- }),
38
- ],
39
- commands: (type, ctx) => [
40
- createCmd(InsertHr, () => (state, dispatch) => {
41
- if (!dispatch)
42
- return true
43
-
44
- const paragraph = ctx.get(schemaCtx).node('paragraph')
45
- const { tr, selection } = state
46
- const { from } = selection
47
- const node = type.create()
48
- if (!node)
49
- return true
50
-
51
- const _tr = tr.replaceSelectionWith(node).insert(from, paragraph)
52
- const sel = Selection.findFrom(_tr.doc.resolve(from), 1, true)
53
- if (!sel)
54
- return true
55
-
56
- dispatch(_tr.setSelection(sel).scrollIntoView())
57
- return true
58
- }),
59
- ],
60
- }
4
+ import { $command, $inputRule, $nodeAttr, $nodeSchema } from '@milkdown/utils'
5
+ import { paragraphSchema } from './paragraph'
6
+
7
+ export const hrAttr = $nodeAttr('hr')
8
+ export const hrSchema = $nodeSchema('hr', ctx => ({
9
+ group: 'block',
10
+ parseDOM: [{ tag: 'hr' }],
11
+ toDOM: node => ['hr', ctx.get(hrAttr.key)(node)],
12
+ parseMarkdown: {
13
+ match: ({ type }) => type === 'thematicBreak',
14
+ runner: (state, _, type) => {
15
+ state.addNode(type)
16
+ },
17
+ },
18
+ toMarkdown: {
19
+ match: node => node.type.name === 'hr',
20
+ runner: (state) => {
21
+ state.addNode('thematicBreak')
22
+ },
23
+ },
24
+ }))
25
+
26
+ export const insertHrInputRule = $inputRule(() => new InputRule(
27
+ /^(?:---|___\s|\*\*\*\s)$/, (state, match, start, end) => {
28
+ const { tr } = state
29
+
30
+ if (match[0])
31
+ tr.replaceWith(start - 1, end, hrSchema.type().create())
32
+
33
+ return tr
34
+ },
35
+ ))
36
+
37
+ export const insertHrCommand = $command('InsertHr', () => () => (state, dispatch) => {
38
+ if (!dispatch)
39
+ return true
40
+
41
+ const paragraph = paragraphSchema.node.type().create()
42
+ const { tr, selection } = state
43
+ const { from } = selection
44
+ const node = hrSchema.type().create()
45
+ if (!node)
46
+ return true
47
+
48
+ const _tr = tr.replaceSelectionWith(node).insert(from, paragraph)
49
+ const sel = Selection.findFrom(_tr.doc.resolve(from), 1, true)
50
+ if (!sel)
51
+ return true
52
+
53
+ dispatch(_tr.setSelection(sel).scrollIntoView())
54
+ return true
61
55
  })