@milkdown/preset-commonmark 6.2.0 → 6.3.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/lib/index.es.js +1012 -1153
- package/lib/index.es.js.map +1 -1
- package/lib/mark/link.d.ts.map +1 -1
- package/lib/node/code-fence.d.ts.map +1 -1
- package/lib/node/heading.d.ts +5 -2
- package/lib/node/heading.d.ts.map +1 -1
- package/lib/node/image.d.ts.map +1 -1
- package/lib/node/list-item.d.ts.map +1 -1
- package/lib/node/ordered-list.d.ts.map +1 -1
- package/lib/plugin/add-order-in-list.d.ts +3 -0
- package/lib/plugin/add-order-in-list.d.ts.map +1 -0
- package/lib/plugin/index.d.ts.map +1 -1
- package/lib/supported-keys.d.ts +1 -0
- package/lib/supported-keys.d.ts.map +1 -1
- package/package.json +7 -5
- package/src/mark/link.ts +3 -2
- package/src/node/code-fence.ts +4 -3
- package/src/node/heading.ts +261 -145
- package/src/node/image.ts +2 -1
- package/src/node/list-item.ts +93 -3
- package/src/node/ordered-list.ts +2 -1
- package/src/plugin/add-order-in-list.ts +19 -0
- package/src/plugin/index.ts +2 -1
- package/src/supported-keys.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milkdown/preset-commonmark",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./lib/index.es.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"commonmark"
|
|
18
18
|
],
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@milkdown/core": "6.
|
|
21
|
-
"@milkdown/prose": "6.
|
|
20
|
+
"@milkdown/core": "6.3.0",
|
|
21
|
+
"@milkdown/prose": "6.3.0",
|
|
22
22
|
"@types/unist": "^2.0.6"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
@@ -26,9 +26,11 @@
|
|
|
26
26
|
"@milkdown/prose": "^6.0.1"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@milkdown/utils": "6.
|
|
29
|
+
"@milkdown/utils": "6.3.0",
|
|
30
|
+
"@milkdown/exception": "6.3.0",
|
|
31
|
+
"unist-util-visit": "^4.0.0",
|
|
30
32
|
"remark-inline-links": "^6.0.0",
|
|
31
|
-
"tslib": "^2.
|
|
33
|
+
"tslib": "^2.4.0"
|
|
32
34
|
},
|
|
33
35
|
"nx": {
|
|
34
36
|
"targets": {
|
package/src/mark/link.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
2
|
import { commandsCtx, createCmd, createCmdKey, schemaCtx, ThemeInputChipType } from '@milkdown/core';
|
|
3
|
+
import { expectDomTypeError, missingRootElement } from '@milkdown/exception';
|
|
3
4
|
import { calculateTextPosition } from '@milkdown/prose';
|
|
4
5
|
import { toggleMark } from '@milkdown/prose/commands';
|
|
5
6
|
import { InputRule } from '@milkdown/prose/inputrules';
|
|
@@ -33,7 +34,7 @@ export const link = createMark<string, LinkOptions>((utils, options) => {
|
|
|
33
34
|
tag: 'a[href]',
|
|
34
35
|
getAttrs: (dom) => {
|
|
35
36
|
if (!(dom instanceof HTMLElement)) {
|
|
36
|
-
throw
|
|
37
|
+
throw expectDomTypeError(dom);
|
|
37
38
|
}
|
|
38
39
|
return { href: dom.getAttribute('href'), title: dom.getAttribute('title') };
|
|
39
40
|
},
|
|
@@ -132,7 +133,7 @@ export const link = createMark<string, LinkOptions>((utils, options) => {
|
|
|
132
133
|
calculateTextPosition(view, input, (start, end, target, parent) => {
|
|
133
134
|
const $editor = view.dom.parentElement;
|
|
134
135
|
if (!$editor) {
|
|
135
|
-
throw
|
|
136
|
+
throw missingRootElement();
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
const selectionWidth = end.left - start.left;
|
package/src/node/code-fence.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
2
|
import { createCmd, createCmdKey, editorViewCtx, ThemeCodeFenceType } from '@milkdown/core';
|
|
3
|
+
import { expectDomTypeError } from '@milkdown/exception';
|
|
3
4
|
import { setBlockType } from '@milkdown/prose/commands';
|
|
4
5
|
import { textblockTypeInputRule } from '@milkdown/prose/inputrules';
|
|
5
6
|
import { Fragment } from '@milkdown/prose/model';
|
|
@@ -60,13 +61,13 @@ export const codeFence = createNode<Keys, { languageList?: string[] }>((utils, o
|
|
|
60
61
|
preserveWhitespace: 'full',
|
|
61
62
|
getAttrs: (dom) => {
|
|
62
63
|
if (!(dom instanceof HTMLElement)) {
|
|
63
|
-
throw
|
|
64
|
+
throw expectDomTypeError(dom);
|
|
64
65
|
}
|
|
65
66
|
return { language: dom.querySelector('pre')?.dataset['language'] };
|
|
66
67
|
},
|
|
67
68
|
getContent: (dom, schema) => {
|
|
68
69
|
if (!(dom instanceof HTMLElement)) {
|
|
69
|
-
throw
|
|
70
|
+
throw expectDomTypeError(dom);
|
|
70
71
|
}
|
|
71
72
|
const textNode = schema.text(dom.querySelector('pre')?.textContent ?? '');
|
|
72
73
|
return Fragment.from(textNode);
|
|
@@ -77,7 +78,7 @@ export const codeFence = createNode<Keys, { languageList?: string[] }>((utils, o
|
|
|
77
78
|
preserveWhitespace: 'full',
|
|
78
79
|
getAttrs: (dom) => {
|
|
79
80
|
if (!(dom instanceof HTMLElement)) {
|
|
80
|
-
throw
|
|
81
|
+
throw expectDomTypeError(dom);
|
|
81
82
|
}
|
|
82
83
|
return { language: dom.dataset['language'] };
|
|
83
84
|
},
|
package/src/node/heading.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
import { createCmd, createCmdKey, editorViewCtx } from '@milkdown/core';
|
|
2
|
+
import { createCmd, createCmdKey, Ctx, editorViewCtx, getPalette, schemaCtx } from '@milkdown/core';
|
|
3
|
+
import { expectDomTypeError } from '@milkdown/exception';
|
|
4
|
+
import { cloneTr } from '@milkdown/prose';
|
|
3
5
|
import { setBlockType } from '@milkdown/prose/commands';
|
|
4
6
|
import { textblockTypeInputRule } from '@milkdown/prose/inputrules';
|
|
5
|
-
import { Fragment, Node } from '@milkdown/prose/model';
|
|
7
|
+
import { Fragment, Node, NodeType } from '@milkdown/prose/model';
|
|
6
8
|
import { EditorState, Plugin, PluginKey, Transaction } from '@milkdown/prose/state';
|
|
7
|
-
import {
|
|
9
|
+
import { Decoration, DecorationSet } from '@milkdown/prose/view';
|
|
10
|
+
import { createNode, createShortcut, Utils } from '@milkdown/utils';
|
|
8
11
|
|
|
9
12
|
import { SupportedKeys } from '../supported-keys';
|
|
10
13
|
|
|
@@ -18,11 +21,14 @@ type Keys =
|
|
|
18
21
|
| SupportedKeys['H3']
|
|
19
22
|
| SupportedKeys['H4']
|
|
20
23
|
| SupportedKeys['H5']
|
|
21
|
-
| SupportedKeys['H6']
|
|
24
|
+
| SupportedKeys['H6']
|
|
25
|
+
| SupportedKeys['DowngradeHeading'];
|
|
22
26
|
|
|
23
27
|
export const TurnIntoHeading = createCmdKey<number>('TurnIntoHeading');
|
|
28
|
+
export const DowngradeHeading = createCmdKey('DowngradeHeading');
|
|
24
29
|
|
|
25
|
-
export const
|
|
30
|
+
export const headingIdPluginKey = new PluginKey('MILKDOWN_HEADING_ID');
|
|
31
|
+
export const headingHashPluginKey = new PluginKey('MILKDOWN_HEADING_HASH');
|
|
26
32
|
|
|
27
33
|
const createId = (node: Node) =>
|
|
28
34
|
node.textContent
|
|
@@ -31,162 +37,272 @@ const createId = (node: Node) =>
|
|
|
31
37
|
.toLowerCase()
|
|
32
38
|
.trim();
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
const headingIdPlugin = (ctx: Ctx, type: NodeType, getId: (node: Node) => string): Plugin => {
|
|
41
|
+
let lock = false;
|
|
42
|
+
const walkThrough = (state: EditorState, callback: (tr: Transaction) => void) => {
|
|
43
|
+
const tr = state.tr;
|
|
44
|
+
state.doc.descendants((node, pos) => {
|
|
45
|
+
if (node.type === type && !lock) {
|
|
46
|
+
if (node.textContent.trim().length === 0) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const attrs = node.attrs;
|
|
50
|
+
const id = getId(node);
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
if (attrs['id'] !== id) {
|
|
53
|
+
tr.setMeta(headingIdPluginKey, true).setNodeMarkup(pos, undefined, {
|
|
54
|
+
...attrs,
|
|
55
|
+
id,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
callback(tr);
|
|
61
|
+
};
|
|
62
|
+
return new Plugin({
|
|
63
|
+
key: headingIdPluginKey,
|
|
64
|
+
props: {
|
|
65
|
+
handleDOMEvents: {
|
|
66
|
+
compositionstart: () => {
|
|
67
|
+
lock = true;
|
|
68
|
+
return false;
|
|
48
69
|
},
|
|
49
|
-
|
|
50
|
-
|
|
70
|
+
compositionend: () => {
|
|
71
|
+
lock = false;
|
|
72
|
+
const view = ctx.get(editorViewCtx);
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
walkThrough(view.state, (tr) => view.dispatch(tr));
|
|
75
|
+
}, 0);
|
|
76
|
+
return false;
|
|
51
77
|
},
|
|
52
78
|
},
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
},
|
|
80
|
+
appendTransaction: (transactions, _, nextState) => {
|
|
81
|
+
let tr: Transaction | null = null;
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
transactions.every((transaction) => !transaction.getMeta(headingIdPluginKey)) &&
|
|
85
|
+
transactions.some((transaction) => transaction.docChanged)
|
|
86
|
+
) {
|
|
87
|
+
walkThrough(nextState, (t) => {
|
|
88
|
+
tr = t;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return tr;
|
|
93
|
+
},
|
|
94
|
+
view: (view) => {
|
|
95
|
+
const doc = view.state.doc;
|
|
96
|
+
let tr = view.state.tr;
|
|
97
|
+
doc.descendants((node, pos) => {
|
|
98
|
+
if (node.type.name === 'heading' && node.attrs['level']) {
|
|
99
|
+
if (!node.attrs['id']) {
|
|
100
|
+
tr = tr.setNodeMarkup(pos, undefined, {
|
|
101
|
+
...node.attrs,
|
|
102
|
+
id: getId(node),
|
|
103
|
+
});
|
|
58
104
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
match: ({ type }) => type === id,
|
|
74
|
-
runner: (state, node, type) => {
|
|
75
|
-
const depth = node['depth'] as number;
|
|
76
|
-
state.openNode(type, { level: depth });
|
|
77
|
-
state.next(node.children);
|
|
78
|
-
state.closeNode();
|
|
79
|
-
},
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
view.dispatch(tr);
|
|
108
|
+
return {};
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const headingHashPlugin = (ctx: Ctx, type: NodeType, utils: Utils): Plugin => {
|
|
114
|
+
return new Plugin({
|
|
115
|
+
key: headingHashPluginKey,
|
|
116
|
+
state: {
|
|
117
|
+
init: () => {
|
|
118
|
+
return DecorationSet.empty;
|
|
80
119
|
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
120
|
+
apply: (tr) => {
|
|
121
|
+
const view = ctx.get(editorViewCtx);
|
|
122
|
+
if (!view.hasFocus || !view.editable) return DecorationSet.empty;
|
|
123
|
+
|
|
124
|
+
const { $from } = tr.selection;
|
|
125
|
+
const node = $from.node();
|
|
126
|
+
if (node.type !== type) {
|
|
127
|
+
return DecorationSet.empty;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const level = node.attrs['level'];
|
|
131
|
+
const getHashes = (level: number) => {
|
|
132
|
+
return Array(level)
|
|
133
|
+
.fill(0)
|
|
134
|
+
.map((_) => `#`)
|
|
135
|
+
.join('');
|
|
136
|
+
};
|
|
137
|
+
const widget = document.createElement('span');
|
|
138
|
+
widget.textContent = getHashes(level);
|
|
139
|
+
widget.contentEditable = 'false';
|
|
140
|
+
utils.themeManager.onFlush(() => {
|
|
141
|
+
const style = utils.getStyle(({ css }) => {
|
|
142
|
+
const palette = getPalette(utils.themeManager);
|
|
143
|
+
return css`
|
|
144
|
+
margin-right: 4px;
|
|
145
|
+
color: ${palette('primary')};
|
|
146
|
+
`;
|
|
147
|
+
});
|
|
148
|
+
if (style) {
|
|
149
|
+
widget.className = style;
|
|
97
150
|
}
|
|
98
|
-
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const deco = Decoration.widget($from.before() + 1, widget, { side: -1 });
|
|
154
|
+
return DecorationSet.create(tr.doc, [deco]);
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
props: {
|
|
158
|
+
handleDOMEvents: {
|
|
159
|
+
focus: (view) => {
|
|
160
|
+
const tr = cloneTr(view.state.tr);
|
|
161
|
+
view.dispatch(tr);
|
|
162
|
+
return false;
|
|
99
163
|
},
|
|
100
164
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
textblockTypeInputRule(new RegExp(`^(#{1,${x}})\\s$`), type, () => ({
|
|
105
|
-
level: x,
|
|
106
|
-
})),
|
|
107
|
-
),
|
|
108
|
-
commands: (type) => [createCmd(TurnIntoHeading, (level = 1) => setBlockType(type, { level }))],
|
|
109
|
-
shortcuts: {
|
|
110
|
-
[SupportedKeys.H1]: createShortcut(TurnIntoHeading, 'Mod-Alt-1', 1),
|
|
111
|
-
[SupportedKeys.H2]: createShortcut(TurnIntoHeading, 'Mod-Alt-2', 2),
|
|
112
|
-
[SupportedKeys.H3]: createShortcut(TurnIntoHeading, 'Mod-Alt-3', 3),
|
|
113
|
-
[SupportedKeys.H4]: createShortcut(TurnIntoHeading, 'Mod-Alt-4', 4),
|
|
114
|
-
[SupportedKeys.H5]: createShortcut(TurnIntoHeading, 'Mod-Alt-5', 5),
|
|
115
|
-
[SupportedKeys.H6]: createShortcut(TurnIntoHeading, 'Mod-Alt-6', 6),
|
|
165
|
+
decorations(this: Plugin, state) {
|
|
166
|
+
return this.getState(state);
|
|
167
|
+
},
|
|
116
168
|
},
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const walkThrough = (state: EditorState, callback: (tr: Transaction) => void) => {
|
|
120
|
-
const tr = state.tr;
|
|
121
|
-
state.doc.descendants((node, pos) => {
|
|
122
|
-
if (node.type === type && !lock) {
|
|
123
|
-
if (node.textContent.trim().length === 0) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const attrs = node.attrs;
|
|
127
|
-
const id = getId(node);
|
|
169
|
+
});
|
|
170
|
+
};
|
|
128
171
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
172
|
+
export const heading = createNode<Keys, { getId: (node: Node) => string; displayHashtag: boolean }>(
|
|
173
|
+
(utils, options) => {
|
|
174
|
+
const id = 'heading';
|
|
175
|
+
|
|
176
|
+
const getId = options?.getId ?? createId;
|
|
177
|
+
const displayHashtag = options?.displayHashtag ?? true;
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
id,
|
|
181
|
+
schema: () => ({
|
|
182
|
+
content: 'inline*',
|
|
183
|
+
group: 'block',
|
|
184
|
+
defining: true,
|
|
185
|
+
attrs: {
|
|
186
|
+
id: {
|
|
187
|
+
default: '',
|
|
188
|
+
},
|
|
189
|
+
level: {
|
|
190
|
+
default: 1,
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
parseDOM: headingIndex.map((x) => ({
|
|
194
|
+
tag: `h${x}`,
|
|
195
|
+
getAttrs: (node) => {
|
|
196
|
+
if (!(node instanceof HTMLElement)) {
|
|
197
|
+
throw expectDomTypeError(node);
|
|
134
198
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
compositionstart: () => {
|
|
145
|
-
lock = true;
|
|
146
|
-
return false;
|
|
147
|
-
},
|
|
148
|
-
compositionend: () => {
|
|
149
|
-
lock = false;
|
|
150
|
-
const view = ctx.get(editorViewCtx);
|
|
151
|
-
setTimeout(() => {
|
|
152
|
-
walkThrough(view.state, (tr) => view.dispatch(tr));
|
|
153
|
-
}, 0);
|
|
154
|
-
return false;
|
|
155
|
-
},
|
|
199
|
+
return { level: x, id: node.id };
|
|
200
|
+
},
|
|
201
|
+
})),
|
|
202
|
+
toDOM: (node) => {
|
|
203
|
+
return [
|
|
204
|
+
`h${node.attrs['level']}`,
|
|
205
|
+
{
|
|
206
|
+
id: node.attrs['id'] || getId(node),
|
|
207
|
+
class: utils.getClassName(node.attrs, `heading h${node.attrs['level']}`),
|
|
156
208
|
},
|
|
209
|
+
0,
|
|
210
|
+
];
|
|
211
|
+
},
|
|
212
|
+
parseMarkdown: {
|
|
213
|
+
match: ({ type }) => type === id,
|
|
214
|
+
runner: (state, node, type) => {
|
|
215
|
+
const depth = node['depth'] as number;
|
|
216
|
+
state.openNode(type, { level: depth });
|
|
217
|
+
state.next(node.children);
|
|
218
|
+
state.closeNode();
|
|
157
219
|
},
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
) {
|
|
165
|
-
|
|
166
|
-
|
|
220
|
+
},
|
|
221
|
+
toMarkdown: {
|
|
222
|
+
match: (node) => node.type.name === id,
|
|
223
|
+
runner: (state, node) => {
|
|
224
|
+
state.openNode('heading', undefined, { depth: node.attrs['level'] });
|
|
225
|
+
const lastIsHardbreak = node.childCount >= 1 && node.lastChild?.type.name === 'hardbreak';
|
|
226
|
+
if (lastIsHardbreak) {
|
|
227
|
+
const contentArr: Node[] = [];
|
|
228
|
+
node.content.forEach((n, _, i) => {
|
|
229
|
+
if (i === node.childCount - 1) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
contentArr.push(n);
|
|
167
233
|
});
|
|
234
|
+
state.next(Fragment.fromArray(contentArr));
|
|
235
|
+
} else {
|
|
236
|
+
state.next(node.content);
|
|
168
237
|
}
|
|
169
|
-
|
|
170
|
-
return tr;
|
|
238
|
+
state.closeNode();
|
|
171
239
|
},
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
240
|
+
},
|
|
241
|
+
}),
|
|
242
|
+
inputRules: (type, ctx) =>
|
|
243
|
+
headingIndex.map((x) =>
|
|
244
|
+
textblockTypeInputRule(new RegExp(`^(#{1,${x}})\\s$`), type, () => {
|
|
245
|
+
const view = ctx.get(editorViewCtx);
|
|
246
|
+
const { $from } = view.state.selection;
|
|
247
|
+
const node = $from.node();
|
|
248
|
+
if (node.type.name === 'heading') {
|
|
249
|
+
let level = Number(node.attrs['level']) + Number(x);
|
|
250
|
+
if (level > 6) {
|
|
251
|
+
level = 6;
|
|
183
252
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
253
|
+
return {
|
|
254
|
+
level,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
level: x,
|
|
259
|
+
};
|
|
260
|
+
}),
|
|
261
|
+
),
|
|
262
|
+
commands: (type, ctx) => [
|
|
263
|
+
createCmd(TurnIntoHeading, (level = 1) => {
|
|
264
|
+
if (level < 1) {
|
|
265
|
+
return setBlockType(level === 0 ? ctx.get(schemaCtx).nodes['paragraph'] || type : type);
|
|
266
|
+
}
|
|
267
|
+
return setBlockType(level === 0 ? ctx.get(schemaCtx).nodes['paragraph'] || type : type, { level });
|
|
188
268
|
}),
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
269
|
+
createCmd(DowngradeHeading, () => {
|
|
270
|
+
return (state, dispatch, view) => {
|
|
271
|
+
const { $from } = state.selection;
|
|
272
|
+
const node = $from.node();
|
|
273
|
+
if (node.type !== type || !state.selection.empty || $from.parentOffset !== 0) return false;
|
|
274
|
+
|
|
275
|
+
const level = node.attrs['level'] - 1;
|
|
276
|
+
if (!level) {
|
|
277
|
+
return setBlockType(ctx.get(schemaCtx).nodes['paragraph'] || type)(state, dispatch, view);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
dispatch?.(
|
|
281
|
+
state.tr.setNodeMarkup(state.selection.$from.before(), undefined, {
|
|
282
|
+
...node.attrs,
|
|
283
|
+
level,
|
|
284
|
+
}),
|
|
285
|
+
);
|
|
286
|
+
return true;
|
|
287
|
+
};
|
|
288
|
+
}),
|
|
289
|
+
],
|
|
290
|
+
shortcuts: {
|
|
291
|
+
[SupportedKeys.H1]: createShortcut(TurnIntoHeading, 'Mod-Alt-1', 1),
|
|
292
|
+
[SupportedKeys.H2]: createShortcut(TurnIntoHeading, 'Mod-Alt-2', 2),
|
|
293
|
+
[SupportedKeys.H3]: createShortcut(TurnIntoHeading, 'Mod-Alt-3', 3),
|
|
294
|
+
[SupportedKeys.H4]: createShortcut(TurnIntoHeading, 'Mod-Alt-4', 4),
|
|
295
|
+
[SupportedKeys.H5]: createShortcut(TurnIntoHeading, 'Mod-Alt-5', 5),
|
|
296
|
+
[SupportedKeys.H6]: createShortcut(TurnIntoHeading, 'Mod-Alt-6', 6),
|
|
297
|
+
[SupportedKeys.DowngradeHeading]: createShortcut(DowngradeHeading, ['Backspace', 'Delete']),
|
|
298
|
+
},
|
|
299
|
+
prosePlugins: (type, ctx) => {
|
|
300
|
+
const plugins = [headingIdPlugin(ctx, type, getId)];
|
|
301
|
+
if (displayHashtag) {
|
|
302
|
+
plugins.push(headingHashPlugin(ctx, type, utils));
|
|
303
|
+
}
|
|
304
|
+
return plugins;
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
},
|
|
308
|
+
);
|
package/src/node/image.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
2
|
import { commandsCtx, createCmd, createCmdKey, ThemeImageType, ThemeInputChipType } from '@milkdown/core';
|
|
3
|
+
import { expectDomTypeError } from '@milkdown/exception';
|
|
3
4
|
import { findSelectedNodeOfType } from '@milkdown/prose';
|
|
4
5
|
import { InputRule } from '@milkdown/prose/inputrules';
|
|
5
6
|
import { Plugin, PluginKey } from '@milkdown/prose/state';
|
|
@@ -41,7 +42,7 @@ export const image = createNode<string, ImageOptions>((utils, options) => {
|
|
|
41
42
|
tag: 'img[src]',
|
|
42
43
|
getAttrs: (dom) => {
|
|
43
44
|
if (!(dom instanceof HTMLElement)) {
|
|
44
|
-
throw
|
|
45
|
+
throw expectDomTypeError(dom);
|
|
45
46
|
}
|
|
46
47
|
return {
|
|
47
48
|
src: dom.getAttribute('src') || '',
|