@blocknote/core 0.26.0 → 0.27.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/dist/blocknote.cjs +11 -10
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +3685 -9960
- package/dist/blocknote.js.map +1 -1
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.js.map +1 -1
- package/dist/en-B7ycW7c8.js +360 -0
- package/dist/en-B7ycW7c8.js.map +1 -0
- package/dist/en-D4taoCs4.cjs +2 -0
- package/dist/en-D4taoCs4.cjs.map +1 -0
- package/dist/locales.cjs +2 -0
- package/dist/locales.cjs.map +1 -0
- package/dist/locales.js +6129 -0
- package/dist/locales.js.map +1 -0
- package/dist/style.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +36 -27
- package/src/api/clipboard/__snapshots__/internal/basicBlocks.html +1 -1
- package/src/api/clipboard/__snapshots__/internal/basicBlocksWithProps.html +1 -1
- package/src/api/clipboard/clipboardExternal.test.ts +1 -1
- package/src/api/clipboard/clipboardInternal.test.ts +1 -1
- package/src/api/clipboard/fromClipboard/acceptedMIMETypes.ts +1 -0
- package/src/api/clipboard/fromClipboard/pasteExtension.ts +96 -42
- package/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/empty/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/empty/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/python/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/python/internal.html +1 -1
- package/src/api/exporters/html/htmlConversion.test.ts +2 -2
- package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +4 -4
- package/src/api/exporters/markdown/__snapshots__/codeBlock/defaultLanguage/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/codeBlock/empty/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/complex/misc/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/lists/basic/markdown.md +8 -6
- package/src/api/exporters/markdown/__snapshots__/lists/nested/markdown.md +6 -6
- package/src/api/exporters/markdown/markdownExporter.test.ts +2 -2
- package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +2 -2
- package/src/api/parsers/html/__snapshots__/parse-codeblocks.json +1 -1
- package/src/api/parsers/html/parseHTML.test.ts +1 -1
- package/src/api/parsers/markdown/__snapshots__/pasted/whitespace bold.json +42 -0
- package/src/api/parsers/markdown/__snapshots__/whitespace bold.json +19 -0
- package/src/api/parsers/markdown/detectMarkdown.ts +60 -0
- package/src/api/parsers/markdown/parseMarkdown.test.ts +7 -2
- package/src/api/parsers/markdown/parseMarkdown.ts +19 -18
- package/src/api/testUtil/cases/defaultSchema.ts +13 -0
- package/src/blocks/CodeBlockContent/CodeBlockContent.ts +100 -69
- package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +98 -0
- package/src/blocks/TableBlockContent/TableExtension.ts +1 -1
- package/src/blocks/defaultBlocks.ts +2 -2
- package/src/editor/Block.css +13 -0
- package/src/editor/BlockNoteEditor.ts +102 -10
- package/src/editor/BlockNoteExtensions.ts +18 -4
- package/src/extensions/Comments/CommentsPlugin.ts +1 -1
- package/src/extensions/HardBreak/HardBreak.ts +35 -0
- package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +100 -3
- package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +3 -1
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +12 -0
- package/src/i18n/index.ts +2 -0
- package/src/i18n/locales/ar.ts +6 -0
- package/src/i18n/locales/de.ts +6 -0
- package/src/i18n/locales/en.ts +6 -0
- package/src/i18n/locales/es.ts +6 -0
- package/src/i18n/locales/fr.ts +6 -0
- package/src/i18n/locales/hr.ts +6 -0
- package/src/i18n/locales/is.ts +6 -0
- package/src/i18n/locales/it.ts +6 -0
- package/src/i18n/locales/ja.ts +6 -0
- package/src/i18n/locales/ko.ts +6 -0
- package/src/i18n/locales/nl.ts +6 -0
- package/src/i18n/locales/no.ts +6 -0
- package/src/i18n/locales/pl.ts +6 -0
- package/src/i18n/locales/pt.ts +6 -0
- package/src/i18n/locales/ru.ts +6 -0
- package/src/i18n/locales/uk.ts +6 -0
- package/src/i18n/locales/vi.ts +6 -0
- package/src/i18n/locales/zh.ts +6 -0
- package/src/index.ts +2 -3
- package/src/locales.ts +1 -0
- package/src/schema/blocks/types.ts +1 -0
- package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.d.ts +1 -1
- package/types/src/api/blockManipulation/setupTestEnv.d.ts +34 -2
- package/types/src/api/clipboard/fromClipboard/acceptedMIMETypes.d.ts +1 -1
- package/types/src/api/clipboard/fromClipboard/fileDropExtension.d.ts +2 -2
- package/types/src/api/clipboard/fromClipboard/pasteExtension.d.ts +3 -3
- package/types/src/api/clipboard/testUtil.d.ts +66 -34
- package/types/src/api/clipboard/toClipboard/copyExtension.d.ts +1 -1
- package/types/src/api/exporters/html/externalHTMLExporter.d.ts +2 -2
- package/types/src/api/exporters/html/internalHTMLSerializer.d.ts +2 -2
- package/types/src/api/exporters/html/util/serializeBlocksExternalHTML.d.ts +1 -1
- package/types/src/api/exporters/html/util/serializeBlocksInternalHTML.d.ts +1 -1
- package/types/src/api/parsers/markdown/detectMarkdown.d.ts +6 -0
- package/types/src/api/parsers/markdown/parseMarkdown.d.ts +1 -0
- package/types/src/api/testUtil/cases/customBlocks.d.ts +72 -40
- package/types/src/api/testUtil/cases/customInlineContent.d.ts +34 -2
- package/types/src/api/testUtil/cases/customStyles.d.ts +34 -2
- package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +3 -3
- package/types/src/blocks/CodeBlockContent/CodeBlockContent.d.ts +46 -34
- package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +3 -3
- package/types/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.d.ts +1 -1
- package/types/src/blocks/HeadingBlockContent/HeadingBlockContent.d.ts +2 -2
- package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +2 -2
- package/types/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +2 -2
- package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +2 -2
- package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +2 -2
- package/types/src/blocks/PageBreakBlockContent/PageBreakBlockContent.d.ts +2 -2
- package/types/src/blocks/PageBreakBlockContent/schema.d.ts +13 -13
- package/types/src/blocks/ParagraphBlockContent/ParagraphBlockContent.d.ts +2 -2
- package/types/src/blocks/QuoteBlockContent/QuoteBlockContent.d.ts +52 -0
- package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +2 -2
- package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +2 -2
- package/types/src/blocks/defaultBlockHelpers.d.ts +2 -2
- package/types/src/blocks/defaultBlocks.d.ts +107 -44
- package/types/src/comments/threadstore/yjs/YjsThreadStore.d.ts +3 -3
- package/types/src/editor/BlockNoteEditor.d.ts +58 -0
- package/types/src/editor/BlockNoteExtensions.d.ts +3 -2
- package/types/src/exporter/mapping.d.ts +2 -2
- package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +1 -1
- package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +1 -1
- package/types/src/extensions/HardBreak/HardBreak.d.ts +2 -0
- package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +4 -4
- package/types/src/extensions/TextColor/TextColorMark.d.ts +1 -1
- package/types/src/i18n/index.d.ts +2 -0
- package/types/src/i18n/locales/en.d.ts +7 -1
- package/types/src/index.d.ts +1 -2
- package/types/src/locales.d.ts +1 -0
- package/types/src/pm-nodes/BlockContainer.d.ts +2 -7
- package/types/src/pm-nodes/BlockGroup.d.ts +2 -7
- package/types/src/schema/blocks/types.d.ts +1 -0
- package/types/src/schema/inlineContent/internal.d.ts +1 -1
- package/README.md +0 -125
- package/src/blocks/CodeBlockContent/defaultSupportedLanguages.ts +0 -116
- package/types/src/extensions/Comments/threadstore/DefaultThreadStoreAuth.d.ts +0 -47
- package/types/src/extensions/Comments/threadstore/ThreadStore.d.ts +0 -121
- package/types/src/extensions/Comments/threadstore/ThreadStoreAuth.d.ts +0 -12
- package/types/src/extensions/Comments/threadstore/TipTapThreadStore.d.ts +0 -97
- package/types/src/extensions/Comments/threadstore/yjs/RESTYjsThreadStore.d.ts +0 -83
- package/types/src/extensions/Comments/threadstore/yjs/YjsThreadStore.d.ts +0 -79
- package/types/src/extensions/Comments/threadstore/yjs/YjsThreadStore.test.d.ts +0 -1
- package/types/src/extensions/Comments/threadstore/yjs/YjsThreadStoreBase.d.ts +0 -15
- package/types/src/extensions/Comments/threadstore/yjs/yjsHelpers.d.ts +0 -13
- package/types/src/extensions/Comments/types.d.ts +0 -109
- package/types/src/models/User.d.ts +0 -5
|
@@ -699,7 +699,7 @@ exports[`Test BlockNote-Prosemirror conversion > Case: default schema > Convert
|
|
|
699
699
|
"content": [
|
|
700
700
|
{
|
|
701
701
|
"attrs": {
|
|
702
|
-
"language": "
|
|
702
|
+
"language": "text",
|
|
703
703
|
},
|
|
704
704
|
"content": [
|
|
705
705
|
{
|
|
@@ -724,7 +724,7 @@ exports[`Test BlockNote-Prosemirror conversion > Case: default schema > Convert
|
|
|
724
724
|
"content": [
|
|
725
725
|
{
|
|
726
726
|
"attrs": {
|
|
727
|
-
"language": "
|
|
727
|
+
"language": "text",
|
|
728
728
|
},
|
|
729
729
|
"type": "codeBlock",
|
|
730
730
|
},
|
|
@@ -13,7 +13,7 @@ async function parseHTMLAndCompareSnapshots(
|
|
|
13
13
|
const blocks = await editor.tryParseHTMLToBlocks(html);
|
|
14
14
|
|
|
15
15
|
const snapshotPath = "./__snapshots__/" + snapshotName + ".json";
|
|
16
|
-
expect(JSON.stringify(blocks, undefined, 2)).toMatchFileSnapshot(
|
|
16
|
+
await expect(JSON.stringify(blocks, undefined, 2)).toMatchFileSnapshot(
|
|
17
17
|
snapshotPath
|
|
18
18
|
);
|
|
19
19
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "0",
|
|
4
|
+
"type": "paragraph",
|
|
5
|
+
"props": {
|
|
6
|
+
"textColor": "default",
|
|
7
|
+
"backgroundColor": "default",
|
|
8
|
+
"textAlignment": "left"
|
|
9
|
+
},
|
|
10
|
+
"content": [
|
|
11
|
+
{
|
|
12
|
+
"type": "text",
|
|
13
|
+
"text": "hello ",
|
|
14
|
+
"styles": {}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"type": "text",
|
|
18
|
+
"text": "beautiful ",
|
|
19
|
+
"styles": {
|
|
20
|
+
"bold": true
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "text",
|
|
25
|
+
"text": " world",
|
|
26
|
+
"styles": {}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"children": []
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "2",
|
|
33
|
+
"type": "paragraph",
|
|
34
|
+
"props": {
|
|
35
|
+
"textColor": "default",
|
|
36
|
+
"backgroundColor": "default",
|
|
37
|
+
"textAlignment": "left"
|
|
38
|
+
},
|
|
39
|
+
"content": [],
|
|
40
|
+
"children": []
|
|
41
|
+
}
|
|
42
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "1",
|
|
4
|
+
"type": "paragraph",
|
|
5
|
+
"props": {
|
|
6
|
+
"textColor": "default",
|
|
7
|
+
"backgroundColor": "default",
|
|
8
|
+
"textAlignment": "left"
|
|
9
|
+
},
|
|
10
|
+
"content": [
|
|
11
|
+
{
|
|
12
|
+
"type": "text",
|
|
13
|
+
"text": "hello **beautiful ** world",
|
|
14
|
+
"styles": {}
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"children": []
|
|
18
|
+
}
|
|
19
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Headings H1-H6.
|
|
2
|
+
const h1 = /(^|\n) {0,3}#{1,6} {1,8}[^\n]{1,64}\r?\n\r?\n\s{0,32}\S/;
|
|
3
|
+
|
|
4
|
+
// Bold, italic, underline, strikethrough, highlight.
|
|
5
|
+
const bold = /(?:\s|^)(_|__|\*|\*\*|~~|==|\+\+)(?!\s).{1,64}(?<!\s)(?=\1)/;
|
|
6
|
+
|
|
7
|
+
// Basic inline link (also captures images).
|
|
8
|
+
const link = /\[[^\]]{1,128}\]\(https?:\/\/\S{1,999}\)/;
|
|
9
|
+
|
|
10
|
+
// Inline code.
|
|
11
|
+
const code = /(?:\s|^)`(?!\s)[^`]{1,48}(?<!\s)`([^\w]|$)/;
|
|
12
|
+
|
|
13
|
+
// Unordered list.
|
|
14
|
+
const ul = /(?:^|\n)\s{0,5}-\s{1}[^\n]+\n\s{0,15}-\s/;
|
|
15
|
+
|
|
16
|
+
// Ordered list.
|
|
17
|
+
const ol = /(?:^|\n)\s{0,5}\d+\.\s{1}[^\n]+\n\s{0,15}\d+\.\s/;
|
|
18
|
+
|
|
19
|
+
// Horizontal rule.
|
|
20
|
+
const hr = /\n{2} {0,3}-{2,48}\n{2}/;
|
|
21
|
+
|
|
22
|
+
// Fenced code block.
|
|
23
|
+
const fences =
|
|
24
|
+
/(?:\n|^)(```|~~~|\$\$)(?!`|~)[^\s]{0,64} {0,64}[^\n]{0,64}\n[\s\S]{0,9999}?\s*\1 {0,64}(?:\n+|$)/;
|
|
25
|
+
|
|
26
|
+
// Classical underlined H1 and H2 headings.
|
|
27
|
+
const title = /(?:\n|^)(?!\s)\w[^\n]{0,64}\r?\n(-|=)\1{0,64}\n\n\s{0,64}(\w|$)/;
|
|
28
|
+
|
|
29
|
+
// Blockquote.
|
|
30
|
+
const blockquote =
|
|
31
|
+
/(?:^|(\r?\n\r?\n))( {0,3}>[^\n]{1,333}\n){1,999}($|(\r?\n))/;
|
|
32
|
+
|
|
33
|
+
// Table Header
|
|
34
|
+
const tableHeader = /^\s*\|(.+\|)+\s*$/m;
|
|
35
|
+
|
|
36
|
+
// Table Divider
|
|
37
|
+
const tableDivider = /^\s*\|(\s*[-:]+[-:]\s*\|)+\s*$/m;
|
|
38
|
+
|
|
39
|
+
// Table Row
|
|
40
|
+
const tableRow = /^\s*\|(.+\|)+\s*$/m;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Returns `true` if the source text might be a markdown document.
|
|
44
|
+
*
|
|
45
|
+
* @param src Source text to analyze.
|
|
46
|
+
*/
|
|
47
|
+
export const isMarkdown = (src: string): boolean =>
|
|
48
|
+
h1.test(src) ||
|
|
49
|
+
bold.test(src) ||
|
|
50
|
+
link.test(src) ||
|
|
51
|
+
code.test(src) ||
|
|
52
|
+
ul.test(src) ||
|
|
53
|
+
ol.test(src) ||
|
|
54
|
+
hr.test(src) ||
|
|
55
|
+
fences.test(src) ||
|
|
56
|
+
title.test(src) ||
|
|
57
|
+
blockquote.test(src) ||
|
|
58
|
+
tableHeader.test(src) ||
|
|
59
|
+
tableDivider.test(src) ||
|
|
60
|
+
tableRow.test(src);
|
|
@@ -12,7 +12,7 @@ async function parseMarkdownAndCompareSnapshots(
|
|
|
12
12
|
const blocks = await editor.tryParseMarkdownToBlocks(md);
|
|
13
13
|
|
|
14
14
|
const snapshotPath = "./__snapshots__/" + snapshotName + ".json";
|
|
15
|
-
expect(JSON.stringify(blocks, undefined, 2)).toMatchFileSnapshot(
|
|
15
|
+
await expect(JSON.stringify(blocks, undefined, 2)).toMatchFileSnapshot(
|
|
16
16
|
snapshotPath
|
|
17
17
|
);
|
|
18
18
|
|
|
@@ -23,7 +23,7 @@ async function parseMarkdownAndCompareSnapshots(
|
|
|
23
23
|
doPaste(editor.prosemirrorView, md, null, true, new ClipboardEvent("paste"));
|
|
24
24
|
|
|
25
25
|
const pastedSnapshotPath = "./__snapshots__/pasted/" + snapshotName + ".json";
|
|
26
|
-
expect(JSON.stringify(editor.document, undefined, 2)).toMatchFileSnapshot(
|
|
26
|
+
await expect(JSON.stringify(editor.document, undefined, 2)).toMatchFileSnapshot(
|
|
27
27
|
pastedSnapshotPath
|
|
28
28
|
);
|
|
29
29
|
|
|
@@ -99,6 +99,11 @@ P*ara*~~grap~~h
|
|
|
99
99
|
* Bullet List Item`;
|
|
100
100
|
await parseMarkdownAndCompareSnapshots(markdown, "complex");
|
|
101
101
|
});
|
|
102
|
+
|
|
103
|
+
it("whitespace bold", async () => {
|
|
104
|
+
const markdown = `hello **beautiful ** world`;
|
|
105
|
+
await parseMarkdownAndCompareSnapshots(markdown, "whitespace bold");
|
|
106
|
+
});
|
|
102
107
|
});
|
|
103
108
|
|
|
104
109
|
describe("Issue 226", () => {
|
|
@@ -48,18 +48,9 @@ function code(state: any, node: any) {
|
|
|
48
48
|
return result;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export async function
|
|
52
|
-
BSchema extends BlockSchema,
|
|
53
|
-
I extends InlineContentSchema,
|
|
54
|
-
S extends StyleSchema
|
|
55
|
-
>(
|
|
56
|
-
markdown: string,
|
|
57
|
-
blockSchema: BSchema,
|
|
58
|
-
icSchema: I,
|
|
59
|
-
styleSchema: S,
|
|
60
|
-
pmSchema: Schema
|
|
61
|
-
): Promise<Block<BSchema, I, S>[]> {
|
|
51
|
+
export async function markdownToHTML(markdown: string): Promise<string> {
|
|
62
52
|
const deps = await initializeESMDependencies();
|
|
53
|
+
|
|
63
54
|
const htmlString = deps.unified
|
|
64
55
|
.unified()
|
|
65
56
|
.use(deps.remarkParse.default)
|
|
@@ -73,11 +64,21 @@ export async function markdownToBlocks<
|
|
|
73
64
|
.use(deps.rehypeStringify.default)
|
|
74
65
|
.processSync(markdown);
|
|
75
66
|
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
67
|
+
return htmlString.value as string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function markdownToBlocks<
|
|
71
|
+
BSchema extends BlockSchema,
|
|
72
|
+
I extends InlineContentSchema,
|
|
73
|
+
S extends StyleSchema
|
|
74
|
+
>(
|
|
75
|
+
markdown: string,
|
|
76
|
+
blockSchema: BSchema,
|
|
77
|
+
icSchema: I,
|
|
78
|
+
styleSchema: S,
|
|
79
|
+
pmSchema: Schema
|
|
80
|
+
): Promise<Block<BSchema, I, S>[]> {
|
|
81
|
+
const htmlString = await markdownToHTML(markdown);
|
|
82
|
+
|
|
83
|
+
return HTMLToBlocks(htmlString, blockSchema, icSchema, styleSchema, pmSchema);
|
|
83
84
|
}
|
|
@@ -23,6 +23,18 @@ export const defaultSchemaTestCases: EditorTestCases<
|
|
|
23
23
|
return BlockNoteEditor.create({
|
|
24
24
|
schema: withPageBreak(BlockNoteSchema.create()),
|
|
25
25
|
uploadFile: uploadToTmpFilesDotOrg_DEV_ONLY,
|
|
26
|
+
codeBlock: {
|
|
27
|
+
supportedLanguages: {
|
|
28
|
+
javascript: {
|
|
29
|
+
name: "JavaScript",
|
|
30
|
+
aliases: ["js"],
|
|
31
|
+
},
|
|
32
|
+
python: {
|
|
33
|
+
name: "Python",
|
|
34
|
+
aliases: ["py"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
26
38
|
});
|
|
27
39
|
},
|
|
28
40
|
documents: [
|
|
@@ -213,6 +225,7 @@ export const defaultSchemaTestCases: EditorTestCases<
|
|
|
213
225
|
blocks: [
|
|
214
226
|
{
|
|
215
227
|
type: "codeBlock",
|
|
228
|
+
props: { language: "javascript" },
|
|
216
229
|
content: "const hello = 'world';\nconsole.log(hello);\n",
|
|
217
230
|
},
|
|
218
231
|
],
|
|
@@ -2,28 +2,65 @@ import { InputRule, isTextSelection } from "@tiptap/core";
|
|
|
2
2
|
import { TextSelection } from "@tiptap/pm/state";
|
|
3
3
|
import { createHighlightPlugin, Parser } from "prosemirror-highlight";
|
|
4
4
|
import { createParser } from "prosemirror-highlight/shiki";
|
|
5
|
-
import {
|
|
6
|
-
BundledLanguage,
|
|
7
|
-
bundledLanguagesInfo,
|
|
8
|
-
createHighlighter,
|
|
9
|
-
Highlighter,
|
|
10
|
-
} from "shiki";
|
|
11
5
|
import {
|
|
12
6
|
createBlockSpecFromStronglyTypedTiptapNode,
|
|
13
7
|
createStronglyTypedTiptapNode,
|
|
14
8
|
PropSchema,
|
|
15
9
|
} from "../../schema/index.js";
|
|
16
10
|
import { createDefaultBlockDOMOutputSpec } from "../defaultBlockHelpers.js";
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
import type { HighlighterGeneric } from "@shikijs/types";
|
|
12
|
+
import { BlockNoteEditor } from "../../index.js";
|
|
13
|
+
|
|
14
|
+
export type CodeBlockOptions = {
|
|
15
|
+
/**
|
|
16
|
+
* Whether to indent lines with a tab when the user presses `Tab` in a code block.
|
|
17
|
+
*
|
|
18
|
+
* @default true
|
|
19
|
+
*/
|
|
20
|
+
indentLineWithTab?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The default language to use for code blocks.
|
|
23
|
+
*
|
|
24
|
+
* @default "text"
|
|
25
|
+
*/
|
|
26
|
+
defaultLanguage?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The languages that are supported in the editor.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* {
|
|
32
|
+
* javascript: {
|
|
33
|
+
* name: "JavaScript",
|
|
34
|
+
* aliases: ["js"],
|
|
35
|
+
* },
|
|
36
|
+
* typescript: {
|
|
37
|
+
* name: "TypeScript",
|
|
38
|
+
* aliases: ["ts"],
|
|
39
|
+
* },
|
|
40
|
+
* }
|
|
41
|
+
*/
|
|
42
|
+
supportedLanguages: Record<
|
|
43
|
+
string,
|
|
44
|
+
{
|
|
45
|
+
/**
|
|
46
|
+
* The display name of the language.
|
|
47
|
+
*/
|
|
48
|
+
name: string;
|
|
49
|
+
/**
|
|
50
|
+
* Aliases for this language.
|
|
51
|
+
*/
|
|
52
|
+
aliases?: string[];
|
|
53
|
+
}
|
|
54
|
+
>;
|
|
55
|
+
/**
|
|
56
|
+
* The highlighter to use for code blocks.
|
|
57
|
+
*/
|
|
58
|
+
createHighlighter?: () => Promise<HighlighterGeneric<any, any>>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
type CodeBlockConfigOptions = {
|
|
62
|
+
editor: BlockNoteEditor<any, any, any>;
|
|
63
|
+
};
|
|
27
64
|
|
|
28
65
|
export const shikiParserSymbol = Symbol.for("blocknote.shikiParser");
|
|
29
66
|
export const shikiHighlighterPromiseSymbol = Symbol.for(
|
|
@@ -31,8 +68,7 @@ export const shikiHighlighterPromiseSymbol = Symbol.for(
|
|
|
31
68
|
);
|
|
32
69
|
export const defaultCodeBlockPropSchema = {
|
|
33
70
|
language: {
|
|
34
|
-
default: "
|
|
35
|
-
values: [...defaultSupportedLanguages.map((lang) => lang.id)],
|
|
71
|
+
default: "text",
|
|
36
72
|
},
|
|
37
73
|
} satisfies PropSchema;
|
|
38
74
|
|
|
@@ -45,18 +81,17 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
45
81
|
defining: true,
|
|
46
82
|
addOptions() {
|
|
47
83
|
return {
|
|
48
|
-
defaultLanguage: "
|
|
84
|
+
defaultLanguage: "text",
|
|
49
85
|
indentLineWithTab: true,
|
|
50
|
-
supportedLanguages:
|
|
86
|
+
supportedLanguages: {},
|
|
51
87
|
};
|
|
52
88
|
},
|
|
53
89
|
addAttributes() {
|
|
54
|
-
const
|
|
55
|
-
.supportedLanguages as SupportedLanguageConfig[];
|
|
90
|
+
const options = this.options as CodeBlockConfigOptions;
|
|
56
91
|
|
|
57
92
|
return {
|
|
58
93
|
language: {
|
|
59
|
-
default:
|
|
94
|
+
default: options.editor.settings.codeBlock.defaultLanguage,
|
|
60
95
|
parseHTML: (inputElement) => {
|
|
61
96
|
let element = inputElement as HTMLElement | null;
|
|
62
97
|
let language: string | null = null;
|
|
@@ -91,17 +126,13 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
91
126
|
return null;
|
|
92
127
|
}
|
|
93
128
|
|
|
94
|
-
return (
|
|
95
|
-
supportedLanguages.find(({ match }) => {
|
|
96
|
-
return match.includes(language);
|
|
97
|
-
})?.id || this.options.defaultLanguage
|
|
98
|
-
);
|
|
129
|
+
return getLanguageId(options.editor.settings.codeBlock, language);
|
|
99
130
|
},
|
|
100
131
|
renderHTML: (attributes) => {
|
|
101
|
-
|
|
102
|
-
return attributes.language && attributes.language !== "text"
|
|
132
|
+
return attributes.language
|
|
103
133
|
? {
|
|
104
134
|
class: `language-${attributes.language}`,
|
|
135
|
+
"data-language": attributes.language,
|
|
105
136
|
}
|
|
106
137
|
: {};
|
|
107
138
|
},
|
|
@@ -143,8 +174,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
143
174
|
};
|
|
144
175
|
},
|
|
145
176
|
addNodeView() {
|
|
146
|
-
const
|
|
147
|
-
.supportedLanguages as SupportedLanguageConfig[];
|
|
177
|
+
const options = this.options as CodeBlockConfigOptions;
|
|
148
178
|
|
|
149
179
|
return ({ editor, node, getPos, HTMLAttributes }) => {
|
|
150
180
|
const pre = document.createElement("pre");
|
|
@@ -169,7 +199,9 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
169
199
|
});
|
|
170
200
|
};
|
|
171
201
|
|
|
172
|
-
|
|
202
|
+
Object.entries(
|
|
203
|
+
options.editor.settings.codeBlock.supportedLanguages
|
|
204
|
+
).forEach(([id, { name }]) => {
|
|
173
205
|
const option = document.createElement("option");
|
|
174
206
|
|
|
175
207
|
option.value = id;
|
|
@@ -178,7 +210,9 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
178
210
|
});
|
|
179
211
|
|
|
180
212
|
selectWrapper.contentEditable = "false";
|
|
181
|
-
select.value =
|
|
213
|
+
select.value =
|
|
214
|
+
node.attrs.language ||
|
|
215
|
+
options.editor.settings.codeBlock.defaultLanguage;
|
|
182
216
|
dom.removeChild(contentDOM);
|
|
183
217
|
dom.appendChild(selectWrapper);
|
|
184
218
|
dom.appendChild(pre);
|
|
@@ -203,24 +237,30 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
203
237
|
};
|
|
204
238
|
},
|
|
205
239
|
addProseMirrorPlugins() {
|
|
206
|
-
const
|
|
207
|
-
.supportedLanguages as SupportedLanguageConfig[];
|
|
240
|
+
const options = this.options as CodeBlockConfigOptions;
|
|
208
241
|
const globalThisForShiki = globalThis as {
|
|
209
|
-
[shikiHighlighterPromiseSymbol]?: Promise<
|
|
242
|
+
[shikiHighlighterPromiseSymbol]?: Promise<HighlighterGeneric<any, any>>;
|
|
210
243
|
[shikiParserSymbol]?: Parser;
|
|
211
244
|
};
|
|
212
245
|
|
|
213
|
-
let highlighter:
|
|
246
|
+
let highlighter: HighlighterGeneric<any, any> | undefined;
|
|
214
247
|
let parser: Parser | undefined;
|
|
215
|
-
|
|
216
|
-
const lazyParser: Parser = (
|
|
248
|
+
let hasWarned = false;
|
|
249
|
+
const lazyParser: Parser = (parserOptions) => {
|
|
250
|
+
if (!options.editor.settings.codeBlock.createHighlighter) {
|
|
251
|
+
if (process.env.NODE_ENV === "development" && !hasWarned) {
|
|
252
|
+
// eslint-disable-next-line no-console
|
|
253
|
+
console.log(
|
|
254
|
+
"For syntax highlighting of code blocks, you must provide a highlighter function"
|
|
255
|
+
);
|
|
256
|
+
hasWarned = true;
|
|
257
|
+
}
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
217
260
|
if (!highlighter) {
|
|
218
261
|
globalThisForShiki[shikiHighlighterPromiseSymbol] =
|
|
219
262
|
globalThisForShiki[shikiHighlighterPromiseSymbol] ||
|
|
220
|
-
createHighlighter(
|
|
221
|
-
themes: ["github-dark"],
|
|
222
|
-
langs: [],
|
|
223
|
-
});
|
|
263
|
+
options.editor.settings.codeBlock.createHighlighter();
|
|
224
264
|
|
|
225
265
|
return globalThisForShiki[shikiHighlighterPromiseSymbol].then(
|
|
226
266
|
(createdHighlighter) => {
|
|
@@ -229,25 +269,25 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
229
269
|
);
|
|
230
270
|
}
|
|
231
271
|
|
|
232
|
-
const language =
|
|
272
|
+
const language = parserOptions.language;
|
|
233
273
|
|
|
234
274
|
if (
|
|
235
275
|
language &&
|
|
236
276
|
language !== "text" &&
|
|
237
277
|
!highlighter.getLoadedLanguages().includes(language) &&
|
|
238
|
-
|
|
239
|
-
bundledLanguagesInfo.find(({ id }) => id === language)
|
|
278
|
+
language in options.editor.settings.codeBlock.supportedLanguages
|
|
240
279
|
) {
|
|
241
|
-
return highlighter.loadLanguage(language
|
|
280
|
+
return highlighter.loadLanguage(language);
|
|
242
281
|
}
|
|
243
282
|
|
|
244
283
|
if (!parser) {
|
|
245
284
|
parser =
|
|
246
|
-
globalThisForShiki[shikiParserSymbol] ||
|
|
285
|
+
globalThisForShiki[shikiParserSymbol] ||
|
|
286
|
+
createParser(highlighter as any);
|
|
247
287
|
globalThisForShiki[shikiParserSymbol] = parser;
|
|
248
288
|
}
|
|
249
289
|
|
|
250
|
-
return parser(
|
|
290
|
+
return parser(parserOptions);
|
|
251
291
|
};
|
|
252
292
|
|
|
253
293
|
const shikiLazyPlugin = createHighlightPlugin({
|
|
@@ -259,8 +299,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
259
299
|
return [shikiLazyPlugin];
|
|
260
300
|
},
|
|
261
301
|
addInputRules() {
|
|
262
|
-
const
|
|
263
|
-
.supportedLanguages as SupportedLanguageConfig[];
|
|
302
|
+
const options = this.options as CodeBlockConfigOptions;
|
|
264
303
|
|
|
265
304
|
return [
|
|
266
305
|
new InputRule({
|
|
@@ -269,10 +308,10 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
269
308
|
const $start = state.doc.resolve(range.from);
|
|
270
309
|
const languageName = match[1].trim();
|
|
271
310
|
const attributes = {
|
|
272
|
-
language:
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
311
|
+
language: getLanguageId(
|
|
312
|
+
options.editor.settings.codeBlock,
|
|
313
|
+
languageName
|
|
314
|
+
),
|
|
276
315
|
};
|
|
277
316
|
|
|
278
317
|
if (
|
|
@@ -383,18 +422,10 @@ export const CodeBlock = createBlockSpecFromStronglyTypedTiptapNode(
|
|
|
383
422
|
defaultCodeBlockPropSchema
|
|
384
423
|
);
|
|
385
424
|
|
|
386
|
-
|
|
387
|
-
return
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
default:
|
|
392
|
-
options.defaultLanguage ||
|
|
393
|
-
defaultCodeBlockPropSchema.language.default,
|
|
394
|
-
values:
|
|
395
|
-
options.supportedLanguages?.map((lang) => lang.id) ||
|
|
396
|
-
defaultCodeBlockPropSchema.language.values,
|
|
397
|
-
},
|
|
398
|
-
}
|
|
425
|
+
function getLanguageId(options: CodeBlockOptions, languageName: string) {
|
|
426
|
+
return (
|
|
427
|
+
Object.entries(options.supportedLanguages).find(([id, { aliases }]) => {
|
|
428
|
+
return aliases?.includes(languageName) || id === languageName;
|
|
429
|
+
})?.[0] || languageName
|
|
399
430
|
);
|
|
400
431
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createBlockSpecFromStronglyTypedTiptapNode,
|
|
3
|
+
createStronglyTypedTiptapNode,
|
|
4
|
+
} from "../../schema/index.js";
|
|
5
|
+
import { createDefaultBlockDOMOutputSpec } from "../defaultBlockHelpers.js";
|
|
6
|
+
import { defaultProps } from "../defaultProps.js";
|
|
7
|
+
import { getBlockInfoFromSelection } from "../../api/getBlockInfoFromPos.js";
|
|
8
|
+
import { updateBlockCommand } from "../../api/blockManipulation/commands/updateBlock/updateBlock.js";
|
|
9
|
+
import { InputRule } from "@tiptap/core";
|
|
10
|
+
|
|
11
|
+
export const quotePropSchema = {
|
|
12
|
+
...defaultProps,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const QuoteBlockContent = createStronglyTypedTiptapNode({
|
|
16
|
+
name: "quote",
|
|
17
|
+
content: "inline*",
|
|
18
|
+
group: "blockContent",
|
|
19
|
+
|
|
20
|
+
addInputRules() {
|
|
21
|
+
return [
|
|
22
|
+
// Creates a block quote when starting with ">".
|
|
23
|
+
new InputRule({
|
|
24
|
+
find: new RegExp(`^>\\s$`),
|
|
25
|
+
handler: ({ state, chain, range }) => {
|
|
26
|
+
const blockInfo = getBlockInfoFromSelection(state);
|
|
27
|
+
if (
|
|
28
|
+
!blockInfo.isBlockContainer ||
|
|
29
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
30
|
+
) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
chain()
|
|
35
|
+
.command(
|
|
36
|
+
updateBlockCommand(
|
|
37
|
+
this.options.editor,
|
|
38
|
+
blockInfo.bnBlock.beforePos,
|
|
39
|
+
{
|
|
40
|
+
type: "quote",
|
|
41
|
+
props: {},
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
// Removes the ">" character used to set the list.
|
|
46
|
+
.deleteRange({ from: range.from, to: range.to });
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
];
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
addKeyboardShortcuts() {
|
|
53
|
+
return {
|
|
54
|
+
"Mod-Alt-q": () => {
|
|
55
|
+
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
56
|
+
if (
|
|
57
|
+
!blockInfo.isBlockContainer ||
|
|
58
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
59
|
+
) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return this.editor.commands.command(
|
|
64
|
+
updateBlockCommand(this.options.editor, blockInfo.bnBlock.beforePos, {
|
|
65
|
+
type: "quote",
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
parseHTML() {
|
|
73
|
+
return [
|
|
74
|
+
{ tag: "div[data-content-type=" + this.name + "]" },
|
|
75
|
+
{
|
|
76
|
+
tag: "blockquote",
|
|
77
|
+
node: "quote",
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
renderHTML({ HTMLAttributes }) {
|
|
83
|
+
return createDefaultBlockDOMOutputSpec(
|
|
84
|
+
this.name,
|
|
85
|
+
"blockquote",
|
|
86
|
+
{
|
|
87
|
+
...(this.options.domAttributes?.blockContent || {}),
|
|
88
|
+
...HTMLAttributes,
|
|
89
|
+
},
|
|
90
|
+
this.options.domAttributes?.inlineContent || {}
|
|
91
|
+
);
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
export const Quote = createBlockSpecFromStronglyTypedTiptapNode(
|
|
96
|
+
QuoteBlockContent,
|
|
97
|
+
quotePropSchema
|
|
98
|
+
);
|
|
@@ -29,14 +29,14 @@ import { BulletListItem } from "./ListItemBlockContent/BulletListItemBlockConten
|
|
|
29
29
|
import { CheckListItem } from "./ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.js";
|
|
30
30
|
import { NumberedListItem } from "./ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.js";
|
|
31
31
|
import { Paragraph } from "./ParagraphBlockContent/ParagraphBlockContent.js";
|
|
32
|
+
import { Quote } from "./QuoteBlockContent/QuoteBlockContent.js";
|
|
32
33
|
import { Table } from "./TableBlockContent/TableBlockContent.js";
|
|
33
34
|
import { VideoBlock } from "./VideoBlockContent/VideoBlockContent.js";
|
|
34
35
|
|
|
35
|
-
export { customizeCodeBlock } from "./CodeBlockContent/CodeBlockContent.js";
|
|
36
|
-
|
|
37
36
|
export const defaultBlockSpecs = {
|
|
38
37
|
paragraph: Paragraph,
|
|
39
38
|
heading: Heading,
|
|
39
|
+
quote: Quote,
|
|
40
40
|
codeBlock: CodeBlock,
|
|
41
41
|
bulletListItem: BulletListItem,
|
|
42
42
|
numberedListItem: NumberedListItem,
|