@milkdown/preset-commonmark 5.4.0 → 6.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 (90) hide show
  1. package/README.md +5 -4
  2. package/lib/index.d.ts +34 -1
  3. package/lib/index.d.ts.map +1 -0
  4. package/lib/index.es.js +320 -573
  5. package/lib/index.es.js.map +1 -1
  6. package/lib/{src/mark → mark}/code-inline.d.ts +1 -1
  7. package/lib/mark/code-inline.d.ts.map +1 -0
  8. package/lib/{src/mark → mark}/em.d.ts +1 -1
  9. package/lib/mark/em.d.ts.map +1 -0
  10. package/lib/mark/index.d.ts +6 -0
  11. package/lib/mark/index.d.ts.map +1 -0
  12. package/lib/{src/mark → mark}/link.d.ts +7 -1
  13. package/lib/mark/link.d.ts.map +1 -0
  14. package/lib/{src/mark → mark}/strong.d.ts +1 -1
  15. package/lib/mark/strong.d.ts.map +1 -0
  16. package/lib/{src/node → node}/blockquote.d.ts +1 -1
  17. package/lib/node/blockquote.d.ts.map +1 -0
  18. package/lib/{src/node → node}/bullet-list.d.ts +1 -1
  19. package/lib/node/bullet-list.d.ts.map +1 -0
  20. package/lib/{src/node → node}/code-fence.d.ts +1 -1
  21. package/lib/node/code-fence.d.ts.map +1 -0
  22. package/lib/{src/node → node}/doc.d.ts +1 -1
  23. package/lib/node/doc.d.ts.map +1 -0
  24. package/lib/{src/node → node}/hardbreak.d.ts +1 -1
  25. package/lib/node/hardbreak.d.ts.map +1 -0
  26. package/lib/{src/node → node}/heading.d.ts +1 -1
  27. package/lib/node/heading.d.ts.map +1 -0
  28. package/lib/{src/node → node}/hr.d.ts +1 -1
  29. package/lib/node/hr.d.ts.map +1 -0
  30. package/lib/{src/node → node}/image.d.ts +5 -5
  31. package/lib/node/image.d.ts.map +1 -0
  32. package/lib/{src/node → node}/index.d.ts +2 -2
  33. package/lib/node/index.d.ts.map +1 -0
  34. package/lib/{src/node → node}/list-item.d.ts +1 -1
  35. package/lib/node/list-item.d.ts.map +1 -0
  36. package/lib/{src/node → node}/ordered-list.d.ts +1 -1
  37. package/lib/node/ordered-list.d.ts.map +1 -0
  38. package/lib/{src/node → node}/paragraph.d.ts +1 -1
  39. package/lib/node/paragraph.d.ts.map +1 -0
  40. package/lib/{src/node → node}/text.d.ts +1 -1
  41. package/lib/node/text.d.ts.map +1 -0
  42. package/lib/{src/plugin → plugin}/filter-html.d.ts +0 -0
  43. package/lib/plugin/filter-html.d.ts.map +1 -0
  44. package/lib/plugin/index.d.ts +2 -0
  45. package/lib/plugin/index.d.ts.map +1 -0
  46. package/lib/{src/supported-keys.d.ts → supported-keys.d.ts} +0 -0
  47. package/lib/supported-keys.d.ts.map +1 -0
  48. package/package.json +33 -10
  49. package/src/mark/code-inline.ts +3 -14
  50. package/src/mark/em.ts +1 -1
  51. package/src/mark/link.ts +98 -24
  52. package/src/mark/strong.ts +2 -8
  53. package/src/node/blockquote.ts +2 -15
  54. package/src/node/bullet-list.ts +1 -1
  55. package/src/node/code-fence.ts +80 -188
  56. package/src/node/hardbreak.ts +3 -2
  57. package/src/node/heading.ts +15 -46
  58. package/src/node/hr.ts +2 -9
  59. package/src/node/image.ts +88 -240
  60. package/src/node/list-item.ts +37 -56
  61. package/src/node/ordered-list.ts +3 -3
  62. package/src/node/paragraph.ts +3 -11
  63. package/src/node/text.ts +1 -1
  64. package/src/plugin/filter-html.ts +10 -4
  65. package/lib/src/index.d.ts +0 -34
  66. package/lib/src/index.d.ts.map +0 -1
  67. package/lib/src/mark/code-inline.d.ts.map +0 -1
  68. package/lib/src/mark/em.d.ts.map +0 -1
  69. package/lib/src/mark/index.d.ts +0 -6
  70. package/lib/src/mark/index.d.ts.map +0 -1
  71. package/lib/src/mark/link.d.ts.map +0 -1
  72. package/lib/src/mark/strong.d.ts.map +0 -1
  73. package/lib/src/node/blockquote.d.ts.map +0 -1
  74. package/lib/src/node/bullet-list.d.ts.map +0 -1
  75. package/lib/src/node/code-fence.d.ts.map +0 -1
  76. package/lib/src/node/doc.d.ts.map +0 -1
  77. package/lib/src/node/hardbreak.d.ts.map +0 -1
  78. package/lib/src/node/heading.d.ts.map +0 -1
  79. package/lib/src/node/hr.d.ts.map +0 -1
  80. package/lib/src/node/image.d.ts.map +0 -1
  81. package/lib/src/node/index.d.ts.map +0 -1
  82. package/lib/src/node/list-item.d.ts.map +0 -1
  83. package/lib/src/node/ordered-list.d.ts.map +0 -1
  84. package/lib/src/node/paragraph.d.ts.map +0 -1
  85. package/lib/src/node/text.d.ts.map +0 -1
  86. package/lib/src/plugin/filter-html.d.ts.map +0 -1
  87. package/lib/src/plugin/index.d.ts +0 -2
  88. package/lib/src/plugin/index.d.ts.map +0 -1
  89. package/lib/src/supported-keys.d.ts.map +0 -1
  90. package/lib/src/types.d.ts +0 -5
