@tiptap/extension-code-block 2.3.2 → 2.5.0-beta.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/index.cjs +20 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -15
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +20 -15
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/extension-code-block/src/code-block.d.ts +19 -3
- package/package.json +3 -3
- package/src/code-block.ts +32 -19
package/dist/index.cjs
CHANGED
|
@@ -5,8 +5,18 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var core = require('@tiptap/core');
|
|
6
6
|
var state = require('@tiptap/pm/state');
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Matches a code block with backticks.
|
|
10
|
+
*/
|
|
8
11
|
const backtickInputRegex = /^```([a-z]+)?[\s\n]$/;
|
|
12
|
+
/**
|
|
13
|
+
* Matches a code block with tildes.
|
|
14
|
+
*/
|
|
9
15
|
const tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/;
|
|
16
|
+
/**
|
|
17
|
+
* This extension allows you to create code blocks.
|
|
18
|
+
* @see https://tiptap.dev/api/nodes/code-block
|
|
19
|
+
*/
|
|
10
20
|
const CodeBlock = core.Node.create({
|
|
11
21
|
name: 'codeBlock',
|
|
12
22
|
addOptions() {
|
|
@@ -183,23 +193,18 @@ const CodeBlock = core.Node.create({
|
|
|
183
193
|
if (!text || !language) {
|
|
184
194
|
return false;
|
|
185
195
|
}
|
|
186
|
-
const { tr } = view.state;
|
|
187
|
-
//
|
|
188
|
-
// if the cursor is at the absolute end of the document, insert the code block before the cursor instead
|
|
189
|
-
// of replacing the selection as the replaceSelectionWith function will cause the insertion to
|
|
190
|
-
// happen at the previous node
|
|
191
|
-
if (view.state.selection.from === view.state.doc.nodeSize - (1 + (view.state.selection.$to.depth * 2))) {
|
|
192
|
-
tr.insert(view.state.selection.from - 1, this.type.create({ language }));
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
tr.replaceSelectionWith(this.type.create({ language }));
|
|
196
|
-
}
|
|
197
|
-
// put cursor inside the newly created code block
|
|
198
|
-
tr.setSelection(state.TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))));
|
|
199
|
-
// add text to code block
|
|
196
|
+
const { tr, schema } = view.state;
|
|
197
|
+
// prepare a text node
|
|
200
198
|
// strip carriage return chars from text pasted as code
|
|
201
199
|
// see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd
|
|
202
|
-
|
|
200
|
+
const textNode = schema.text(text.replace(/\r\n?/g, '\n'));
|
|
201
|
+
// create a code block with the text node
|
|
202
|
+
// replace selection with the code block
|
|
203
|
+
tr.replaceSelectionWith(this.type.create({ language }, textNode));
|
|
204
|
+
if (tr.selection.$from.parent.type !== this.type) {
|
|
205
|
+
// put cursor inside the newly created code block
|
|
206
|
+
tr.setSelection(state.TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))));
|
|
207
|
+
}
|
|
203
208
|
// store meta information
|
|
204
209
|
// this is useful for other plugins that depends on the paste event
|
|
205
210
|
// like the paste rule plugin
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/code-block.ts"],"sourcesContent":["import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'\n\nexport interface CodeBlockOptions {\n /**\n * Adds a prefix to language classes that are applied to code tags.\n * Defaults to `'language-'`.\n */\n languageClassPrefix: string\n /**\n * Define whether the node should be exited on triple enter.\n * Defaults to `true`.\n */\n exitOnTripleEnter: boolean\n /**\n * Define whether the node should be exited on arrow down if there is no node after it.\n * Defaults to `true`.\n */\n exitOnArrowDown: boolean\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: Record<string, any>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n codeBlock: {\n /**\n * Set a code block\n */\n setCodeBlock: (attributes?: { language: string }) => ReturnType\n /**\n * Toggle a code block\n */\n toggleCodeBlock: (attributes?: { language: string }) => ReturnType\n }\n }\n}\n\nexport const backtickInputRegex = /^```([a-z]+)?[\\s\\n]$/\nexport const tildeInputRegex = /^~~~([a-z]+)?[\\s\\n]$/\n\nexport const CodeBlock = Node.create<CodeBlockOptions>({\n name: 'codeBlock',\n\n addOptions() {\n return {\n languageClassPrefix: 'language-',\n exitOnTripleEnter: true,\n exitOnArrowDown: true,\n HTMLAttributes: {},\n }\n },\n\n content: 'text*',\n\n marks: '',\n\n group: 'block',\n\n code: true,\n\n defining: true,\n\n addAttributes() {\n return {\n language: {\n default: null,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n const classNames = [...(element.firstElementChild?.classList || [])]\n const languages = classNames\n .filter(className => className.startsWith(languageClassPrefix))\n .map(className => className.replace(languageClassPrefix, ''))\n const language = languages[0]\n\n if (!language) {\n return null\n }\n\n return language\n },\n rendered: false,\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'pre',\n preserveWhitespace: 'full',\n },\n ]\n },\n\n renderHTML({ node, HTMLAttributes }) {\n return [\n 'pre',\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),\n [\n 'code',\n {\n class: node.attrs.language\n ? this.options.languageClassPrefix + node.attrs.language\n : null,\n },\n 0,\n ],\n ]\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes => ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes => ({ commands }) => {\n return commands.toggleNode(this.name, 'paragraph', attributes)\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-Alt-c': () => this.editor.commands.toggleCodeBlock(),\n\n // remove code block when at start of document or code block is empty\n Backspace: () => {\n const { empty, $anchor } = this.editor.state.selection\n const isAtStart = $anchor.pos === 1\n\n if (!empty || $anchor.parent.type.name !== this.name) {\n return false\n }\n\n if (isAtStart || !$anchor.parent.textContent.length) {\n return this.editor.commands.clearNodes()\n }\n\n return false\n },\n\n // exit node on triple enter\n Enter: ({ editor }) => {\n if (!this.options.exitOnTripleEnter) {\n return false\n }\n\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n const endsWithDoubleNewline = $from.parent.textContent.endsWith('\\n\\n')\n\n if (!isAtEnd || !endsWithDoubleNewline) {\n return false\n }\n\n return editor\n .chain()\n .command(({ tr }) => {\n tr.delete($from.pos - 2, $from.pos)\n\n return true\n })\n .exitCode()\n .run()\n },\n\n // exit node on arrow down\n ArrowDown: ({ editor }) => {\n if (!this.options.exitOnArrowDown) {\n return false\n }\n\n const { state } = editor\n const { selection, doc } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n\n if (!isAtEnd) {\n return false\n }\n\n const after = $from.after()\n\n if (after === undefined) {\n return false\n }\n\n const nodeAfter = doc.nodeAt(after)\n\n if (nodeAfter) {\n return false\n }\n\n return editor.commands.exitCode()\n },\n }\n },\n\n addInputRules() {\n return [\n textblockTypeInputRule({\n find: backtickInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n textblockTypeInputRule({\n find: tildeInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n ]\n },\n\n addProseMirrorPlugins() {\n return [\n // this plugin creates a code block for pasted content from VS Code\n // we can also detect the copied code language\n new Plugin({\n key: new PluginKey('codeBlockVSCodeHandler'),\n props: {\n handlePaste: (view, event) => {\n if (!event.clipboardData) {\n return false\n }\n\n // don’t create a new code block within code blocks\n if (this.editor.isActive(this.type.name)) {\n return false\n }\n\n const text = event.clipboardData.getData('text/plain')\n const vscode = event.clipboardData.getData('vscode-editor-data')\n const vscodeData = vscode ? JSON.parse(vscode) : undefined\n const language = vscodeData?.mode\n\n if (!text || !language) {\n return false\n }\n\n const { tr } = view.state\n\n // create an empty code block´\n // if the cursor is at the absolute end of the document, insert the code block before the cursor instead\n // of replacing the selection as the replaceSelectionWith function will cause the insertion to\n // happen at the previous node\n if (view.state.selection.from === view.state.doc.nodeSize - (1 + (view.state.selection.$to.depth * 2))) {\n tr.insert(view.state.selection.from - 1, this.type.create({ language }))\n } else {\n tr.replaceSelectionWith(this.type.create({ language }))\n }\n\n // put cursor inside the newly created code block\n tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))))\n\n // add text to code block\n // strip carriage return chars from text pasted as code\n // see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd\n tr.insertText(text.replace(/\\r\\n?/g, '\\n'))\n\n // store meta information\n // this is useful for other plugins that depends on the paste event\n // like the paste rule plugin\n tr.setMeta('paste', true)\n\n view.dispatch(tr)\n\n return true\n },\n },\n }),\n ]\n },\n})\n"],"names":["Node","mergeAttributes","textblockTypeInputRule","Plugin","PluginKey","TextSelection"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/code-block.ts"],"sourcesContent":["import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'\n\nexport interface CodeBlockOptions {\n /**\n * Adds a prefix to language classes that are applied to code tags.\n * @default 'language-'\n */\n languageClassPrefix: string\n /**\n * Define whether the node should be exited on triple enter.\n * @default true\n */\n exitOnTripleEnter: boolean\n /**\n * Define whether the node should be exited on arrow down if there is no node after it.\n * @default true\n */\n exitOnArrowDown: boolean\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n codeBlock: {\n /**\n * Set a code block\n * @param attributes Code block attributes\n * @example editor.commands.setCodeBlock({ language: 'javascript' })\n */\n setCodeBlock: (attributes?: { language: string }) => ReturnType\n /**\n * Toggle a code block\n * @param attributes Code block attributes\n * @example editor.commands.toggleCodeBlock({ language: 'javascript' })\n */\n toggleCodeBlock: (attributes?: { language: string }) => ReturnType\n }\n }\n}\n\n/**\n * Matches a code block with backticks.\n */\nexport const backtickInputRegex = /^```([a-z]+)?[\\s\\n]$/\n\n/**\n * Matches a code block with tildes.\n */\nexport const tildeInputRegex = /^~~~([a-z]+)?[\\s\\n]$/\n\n/**\n * This extension allows you to create code blocks.\n * @see https://tiptap.dev/api/nodes/code-block\n */\nexport const CodeBlock = Node.create<CodeBlockOptions>({\n name: 'codeBlock',\n\n addOptions() {\n return {\n languageClassPrefix: 'language-',\n exitOnTripleEnter: true,\n exitOnArrowDown: true,\n HTMLAttributes: {},\n }\n },\n\n content: 'text*',\n\n marks: '',\n\n group: 'block',\n\n code: true,\n\n defining: true,\n\n addAttributes() {\n return {\n language: {\n default: null,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n const classNames = [...(element.firstElementChild?.classList || [])]\n const languages = classNames\n .filter(className => className.startsWith(languageClassPrefix))\n .map(className => className.replace(languageClassPrefix, ''))\n const language = languages[0]\n\n if (!language) {\n return null\n }\n\n return language\n },\n rendered: false,\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'pre',\n preserveWhitespace: 'full',\n },\n ]\n },\n\n renderHTML({ node, HTMLAttributes }) {\n return [\n 'pre',\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),\n [\n 'code',\n {\n class: node.attrs.language\n ? this.options.languageClassPrefix + node.attrs.language\n : null,\n },\n 0,\n ],\n ]\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes => ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes => ({ commands }) => {\n return commands.toggleNode(this.name, 'paragraph', attributes)\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-Alt-c': () => this.editor.commands.toggleCodeBlock(),\n\n // remove code block when at start of document or code block is empty\n Backspace: () => {\n const { empty, $anchor } = this.editor.state.selection\n const isAtStart = $anchor.pos === 1\n\n if (!empty || $anchor.parent.type.name !== this.name) {\n return false\n }\n\n if (isAtStart || !$anchor.parent.textContent.length) {\n return this.editor.commands.clearNodes()\n }\n\n return false\n },\n\n // exit node on triple enter\n Enter: ({ editor }) => {\n if (!this.options.exitOnTripleEnter) {\n return false\n }\n\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n const endsWithDoubleNewline = $from.parent.textContent.endsWith('\\n\\n')\n\n if (!isAtEnd || !endsWithDoubleNewline) {\n return false\n }\n\n return editor\n .chain()\n .command(({ tr }) => {\n tr.delete($from.pos - 2, $from.pos)\n\n return true\n })\n .exitCode()\n .run()\n },\n\n // exit node on arrow down\n ArrowDown: ({ editor }) => {\n if (!this.options.exitOnArrowDown) {\n return false\n }\n\n const { state } = editor\n const { selection, doc } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n\n if (!isAtEnd) {\n return false\n }\n\n const after = $from.after()\n\n if (after === undefined) {\n return false\n }\n\n const nodeAfter = doc.nodeAt(after)\n\n if (nodeAfter) {\n return false\n }\n\n return editor.commands.exitCode()\n },\n }\n },\n\n addInputRules() {\n return [\n textblockTypeInputRule({\n find: backtickInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n textblockTypeInputRule({\n find: tildeInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n ]\n },\n\n addProseMirrorPlugins() {\n return [\n // this plugin creates a code block for pasted content from VS Code\n // we can also detect the copied code language\n new Plugin({\n key: new PluginKey('codeBlockVSCodeHandler'),\n props: {\n handlePaste: (view, event) => {\n if (!event.clipboardData) {\n return false\n }\n\n // don’t create a new code block within code blocks\n if (this.editor.isActive(this.type.name)) {\n return false\n }\n\n const text = event.clipboardData.getData('text/plain')\n const vscode = event.clipboardData.getData('vscode-editor-data')\n const vscodeData = vscode ? JSON.parse(vscode) : undefined\n const language = vscodeData?.mode\n\n if (!text || !language) {\n return false\n }\n\n const { tr, schema } = view.state\n\n // prepare a text node\n // strip carriage return chars from text pasted as code\n // see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd\n const textNode = schema.text(text.replace(/\\r\\n?/g, '\\n'))\n\n // create a code block with the text node\n // replace selection with the code block\n tr.replaceSelectionWith(this.type.create({ language }, textNode))\n\n if (tr.selection.$from.parent.type !== this.type) {\n // put cursor inside the newly created code block\n tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))))\n }\n\n // store meta information\n // this is useful for other plugins that depends on the paste event\n // like the paste rule plugin\n tr.setMeta('paste', true)\n\n view.dispatch(tr)\n\n return true\n },\n },\n }),\n ]\n },\n})\n"],"names":["Node","mergeAttributes","textblockTypeInputRule","Plugin","PluginKey","TextSelection"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { Node, mergeAttributes, textblockTypeInputRule } from '@tiptap/core';
|
|
2
2
|
import { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Matches a code block with backticks.
|
|
6
|
+
*/
|
|
4
7
|
const backtickInputRegex = /^```([a-z]+)?[\s\n]$/;
|
|
8
|
+
/**
|
|
9
|
+
* Matches a code block with tildes.
|
|
10
|
+
*/
|
|
5
11
|
const tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/;
|
|
12
|
+
/**
|
|
13
|
+
* This extension allows you to create code blocks.
|
|
14
|
+
* @see https://tiptap.dev/api/nodes/code-block
|
|
15
|
+
*/
|
|
6
16
|
const CodeBlock = Node.create({
|
|
7
17
|
name: 'codeBlock',
|
|
8
18
|
addOptions() {
|
|
@@ -179,23 +189,18 @@ const CodeBlock = Node.create({
|
|
|
179
189
|
if (!text || !language) {
|
|
180
190
|
return false;
|
|
181
191
|
}
|
|
182
|
-
const { tr } = view.state;
|
|
183
|
-
//
|
|
184
|
-
// if the cursor is at the absolute end of the document, insert the code block before the cursor instead
|
|
185
|
-
// of replacing the selection as the replaceSelectionWith function will cause the insertion to
|
|
186
|
-
// happen at the previous node
|
|
187
|
-
if (view.state.selection.from === view.state.doc.nodeSize - (1 + (view.state.selection.$to.depth * 2))) {
|
|
188
|
-
tr.insert(view.state.selection.from - 1, this.type.create({ language }));
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
tr.replaceSelectionWith(this.type.create({ language }));
|
|
192
|
-
}
|
|
193
|
-
// put cursor inside the newly created code block
|
|
194
|
-
tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))));
|
|
195
|
-
// add text to code block
|
|
192
|
+
const { tr, schema } = view.state;
|
|
193
|
+
// prepare a text node
|
|
196
194
|
// strip carriage return chars from text pasted as code
|
|
197
195
|
// see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd
|
|
198
|
-
|
|
196
|
+
const textNode = schema.text(text.replace(/\r\n?/g, '\n'));
|
|
197
|
+
// create a code block with the text node
|
|
198
|
+
// replace selection with the code block
|
|
199
|
+
tr.replaceSelectionWith(this.type.create({ language }, textNode));
|
|
200
|
+
if (tr.selection.$from.parent.type !== this.type) {
|
|
201
|
+
// put cursor inside the newly created code block
|
|
202
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))));
|
|
203
|
+
}
|
|
199
204
|
// store meta information
|
|
200
205
|
// this is useful for other plugins that depends on the paste event
|
|
201
206
|
// like the paste rule plugin
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/code-block.ts"],"sourcesContent":["import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'\n\nexport interface CodeBlockOptions {\n /**\n * Adds a prefix to language classes that are applied to code tags.\n * Defaults to `'language-'`.\n */\n languageClassPrefix: string\n /**\n * Define whether the node should be exited on triple enter.\n * Defaults to `true`.\n */\n exitOnTripleEnter: boolean\n /**\n * Define whether the node should be exited on arrow down if there is no node after it.\n * Defaults to `true`.\n */\n exitOnArrowDown: boolean\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: Record<string, any>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n codeBlock: {\n /**\n * Set a code block\n */\n setCodeBlock: (attributes?: { language: string }) => ReturnType\n /**\n * Toggle a code block\n */\n toggleCodeBlock: (attributes?: { language: string }) => ReturnType\n }\n }\n}\n\nexport const backtickInputRegex = /^```([a-z]+)?[\\s\\n]$/\nexport const tildeInputRegex = /^~~~([a-z]+)?[\\s\\n]$/\n\nexport const CodeBlock = Node.create<CodeBlockOptions>({\n name: 'codeBlock',\n\n addOptions() {\n return {\n languageClassPrefix: 'language-',\n exitOnTripleEnter: true,\n exitOnArrowDown: true,\n HTMLAttributes: {},\n }\n },\n\n content: 'text*',\n\n marks: '',\n\n group: 'block',\n\n code: true,\n\n defining: true,\n\n addAttributes() {\n return {\n language: {\n default: null,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n const classNames = [...(element.firstElementChild?.classList || [])]\n const languages = classNames\n .filter(className => className.startsWith(languageClassPrefix))\n .map(className => className.replace(languageClassPrefix, ''))\n const language = languages[0]\n\n if (!language) {\n return null\n }\n\n return language\n },\n rendered: false,\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'pre',\n preserveWhitespace: 'full',\n },\n ]\n },\n\n renderHTML({ node, HTMLAttributes }) {\n return [\n 'pre',\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),\n [\n 'code',\n {\n class: node.attrs.language\n ? this.options.languageClassPrefix + node.attrs.language\n : null,\n },\n 0,\n ],\n ]\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes => ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes => ({ commands }) => {\n return commands.toggleNode(this.name, 'paragraph', attributes)\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-Alt-c': () => this.editor.commands.toggleCodeBlock(),\n\n // remove code block when at start of document or code block is empty\n Backspace: () => {\n const { empty, $anchor } = this.editor.state.selection\n const isAtStart = $anchor.pos === 1\n\n if (!empty || $anchor.parent.type.name !== this.name) {\n return false\n }\n\n if (isAtStart || !$anchor.parent.textContent.length) {\n return this.editor.commands.clearNodes()\n }\n\n return false\n },\n\n // exit node on triple enter\n Enter: ({ editor }) => {\n if (!this.options.exitOnTripleEnter) {\n return false\n }\n\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n const endsWithDoubleNewline = $from.parent.textContent.endsWith('\\n\\n')\n\n if (!isAtEnd || !endsWithDoubleNewline) {\n return false\n }\n\n return editor\n .chain()\n .command(({ tr }) => {\n tr.delete($from.pos - 2, $from.pos)\n\n return true\n })\n .exitCode()\n .run()\n },\n\n // exit node on arrow down\n ArrowDown: ({ editor }) => {\n if (!this.options.exitOnArrowDown) {\n return false\n }\n\n const { state } = editor\n const { selection, doc } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n\n if (!isAtEnd) {\n return false\n }\n\n const after = $from.after()\n\n if (after === undefined) {\n return false\n }\n\n const nodeAfter = doc.nodeAt(after)\n\n if (nodeAfter) {\n return false\n }\n\n return editor.commands.exitCode()\n },\n }\n },\n\n addInputRules() {\n return [\n textblockTypeInputRule({\n find: backtickInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n textblockTypeInputRule({\n find: tildeInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n ]\n },\n\n addProseMirrorPlugins() {\n return [\n // this plugin creates a code block for pasted content from VS Code\n // we can also detect the copied code language\n new Plugin({\n key: new PluginKey('codeBlockVSCodeHandler'),\n props: {\n handlePaste: (view, event) => {\n if (!event.clipboardData) {\n return false\n }\n\n // don’t create a new code block within code blocks\n if (this.editor.isActive(this.type.name)) {\n return false\n }\n\n const text = event.clipboardData.getData('text/plain')\n const vscode = event.clipboardData.getData('vscode-editor-data')\n const vscodeData = vscode ? JSON.parse(vscode) : undefined\n const language = vscodeData?.mode\n\n if (!text || !language) {\n return false\n }\n\n const { tr } = view.state\n\n // create an empty code block´\n // if the cursor is at the absolute end of the document, insert the code block before the cursor instead\n // of replacing the selection as the replaceSelectionWith function will cause the insertion to\n // happen at the previous node\n if (view.state.selection.from === view.state.doc.nodeSize - (1 + (view.state.selection.$to.depth * 2))) {\n tr.insert(view.state.selection.from - 1, this.type.create({ language }))\n } else {\n tr.replaceSelectionWith(this.type.create({ language }))\n }\n\n // put cursor inside the newly created code block\n tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))))\n\n // add text to code block\n // strip carriage return chars from text pasted as code\n // see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd\n tr.insertText(text.replace(/\\r\\n?/g, '\\n'))\n\n // store meta information\n // this is useful for other plugins that depends on the paste event\n // like the paste rule plugin\n tr.setMeta('paste', true)\n\n view.dispatch(tr)\n\n return true\n },\n },\n }),\n ]\n },\n})\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/code-block.ts"],"sourcesContent":["import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'\n\nexport interface CodeBlockOptions {\n /**\n * Adds a prefix to language classes that are applied to code tags.\n * @default 'language-'\n */\n languageClassPrefix: string\n /**\n * Define whether the node should be exited on triple enter.\n * @default true\n */\n exitOnTripleEnter: boolean\n /**\n * Define whether the node should be exited on arrow down if there is no node after it.\n * @default true\n */\n exitOnArrowDown: boolean\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n codeBlock: {\n /**\n * Set a code block\n * @param attributes Code block attributes\n * @example editor.commands.setCodeBlock({ language: 'javascript' })\n */\n setCodeBlock: (attributes?: { language: string }) => ReturnType\n /**\n * Toggle a code block\n * @param attributes Code block attributes\n * @example editor.commands.toggleCodeBlock({ language: 'javascript' })\n */\n toggleCodeBlock: (attributes?: { language: string }) => ReturnType\n }\n }\n}\n\n/**\n * Matches a code block with backticks.\n */\nexport const backtickInputRegex = /^```([a-z]+)?[\\s\\n]$/\n\n/**\n * Matches a code block with tildes.\n */\nexport const tildeInputRegex = /^~~~([a-z]+)?[\\s\\n]$/\n\n/**\n * This extension allows you to create code blocks.\n * @see https://tiptap.dev/api/nodes/code-block\n */\nexport const CodeBlock = Node.create<CodeBlockOptions>({\n name: 'codeBlock',\n\n addOptions() {\n return {\n languageClassPrefix: 'language-',\n exitOnTripleEnter: true,\n exitOnArrowDown: true,\n HTMLAttributes: {},\n }\n },\n\n content: 'text*',\n\n marks: '',\n\n group: 'block',\n\n code: true,\n\n defining: true,\n\n addAttributes() {\n return {\n language: {\n default: null,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n const classNames = [...(element.firstElementChild?.classList || [])]\n const languages = classNames\n .filter(className => className.startsWith(languageClassPrefix))\n .map(className => className.replace(languageClassPrefix, ''))\n const language = languages[0]\n\n if (!language) {\n return null\n }\n\n return language\n },\n rendered: false,\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'pre',\n preserveWhitespace: 'full',\n },\n ]\n },\n\n renderHTML({ node, HTMLAttributes }) {\n return [\n 'pre',\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),\n [\n 'code',\n {\n class: node.attrs.language\n ? this.options.languageClassPrefix + node.attrs.language\n : null,\n },\n 0,\n ],\n ]\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes => ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes => ({ commands }) => {\n return commands.toggleNode(this.name, 'paragraph', attributes)\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-Alt-c': () => this.editor.commands.toggleCodeBlock(),\n\n // remove code block when at start of document or code block is empty\n Backspace: () => {\n const { empty, $anchor } = this.editor.state.selection\n const isAtStart = $anchor.pos === 1\n\n if (!empty || $anchor.parent.type.name !== this.name) {\n return false\n }\n\n if (isAtStart || !$anchor.parent.textContent.length) {\n return this.editor.commands.clearNodes()\n }\n\n return false\n },\n\n // exit node on triple enter\n Enter: ({ editor }) => {\n if (!this.options.exitOnTripleEnter) {\n return false\n }\n\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n const endsWithDoubleNewline = $from.parent.textContent.endsWith('\\n\\n')\n\n if (!isAtEnd || !endsWithDoubleNewline) {\n return false\n }\n\n return editor\n .chain()\n .command(({ tr }) => {\n tr.delete($from.pos - 2, $from.pos)\n\n return true\n })\n .exitCode()\n .run()\n },\n\n // exit node on arrow down\n ArrowDown: ({ editor }) => {\n if (!this.options.exitOnArrowDown) {\n return false\n }\n\n const { state } = editor\n const { selection, doc } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n\n if (!isAtEnd) {\n return false\n }\n\n const after = $from.after()\n\n if (after === undefined) {\n return false\n }\n\n const nodeAfter = doc.nodeAt(after)\n\n if (nodeAfter) {\n return false\n }\n\n return editor.commands.exitCode()\n },\n }\n },\n\n addInputRules() {\n return [\n textblockTypeInputRule({\n find: backtickInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n textblockTypeInputRule({\n find: tildeInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n ]\n },\n\n addProseMirrorPlugins() {\n return [\n // this plugin creates a code block for pasted content from VS Code\n // we can also detect the copied code language\n new Plugin({\n key: new PluginKey('codeBlockVSCodeHandler'),\n props: {\n handlePaste: (view, event) => {\n if (!event.clipboardData) {\n return false\n }\n\n // don’t create a new code block within code blocks\n if (this.editor.isActive(this.type.name)) {\n return false\n }\n\n const text = event.clipboardData.getData('text/plain')\n const vscode = event.clipboardData.getData('vscode-editor-data')\n const vscodeData = vscode ? JSON.parse(vscode) : undefined\n const language = vscodeData?.mode\n\n if (!text || !language) {\n return false\n }\n\n const { tr, schema } = view.state\n\n // prepare a text node\n // strip carriage return chars from text pasted as code\n // see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd\n const textNode = schema.text(text.replace(/\\r\\n?/g, '\\n'))\n\n // create a code block with the text node\n // replace selection with the code block\n tr.replaceSelectionWith(this.type.create({ language }, textNode))\n\n if (tr.selection.$from.parent.type !== this.type) {\n // put cursor inside the newly created code block\n tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))))\n }\n\n // store meta information\n // this is useful for other plugins that depends on the paste event\n // like the paste rule plugin\n tr.setMeta('paste', true)\n\n view.dispatch(tr)\n\n return true\n },\n },\n }),\n ]\n },\n})\n"],"names":[],"mappings":"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"}
|
package/dist/index.umd.js
CHANGED
|
@@ -4,8 +4,18 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@tiptap/extension-code-block"] = {}, global.core, global.state));
|
|
5
5
|
})(this, (function (exports, core, state) { 'use strict';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Matches a code block with backticks.
|
|
9
|
+
*/
|
|
7
10
|
const backtickInputRegex = /^```([a-z]+)?[\s\n]$/;
|
|
11
|
+
/**
|
|
12
|
+
* Matches a code block with tildes.
|
|
13
|
+
*/
|
|
8
14
|
const tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/;
|
|
15
|
+
/**
|
|
16
|
+
* This extension allows you to create code blocks.
|
|
17
|
+
* @see https://tiptap.dev/api/nodes/code-block
|
|
18
|
+
*/
|
|
9
19
|
const CodeBlock = core.Node.create({
|
|
10
20
|
name: 'codeBlock',
|
|
11
21
|
addOptions() {
|
|
@@ -182,23 +192,18 @@
|
|
|
182
192
|
if (!text || !language) {
|
|
183
193
|
return false;
|
|
184
194
|
}
|
|
185
|
-
const { tr } = view.state;
|
|
186
|
-
//
|
|
187
|
-
// if the cursor is at the absolute end of the document, insert the code block before the cursor instead
|
|
188
|
-
// of replacing the selection as the replaceSelectionWith function will cause the insertion to
|
|
189
|
-
// happen at the previous node
|
|
190
|
-
if (view.state.selection.from === view.state.doc.nodeSize - (1 + (view.state.selection.$to.depth * 2))) {
|
|
191
|
-
tr.insert(view.state.selection.from - 1, this.type.create({ language }));
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
tr.replaceSelectionWith(this.type.create({ language }));
|
|
195
|
-
}
|
|
196
|
-
// put cursor inside the newly created code block
|
|
197
|
-
tr.setSelection(state.TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))));
|
|
198
|
-
// add text to code block
|
|
195
|
+
const { tr, schema } = view.state;
|
|
196
|
+
// prepare a text node
|
|
199
197
|
// strip carriage return chars from text pasted as code
|
|
200
198
|
// see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd
|
|
201
|
-
|
|
199
|
+
const textNode = schema.text(text.replace(/\r\n?/g, '\n'));
|
|
200
|
+
// create a code block with the text node
|
|
201
|
+
// replace selection with the code block
|
|
202
|
+
tr.replaceSelectionWith(this.type.create({ language }, textNode));
|
|
203
|
+
if (tr.selection.$from.parent.type !== this.type) {
|
|
204
|
+
// put cursor inside the newly created code block
|
|
205
|
+
tr.setSelection(state.TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))));
|
|
206
|
+
}
|
|
202
207
|
// store meta information
|
|
203
208
|
// this is useful for other plugins that depends on the paste event
|
|
204
209
|
// like the paste rule plugin
|
package/dist/index.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.umd.js","sources":["../src/code-block.ts"],"sourcesContent":["import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'\n\nexport interface CodeBlockOptions {\n /**\n * Adds a prefix to language classes that are applied to code tags.\n * Defaults to `'language-'`.\n */\n languageClassPrefix: string\n /**\n * Define whether the node should be exited on triple enter.\n * Defaults to `true`.\n */\n exitOnTripleEnter: boolean\n /**\n * Define whether the node should be exited on arrow down if there is no node after it.\n * Defaults to `true`.\n */\n exitOnArrowDown: boolean\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: Record<string, any>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n codeBlock: {\n /**\n * Set a code block\n */\n setCodeBlock: (attributes?: { language: string }) => ReturnType\n /**\n * Toggle a code block\n */\n toggleCodeBlock: (attributes?: { language: string }) => ReturnType\n }\n }\n}\n\nexport const backtickInputRegex = /^```([a-z]+)?[\\s\\n]$/\nexport const tildeInputRegex = /^~~~([a-z]+)?[\\s\\n]$/\n\nexport const CodeBlock = Node.create<CodeBlockOptions>({\n name: 'codeBlock',\n\n addOptions() {\n return {\n languageClassPrefix: 'language-',\n exitOnTripleEnter: true,\n exitOnArrowDown: true,\n HTMLAttributes: {},\n }\n },\n\n content: 'text*',\n\n marks: '',\n\n group: 'block',\n\n code: true,\n\n defining: true,\n\n addAttributes() {\n return {\n language: {\n default: null,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n const classNames = [...(element.firstElementChild?.classList || [])]\n const languages = classNames\n .filter(className => className.startsWith(languageClassPrefix))\n .map(className => className.replace(languageClassPrefix, ''))\n const language = languages[0]\n\n if (!language) {\n return null\n }\n\n return language\n },\n rendered: false,\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'pre',\n preserveWhitespace: 'full',\n },\n ]\n },\n\n renderHTML({ node, HTMLAttributes }) {\n return [\n 'pre',\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),\n [\n 'code',\n {\n class: node.attrs.language\n ? this.options.languageClassPrefix + node.attrs.language\n : null,\n },\n 0,\n ],\n ]\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes => ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes => ({ commands }) => {\n return commands.toggleNode(this.name, 'paragraph', attributes)\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-Alt-c': () => this.editor.commands.toggleCodeBlock(),\n\n // remove code block when at start of document or code block is empty\n Backspace: () => {\n const { empty, $anchor } = this.editor.state.selection\n const isAtStart = $anchor.pos === 1\n\n if (!empty || $anchor.parent.type.name !== this.name) {\n return false\n }\n\n if (isAtStart || !$anchor.parent.textContent.length) {\n return this.editor.commands.clearNodes()\n }\n\n return false\n },\n\n // exit node on triple enter\n Enter: ({ editor }) => {\n if (!this.options.exitOnTripleEnter) {\n return false\n }\n\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n const endsWithDoubleNewline = $from.parent.textContent.endsWith('\\n\\n')\n\n if (!isAtEnd || !endsWithDoubleNewline) {\n return false\n }\n\n return editor\n .chain()\n .command(({ tr }) => {\n tr.delete($from.pos - 2, $from.pos)\n\n return true\n })\n .exitCode()\n .run()\n },\n\n // exit node on arrow down\n ArrowDown: ({ editor }) => {\n if (!this.options.exitOnArrowDown) {\n return false\n }\n\n const { state } = editor\n const { selection, doc } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n\n if (!isAtEnd) {\n return false\n }\n\n const after = $from.after()\n\n if (after === undefined) {\n return false\n }\n\n const nodeAfter = doc.nodeAt(after)\n\n if (nodeAfter) {\n return false\n }\n\n return editor.commands.exitCode()\n },\n }\n },\n\n addInputRules() {\n return [\n textblockTypeInputRule({\n find: backtickInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n textblockTypeInputRule({\n find: tildeInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n ]\n },\n\n addProseMirrorPlugins() {\n return [\n // this plugin creates a code block for pasted content from VS Code\n // we can also detect the copied code language\n new Plugin({\n key: new PluginKey('codeBlockVSCodeHandler'),\n props: {\n handlePaste: (view, event) => {\n if (!event.clipboardData) {\n return false\n }\n\n // don’t create a new code block within code blocks\n if (this.editor.isActive(this.type.name)) {\n return false\n }\n\n const text = event.clipboardData.getData('text/plain')\n const vscode = event.clipboardData.getData('vscode-editor-data')\n const vscodeData = vscode ? JSON.parse(vscode) : undefined\n const language = vscodeData?.mode\n\n if (!text || !language) {\n return false\n }\n\n const { tr } = view.state\n\n // create an empty code block´\n // if the cursor is at the absolute end of the document, insert the code block before the cursor instead\n // of replacing the selection as the replaceSelectionWith function will cause the insertion to\n // happen at the previous node\n if (view.state.selection.from === view.state.doc.nodeSize - (1 + (view.state.selection.$to.depth * 2))) {\n tr.insert(view.state.selection.from - 1, this.type.create({ language }))\n } else {\n tr.replaceSelectionWith(this.type.create({ language }))\n }\n\n // put cursor inside the newly created code block\n tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))))\n\n // add text to code block\n // strip carriage return chars from text pasted as code\n // see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd\n tr.insertText(text.replace(/\\r\\n?/g, '\\n'))\n\n // store meta information\n // this is useful for other plugins that depends on the paste event\n // like the paste rule plugin\n tr.setMeta('paste', true)\n\n view.dispatch(tr)\n\n return true\n },\n },\n }),\n ]\n },\n})\n"],"names":["Node","mergeAttributes","textblockTypeInputRule","Plugin","PluginKey","TextSelection"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/code-block.ts"],"sourcesContent":["import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'\n\nexport interface CodeBlockOptions {\n /**\n * Adds a prefix to language classes that are applied to code tags.\n * @default 'language-'\n */\n languageClassPrefix: string\n /**\n * Define whether the node should be exited on triple enter.\n * @default true\n */\n exitOnTripleEnter: boolean\n /**\n * Define whether the node should be exited on arrow down if there is no node after it.\n * @default true\n */\n exitOnArrowDown: boolean\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n codeBlock: {\n /**\n * Set a code block\n * @param attributes Code block attributes\n * @example editor.commands.setCodeBlock({ language: 'javascript' })\n */\n setCodeBlock: (attributes?: { language: string }) => ReturnType\n /**\n * Toggle a code block\n * @param attributes Code block attributes\n * @example editor.commands.toggleCodeBlock({ language: 'javascript' })\n */\n toggleCodeBlock: (attributes?: { language: string }) => ReturnType\n }\n }\n}\n\n/**\n * Matches a code block with backticks.\n */\nexport const backtickInputRegex = /^```([a-z]+)?[\\s\\n]$/\n\n/**\n * Matches a code block with tildes.\n */\nexport const tildeInputRegex = /^~~~([a-z]+)?[\\s\\n]$/\n\n/**\n * This extension allows you to create code blocks.\n * @see https://tiptap.dev/api/nodes/code-block\n */\nexport const CodeBlock = Node.create<CodeBlockOptions>({\n name: 'codeBlock',\n\n addOptions() {\n return {\n languageClassPrefix: 'language-',\n exitOnTripleEnter: true,\n exitOnArrowDown: true,\n HTMLAttributes: {},\n }\n },\n\n content: 'text*',\n\n marks: '',\n\n group: 'block',\n\n code: true,\n\n defining: true,\n\n addAttributes() {\n return {\n language: {\n default: null,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n const classNames = [...(element.firstElementChild?.classList || [])]\n const languages = classNames\n .filter(className => className.startsWith(languageClassPrefix))\n .map(className => className.replace(languageClassPrefix, ''))\n const language = languages[0]\n\n if (!language) {\n return null\n }\n\n return language\n },\n rendered: false,\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'pre',\n preserveWhitespace: 'full',\n },\n ]\n },\n\n renderHTML({ node, HTMLAttributes }) {\n return [\n 'pre',\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),\n [\n 'code',\n {\n class: node.attrs.language\n ? this.options.languageClassPrefix + node.attrs.language\n : null,\n },\n 0,\n ],\n ]\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes => ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes => ({ commands }) => {\n return commands.toggleNode(this.name, 'paragraph', attributes)\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-Alt-c': () => this.editor.commands.toggleCodeBlock(),\n\n // remove code block when at start of document or code block is empty\n Backspace: () => {\n const { empty, $anchor } = this.editor.state.selection\n const isAtStart = $anchor.pos === 1\n\n if (!empty || $anchor.parent.type.name !== this.name) {\n return false\n }\n\n if (isAtStart || !$anchor.parent.textContent.length) {\n return this.editor.commands.clearNodes()\n }\n\n return false\n },\n\n // exit node on triple enter\n Enter: ({ editor }) => {\n if (!this.options.exitOnTripleEnter) {\n return false\n }\n\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n const endsWithDoubleNewline = $from.parent.textContent.endsWith('\\n\\n')\n\n if (!isAtEnd || !endsWithDoubleNewline) {\n return false\n }\n\n return editor\n .chain()\n .command(({ tr }) => {\n tr.delete($from.pos - 2, $from.pos)\n\n return true\n })\n .exitCode()\n .run()\n },\n\n // exit node on arrow down\n ArrowDown: ({ editor }) => {\n if (!this.options.exitOnArrowDown) {\n return false\n }\n\n const { state } = editor\n const { selection, doc } = state\n const { $from, empty } = selection\n\n if (!empty || $from.parent.type !== this.type) {\n return false\n }\n\n const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2\n\n if (!isAtEnd) {\n return false\n }\n\n const after = $from.after()\n\n if (after === undefined) {\n return false\n }\n\n const nodeAfter = doc.nodeAt(after)\n\n if (nodeAfter) {\n return false\n }\n\n return editor.commands.exitCode()\n },\n }\n },\n\n addInputRules() {\n return [\n textblockTypeInputRule({\n find: backtickInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n textblockTypeInputRule({\n find: tildeInputRegex,\n type: this.type,\n getAttributes: match => ({\n language: match[1],\n }),\n }),\n ]\n },\n\n addProseMirrorPlugins() {\n return [\n // this plugin creates a code block for pasted content from VS Code\n // we can also detect the copied code language\n new Plugin({\n key: new PluginKey('codeBlockVSCodeHandler'),\n props: {\n handlePaste: (view, event) => {\n if (!event.clipboardData) {\n return false\n }\n\n // don’t create a new code block within code blocks\n if (this.editor.isActive(this.type.name)) {\n return false\n }\n\n const text = event.clipboardData.getData('text/plain')\n const vscode = event.clipboardData.getData('vscode-editor-data')\n const vscodeData = vscode ? JSON.parse(vscode) : undefined\n const language = vscodeData?.mode\n\n if (!text || !language) {\n return false\n }\n\n const { tr, schema } = view.state\n\n // prepare a text node\n // strip carriage return chars from text pasted as code\n // see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd\n const textNode = schema.text(text.replace(/\\r\\n?/g, '\\n'))\n\n // create a code block with the text node\n // replace selection with the code block\n tr.replaceSelectionWith(this.type.create({ language }, textNode))\n\n if (tr.selection.$from.parent.type !== this.type) {\n // put cursor inside the newly created code block\n tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))))\n }\n\n // store meta information\n // this is useful for other plugins that depends on the paste event\n // like the paste rule plugin\n tr.setMeta('paste', true)\n\n view.dispatch(tr)\n\n return true\n },\n },\n }),\n ]\n },\n})\n"],"names":["Node","mergeAttributes","textblockTypeInputRule","Plugin","PluginKey","TextSelection"],"mappings":"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"}
|
|
@@ -2,21 +2,23 @@ import { Node } from '@tiptap/core';
|
|
|
2
2
|
export interface CodeBlockOptions {
|
|
3
3
|
/**
|
|
4
4
|
* Adds a prefix to language classes that are applied to code tags.
|
|
5
|
-
*
|
|
5
|
+
* @default 'language-'
|
|
6
6
|
*/
|
|
7
7
|
languageClassPrefix: string;
|
|
8
8
|
/**
|
|
9
9
|
* Define whether the node should be exited on triple enter.
|
|
10
|
-
*
|
|
10
|
+
* @default true
|
|
11
11
|
*/
|
|
12
12
|
exitOnTripleEnter: boolean;
|
|
13
13
|
/**
|
|
14
14
|
* Define whether the node should be exited on arrow down if there is no node after it.
|
|
15
|
-
*
|
|
15
|
+
* @default true
|
|
16
16
|
*/
|
|
17
17
|
exitOnArrowDown: boolean;
|
|
18
18
|
/**
|
|
19
19
|
* Custom HTML attributes that should be added to the rendered HTML tag.
|
|
20
|
+
* @default {}
|
|
21
|
+
* @example { class: 'foo' }
|
|
20
22
|
*/
|
|
21
23
|
HTMLAttributes: Record<string, any>;
|
|
22
24
|
}
|
|
@@ -25,12 +27,16 @@ declare module '@tiptap/core' {
|
|
|
25
27
|
codeBlock: {
|
|
26
28
|
/**
|
|
27
29
|
* Set a code block
|
|
30
|
+
* @param attributes Code block attributes
|
|
31
|
+
* @example editor.commands.setCodeBlock({ language: 'javascript' })
|
|
28
32
|
*/
|
|
29
33
|
setCodeBlock: (attributes?: {
|
|
30
34
|
language: string;
|
|
31
35
|
}) => ReturnType;
|
|
32
36
|
/**
|
|
33
37
|
* Toggle a code block
|
|
38
|
+
* @param attributes Code block attributes
|
|
39
|
+
* @example editor.commands.toggleCodeBlock({ language: 'javascript' })
|
|
34
40
|
*/
|
|
35
41
|
toggleCodeBlock: (attributes?: {
|
|
36
42
|
language: string;
|
|
@@ -38,6 +44,16 @@ declare module '@tiptap/core' {
|
|
|
38
44
|
};
|
|
39
45
|
}
|
|
40
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Matches a code block with backticks.
|
|
49
|
+
*/
|
|
41
50
|
export declare const backtickInputRegex: RegExp;
|
|
51
|
+
/**
|
|
52
|
+
* Matches a code block with tildes.
|
|
53
|
+
*/
|
|
42
54
|
export declare const tildeInputRegex: RegExp;
|
|
55
|
+
/**
|
|
56
|
+
* This extension allows you to create code blocks.
|
|
57
|
+
* @see https://tiptap.dev/api/nodes/code-block
|
|
58
|
+
*/
|
|
43
59
|
export declare const CodeBlock: Node<CodeBlockOptions, any>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiptap/extension-code-block",
|
|
3
3
|
"description": "code block extension for tiptap",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.0-beta.0",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@tiptap/core": "^2.
|
|
33
|
-
"@tiptap/pm": "^2.
|
|
32
|
+
"@tiptap/core": "^2.5.0-beta.0",
|
|
33
|
+
"@tiptap/pm": "^2.5.0-beta.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@tiptap/core": "^2.0.0",
|
package/src/code-block.ts
CHANGED
|
@@ -4,21 +4,23 @@ import { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'
|
|
|
4
4
|
export interface CodeBlockOptions {
|
|
5
5
|
/**
|
|
6
6
|
* Adds a prefix to language classes that are applied to code tags.
|
|
7
|
-
*
|
|
7
|
+
* @default 'language-'
|
|
8
8
|
*/
|
|
9
9
|
languageClassPrefix: string
|
|
10
10
|
/**
|
|
11
11
|
* Define whether the node should be exited on triple enter.
|
|
12
|
-
*
|
|
12
|
+
* @default true
|
|
13
13
|
*/
|
|
14
14
|
exitOnTripleEnter: boolean
|
|
15
15
|
/**
|
|
16
16
|
* Define whether the node should be exited on arrow down if there is no node after it.
|
|
17
|
-
*
|
|
17
|
+
* @default true
|
|
18
18
|
*/
|
|
19
19
|
exitOnArrowDown: boolean
|
|
20
20
|
/**
|
|
21
21
|
* Custom HTML attributes that should be added to the rendered HTML tag.
|
|
22
|
+
* @default {}
|
|
23
|
+
* @example { class: 'foo' }
|
|
22
24
|
*/
|
|
23
25
|
HTMLAttributes: Record<string, any>
|
|
24
26
|
}
|
|
@@ -28,19 +30,34 @@ declare module '@tiptap/core' {
|
|
|
28
30
|
codeBlock: {
|
|
29
31
|
/**
|
|
30
32
|
* Set a code block
|
|
33
|
+
* @param attributes Code block attributes
|
|
34
|
+
* @example editor.commands.setCodeBlock({ language: 'javascript' })
|
|
31
35
|
*/
|
|
32
36
|
setCodeBlock: (attributes?: { language: string }) => ReturnType
|
|
33
37
|
/**
|
|
34
38
|
* Toggle a code block
|
|
39
|
+
* @param attributes Code block attributes
|
|
40
|
+
* @example editor.commands.toggleCodeBlock({ language: 'javascript' })
|
|
35
41
|
*/
|
|
36
42
|
toggleCodeBlock: (attributes?: { language: string }) => ReturnType
|
|
37
43
|
}
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Matches a code block with backticks.
|
|
49
|
+
*/
|
|
41
50
|
export const backtickInputRegex = /^```([a-z]+)?[\s\n]$/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Matches a code block with tildes.
|
|
54
|
+
*/
|
|
42
55
|
export const tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/
|
|
43
56
|
|
|
57
|
+
/**
|
|
58
|
+
* This extension allows you to create code blocks.
|
|
59
|
+
* @see https://tiptap.dev/api/nodes/code-block
|
|
60
|
+
*/
|
|
44
61
|
export const CodeBlock = Node.create<CodeBlockOptions>({
|
|
45
62
|
name: 'codeBlock',
|
|
46
63
|
|
|
@@ -258,25 +275,21 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
|
|
|
258
275
|
return false
|
|
259
276
|
}
|
|
260
277
|
|
|
261
|
-
const { tr } = view.state
|
|
262
|
-
|
|
263
|
-
// create an empty code block´
|
|
264
|
-
// if the cursor is at the absolute end of the document, insert the code block before the cursor instead
|
|
265
|
-
// of replacing the selection as the replaceSelectionWith function will cause the insertion to
|
|
266
|
-
// happen at the previous node
|
|
267
|
-
if (view.state.selection.from === view.state.doc.nodeSize - (1 + (view.state.selection.$to.depth * 2))) {
|
|
268
|
-
tr.insert(view.state.selection.from - 1, this.type.create({ language }))
|
|
269
|
-
} else {
|
|
270
|
-
tr.replaceSelectionWith(this.type.create({ language }))
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// put cursor inside the newly created code block
|
|
274
|
-
tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))))
|
|
278
|
+
const { tr, schema } = view.state
|
|
275
279
|
|
|
276
|
-
//
|
|
280
|
+
// prepare a text node
|
|
277
281
|
// strip carriage return chars from text pasted as code
|
|
278
282
|
// see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd
|
|
279
|
-
|
|
283
|
+
const textNode = schema.text(text.replace(/\r\n?/g, '\n'))
|
|
284
|
+
|
|
285
|
+
// create a code block with the text node
|
|
286
|
+
// replace selection with the code block
|
|
287
|
+
tr.replaceSelectionWith(this.type.create({ language }, textNode))
|
|
288
|
+
|
|
289
|
+
if (tr.selection.$from.parent.type !== this.type) {
|
|
290
|
+
// put cursor inside the newly created code block
|
|
291
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))))
|
|
292
|
+
}
|
|
280
293
|
|
|
281
294
|
// store meta information
|
|
282
295
|
// this is useful for other plugins that depends on the paste event
|