diff --git a/docs/package.json b/docs/package.json
index 5969a94eb9..d723b33489 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -11,6 +11,7 @@
"dependencies": {
"@blocknote/ariakit": "^0.26.0",
"@blocknote/core": "^0.26.0",
+ "@blocknote/code-block": "^0.26.0",
"@blocknote/mantine": "^0.26.0",
"@blocknote/react": "^0.26.0",
"@blocknote/shadcn": "^0.26.0",
diff --git a/docs/pages/docs/advanced/code-blocks.mdx b/docs/pages/docs/advanced/code-blocks.mdx
new file mode 100644
index 0000000000..582e91597e
--- /dev/null
+++ b/docs/pages/docs/advanced/code-blocks.mdx
@@ -0,0 +1,88 @@
+---
+title: Code Block Syntax Highlighting
+description: This section explains how to add syntax highlighting to code blocks.
+imageTitle: Code Block Syntax Highlighting
+---
+
+import { Example } from "@/components/example";
+
+# Code Block Syntax Highlighting
+
+To enable code block syntax highlighting, you can use the `codeBlock` option in the `useCreateBlockNote` hook. This is excluded by default to reduce bundle size.
+
+We've created a default setup which automatically includes some of the most common languages in the most optimized way possible. The language syntaxes are loaded on-demand to ensure the smallest bundle size for your users.
+
+To use it, you can do the following:
+
+```sh
+npm install @blocknote/code-block
+```
+
+And then you can use it like this:
+
+```tsx
+import { codeBlock } from "@blocknote/code-block";
+
+export default function App() {
+ // Creates a new editor instance.
+ const editor = useCreateBlockNote({
+ codeBlock,
+ });
+
+ // Renders the editor instance using a React component.
+ return
Paragraph
Numbered List Item
Bullet List Item
Check List Item
console.log("Hello World");
Table Cell | Table Cell | Table Cell |
Table Cell | Table Cell | Table Cell |
Table Cell | Table Cell | Table Cell |
Add image
\ No newline at end of file +Paragraph
Numbered List Item
Bullet List Item
Check List Item
console.log("Hello World");
Table Cell | Table Cell | Table Cell |
Table Cell | Table Cell | Table Cell |
Table Cell | Table Cell | Table Cell |
Add image
\ No newline at end of file diff --git a/packages/core/src/api/clipboard/__snapshots__/internal/basicBlocksWithProps.html b/packages/core/src/api/clipboard/__snapshots__/internal/basicBlocksWithProps.html index 4397413824..eeaec68e38 100644 --- a/packages/core/src/api/clipboard/__snapshots__/internal/basicBlocksWithProps.html +++ b/packages/core/src/api/clipboard/__snapshots__/internal/basicBlocksWithProps.html @@ -1 +1 @@ -Paragraph
Numbered List Item
Bullet List Item
Check List Item
console.log("Hello World");
Table Cell | Table Cell | Table Cell |
Table Cell | Table Cell | Table Cell |
Table Cell | Table Cell | Table Cell |
Paragraph
Numbered List Item
Bullet List Item
Check List Item
console.log("Hello World");
Table Cell | Table Cell | Table Cell |
Table Cell | Table Cell | Table Cell |
Table Cell | Table Cell | Table Cell |
const hello = 'world';
console.log(hello);
\ No newline at end of file
+const hello = 'world';
console.log(hello);
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/internal.html b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/internal.html
index 48fd641e09..d8a93830eb 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/internal.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/internal.html
@@ -1,3 +1,3 @@
-const hello = 'world';
+const hello = 'world';
console.log(hello);
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/external.html b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/external.html
index 411e8e3b89..8db77070b5 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/external.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/external.html
@@ -1 +1 @@
-console.log('Hello, world!');
\ No newline at end of file
+console.log('Hello, world!');
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/internal.html b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/internal.html
index 43aae52f35..ef3a373f9a 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/internal.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/internal.html
@@ -1 +1 @@
-console.log('Hello, world!');
\ No newline at end of file
+console.log('Hello, world!');
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/empty/external.html b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/empty/external.html
index 1cffb258fb..22baaf1757 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/empty/external.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/empty/external.html
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/empty/internal.html b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/empty/internal.html
index cd1f144f1b..abe6b97dc8 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/empty/internal.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/empty/internal.html
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/python/external.html b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/python/external.html
index e7ee13c56b..4872e5904f 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/python/external.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/python/external.html
@@ -1 +1 @@
-print('Hello, world!')
\ No newline at end of file
+print('Hello, world!')
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/python/internal.html b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/python/internal.html
index 22e04a4e7e..fee165d05a 100644
--- a/packages/core/src/api/exporters/html/__snapshots__/codeBlock/python/internal.html
+++ b/packages/core/src/api/exporters/html/__snapshots__/codeBlock/python/internal.html
@@ -1 +1 @@
-print('Hello, world!')
\ No newline at end of file
+print('Hello, world!')
\ No newline at end of file
diff --git a/packages/core/src/api/exporters/markdown/__snapshots__/codeBlock/defaultLanguage/markdown.md b/packages/core/src/api/exporters/markdown/__snapshots__/codeBlock/defaultLanguage/markdown.md
index eca2b94e33..f5b118ae95 100644
--- a/packages/core/src/api/exporters/markdown/__snapshots__/codeBlock/defaultLanguage/markdown.md
+++ b/packages/core/src/api/exporters/markdown/__snapshots__/codeBlock/defaultLanguage/markdown.md
@@ -1,3 +1,3 @@
-```javascript
+```text
console.log('Hello, world!');
```
diff --git a/packages/core/src/api/exporters/markdown/__snapshots__/codeBlock/empty/markdown.md b/packages/core/src/api/exporters/markdown/__snapshots__/codeBlock/empty/markdown.md
index 04144d877f..b5c9416ec5 100644
--- a/packages/core/src/api/exporters/markdown/__snapshots__/codeBlock/empty/markdown.md
+++ b/packages/core/src/api/exporters/markdown/__snapshots__/codeBlock/empty/markdown.md
@@ -1,2 +1,2 @@
-```javascript
+```text
```
diff --git a/packages/core/src/api/exporters/markdown/markdownExporter.test.ts b/packages/core/src/api/exporters/markdown/markdownExporter.test.ts
index 2eb7f67851..1b9e93c414 100644
--- a/packages/core/src/api/exporters/markdown/markdownExporter.test.ts
+++ b/packages/core/src/api/exporters/markdown/markdownExporter.test.ts
@@ -71,7 +71,7 @@ describe("markdownExporter", () => {
for (const document of testCase.documents) {
// eslint-disable-next-line no-loop-func
- it("Convert " + document.name + " to HTML", async () => {
+ it("Convert " + document.name + " to Markdown", async () => {
const nameSplit = document.name.split("/");
await convertToMarkdownAndCompareSnapshots(
editor,
diff --git a/packages/core/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap b/packages/core/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap
index 245ab3ba9c..cfc33073f3 100644
--- a/packages/core/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap
+++ b/packages/core/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap
@@ -699,7 +699,7 @@ exports[`Test BlockNote-Prosemirror conversion > Case: default schema > Convert
"content": [
{
"attrs": {
- "language": "javascript",
+ "language": "text",
},
"content": [
{
@@ -724,7 +724,7 @@ exports[`Test BlockNote-Prosemirror conversion > Case: default schema > Convert
"content": [
{
"attrs": {
- "language": "javascript",
+ "language": "text",
},
"type": "codeBlock",
},
diff --git a/packages/core/src/api/parsers/html/__snapshots__/parse-codeblocks.json b/packages/core/src/api/parsers/html/__snapshots__/parse-codeblocks.json
index 1481967f47..6387ab020d 100644
--- a/packages/core/src/api/parsers/html/__snapshots__/parse-codeblocks.json
+++ b/packages/core/src/api/parsers/html/__snapshots__/parse-codeblocks.json
@@ -3,7 +3,7 @@
"id": "1",
"type": "codeBlock",
"props": {
- "language": "javascript"
+ "language": "text"
},
"content": [
{
diff --git a/packages/core/src/api/testUtil/cases/defaultSchema.ts b/packages/core/src/api/testUtil/cases/defaultSchema.ts
index 2dbfaffb5c..2b1b47e5ec 100644
--- a/packages/core/src/api/testUtil/cases/defaultSchema.ts
+++ b/packages/core/src/api/testUtil/cases/defaultSchema.ts
@@ -23,6 +23,18 @@ export const defaultSchemaTestCases: EditorTestCases<
return BlockNoteEditor.create({
schema: withPageBreak(BlockNoteSchema.create()),
uploadFile: uploadToTmpFilesDotOrg_DEV_ONLY,
+ codeBlock: {
+ supportedLanguages: {
+ javascript: {
+ name: "JavaScript",
+ aliases: ["js"],
+ },
+ python: {
+ name: "Python",
+ aliases: ["py"],
+ },
+ },
+ },
});
},
documents: [
@@ -213,6 +225,7 @@ export const defaultSchemaTestCases: EditorTestCases<
blocks: [
{
type: "codeBlock",
+ props: { language: "javascript" },
content: "const hello = 'world';\nconsole.log(hello);\n",
},
],
diff --git a/packages/core/src/blocks/CodeBlockContent/CodeBlockContent.ts b/packages/core/src/blocks/CodeBlockContent/CodeBlockContent.ts
index 2143d9f686..094a0b65e3 100644
--- a/packages/core/src/blocks/CodeBlockContent/CodeBlockContent.ts
+++ b/packages/core/src/blocks/CodeBlockContent/CodeBlockContent.ts
@@ -2,28 +2,65 @@ import { InputRule, isTextSelection } from "@tiptap/core";
import { TextSelection } from "@tiptap/pm/state";
import { createHighlightPlugin, Parser } from "prosemirror-highlight";
import { createParser } from "prosemirror-highlight/shiki";
-import {
- BundledLanguage,
- bundledLanguagesInfo,
- createHighlighter,
- Highlighter,
-} from "shiki";
import {
createBlockSpecFromStronglyTypedTiptapNode,
createStronglyTypedTiptapNode,
PropSchema,
} from "../../schema/index.js";
import { createDefaultBlockDOMOutputSpec } from "../defaultBlockHelpers.js";
-import {
- defaultSupportedLanguages,
- SupportedLanguageConfig,
-} from "./defaultSupportedLanguages.js";
-
-interface CodeBlockOptions {
- defaultLanguage: string;
- indentLineWithTab: boolean;
- supportedLanguages: SupportedLanguageConfig[];
-}
+import type { HighlighterGeneric } from "@shikijs/types";
+import { BlockNoteEditor } from "../../index.js";
+
+export type CodeBlockOptions = {
+ /**
+ * Whether to indent lines with a tab when the user presses `Tab` in a code block.
+ *
+ * @default true
+ */
+ indentLineWithTab?: boolean;
+ /**
+ * The default language to use for code blocks.
+ *
+ * @default "text"
+ */
+ defaultLanguage?: string;
+ /**
+ * The languages that are supported in the editor.
+ *
+ * @example
+ * {
+ * javascript: {
+ * name: "JavaScript",
+ * aliases: ["js"],
+ * },
+ * typescript: {
+ * name: "TypeScript",
+ * aliases: ["ts"],
+ * },
+ * }
+ */
+ supportedLanguages: Record<
+ string,
+ {
+ /**
+ * The display name of the language.
+ */
+ name: string;
+ /**
+ * Aliases for this language.
+ */
+ aliases?: string[];
+ }
+ >;
+ /**
+ * The highlighter to use for code blocks.
+ */
+ createHighlighter?: () => Promise>;
+};
+
+type CodeBlockConfigOptions = {
+ editor: BlockNoteEditor;
+};
export const shikiParserSymbol = Symbol.for("blocknote.shikiParser");
export const shikiHighlighterPromiseSymbol = Symbol.for(
@@ -31,8 +68,7 @@ export const shikiHighlighterPromiseSymbol = Symbol.for(
);
export const defaultCodeBlockPropSchema = {
language: {
- default: "javascript",
- values: [...defaultSupportedLanguages.map((lang) => lang.id)],
+ default: "text",
},
} satisfies PropSchema;
@@ -45,18 +81,17 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
defining: true,
addOptions() {
return {
- defaultLanguage: "javascript",
+ defaultLanguage: "text",
indentLineWithTab: true,
- supportedLanguages: defaultSupportedLanguages,
+ supportedLanguages: {},
};
},
addAttributes() {
- const supportedLanguages = this.options
- .supportedLanguages as SupportedLanguageConfig[];
+ const options = this.options as CodeBlockConfigOptions;
return {
language: {
- default: this.options.defaultLanguage,
+ default: options.editor.settings.codeBlock.defaultLanguage,
parseHTML: (inputElement) => {
let element = inputElement as HTMLElement | null;
let language: string | null = null;
@@ -91,17 +126,13 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
return null;
}
- return (
- supportedLanguages.find(({ match }) => {
- return match.includes(language);
- })?.id || this.options.defaultLanguage
- );
+ return getLanguageId(options.editor.settings.codeBlock, language);
},
renderHTML: (attributes) => {
- // TODO: Use `data-language="..."` instead for easier parsing
- return attributes.language && attributes.language !== "text"
+ return attributes.language
? {
class: `language-${attributes.language}`,
+ "data-language": attributes.language,
}
: {};
},
@@ -143,8 +174,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
};
},
addNodeView() {
- const supportedLanguages = this.options
- .supportedLanguages as SupportedLanguageConfig[];
+ const options = this.options as CodeBlockConfigOptions;
return ({ editor, node, getPos, HTMLAttributes }) => {
const pre = document.createElement("pre");
@@ -169,7 +199,9 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
});
};
- supportedLanguages.forEach(({ id, name }) => {
+ Object.entries(
+ options.editor.settings.codeBlock.supportedLanguages
+ ).forEach(([id, { name }]) => {
const option = document.createElement("option");
option.value = id;
@@ -178,7 +210,9 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
});
selectWrapper.contentEditable = "false";
- select.value = node.attrs.language || this.options.defaultLanguage;
+ select.value =
+ node.attrs.language ||
+ options.editor.settings.codeBlock.defaultLanguage;
dom.removeChild(contentDOM);
dom.appendChild(selectWrapper);
dom.appendChild(pre);
@@ -203,24 +237,30 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
};
},
addProseMirrorPlugins() {
- const supportedLanguages = this.options
- .supportedLanguages as SupportedLanguageConfig[];
+ const options = this.options as CodeBlockConfigOptions;
const globalThisForShiki = globalThis as {
- [shikiHighlighterPromiseSymbol]?: Promise;
+ [shikiHighlighterPromiseSymbol]?: Promise>;
[shikiParserSymbol]?: Parser;
};
- let highlighter: Highlighter | undefined;
+ let highlighter: HighlighterGeneric | undefined;
let parser: Parser | undefined;
-
- const lazyParser: Parser = (options) => {
+ let hasWarned = false;
+ const lazyParser: Parser = (parserOptions) => {
+ if (!options.editor.settings.codeBlock.createHighlighter) {
+ if (process.env.NODE_ENV === "development" && !hasWarned) {
+ // eslint-disable-next-line no-console
+ console.log(
+ "For syntax highlighting of code blocks, you must provide a highlighter function"
+ );
+ hasWarned = true;
+ }
+ return [];
+ }
if (!highlighter) {
globalThisForShiki[shikiHighlighterPromiseSymbol] =
globalThisForShiki[shikiHighlighterPromiseSymbol] ||
- createHighlighter({
- themes: ["github-dark"],
- langs: [],
- });
+ options.editor.settings.codeBlock.createHighlighter();
return globalThisForShiki[shikiHighlighterPromiseSymbol].then(
(createdHighlighter) => {
@@ -229,25 +269,25 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
);
}
- const language = options.language;
+ const language = parserOptions.language;
if (
language &&
language !== "text" &&
!highlighter.getLoadedLanguages().includes(language) &&
- supportedLanguages.find(({ id }) => id === language) &&
- bundledLanguagesInfo.find(({ id }) => id === language)
+ language in options.editor.settings.codeBlock.supportedLanguages
) {
- return highlighter.loadLanguage(language as BundledLanguage);
+ return highlighter.loadLanguage(language);
}
if (!parser) {
parser =
- globalThisForShiki[shikiParserSymbol] || createParser(highlighter);
+ globalThisForShiki[shikiParserSymbol] ||
+ createParser(highlighter as any);
globalThisForShiki[shikiParserSymbol] = parser;
}
- return parser(options);
+ return parser(parserOptions);
};
const shikiLazyPlugin = createHighlightPlugin({
@@ -259,8 +299,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
return [shikiLazyPlugin];
},
addInputRules() {
- const supportedLanguages = this.options
- .supportedLanguages as SupportedLanguageConfig[];
+ const options = this.options as CodeBlockConfigOptions;
return [
new InputRule({
@@ -269,10 +308,10 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
const $start = state.doc.resolve(range.from);
const languageName = match[1].trim();
const attributes = {
- language:
- supportedLanguages.find(({ match }) => {
- return match.includes(languageName);
- })?.id || this.options.defaultLanguage,
+ language: getLanguageId(
+ options.editor.settings.codeBlock,
+ languageName
+ ),
};
if (
@@ -383,18 +422,10 @@ export const CodeBlock = createBlockSpecFromStronglyTypedTiptapNode(
defaultCodeBlockPropSchema
);
-export function customizeCodeBlock(options: Partial) {
- return createBlockSpecFromStronglyTypedTiptapNode(
- CodeBlockContent.configure(options),
- {
- language: {
- default:
- options.defaultLanguage ||
- defaultCodeBlockPropSchema.language.default,
- values:
- options.supportedLanguages?.map((lang) => lang.id) ||
- defaultCodeBlockPropSchema.language.values,
- },
- }
+function getLanguageId(options: CodeBlockOptions, languageName: string) {
+ return (
+ Object.entries(options.supportedLanguages).find(([id, { aliases }]) => {
+ return aliases?.includes(languageName) || id === languageName;
+ })?.[0] || languageName
);
}
diff --git a/packages/core/src/blocks/CodeBlockContent/defaultSupportedLanguages.ts b/packages/core/src/blocks/CodeBlockContent/defaultSupportedLanguages.ts
deleted file mode 100644
index b5da23b0cd..0000000000
--- a/packages/core/src/blocks/CodeBlockContent/defaultSupportedLanguages.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-import { bundledLanguagesInfo } from "shiki";
-
-export type SupportedLanguageConfig = {
- id: string;
- name: string;
- match: string[];
-};
-
-export const defaultSupportedLanguages: SupportedLanguageConfig[] = [
- {
- id: "text",
- name: "Plain Text",
- match: ["text", "txt", "plain"],
- },
- ...bundledLanguagesInfo
- .filter((lang) => {
- return [
- "c",
- "cpp",
- "css",
- "glsl",
- "graphql",
- "haml",
- "html",
- "java",
- "javascript",
- "json",
- "jsonc",
- "jsonl",
- "jsx",
- "julia",
- "less",
- "markdown",
- "mdx",
- "php",
- "postcss",
- "pug",
- "python",
- "r",
- "regexp",
- "sass",
- "scss",
- "shellscript",
- "sql",
- "svelte",
- "typescript",
- "vue",
- "vue-html",
- "wasm",
- "wgsl",
- "xml",
- "yaml",
- ].includes(lang.id);
- })
- .map((lang) => ({
- match: [lang.id, ...(lang.aliases || [])],
- id: lang.id,
- name: lang.name,
- })),
- { id: "tsx", name: "TSX", match: ["tsx", "typescriptreact"] },
- {
- id: "haskell",
- name: "Haskell",
- match: ["haskell", "hs"],
- },
- {
- id: "csharp",
- name: "C#",
- match: ["c#", "csharp", "cs"],
- },
- {
- id: "latex",
- name: "LaTeX",
- match: ["latex"],
- },
- {
- id: "lua",
- name: "Lua",
- match: ["lua"],
- },
- {
- id: "mermaid",
- name: "Mermaid",
- match: ["mermaid", "mmd"],
- },
- {
- id: "ruby",
- name: "Ruby",
- match: ["ruby", "rb"],
- },
- {
- id: "rust",
- name: "Rust",
- match: ["rust", "rs"],
- },
- {
- id: "scala",
- name: "Scala",
- match: ["scala"],
- },
- {
- id: "swift",
- name: "Swift",
- match: ["swift"],
- },
- {
- id: "kotlin",
- name: "Kotlin",
- match: ["kotlin", "kt", "kts"],
- },
- {
- id: "objective-c",
- name: "Objective C",
- match: ["objective-c", "objc"],
- },
-];
diff --git a/packages/core/src/blocks/defaultBlocks.ts b/packages/core/src/blocks/defaultBlocks.ts
index 81dc0e49ab..e57e59f489 100644
--- a/packages/core/src/blocks/defaultBlocks.ts
+++ b/packages/core/src/blocks/defaultBlocks.ts
@@ -32,8 +32,6 @@ import { Paragraph } from "./ParagraphBlockContent/ParagraphBlockContent.js";
import { Table } from "./TableBlockContent/TableBlockContent.js";
import { VideoBlock } from "./VideoBlockContent/VideoBlockContent.js";
-export { customizeCodeBlock } from "./CodeBlockContent/CodeBlockContent.js";
-
export const defaultBlockSpecs = {
paragraph: Paragraph,
heading: Heading,
diff --git a/packages/core/src/editor/BlockNoteEditor.ts b/packages/core/src/editor/BlockNoteEditor.ts
index a0fd0a1d53..72582272e0 100644
--- a/packages/core/src/editor/BlockNoteEditor.ts
+++ b/packages/core/src/editor/BlockNoteEditor.ts
@@ -101,6 +101,7 @@ import { nodeToBlock } from "../api/nodeConversions/nodeToBlock.js";
import type { ThreadStore, User } from "../comments/index.js";
import "../style.css";
import { EventEmitter } from "../util/EventEmitter.js";
+import { CodeBlockOptions } from "../blocks/CodeBlockContent/CodeBlockContent.js";
export type BlockNoteExtensionFactory = (
editor: BlockNoteEditor
@@ -156,6 +157,11 @@ export type BlockNoteEditorOptions<
showCursorLabels?: "always" | "activity";
};
+ /**
+ * Options for code blocks.
+ */
+ codeBlock?: CodeBlockOptions;
+
comments: {
threadStore: ThreadStore;
};
@@ -442,6 +448,7 @@ export class BlockNoteEditor<
cellTextColor: boolean;
headers: boolean;
};
+ codeBlock: CodeBlockOptions;
};
public static create<
@@ -489,6 +496,12 @@ export class BlockNoteEditor<
cellTextColor: options?.tables?.cellTextColor ?? false,
headers: options?.tables?.headers ?? false,
},
+ codeBlock: {
+ indentLineWithTab: options?.codeBlock?.indentLineWithTab ?? true,
+ defaultLanguage: options?.codeBlock?.defaultLanguage ?? "text",
+ supportedLanguages: options?.codeBlock?.supportedLanguages ?? {},
+ createHighlighter: options?.codeBlock?.createHighlighter ?? undefined,
+ },
};
// apply defaults
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 3ec64b0dc6..7439917132 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -56,6 +56,8 @@ export * from "./util/esmDependencies.js";
export * from "./util/table.js";
export * from "./util/string.js";
export * from "./util/typescript.js";
+
+export type { CodeBlockOptions } from "./blocks/CodeBlockContent/CodeBlockContent.js";
export { UnreachableCaseError, assertEmpty } from "./util/typescript.js";
export { locales };
@@ -69,4 +71,3 @@ export * from "./extensions/UniqueID/UniqueID.js";
export * from "./api/exporters/markdown/markdownExporter.js";
export * from "./api/parsers/html/parseHTML.js";
export * from "./api/parsers/markdown/parseMarkdown.js";
-
diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json
index a894e90f9e..bbfc728065 100644
--- a/packages/core/tsconfig.json
+++ b/packages/core/tsconfig.json
@@ -4,7 +4,7 @@
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext", "DOM"],
- "moduleResolution": "Node",
+ "moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"sourceMap": true,
diff --git a/packages/core/vite.config.ts b/packages/core/vite.config.ts
index c5f9b60463..57d6d3a2f0 100644
--- a/packages/core/vite.config.ts
+++ b/packages/core/vite.config.ts
@@ -32,7 +32,11 @@ export default defineConfig({
if (deps.includes(source)) {
return true;
}
- return source.startsWith("prosemirror-") || source.startsWith("shiki/");
+ return (
+ source.startsWith("prosemirror-") ||
+ source.startsWith("@shikijs/lang") ||
+ source.startsWith("@shikijs/theme")
+ );
},
output: {
// Provide global variables to use in the UMD build
diff --git a/playground/package.json b/playground/package.json
index 5e8552dc8f..670d3a1592 100644
--- a/playground/package.json
+++ b/playground/package.json
@@ -13,6 +13,7 @@
"@aws-sdk/client-s3": "^3.609.0",
"@aws-sdk/s3-request-presigner": "^3.609.0",
"@blocknote/ariakit": "^0.26.0",
+ "@blocknote/code-block": "^0.26.0",
"@blocknote/core": "^0.26.0",
"@blocknote/mantine": "^0.26.0",
"@blocknote/react": "^0.26.0",
diff --git a/playground/src/examples.gen.tsx b/playground/src/examples.gen.tsx
index 122f6c5c9a..5d1fc918c2 100644
--- a/playground/src/examples.gen.tsx
+++ b/playground/src/examples.gen.tsx
@@ -798,6 +798,42 @@
"pathFromRoot": "examples/04-theming",
"slug": "theming"
}
+ },
+ {
+ "projectSlug": "code-block",
+ "fullSlug": "theming/code-block",
+ "pathFromRoot": "examples/04-theming/06-code-block",
+ "config": {
+ "playground": true,
+ "docs": true,
+ "author": "nperez0111",
+ "tags": [
+ "Basic"
+ ]
+ },
+ "title": "Code Block Syntax Highlighting",
+ "group": {
+ "pathFromRoot": "examples/04-theming",
+ "slug": "theming"
+ }
+ },
+ {
+ "projectSlug": "custom-code-block",
+ "fullSlug": "theming/custom-code-block",
+ "pathFromRoot": "examples/04-theming/07-custom-code-block",
+ "config": {
+ "playground": true,
+ "docs": true,
+ "author": "nperez0111",
+ "tags": [
+ "Basic"
+ ]
+ },
+ "title": "Custom Code Block Theme & Language",
+ "group": {
+ "pathFromRoot": "examples/04-theming",
+ "slug": "theming"
+ }
}
]
},