@@ -1,5 +1,5 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import { createCmd, createCmdKey, themeToolCtx } from '@milkdown/core';
2
+ import { createCmd, createCmdKey, editorViewCtx, ThemeCodeFenceType } from '@milkdown/core';
3
3
  import { setBlockType, textblockTypeInputRule } from '@milkdown/prose';
4
4
  import { createNode, createShortcut } from '@milkdown/utils';
5
5
 
@@ -29,123 +29,15 @@ const languageOptions = [
29
29
  export const backtickInputRegex = /^```(?<language>[a-z]*)?[\s\n]$/;
30
30
  export const tildeInputRegex = /^~~~(?<language>[a-z]*)?[\s\n]$/;
31
31
 
32
- export const TurnIntoCodeFence = createCmdKey();
32
+ export const TurnIntoCodeFence = createCmdKey('TurnIntoCodeFence');
33
33
 
34
34
  const id = 'fence';
35
35
  export const codeFence = createNode<Keys, { languageList?: string[] }>((utils, options) => {
36
- const style = utils.getStyle(({ palette, mixin, size, font }, { css }) => {
37
- const { shadow, scrollbar, border } = mixin;
38
- const { lineWidth, radius } = size;
39
- return css`
40
- background-color: ${palette('background')};
41
- color: ${palette('neutral')};
42
- font-size: 0.85rem;
43
- padding: 1.2rem 0.4rem 1.4rem;
44
- border-radius: ${radius};
45
- font-family: ${font.typography};
46
-
47
- * {
48
- margin: 0;
49
- }
50
-
51
- .code-fence_select-wrapper {
52
- position: relative;
53
- }
54
-
55
- .code-fence_value {
56
- width: 10.25rem;
57
- box-sizing: border-box;
58
- border-radius: ${size.radius};
59
- margin: 0 1.2rem 1.2rem;
60
- ${border()};
61
- ${shadow()};
62
- cursor: pointer;
63
- background-color: ${palette('surface')};
64
- position: relative;
65
- display: flex;
66
- color: ${palette('neutral', 0.87)};
67
- letter-spacing: 0.5px;
68
- height: 2.625rem;
69
- align-items: center;
70
-
71
- & > .icon {
72
- width: 2.625rem;
73
- height: 100%;
74
- display: flex;
75
- justify-content: center;
76
- align-items: center;
77
- color: ${palette('solid', 0.87)};
78
- border-left: ${lineWidth} solid ${palette('line')};
79
-
80
- text-align: center;
81
- transition: all 0.2s ease-in-out;
82
- &:hover {
83
- background: ${palette('background')};
84
- color: ${palette('primary')};
85
- }
86
- }
87
-
88
- > span:first-child {
89
- padding-left: 1rem;
90
- flex: 1;
91
- font-weight: 500;
92
- }
93
- }
94
-
95
- .code-fence_select-option {
96
- list-style: none;
97
- line-height: 2rem;
98
- padding-left: 1rem;
99
- cursor: pointer;
100
- :hover {
101
- background: ${palette('secondary', 0.12)};
102
- color: ${palette('primary')};
103
- }
104
- }
105
-
106
- .code-fence_select {
107
- &[data-fold='true'] {
108
- display: none;
109
- }
110
-
111
- font-weight: 500;
112
- position: absolute;
113
- z-index: 1;
114
- top: 2.625rem;
115
- box-sizing: border-box;
116
- left: 1.2rem;
117
- padding: 0.5rem 0;
118
- max-height: 16.75rem;
119
- width: 10.25rem;
120
- ${border()};
121
- ${shadow()};
122
- background-color: ${palette('surface')};
123
- border-top: none;
124
- overflow-y: auto;
125
- display: flex;
126
- flex-direction: column;
127
-
128
- ${scrollbar('y')}
129
- }
130
-
131
- code {
132
- line-height: 1.5;
133
- font-family: ${font.code};
134
- }
135
-
136
- pre {
137
- font-family: ${font.code};
138
- margin: 0 1.2rem !important;
139
- white-space: pre;
140
- overflow: auto;
141
- ${scrollbar('x')};
142
- }
143
- `;
144
- });
36
+ const languageList = options?.languageList || languageOptions;
145
37
 
146
38
  return {
147
39
  id,
148
- schema: () => ({
40
+ schema: (ctx) => ({
149
41
  content: 'text*',
150
42
  group: 'block',
151
43
  marks: '',
@@ -167,25 +59,66 @@ export const codeFence = createNode<Keys, { languageList?: string[] }>((utils, o
167
59
  if (!(dom instanceof HTMLElement)) {
168
60
  throw new Error('Parse DOM error.');
169
61
  }
170
- return { language: dom.dataset.language };
62
+ return { language: dom.dataset['language'] };
171
63
  },
172
64
  },
173
65
  ],
174
66
  toDOM: (node) => {
67
+ const select = document.createElement('select');
68
+ languageList.forEach((lang) => {
69
+ const option = document.createElement('option');
70
+ option.value = lang;
71
+ option.innerText = !lang ? '--' : lang;
72
+ if (lang === node.attrs['language']) {
73
+ option.selected = true;
74
+ }
75
+ select.appendChild(option);
76
+ });
77
+ select.onchange = (e) => {
78
+ const target = e.target;
79
+ if (!(target instanceof HTMLSelectElement)) {
80
+ return;
81
+ }
82
+ const view = ctx.get(editorViewCtx);
83
+ if (!view.editable) {
84
+ target.value = node.attrs['language'];
85
+ return;
86
+ }
87
+
88
+ const { top, left } = target.getBoundingClientRect();
89
+ const result = view.posAtCoords({ top, left });
90
+ if (!result) return;
91
+
92
+ const { tr } = view.state;
93
+
94
+ view.dispatch(
95
+ tr.setNodeMarkup(result.inside, undefined, {
96
+ ...node.attrs,
97
+ language: target.value,
98
+ }),
99
+ );
100
+ };
175
101
  return [
176
- 'pre',
102
+ 'div',
177
103
  {
178
- 'data-language': node.attrs.language,
179
- class: utils.getClassName(node.attrs, 'code-fence', style),
104
+ class: 'code-fence-container',
180
105
  },
181
- ['code', { spellCheck: 'false' }, 0],
106
+ select,
107
+ [
108
+ 'pre',
109
+ {
110
+ 'data-language': node.attrs['language'],
111
+ class: utils.getClassName(node.attrs, 'code-fence'),
112
+ },
113
+ ['code', { spellCheck: 'false' }, 0],
114
+ ],
182
115
  ];
183
116
  },
184
117
  parseMarkdown: {
185
118
  match: ({ type }) => type === 'code',
186
119
  runner: (state, node, type) => {
187
- const language = node.lang as string;
188
- const value = node.value as string;
120
+ const language = node['lang'] as string;
121
+ const value = node['value'] as string;
189
122
  state.openNode(type, { language });
190
123
  if (value) {
191
124
  state.addText(value);
@@ -197,7 +130,7 @@ export const codeFence = createNode<Keys, { languageList?: string[] }>((utils, o
197
130
  match: (node) => node.type.name === id,
198
131
  runner: (state, node) => {
199
132
  state.addNode('code', undefined, node.content.firstChild?.text || '', {
200
- lang: node.attrs.language,
133
+ lang: node.attrs['language'],
201
134
  });
202
135
  },
203
136
  },
@@ -218,103 +151,62 @@ export const codeFence = createNode<Keys, { languageList?: string[] }>((utils, o
218
151
  shortcuts: {
219
152
  [SupportedKeys.CodeFence]: createShortcut(TurnIntoCodeFence, 'Mod-Alt-c'),
220
153
  },
221
- view: (ctx) => (node, view, getPos) => {
222
- const container = document.createElement('div');
223
- const selectWrapper = document.createElement('div');
224
- const select = document.createElement('ul');
225
- const pre = document.createElement('pre');
226
- const code = document.createElement('code');
154
+ view: () => (node, view, getPos) => {
155
+ let currNode = node;
227
156
 
228
- const valueWrapper = document.createElement('div');
229
- valueWrapper.className = 'code-fence_value';
230
- const value = document.createElement('span');
231
- valueWrapper.appendChild(value);
232
- if (view.editable) {
233
- valueWrapper.appendChild(ctx.get(themeToolCtx).slots.icon('downArrow'));
234
- }
235
-
236
- select.className = 'code-fence_select';
237
- select.addEventListener('mousedown', (e) => {
238
- e.preventDefault();
239
- e.stopPropagation();
240
-
241
- if (!view.editable) return;
242
-
243
- const el = e.target;
244
- if (!(el instanceof HTMLLIElement)) return;
157
+ const onSelectLanguage = (language: string) => {
245
158
  const { tr } = view.state;
246
-
247
159
  view.dispatch(
248
160
  tr.setNodeMarkup(getPos(), undefined, {
249
161
  fold: true,
250
- language: el.dataset.value,
162
+ language,
251
163
  }),
252
164
  );
253
- });
254
- valueWrapper.addEventListener('mousedown', (e) => {
255
- e.preventDefault();
256
- e.stopPropagation();
257
-
258
- if (!view.editable) return;
165
+ };
166
+ const onBlur = () => {
259
167
  const { tr } = view.state;
260
168
 
261
169
  view.dispatch(
262
170
  tr.setNodeMarkup(getPos(), undefined, {
263
- fold: false,
264
- language: container.dataset.language,
171
+ ...currNode.attrs,
172
+ fold: true,
265
173
  }),
266
174
  );
267
- });
268
- document.addEventListener('mousedown', () => {
269
- if (!view.editable || select.dataset.fold === 'true') return;
270
-
175
+ };
176
+ const onFocus = () => {
271
177
  const { tr } = view.state;
272
178
 
273
179
  view.dispatch(
274
180
  tr.setNodeMarkup(getPos(), undefined, {
275
- fold: true,
276
- language: container.dataset.language,
181
+ ...currNode.attrs,
182
+ fold: false,
277
183
  }),
278
184
  );
279
- });
185
+ };
280
186
 
281
- (options?.languageList || languageOptions).forEach((lang) => {
282
- const option = document.createElement('li');
283
- option.className = 'code-fence_select-option';
284
- option.innerText = lang || '--';
285
- select.appendChild(option);
286
- option.setAttribute('data-value', lang);
187
+ const renderer = utils.themeManager.get<ThemeCodeFenceType>('code-fence', {
188
+ onBlur,
189
+ onFocus,
190
+ onSelectLanguage,
191
+ editable: () => view.editable,
192
+ languageList,
287
193
  });
194
+ if (!renderer) return {};
288
195
 
289
- code.spellcheck = false;
290
- selectWrapper.className = 'code-fence_select-wrapper';
291
- selectWrapper.contentEditable = 'false';
292
- selectWrapper.append(valueWrapper);
293
- selectWrapper.append(select);
294
- pre.append(code);
295
- const codeContent = document.createElement('div');
296
- code.append(codeContent);
297
- codeContent.style.whiteSpace = 'inherit';
298
-
299
- container.append(selectWrapper, pre);
300
- container.setAttribute('class', utils.getClassName(node.attrs, 'code-fence', style));
301
- container.setAttribute('data-language', node.attrs.language);
302
- value.innerText = node.attrs.language || '--';
303
- select.setAttribute('data-fold', node.attrs.fold ? 'true' : 'false');
196
+ const { dom, contentDOM, onUpdate, onDestroy } = renderer;
197
+ onUpdate(currNode);
304
198
 
305
199
  return {
306
- dom: container,
307
- contentDOM: codeContent,
200
+ dom,
201
+ contentDOM,
308
202
  update: (updatedNode) => {
309
203
  if (updatedNode.type.name !== id) return false;
310
-
311
- const lang = updatedNode.attrs.language;
312
- container.dataset.language = lang;
313
- value.innerText = lang || '--';
314
- select.setAttribute('data-fold', updatedNode.attrs.fold ? 'true' : 'false');
204
+ currNode = updatedNode;
205
+ onUpdate(currNode);
315
206
 
316
207
  return true;
317
208
  },
209
+ destroy: onDestroy,
318
210
  };
319
211
  },
320
212
  };
@@ -7,7 +7,7 @@ import { SupportedKeys } from '../supported-keys';
7
7
 
8
8
  type Keys = SupportedKeys['HardBreak'];
9
9
 
10
- export const InsertHardbreak = createCmdKey();
10
+ export const InsertHardbreak = createCmdKey('InsertHardbreak');
11
11
 
12
12
  export const hardbreak = createNode<Keys>((utils) => {
13
13
  return {
@@ -42,10 +42,11 @@ export const hardbreak = createNode<Keys>((utils) => {
42
42
  },
43
43
  prosePlugins: (type) => [
44
44
  new Plugin({
45
- key: new PluginKey('hardbreak-marks'),
45
+ key: new PluginKey('MILKDOWN_PLUGIN_HARDBREAK_MARKS'),
46
46
  appendTransaction: (trs, _oldState, newState) => {
47
47
  if (!trs.length) return;
48
48
  const [tr] = trs;
49
+ if (!tr) return;
49
50
 
50
51
  const [step] = tr.steps;
51
52
 
@@ -25,49 +25,13 @@ type Keys =
25
25
  | SupportedKeys['H5']
26
26
  | SupportedKeys['H6'];
27
27
 
28
- export const TurnIntoHeading = createCmdKey<number>();
28
+ export const TurnIntoHeading = createCmdKey<number>('TurnIntoHeading');
29
29
 
30
30
  export const headingPluginKey = new PluginKey('MILKDOWN_PLUGIN_ID');
31
31
 
32
32
  export const heading = createNode<Keys>((utils) => {
33
33
  const id = 'heading';
34
34
 
35
- const style = (level: number) =>
36
- utils.getStyle((_, { css }) => {
37
- const headingMap: Record<number, string> = {
38
- 1: css`
39
- font-size: 3rem;
40
- line-height: 3.5rem;
41
- `,
42
- 2: css`
43
- font-size: 2.5rem;
44
- line-height: 3rem;
45
- `,
46
- 3: css`
47
- font-size: 2.125rem;
48
- line-height: 2.25rem;
49
- `,
50
- 4: css`
51
- font-size: 1.75rem;
52
- line-height: 2rem;
53
- `,
54
- 5: css`
55
- font-size: 1.5rem;
56
- line-height: 1.5rem;
57
- `,
58
- 6: css`
59
- font-size: 1.25rem;
60
- line-height: 1.25rem;
61
- `,
62
- };
63
-
64
- return css`
65
- ${headingMap[level] || ''}
66
- margin: 2.5rem 0 !important;
67
- font-weight: 400;
68
- `;
69
- });
70
-
71
35
  return {
72
36
  id,
73
37
  schema: () => ({
@@ -93,10 +57,10 @@ export const heading = createNode<Keys>((utils) => {
93
57
  })),
94
58
  toDOM: (node) => {
95
59
  return [
96
- `h${node.attrs.level}`,
60
+ `h${node.attrs['level']}`,
97
61
  {
98
- id: node.attrs.id || node.textContent.split(' ').join('-').toLocaleLowerCase(),
99
- class: utils.getClassName(node.attrs, `heading h${node.attrs.level}`, style(node.attrs.level)),
62
+ id: node.attrs['id'] || node.textContent.split(' ').join('-').toLocaleLowerCase(),
63
+ class: utils.getClassName(node.attrs, `heading h${node.attrs['level']}`),
100
64
  },
101
65
  0,
102
66
  ];
@@ -104,7 +68,7 @@ export const heading = createNode<Keys>((utils) => {
104
68
  parseMarkdown: {
105
69
  match: ({ type }) => type === id,
106
70
  runner: (state, node, type) => {
107
- const depth = node.depth as number;
71
+ const depth = node['depth'] as number;
108
72
  state.openNode(type, { level: depth });
109
73
  state.next(node.children);
110
74
  state.closeNode();
@@ -113,7 +77,7 @@ export const heading = createNode<Keys>((utils) => {
113
77
  toMarkdown: {
114
78
  match: (node) => node.type.name === id,
115
79
  runner: (state, node) => {
116
- state.openNode('heading', undefined, { depth: node.attrs.level });
80
+ state.openNode('heading', undefined, { depth: node.attrs['level'] });
117
81
  state.next(node.content);
118
82
  state.closeNode();
119
83
  },
@@ -152,8 +116,8 @@ export const heading = createNode<Keys>((utils) => {
152
116
  const attrs = node.attrs;
153
117
  const id = createId(node);
154
118
 
155
- if (attrs.id !== id) {
156
- tr.setNodeMarkup(pos, undefined, {
119
+ if (attrs['id'] !== id) {
120
+ tr.setMeta(headingPluginKey, true).setNodeMarkup(pos, undefined, {
157
121
  ...attrs,
158
122
  id,
159
123
  });
@@ -174,7 +138,9 @@ export const heading = createNode<Keys>((utils) => {
174
138
  compositionend: () => {
175
139
  lock = false;
176
140
  const view = ctx.get(editorViewCtx);
177
- walkThrough(view.state, (tr) => view.dispatch(tr));
141
+ setTimeout(() => {
142
+ walkThrough(view.state, (tr) => view.dispatch(tr));
143
+ }, 0);
178
144
  return false;
179
145
  },
180
146
  },
@@ -182,7 +148,10 @@ export const heading = createNode<Keys>((utils) => {
182
148
  appendTransaction: (transactions, _, nextState) => {
183
149
  let tr: Transaction | null = null;
184
150
 
185
- if (transactions.some((transaction) => transaction.docChanged)) {
151
+ if (
152
+ transactions.every((transaction) => !transaction.getMeta(headingPluginKey)) &&
153
+ transactions.some((transaction) => transaction.docChanged)
154
+ ) {
186
155
  walkThrough(nextState, (t) => {
187
156
  tr = t;
188
157
  });
package/src/node/hr.ts CHANGED
@@ -4,21 +4,14 @@ import { InputRule, Selection } from '@milkdown/prose';
4
4
  import { createNode } from '@milkdown/utils';
5
5
 
6
6
  const id = 'hr';
7
- export const InsertHr = createCmdKey<string>();
7
+ export const InsertHr = createCmdKey<string>('InsertHr');
8
8
  export const hr = createNode((utils) => {
9
- const style = utils.getStyle(
10
- (themeTool, { css }) => css`
11
- height: ${themeTool.size.lineWidth};
12
- background-color: ${themeTool.palette('line')};
13
- border-width: 0;
14
- `,
15
- );
16
9
  return {
17
10
  id,
18
11
  schema: () => ({
19
12
  group: 'block',
20
13
  parseDOM: [{ tag: 'hr' }],
21
- toDOM: (node) => ['hr', { class: utils.getClassName(node.attrs, id, style) }],
14
+ toDOM: (node) => ['hr', { class: utils.getClassName(node.attrs, id) }],
22
15
  parseMarkdown: {
23
16
  match: ({ type }) => type === 'thematicBreak',
24
17
  runner: (state, _, type) => {