@milkdown/preset-gfm 7.22.1 → 7.22.2
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/__test__/inline-br-containers.spec.d.ts +2 -0
- package/lib/__test__/inline-br-containers.spec.d.ts.map +1 -0
- package/lib/__test__/task-list-spread-roundtrip.spec.d.ts +1 -1
- package/lib/__test__/task-list-spread-roundtrip.spec.d.ts.map +1 -1
- package/lib/__test__/test-utils.d.ts +7 -0
- package/lib/__test__/test-utils.d.ts.map +1 -0
- package/lib/index.js +14 -2
- package/lib/index.js.map +1 -1
- package/lib/node/footnote/definition.d.ts.map +1 -1
- package/lib/node/footnote/reference.d.ts.map +1 -1
- package/lib/node/table/input.d.ts.map +1 -1
- package/lib/node/table/schema.d.ts.map +1 -1
- package/lib/node/task-list-item.d.ts.map +1 -1
- package/lib/plugin/auto-insert-span-plugin.d.ts.map +1 -1
- package/lib/plugin/column-resizing-plugin.d.ts.map +1 -1
- package/lib/plugin/remark-gfm-plugin.d.ts.map +1 -1
- package/lib/plugin/table-editing-plugin.d.ts.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/__test__/inline-br-containers.spec.ts +106 -0
- package/src/__test__/table-header-row.spec.ts +1 -2
- package/src/__test__/task-list-spread-roundtrip.spec.ts +9 -32
- package/src/__test__/test-utils.ts +63 -0
- package/src/node/footnote/definition.ts +2 -1
- package/src/node/footnote/reference.ts +2 -1
- package/src/node/table/input.ts +9 -8
- package/src/node/table/schema.ts +35 -8
- package/src/node/task-list-item.ts +4 -3
- package/src/plugin/auto-insert-span-plugin.ts +3 -2
- package/src/plugin/column-resizing-plugin.ts +2 -1
- package/src/plugin/keep-table-align-plugin.ts +1 -1
- package/src/plugin/remark-gfm-plugin.ts +2 -1
- package/src/plugin/table-editing-plugin.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milkdown/preset-gfm",
|
|
3
|
-
"version": "7.22.
|
|
3
|
+
"version": "7.22.2",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"gfm",
|
|
6
6
|
"markdown",
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"@milkdown/core": "7.22.2",
|
|
31
|
+
"@milkdown/ctx": "7.22.2",
|
|
32
|
+
"@milkdown/exception": "7.22.2",
|
|
33
|
+
"@milkdown/preset-commonmark": "7.22.2",
|
|
34
|
+
"@milkdown/prose": "7.22.2",
|
|
35
|
+
"@milkdown/transformer": "7.22.2",
|
|
36
|
+
"@milkdown/utils": "7.22.2",
|
|
30
37
|
"prosemirror-safari-ime-span": "^1.0.1",
|
|
31
|
-
"remark-gfm": "^4.0.1"
|
|
32
|
-
"@milkdown/core": "7.22.1",
|
|
33
|
-
"@milkdown/exception": "7.22.1",
|
|
34
|
-
"@milkdown/preset-commonmark": "7.22.1",
|
|
35
|
-
"@milkdown/prose": "7.22.1",
|
|
36
|
-
"@milkdown/utils": "7.22.1",
|
|
37
|
-
"@milkdown/transformer": "7.22.1",
|
|
38
|
-
"@milkdown/ctx": "7.22.1"
|
|
38
|
+
"remark-gfm": "^4.0.1"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "vite build"
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { editorViewCtx } from '@milkdown/core'
|
|
2
|
+
import { getMarkdown } from '@milkdown/utils'
|
|
3
|
+
import { describe, expect, it } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import { countBrAtomsIn, roundTrip, withEditor } from './test-utils'
|
|
6
|
+
|
|
7
|
+
describe('inline br in gfm containers', () => {
|
|
8
|
+
it('does not drop a footnote whose definition has a block <br>', async () => {
|
|
9
|
+
const input = '[^note]: foo\n\n <br />\n\n bar\n\n'
|
|
10
|
+
const output = await roundTrip(input)
|
|
11
|
+
expect(output).toBe('[^note]: foo\n\n <br />\n\n bar\n')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('keeps a referenced footnote whose definition has a block <br>', async () => {
|
|
15
|
+
const input = 'See [^note].\n\n[^note]: foo\n\n <br />\n\n bar\n\n'
|
|
16
|
+
const output = await roundTrip(input)
|
|
17
|
+
expect(output).toBe(
|
|
18
|
+
'See [^note].\n\n[^note]: foo\n\n <br />\n\n bar\n'
|
|
19
|
+
)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('keeps an inline <br> inside a footnote definition', async () => {
|
|
23
|
+
const output = await roundTrip('[^note]: foo<br>bar\n')
|
|
24
|
+
expect(output).toBe('[^note]: foo<br>bar\n')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// A lone <br> in a table cell is byte-identical to the placeholder the
|
|
28
|
+
// old serializer emitted for every empty cell, so it folds into an
|
|
29
|
+
// empty cell; the serializer no longer emits placeholders inside cells.
|
|
30
|
+
it('folds a lone table-cell <br> into an empty cell', async () => {
|
|
31
|
+
const input = '| <br> | c |\n| --- | --- |\n| x | y |\n'
|
|
32
|
+
expect(await countBrAtomsIn(input)).toBe(0)
|
|
33
|
+
const output = await roundTrip(input)
|
|
34
|
+
expect(output).toBe('| | c |\n| - | - |\n| x | y |\n')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('loads a legacy empty-cell placeholder as an empty cell', async () => {
|
|
38
|
+
const input = '| <br /> | c |\n| --- | --- |\n| x | y |\n'
|
|
39
|
+
expect(await countBrAtomsIn(input)).toBe(0)
|
|
40
|
+
const output = await roundTrip(input)
|
|
41
|
+
expect(output).toBe('| | c |\n| - | - |\n| x | y |\n')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('round-trips an empty table cell without a placeholder', async () => {
|
|
45
|
+
const input = '| | c |\n| --- | --- |\n| x | y |\n'
|
|
46
|
+
const output = await roundTrip(input)
|
|
47
|
+
expect(output).toBe('| | c |\n| - | - |\n| x | y |\n')
|
|
48
|
+
expect(output).not.toContain('<br')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('keeps consecutive <br>s inside a table cell as atoms', async () => {
|
|
52
|
+
const input = '| <br><br> | c |\n| --- | --- |\n| x | y |\n'
|
|
53
|
+
expect(await countBrAtomsIn(input)).toBe(2)
|
|
54
|
+
const output = await roundTrip(input)
|
|
55
|
+
expect(output).toBe(
|
|
56
|
+
'| <br><br> | c |\n| -------- | - |\n| x | y |\n'
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('keeps sibling text and an inline <br> inside a table cell', async () => {
|
|
61
|
+
const input = '| a<br>b | c |\n| --- | --- |\n| x | y |\n'
|
|
62
|
+
expect(await countBrAtomsIn(input)).toBe(1)
|
|
63
|
+
const output = await roundTrip(input)
|
|
64
|
+
expect(output).toBe('| a<br>b | c |\n| ------ | - |\n| x | y |\n')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// An HTML paste can put a trailing hardbreak in a cell. The
|
|
68
|
+
// serializer drops it, the same as the paragraph serializer does.
|
|
69
|
+
// Otherwise it renders as a trailing space that the next parse trims,
|
|
70
|
+
// and two consecutive saves diverge.
|
|
71
|
+
it('drops a trailing hardbreak in a cell so saves stay stable', async () => {
|
|
72
|
+
const output = await withEditor(
|
|
73
|
+
'| a | c |\n| --- | --- |\n| x | y |\n',
|
|
74
|
+
(editor) =>
|
|
75
|
+
editor.action((ctx) => {
|
|
76
|
+
const view = ctx.get(editorViewCtx)
|
|
77
|
+
const { state } = view
|
|
78
|
+
const hardbreak = state.schema.nodes.hardbreak
|
|
79
|
+
if (!hardbreak) throw new Error('hardbreak type missing')
|
|
80
|
+
let pos = -1
|
|
81
|
+
state.doc.descendants((node, nodePos) => {
|
|
82
|
+
if (
|
|
83
|
+
pos === -1 &&
|
|
84
|
+
node.type.name === 'paragraph' &&
|
|
85
|
+
node.textContent === 'a'
|
|
86
|
+
)
|
|
87
|
+
pos = nodePos + node.nodeSize - 1
|
|
88
|
+
return pos === -1
|
|
89
|
+
})
|
|
90
|
+
view.dispatch(state.tr.insert(pos, hardbreak.create()))
|
|
91
|
+
return getMarkdown()(ctx)
|
|
92
|
+
})
|
|
93
|
+
)
|
|
94
|
+
expect(output).toBe('| a | c |\n| - | - |\n| x | y |\n')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('keeps a lone <br> inside strikethrough', async () => {
|
|
98
|
+
const output = await roundTrip('~~<br>~~\n')
|
|
99
|
+
expect(output).toBe('~~<br>~~\n')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('keeps an inline <br> inside a task list item', async () => {
|
|
103
|
+
const output = await roundTrip('- [ ] foo<br>bar\n')
|
|
104
|
+
expect(output).toBe('* [ ] foo<br>bar\n')
|
|
105
|
+
})
|
|
106
|
+
})
|
|
@@ -30,7 +30,7 @@ it('should not crash when serializing a table with empty header row', async () =
|
|
|
30
30
|
const schema = editor.ctx.get(schemaCtx)
|
|
31
31
|
const serializer = editor.ctx.get(serializerCtx)
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// An empty header row has a content size of 0.
|
|
34
34
|
const tableHeaderRow = schema.nodes.table_header_row!.create()
|
|
35
35
|
const tableRow = schema.nodes.table_row!.create(null, [
|
|
36
36
|
schema.nodes.table_cell!.create(
|
|
@@ -41,7 +41,6 @@ it('should not crash when serializing a table with empty header row', async () =
|
|
|
41
41
|
const table = schema.nodes.table!.create(null, [tableHeaderRow, tableRow])
|
|
42
42
|
const doc = schema.topNodeType.createAndFill(null, [table])!
|
|
43
43
|
|
|
44
|
-
// This should not throw
|
|
45
44
|
expect(() => serializer(doc)).not.toThrow()
|
|
46
45
|
})
|
|
47
46
|
|
|
@@ -1,31 +1,7 @@
|
|
|
1
|
-
import '@
|
|
2
|
-
import {
|
|
3
|
-
defaultValueCtx,
|
|
4
|
-
Editor,
|
|
5
|
-
editorViewCtx,
|
|
6
|
-
schemaCtx,
|
|
7
|
-
} from '@milkdown/core'
|
|
8
|
-
import { commonmark } from '@milkdown/preset-commonmark'
|
|
9
|
-
import { getMarkdown } from '@milkdown/utils'
|
|
1
|
+
import { editorViewCtx, schemaCtx } from '@milkdown/core'
|
|
10
2
|
import { describe, expect, it } from 'vitest'
|
|
11
3
|
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
async function createEditor(defaultValue: string) {
|
|
15
|
-
const editor = Editor.make()
|
|
16
|
-
editor.config((ctx) => {
|
|
17
|
-
ctx.set(defaultValueCtx, defaultValue)
|
|
18
|
-
})
|
|
19
|
-
editor.use(commonmark)
|
|
20
|
-
editor.use(gfm)
|
|
21
|
-
await editor.create()
|
|
22
|
-
return editor
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async function roundTrip(markdown: string) {
|
|
26
|
-
const editor = await createEditor(markdown)
|
|
27
|
-
return editor.action(getMarkdown())
|
|
28
|
-
}
|
|
4
|
+
import { roundTrip, withEditor } from './test-utils'
|
|
29
5
|
|
|
30
6
|
// https://github.com/Milkdown/milkdown/issues/2419
|
|
31
7
|
// Tight task lists must round-trip as tight, and the checkbox syntax
|
|
@@ -59,12 +35,13 @@ describe('task list spread round-trip (#2419)', () => {
|
|
|
59
35
|
// does not throw on documents containing task lists.
|
|
60
36
|
cases.forEach((markdown) => {
|
|
61
37
|
it(`should round-trip through nodeFromJSON for ${JSON.stringify(markdown)}`, async () => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
38
|
+
await withEditor(markdown, (editor) => {
|
|
39
|
+
const doc = editor.ctx.get(editorViewCtx).state.doc
|
|
40
|
+
const schema = editor.ctx.get(schemaCtx)
|
|
41
|
+
const json = doc.toJSON()
|
|
42
|
+
expect(() => schema.nodeFromJSON(json)).not.toThrow()
|
|
43
|
+
expect(schema.nodeFromJSON(json).eq(doc)).toBe(true)
|
|
44
|
+
})
|
|
68
45
|
})
|
|
69
46
|
})
|
|
70
47
|
})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { defaultValueCtx, Editor, editorViewCtx } from '@milkdown/core'
|
|
2
|
+
import { commonmark } from '@milkdown/preset-commonmark'
|
|
3
|
+
import { getMarkdown } from '@milkdown/utils'
|
|
4
|
+
|
|
5
|
+
import { gfm } from '..'
|
|
6
|
+
|
|
7
|
+
type EditorPlugins = Parameters<Editor['use']>[0]
|
|
8
|
+
|
|
9
|
+
/// Run `fn` against a fresh editor loaded with `defaultValue` and destroy
|
|
10
|
+
/// the editor afterwards. Uses the commonmark and gfm presets unless
|
|
11
|
+
/// `plugins` are given, in which case exactly those are used.
|
|
12
|
+
export async function withEditor<T>(
|
|
13
|
+
defaultValue: string,
|
|
14
|
+
fn: (editor: Editor) => T | Promise<T>,
|
|
15
|
+
...plugins: EditorPlugins[]
|
|
16
|
+
): Promise<T> {
|
|
17
|
+
const editor = Editor.make()
|
|
18
|
+
editor.config((ctx) => {
|
|
19
|
+
ctx.set(defaultValueCtx, defaultValue)
|
|
20
|
+
})
|
|
21
|
+
for (const plugin of plugins.length ? plugins : [commonmark, gfm])
|
|
22
|
+
editor.use(plugin)
|
|
23
|
+
// Outside the try: destroy() never settles on a half-created editor, so
|
|
24
|
+
// a creation failure must propagate directly instead of timing out.
|
|
25
|
+
await editor.create()
|
|
26
|
+
try {
|
|
27
|
+
return await fn(editor)
|
|
28
|
+
} finally {
|
|
29
|
+
await editor.destroy().catch(() => {})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function roundTrip(markdown: string, ...plugins: EditorPlugins[]) {
|
|
34
|
+
return withEditor(
|
|
35
|
+
markdown,
|
|
36
|
+
(editor) => editor.action(getMarkdown()),
|
|
37
|
+
...plugins
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Keep in sync with `isBrHtmlValue` in
|
|
42
|
+
// preset-commonmark/src/__internal__/empty-line-br.ts (deliberately not
|
|
43
|
+
// part of the public API).
|
|
44
|
+
const BR_VALUE_PATTERN = /^<br\s*\/?\s*>$/i
|
|
45
|
+
|
|
46
|
+
/// Load `markdown` and count the html atoms holding a `<br>` in the
|
|
47
|
+
/// resulting document.
|
|
48
|
+
export function countBrAtomsIn(markdown: string) {
|
|
49
|
+
return withEditor(markdown, (editor) =>
|
|
50
|
+
editor.action((ctx) => {
|
|
51
|
+
const doc = ctx.get(editorViewCtx).state.doc
|
|
52
|
+
let count = 0
|
|
53
|
+
doc.descendants((node) => {
|
|
54
|
+
if (
|
|
55
|
+
node.type.name === 'html' &&
|
|
56
|
+
BR_VALUE_PATTERN.test(String(node.attrs.value).trim())
|
|
57
|
+
)
|
|
58
|
+
count += 1
|
|
59
|
+
})
|
|
60
|
+
return count
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -38,7 +38,8 @@ export const footnoteDefinitionSchema = $nodeSchema(
|
|
|
38
38
|
return [
|
|
39
39
|
'dl',
|
|
40
40
|
{
|
|
41
|
-
// TODO: add a prosemirror plugin
|
|
41
|
+
// TODO: add a prosemirror plugin that syncs the label on a
|
|
42
|
+
// change.
|
|
42
43
|
'data-label': label,
|
|
43
44
|
'data-type': id,
|
|
44
45
|
},
|
|
@@ -35,7 +35,8 @@ export const footnoteReferenceSchema = $nodeSchema(
|
|
|
35
35
|
return [
|
|
36
36
|
'sup',
|
|
37
37
|
{
|
|
38
|
-
// TODO: add a prosemirror plugin
|
|
38
|
+
// TODO: add a prosemirror plugin that syncs the label on a
|
|
39
|
+
// change.
|
|
39
40
|
'data-label': label,
|
|
40
41
|
'data-type': id,
|
|
41
42
|
},
|
package/src/node/table/input.ts
CHANGED
|
@@ -59,10 +59,10 @@ withMeta(insertTableInputRule, {
|
|
|
59
59
|
group: 'Table',
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
/// A paste rule
|
|
63
|
-
///
|
|
64
|
-
///
|
|
65
|
-
///
|
|
62
|
+
/// A paste rule that fixes a table without header cells. An editor such
|
|
63
|
+
/// as Google Docs creates a table without header cells, which the
|
|
64
|
+
/// Markdown schema does not support. This rule promotes the first data
|
|
65
|
+
/// row to a header row, or adds empty header cells as a fallback.
|
|
66
66
|
export const tablePasteRule = $pasteRule((ctx) => ({
|
|
67
67
|
run: (slice, _view, isPlainText) => {
|
|
68
68
|
if (isPlainText) {
|
|
@@ -124,9 +124,9 @@ export const tablePasteRule = $pasteRule((ctx) => ({
|
|
|
124
124
|
return newTable
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
// Wrap consecutive orphaned table_row nodes
|
|
128
|
-
// into a
|
|
129
|
-
//
|
|
127
|
+
// Wrap the consecutive orphaned table_row nodes at the top level of
|
|
128
|
+
// a fragment into a table. `parseSlice` breaks a table apart this
|
|
129
|
+
// way, for example on a paste of several tables from Google Docs.
|
|
130
130
|
function wrapOrphanedRows(fragment: FragmentType): FragmentType {
|
|
131
131
|
const rowType = tableRowSchema.type(ctx)
|
|
132
132
|
const nodes: ProsemirrorNode[] = []
|
|
@@ -136,7 +136,8 @@ export const tablePasteRule = $pasteRule((ctx) => ({
|
|
|
136
136
|
function flushPendingRows() {
|
|
137
137
|
if (pendingRows.length === 0) return
|
|
138
138
|
|
|
139
|
-
//
|
|
139
|
+
// An empty table_header_row lets `fixTable` promote the first
|
|
140
|
+
// data row.
|
|
140
141
|
const emptyHeaderRow = tableHeaderRowSchema.type(ctx).createAndFill()!
|
|
141
142
|
const table = tableSchema
|
|
142
143
|
.type(ctx)
|
package/src/node/table/schema.ts
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
|
-
import type { NodeType } from '@milkdown/prose/model'
|
|
2
|
-
import type { MarkdownNode } from '@milkdown/transformer'
|
|
1
|
+
import type { Node, NodeType } from '@milkdown/prose/model'
|
|
2
|
+
import type { MarkdownNode, SerializerState } from '@milkdown/transformer'
|
|
3
3
|
|
|
4
4
|
import { tableNodes } from '@milkdown/prose/tables'
|
|
5
5
|
import { $nodeSchema } from '@milkdown/utils'
|
|
6
6
|
|
|
7
7
|
import { withMeta } from '../../__internal__'
|
|
8
8
|
|
|
9
|
+
/// A cell holds exactly one paragraph. See `cellContent` below. An mdast
|
|
10
|
+
/// tableCell holds phrasing content directly. Serializing the inline
|
|
11
|
+
/// content of the paragraph keeps the paragraph serializer and its
|
|
12
|
+
/// empty-line placeholder out of a table cell. The parse runners mirror
|
|
13
|
+
/// this and wrap the flat cell content in a paragraph.
|
|
14
|
+
///
|
|
15
|
+
/// Like `serializeText` in the paragraph runner, a trailing hardbreak is
|
|
16
|
+
/// dropped: mdast renders it as a trailing space inside a cell, which
|
|
17
|
+
/// the next parse trims, so keeping it makes consecutive saves diverge.
|
|
18
|
+
/// Cells that are not a single paragraph (schema-invalid but loadable
|
|
19
|
+
/// from JSON documents) are serialized as-is instead of losing children.
|
|
20
|
+
function serializeCellContent(state: SerializerState, node: Node) {
|
|
21
|
+
const paragraph = node.content.firstChild
|
|
22
|
+
if (node.childCount !== 1 || paragraph?.type.name !== 'paragraph') {
|
|
23
|
+
state.next(node.content)
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
const last = paragraph.lastChild
|
|
27
|
+
const content =
|
|
28
|
+
last?.type.name === 'hardbreak'
|
|
29
|
+
? paragraph.content.cut(0, paragraph.content.size - last.nodeSize)
|
|
30
|
+
: paragraph.content
|
|
31
|
+
state.next(content)
|
|
32
|
+
}
|
|
33
|
+
|
|
9
34
|
const originalSchema = tableNodes({
|
|
10
35
|
tableGroup: 'block',
|
|
11
36
|
cellContent: 'paragraph',
|
|
@@ -104,8 +129,8 @@ export const tableHeaderRowSchema = $nodeSchema('table_header_row', () => ({
|
|
|
104
129
|
toMarkdown: {
|
|
105
130
|
match: (node) => node.type.name === 'table_header_row',
|
|
106
131
|
runner: (state, node) => {
|
|
107
|
-
//
|
|
108
|
-
//
|
|
132
|
+
// An empty row needs no table row, and skipping it keeps remark
|
|
133
|
+
// from crashing.
|
|
109
134
|
if (node.content.size === 0) {
|
|
110
135
|
return
|
|
111
136
|
}
|
|
@@ -147,8 +172,8 @@ export const tableRowSchema = $nodeSchema('table_row', () => ({
|
|
|
147
172
|
toMarkdown: {
|
|
148
173
|
match: (node) => node.type.name === 'table_row',
|
|
149
174
|
runner: (state, node) => {
|
|
150
|
-
//
|
|
151
|
-
//
|
|
175
|
+
// An empty row needs no table row, and skipping it keeps remark
|
|
176
|
+
// from crashing.
|
|
152
177
|
if (node.content.size === 0) {
|
|
153
178
|
return
|
|
154
179
|
}
|
|
@@ -188,7 +213,9 @@ export const tableCellSchema = $nodeSchema('table_cell', () => ({
|
|
|
188
213
|
toMarkdown: {
|
|
189
214
|
match: (node) => node.type.name === 'table_cell',
|
|
190
215
|
runner: (state, node) => {
|
|
191
|
-
state.openNode('tableCell')
|
|
216
|
+
state.openNode('tableCell')
|
|
217
|
+
serializeCellContent(state, node)
|
|
218
|
+
state.closeNode()
|
|
192
219
|
},
|
|
193
220
|
},
|
|
194
221
|
}))
|
|
@@ -222,7 +249,7 @@ export const tableHeaderSchema = $nodeSchema('table_header', () => ({
|
|
|
222
249
|
match: (node) => node.type.name === 'table_header',
|
|
223
250
|
runner: (state, node) => {
|
|
224
251
|
state.openNode('tableCell')
|
|
225
|
-
state
|
|
252
|
+
serializeCellContent(state, node)
|
|
226
253
|
state.closeNode()
|
|
227
254
|
},
|
|
228
255
|
},
|
|
@@ -5,7 +5,8 @@ import { $inputRule } from '@milkdown/utils'
|
|
|
5
5
|
|
|
6
6
|
import { withMeta } from '../__internal__'
|
|
7
7
|
|
|
8
|
-
/// This schema extends the [list item](/preset-commonmark#list-item)
|
|
8
|
+
/// This schema extends the [list item](/preset-commonmark#list-item)
|
|
9
|
+
/// schema and adds task list support to it.
|
|
9
10
|
export const extendListItemSchemaForTask = listItemSchema.extendSchema(
|
|
10
11
|
(prev) => {
|
|
11
12
|
return (ctx) => {
|
|
@@ -109,8 +110,8 @@ withMeta(extendListItemSchemaForTask.ctx, {
|
|
|
109
110
|
group: 'ListItem',
|
|
110
111
|
})
|
|
111
112
|
|
|
112
|
-
///
|
|
113
|
-
///
|
|
113
|
+
/// An input rule that wraps a block in a task list node. Type `[ ] ` or
|
|
114
|
+
/// `[x] ` to wrap the block and set its checked status.
|
|
114
115
|
export const wrapInTaskListInputRule = $inputRule(() => {
|
|
115
116
|
return new InputRule(
|
|
116
117
|
/^\[(?<checked>\s|x)\]\s$/,
|
|
@@ -3,8 +3,9 @@ import { imeSpan } from 'prosemirror-safari-ime-span'
|
|
|
3
3
|
|
|
4
4
|
import { withMeta } from '../__internal__'
|
|
5
5
|
|
|
6
|
-
/// This plugin
|
|
7
|
-
///
|
|
6
|
+
/// This plugin fixes an IME composing bug in a table in the Safari browser. See
|
|
7
|
+
/// the
|
|
8
|
+
/// [original discussion](https://discuss.prosemirror.net/t/ime-composing-problems-on-td-or-th-element-in-safari-browser/4501).
|
|
8
9
|
export const autoInsertSpanPlugin = $prose(() => imeSpan)
|
|
9
10
|
|
|
10
11
|
withMeta(autoInsertSpanPlugin, {
|
|
@@ -3,7 +3,8 @@ import { $prose } from '@milkdown/utils'
|
|
|
3
3
|
|
|
4
4
|
import { withMeta } from '../__internal__'
|
|
5
5
|
|
|
6
|
-
/// This plugin
|
|
6
|
+
/// This plugin wraps the `columnResizing` plugin from
|
|
7
|
+
/// [prosemirror-tables](https://github.com/ProseMirror/prosemirror-tables).
|
|
7
8
|
export const columnResizingPlugin = $prose(() => columnResizing({}))
|
|
8
9
|
|
|
9
10
|
withMeta(columnResizingPlugin, {
|
|
@@ -47,7 +47,7 @@ export const keepTableAlignPlugin = $prose(() => {
|
|
|
47
47
|
const tableRow = $pos.node($pos.depth)
|
|
48
48
|
const table = $pos.node($pos.depth - 1)
|
|
49
49
|
const tableHeaderRow = table.firstChild
|
|
50
|
-
// TODO:
|
|
50
|
+
// TODO: add a header row when the table has none.
|
|
51
51
|
if (!tableHeaderRow) return
|
|
52
52
|
|
|
53
53
|
const index = getChildIndex(node, tableRow)
|
|
@@ -6,7 +6,8 @@ import remarkGFM from 'remark-gfm'
|
|
|
6
6
|
|
|
7
7
|
import { withMeta } from '../__internal__'
|
|
8
8
|
|
|
9
|
-
/// This plugin
|
|
9
|
+
/// This plugin wraps
|
|
10
|
+
/// [remark-gfm](https://github.com/remarkjs/remark-gfm).
|
|
10
11
|
export const remarkGFMPlugin: $Remark<'remarkGFM', Options | null | undefined> =
|
|
11
12
|
$remark('remarkGFM', () => remarkGFM)
|
|
12
13
|
|
|
@@ -3,7 +3,8 @@ import { $prose } from '@milkdown/utils'
|
|
|
3
3
|
|
|
4
4
|
import { withMeta } from '../__internal__'
|
|
5
5
|
|
|
6
|
-
/// This plugin
|
|
6
|
+
/// This plugin wraps the `tableEditing` plugin from
|
|
7
|
+
/// [prosemirror-tables](https://github.com/ProseMirror/prosemirror-tables).
|
|
7
8
|
export const tableEditingPlugin = $prose(() =>
|
|
8
9
|
tableEditing({ allowTableNodeSelection: true })
|
|
9
10
|
)
|