New:Socket for Asana Is Now Available.Learn more
Sign In

@tiptap/extension-code-block

Package Overview
Dependencies
Maintainers
6
Versions
356
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tiptap/extension-code-block - npm Package Compare versions

Comparing version
3.30.2
to
3.30.3
+1
dist/index.d.cts.map
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/code-block.ts"],"mappings":";;UAKiB;;;;;EAKf;;;;;EAKA;;;;;EAKA;;;;;EAKA;;;;;;EAMA;;;;;EAKA;;;;;EAKA;;;;;;EAMA,gBAAgB;;;YAIN,SAAS;IACjB;;;;;;MAME,eAAe;QAAe;YAAuB;;;;;;MAMrD,kBAAkB;QAAe;YAAuB;;;;;;;cAQjD,oBAAkB;;;;cAKlB,iBAAe;;;;;cAMf,WAAS,KAAA"}
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/code-block.ts"],"mappings":";;UAKiB;;;;;EAKf;;;;;EAKA;;;;;EAKA;;;;;EAKA;;;;;;EAMA;;;;;EAKA;;;;;EAKA;;;;;;EAMA,gBAAgB;;;YAIN,SAAS;IACjB;;;;;;MAME,eAAe;QAAe;YAAuB;;;;;;MAMrD,kBAAkB;QAAe;YAAuB;;;;;;;cAQjD,oBAAkB;;;;cAKlB,iBAAe;;;;;cAMf,WAAS,KAAA"}
+251
-370

@@ -1,374 +0,255 @@

"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
CodeBlock: () => CodeBlock,
backtickInputRegex: () => backtickInputRegex,
default: () => index_default,
tildeInputRegex: () => tildeInputRegex
Object.defineProperties(exports, {
__esModule: { value: true },
[Symbol.toStringTag]: { value: "Module" }
});
module.exports = __toCommonJS(index_exports);
// src/code-block.ts
var import_core = require("@tiptap/core");
var import_state = require("@tiptap/pm/state");
var DEFAULT_TAB_SIZE = 4;
var backtickInputRegex = /^```([a-z]+)?[\s\n]$/;
var tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/;
var CodeBlock = import_core.Node.create({
name: "codeBlock",
addOptions() {
return {
languageClassPrefix: "language-",
exitOnTripleEnter: true,
exitOnArrowDown: true,
exitOnArrowUp: true,
defaultLanguage: null,
enableTabIndentation: false,
tabSize: DEFAULT_TAB_SIZE,
HTMLAttributes: {}
};
},
content: "text*",
marks: "",
group: "block",
code: true,
defining: true,
addAttributes() {
return {
language: {
default: this.options.defaultLanguage,
parseHTML: (element) => {
var _a;
const { languageClassPrefix } = this.options;
if (!languageClassPrefix) {
return null;
}
const classNames = [...((_a = element.firstElementChild) == null ? void 0 : _a.classList) || []];
const languages = classNames.filter((className) => className.startsWith(languageClassPrefix)).map((className) => className.replace(languageClassPrefix, ""));
const language = languages[0];
if (!language) {
return null;
}
return language;
},
rendered: false
}
};
},
parseHTML() {
return [
{
tag: "pre",
preserveWhitespace: "full"
}
];
},
renderHTML({ node, HTMLAttributes }) {
return [
"pre",
(0, import_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
[
"code",
{
class: node.attrs.language ? this.options.languageClassPrefix + node.attrs.language : null
},
0
]
];
},
markdownTokenName: "code",
parseMarkdown: (token, helpers) => {
var _a, _b;
if (((_a = token.raw) == null ? void 0 : _a.startsWith("```")) === false && ((_b = token.raw) == null ? void 0 : _b.startsWith("~~~")) === false && token.codeBlockStyle !== "indented") {
return [];
}
return helpers.createNode(
"codeBlock",
{ language: token.lang || null },
token.text ? [helpers.createTextNode(token.text)] : []
);
},
renderMarkdown: (node, h) => {
var _a;
let output = "";
const language = ((_a = node.attrs) == null ? void 0 : _a.language) || "";
if (!node.content) {
output = `\`\`\`${language}
\`\`\``;
} else {
const lines = [`\`\`\`${language}`, h.renderChildren(node.content), "```"];
output = lines.join("\n");
}
return output;
},
addCommands() {
return {
setCodeBlock: (attributes) => ({ commands }) => {
return commands.setNode(this.name, attributes);
},
toggleCodeBlock: (attributes) => ({ commands }) => {
return commands.toggleNode(this.name, "paragraph", attributes);
}
};
},
addKeyboardShortcuts() {
return {
"Mod-Alt-c": () => this.editor.commands.toggleCodeBlock(),
// remove code block when at start of document or code block is empty
Backspace: () => {
const { empty, $anchor } = this.editor.state.selection;
const isAtStart = $anchor.pos === 1;
if (!empty || $anchor.parent.type.name !== this.name) {
return false;
}
if (isAtStart || !$anchor.parent.textContent.length) {
return this.editor.commands.clearNodes();
}
return false;
},
// handle tab indentation
Tab: ({ editor }) => {
var _a;
if (!this.options.enableTabIndentation) {
return false;
}
const tabSize = (_a = this.options.tabSize) != null ? _a : DEFAULT_TAB_SIZE;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if ($from.parent.type !== this.type) {
return false;
}
const indent = " ".repeat(tabSize);
if (empty) {
return editor.commands.insertContent(indent);
}
return editor.commands.command(({ tr }) => {
const { from, to } = selection;
const text = state.doc.textBetween(from, to, "\n", "\n");
const lines = text.split("\n");
const indentedText = lines.map((line) => indent + line).join("\n");
tr.replaceWith(from, to, state.schema.text(indentedText));
return true;
});
},
// handle shift+tab reverse indentation
"Shift-Tab": ({ editor }) => {
var _a;
if (!this.options.enableTabIndentation) {
return false;
}
const tabSize = (_a = this.options.tabSize) != null ? _a : DEFAULT_TAB_SIZE;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if ($from.parent.type !== this.type) {
return false;
}
if (empty) {
return editor.commands.command(({ tr }) => {
var _a2;
const { pos } = $from;
const codeBlockStart = $from.start();
const codeBlockEnd = $from.end();
const allText = state.doc.textBetween(codeBlockStart, codeBlockEnd, "\n", "\n");
const lines = allText.split("\n");
let currentLineIndex = 0;
let charCount = 0;
const relativeCursorPos = pos - codeBlockStart;
for (let i = 0; i < lines.length; i += 1) {
if (charCount + lines[i].length >= relativeCursorPos) {
currentLineIndex = i;
break;
}
charCount += lines[i].length + 1;
}
const currentLine = lines[currentLineIndex];
const leadingSpaces = ((_a2 = currentLine.match(/^ */)) == null ? void 0 : _a2[0]) || "";
const spacesToRemove = Math.min(leadingSpaces.length, tabSize);
if (spacesToRemove === 0) {
return true;
}
let lineStartPos = codeBlockStart;
for (let i = 0; i < currentLineIndex; i += 1) {
lineStartPos += lines[i].length + 1;
}
tr.delete(lineStartPos, lineStartPos + spacesToRemove);
const cursorPosInLine = pos - lineStartPos;
if (cursorPosInLine <= spacesToRemove) {
tr.setSelection(import_state.TextSelection.create(tr.doc, lineStartPos));
}
return true;
});
}
return editor.commands.command(({ tr }) => {
const { from, to } = selection;
const text = state.doc.textBetween(from, to, "\n", "\n");
const lines = text.split("\n");
const reverseIndentText = lines.map((line) => {
var _a2;
const leadingSpaces = ((_a2 = line.match(/^ */)) == null ? void 0 : _a2[0]) || "";
const spacesToRemove = Math.min(leadingSpaces.length, tabSize);
return line.slice(spacesToRemove);
}).join("\n");
tr.replaceWith(from, to, state.schema.text(reverseIndentText));
return true;
});
},
// exit node on triple enter
Enter: ({ editor }) => {
if (!this.options.exitOnTripleEnter) {
return false;
}
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) {
return false;
}
const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2;
const endsWithDoubleNewline = $from.parent.textContent.endsWith("\n\n");
if (!isAtEnd || !endsWithDoubleNewline) {
return false;
}
return editor.chain().command(({ tr }) => {
tr.delete($from.pos - 2, $from.pos);
return true;
}).exitCode().run();
},
// exit node on arrow up if there is no node before it
ArrowUp: ({ editor }) => {
if (!this.options.exitOnArrowUp) {
return false;
}
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) {
return false;
}
if ($from.parentOffset !== 0) {
return false;
}
const before = $from.before();
if (before > 0) {
return false;
}
return editor.commands.insertDefaultBlock({ pos: before });
},
// exit node on arrow down
ArrowDown: ({ editor }) => {
if (!this.options.exitOnArrowDown) {
return false;
}
const { state } = editor;
const { selection, doc } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) {
return false;
}
const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2;
if (!isAtEnd) {
return false;
}
const after = $from.after();
if (after === void 0) {
return false;
}
const nodeAfter = doc.nodeAt(after);
if (nodeAfter) {
return editor.commands.command(({ tr }) => {
tr.setSelection(import_state.Selection.near(doc.resolve(after)));
return true;
});
}
return editor.commands.exitCode();
}
};
},
addInputRules() {
return [
(0, import_core.textblockTypeInputRule)({
find: backtickInputRegex,
type: this.type,
getAttributes: (match) => ({
language: match[1]
})
}),
(0, import_core.textblockTypeInputRule)({
find: tildeInputRegex,
type: this.type,
getAttributes: (match) => ({
language: match[1]
})
})
];
},
addProseMirrorPlugins() {
return [
// this plugin creates a code block for pasted content from VS Code
// we can also detect the copied code language
new import_state.Plugin({
key: new import_state.PluginKey("codeBlockVSCodeHandler"),
props: {
handlePaste: (view, event) => {
if (!event.clipboardData) {
return false;
}
if (this.editor.isActive(this.type.name)) {
return false;
}
const text = event.clipboardData.getData("text/plain");
const vscode = event.clipboardData.getData("vscode-editor-data");
const vscodeData = vscode ? JSON.parse(vscode) : void 0;
const language = vscodeData == null ? void 0 : vscodeData.mode;
if (!text || !language) {
return false;
}
const { tr, schema } = view.state;
const textNode = schema.text(text.replace(/\r\n?/g, "\n"));
tr.replaceSelectionWith(this.type.create({ language }, textNode));
if (tr.selection.$from.parent.type !== this.type) {
tr.setSelection(
import_state.TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2)))
);
}
tr.setMeta("paste", true);
view.dispatch(tr);
return true;
}
}
})
];
}
let _tiptap_core = require("@tiptap/core");
let _tiptap_pm_state = require("@tiptap/pm/state");
//#region src/code-block.ts
const DEFAULT_TAB_SIZE = 4;
/**
* Matches a code block with backticks.
*/
const backtickInputRegex = /^```([a-z]+)?[\s\n]$/;
/**
* Matches a code block with tildes.
*/
const tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/;
/**
* This extension allows you to create code blocks.
* @see https://tiptap.dev/api/nodes/code-block
*/
const CodeBlock = _tiptap_core.Node.create({
name: "codeBlock",
addOptions() {
return {
languageClassPrefix: "language-",
exitOnTripleEnter: true,
exitOnArrowDown: true,
exitOnArrowUp: true,
defaultLanguage: null,
enableTabIndentation: false,
tabSize: DEFAULT_TAB_SIZE,
HTMLAttributes: {}
};
},
content: "text*",
marks: "",
group: "block",
code: true,
defining: true,
addAttributes() {
return { language: {
default: this.options.defaultLanguage,
parseHTML: (element) => {
var _element$firstElement;
const { languageClassPrefix } = this.options;
if (!languageClassPrefix) return null;
const language = [...((_element$firstElement = element.firstElementChild) === null || _element$firstElement === void 0 ? void 0 : _element$firstElement.classList) || []].filter((className) => className.startsWith(languageClassPrefix)).map((className) => className.replace(languageClassPrefix, ""))[0];
if (!language) return null;
return language;
},
rendered: false
} };
},
parseHTML() {
return [{
tag: "pre",
preserveWhitespace: "full"
}];
},
renderHTML({ node, HTMLAttributes }) {
return [
"pre",
(0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
[
"code",
{ class: node.attrs.language ? this.options.languageClassPrefix + node.attrs.language : null },
0
]
];
},
markdownTokenName: "code",
parseMarkdown: (token, helpers) => {
var _token$raw, _token$raw2;
if (((_token$raw = token.raw) === null || _token$raw === void 0 ? void 0 : _token$raw.startsWith("```")) === false && ((_token$raw2 = token.raw) === null || _token$raw2 === void 0 ? void 0 : _token$raw2.startsWith("~~~")) === false && token.codeBlockStyle !== "indented") return [];
return helpers.createNode("codeBlock", { language: token.lang || null }, token.text ? [helpers.createTextNode(token.text)] : []);
},
renderMarkdown: (node, h) => {
var _node$attrs;
let output = "";
const language = ((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.language) || "";
if (!node.content) output = `\`\`\`${language}\n\n\`\`\``;
else output = [
`\`\`\`${language}`,
h.renderChildren(node.content),
"```"
].join("\n");
return output;
},
addCommands() {
return {
setCodeBlock: (attributes) => ({ commands }) => {
return commands.setNode(this.name, attributes);
},
toggleCodeBlock: (attributes) => ({ commands }) => {
return commands.toggleNode(this.name, "paragraph", attributes);
}
};
},
addKeyboardShortcuts() {
return {
"Mod-Alt-c": () => this.editor.commands.toggleCodeBlock(),
Backspace: () => {
const { empty, $anchor } = this.editor.state.selection;
const isAtStart = $anchor.pos === 1;
if (!empty || $anchor.parent.type.name !== this.name) return false;
if (isAtStart || !$anchor.parent.textContent.length) return this.editor.commands.clearNodes();
return false;
},
Tab: ({ editor }) => {
var _this$options$tabSize;
if (!this.options.enableTabIndentation) return false;
const tabSize = (_this$options$tabSize = this.options.tabSize) !== null && _this$options$tabSize !== void 0 ? _this$options$tabSize : DEFAULT_TAB_SIZE;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if ($from.parent.type !== this.type) return false;
const indent = " ".repeat(tabSize);
if (empty) return editor.commands.insertContent(indent);
return editor.commands.command(({ tr }) => {
const { from, to } = selection;
const indentedText = state.doc.textBetween(from, to, "\n", "\n").split("\n").map((line) => indent + line).join("\n");
tr.replaceWith(from, to, state.schema.text(indentedText));
return true;
});
},
"Shift-Tab": ({ editor }) => {
var _this$options$tabSize2;
if (!this.options.enableTabIndentation) return false;
const tabSize = (_this$options$tabSize2 = this.options.tabSize) !== null && _this$options$tabSize2 !== void 0 ? _this$options$tabSize2 : DEFAULT_TAB_SIZE;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if ($from.parent.type !== this.type) return false;
if (empty) return editor.commands.command(({ tr }) => {
var _currentLine$match;
const { pos } = $from;
const codeBlockStart = $from.start();
const codeBlockEnd = $from.end();
const lines = state.doc.textBetween(codeBlockStart, codeBlockEnd, "\n", "\n").split("\n");
let currentLineIndex = 0;
let charCount = 0;
const relativeCursorPos = pos - codeBlockStart;
for (let i = 0; i < lines.length; i += 1) {
if (charCount + lines[i].length >= relativeCursorPos) {
currentLineIndex = i;
break;
}
charCount += lines[i].length + 1;
}
const leadingSpaces = ((_currentLine$match = lines[currentLineIndex].match(/^ */)) === null || _currentLine$match === void 0 ? void 0 : _currentLine$match[0]) || "";
const spacesToRemove = Math.min(leadingSpaces.length, tabSize);
if (spacesToRemove === 0) return true;
let lineStartPos = codeBlockStart;
for (let i = 0; i < currentLineIndex; i += 1) lineStartPos += lines[i].length + 1;
tr.delete(lineStartPos, lineStartPos + spacesToRemove);
if (pos - lineStartPos <= spacesToRemove) tr.setSelection(_tiptap_pm_state.TextSelection.create(tr.doc, lineStartPos));
return true;
});
return editor.commands.command(({ tr }) => {
const { from, to } = selection;
const reverseIndentText = state.doc.textBetween(from, to, "\n", "\n").split("\n").map((line) => {
var _line$match;
const leadingSpaces = ((_line$match = line.match(/^ */)) === null || _line$match === void 0 ? void 0 : _line$match[0]) || "";
const spacesToRemove = Math.min(leadingSpaces.length, tabSize);
return line.slice(spacesToRemove);
}).join("\n");
tr.replaceWith(from, to, state.schema.text(reverseIndentText));
return true;
});
},
Enter: ({ editor }) => {
if (!this.options.exitOnTripleEnter) return false;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) return false;
const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2;
const endsWithDoubleNewline = $from.parent.textContent.endsWith("\n\n");
if (!isAtEnd || !endsWithDoubleNewline) return false;
return editor.chain().command(({ tr }) => {
tr.delete($from.pos - 2, $from.pos);
return true;
}).exitCode().run();
},
ArrowUp: ({ editor }) => {
if (!this.options.exitOnArrowUp) return false;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) return false;
if ($from.parentOffset !== 0) return false;
const before = $from.before();
if (before > 0) return false;
return editor.commands.insertDefaultBlock({ pos: before });
},
ArrowDown: ({ editor }) => {
if (!this.options.exitOnArrowDown) return false;
const { state } = editor;
const { selection, doc } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) return false;
if (!($from.parentOffset === $from.parent.nodeSize - 2)) return false;
const after = $from.after();
if (after === void 0) return false;
if (doc.nodeAt(after)) return editor.commands.command(({ tr }) => {
tr.setSelection(_tiptap_pm_state.Selection.near(doc.resolve(after)));
return true;
});
return editor.commands.exitCode();
}
};
},
addInputRules() {
return [(0, _tiptap_core.textblockTypeInputRule)({
find: backtickInputRegex,
type: this.type,
getAttributes: (match) => ({ language: match[1] })
}), (0, _tiptap_core.textblockTypeInputRule)({
find: tildeInputRegex,
type: this.type,
getAttributes: (match) => ({ language: match[1] })
})];
},
addProseMirrorPlugins() {
return [new _tiptap_pm_state.Plugin({
key: new _tiptap_pm_state.PluginKey("codeBlockVSCodeHandler"),
props: { handlePaste: (view, event) => {
if (!event.clipboardData) return false;
if (this.editor.isActive(this.type.name)) return false;
const text = event.clipboardData.getData("text/plain");
const vscode = event.clipboardData.getData("vscode-editor-data");
const vscodeData = vscode ? JSON.parse(vscode) : void 0;
const language = vscodeData === null || vscodeData === void 0 ? void 0 : vscodeData.mode;
if (!text || !language) return false;
const { tr, schema } = view.state;
const textNode = schema.text(text.replace(/\r\n?/g, "\n"));
tr.replaceSelectionWith(this.type.create({ language }, textNode));
if (tr.selection.$from.parent.type !== this.type) tr.setSelection(_tiptap_pm_state.TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))));
tr.setMeta("paste", true);
view.dispatch(tr);
return true;
} }
})];
}
});
//#endregion
//#region src/index.ts
var src_default = CodeBlock;
//#endregion
exports.CodeBlock = CodeBlock;
exports.backtickInputRegex = backtickInputRegex;
exports.default = src_default;
exports.tildeInputRegex = tildeInputRegex;
// src/index.ts
var index_default = CodeBlock;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CodeBlock,
backtickInputRegex,
tildeInputRegex
});
//# sourceMappingURL=index.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.ts","../src/code-block.ts"],"sourcesContent":["import { CodeBlock } from './code-block.js'\n\nexport * from './code-block.js'\n\nexport default CodeBlock\n","import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, Selection, TextSelection } from '@tiptap/pm/state'\n\nconst DEFAULT_TAB_SIZE = 4\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 | null | undefined\n /**\n * Define whether the node should be exited on triple enter.\n * @default true\n */\n exitOnTripleEnter: boolean | null | undefined\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 | null | undefined\n /**\n * Define whether the node should be exited on arrow up if there is no node before it.\n * @default true\n */\n exitOnArrowUp: boolean | null | undefined\n /**\n * The default language.\n * @default null\n * @example 'js'\n */\n defaultLanguage: string | null | undefined\n /**\n * Enable tab key for indentation in code blocks.\n * @default false\n */\n enableTabIndentation: boolean | null | undefined\n /**\n * The number of spaces to use for tab indentation.\n * @default 4\n */\n tabSize: number | null | undefined\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 exitOnArrowUp: true,\n defaultLanguage: null,\n enableTabIndentation: false,\n tabSize: DEFAULT_TAB_SIZE,\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: this.options.defaultLanguage,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n\n if (!languageClassPrefix) {\n return null\n }\n\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 markdownTokenName: 'code',\n\n parseMarkdown: (token, helpers) => {\n if (\n token.raw?.startsWith('```') === false &&\n token.raw?.startsWith('~~~') === false &&\n token.codeBlockStyle !== 'indented'\n ) {\n return []\n }\n\n return helpers.createNode(\n 'codeBlock',\n { language: token.lang || null },\n token.text ? [helpers.createTextNode(token.text)] : [],\n )\n },\n\n renderMarkdown: (node, h) => {\n let output = ''\n const language = node.attrs?.language || ''\n\n if (!node.content) {\n output = `\\`\\`\\`${language}\\n\\n\\`\\`\\``\n } else {\n const lines = [`\\`\\`\\`${language}`, h.renderChildren(node.content), '```']\n output = lines.join('\\n')\n }\n\n return output\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes =>\n ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes =>\n ({ 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 // handle tab indentation\n Tab: ({ editor }) => {\n if (!this.options.enableTabIndentation) {\n return false\n }\n\n const tabSize = this.options.tabSize ?? DEFAULT_TAB_SIZE\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if ($from.parent.type !== this.type) {\n return false\n }\n\n const indent = ' '.repeat(tabSize)\n\n if (empty) {\n return editor.commands.insertContent(indent)\n }\n\n return editor.commands.command(({ tr }) => {\n const { from, to } = selection\n const text = state.doc.textBetween(from, to, '\\n', '\\n')\n const lines = text.split('\\n')\n const indentedText = lines.map(line => indent + line).join('\\n')\n\n tr.replaceWith(from, to, state.schema.text(indentedText))\n return true\n })\n },\n\n // handle shift+tab reverse indentation\n 'Shift-Tab': ({ editor }) => {\n if (!this.options.enableTabIndentation) {\n return false\n }\n\n const tabSize = this.options.tabSize ?? DEFAULT_TAB_SIZE\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if ($from.parent.type !== this.type) {\n return false\n }\n\n if (empty) {\n return editor.commands.command(({ tr }) => {\n const { pos } = $from\n const codeBlockStart = $from.start()\n const codeBlockEnd = $from.end()\n\n const allText = state.doc.textBetween(codeBlockStart, codeBlockEnd, '\\n', '\\n')\n const lines = allText.split('\\n')\n\n let currentLineIndex = 0\n let charCount = 0\n const relativeCursorPos = pos - codeBlockStart\n\n for (let i = 0; i < lines.length; i += 1) {\n if (charCount + lines[i].length >= relativeCursorPos) {\n currentLineIndex = i\n break\n }\n charCount += lines[i].length + 1\n }\n\n const currentLine = lines[currentLineIndex]\n const leadingSpaces = currentLine.match(/^ */)?.[0] || ''\n const spacesToRemove = Math.min(leadingSpaces.length, tabSize)\n\n if (spacesToRemove === 0) {\n return true\n }\n\n let lineStartPos = codeBlockStart\n for (let i = 0; i < currentLineIndex; i += 1) {\n lineStartPos += lines[i].length + 1\n }\n\n tr.delete(lineStartPos, lineStartPos + spacesToRemove)\n\n const cursorPosInLine = pos - lineStartPos\n if (cursorPosInLine <= spacesToRemove) {\n tr.setSelection(TextSelection.create(tr.doc, lineStartPos))\n }\n\n return true\n })\n }\n\n return editor.commands.command(({ tr }) => {\n const { from, to } = selection\n const text = state.doc.textBetween(from, to, '\\n', '\\n')\n const lines = text.split('\\n')\n const reverseIndentText = lines\n .map(line => {\n const leadingSpaces = line.match(/^ */)?.[0] || ''\n const spacesToRemove = Math.min(leadingSpaces.length, tabSize)\n return line.slice(spacesToRemove)\n })\n .join('\\n')\n\n tr.replaceWith(from, to, state.schema.text(reverseIndentText))\n return true\n })\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 up if there is no node before it\n ArrowUp: ({ editor }) => {\n if (!this.options.exitOnArrowUp) {\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 if ($from.parentOffset !== 0) {\n return false\n }\n\n const before = $from.before()\n\n if (before > 0) {\n return false\n }\n\n return editor.commands.insertDefaultBlock({ pos: before })\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 editor.commands.command(({ tr }) => {\n tr.setSelection(Selection.near(doc.resolve(after)))\n return true\n })\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(\n TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))),\n )\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"],"mappings":"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","names":["_a"]}
{"version":3,"file":"index.cjs","names":["Node","TextSelection","Selection","Plugin","PluginKey"],"sources":["../src/code-block.ts","../src/index.ts"],"sourcesContent":["import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, Selection, TextSelection } from '@tiptap/pm/state'\n\nconst DEFAULT_TAB_SIZE = 4\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 | null | undefined\n /**\n * Define whether the node should be exited on triple enter.\n * @default true\n */\n exitOnTripleEnter: boolean | null | undefined\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 | null | undefined\n /**\n * Define whether the node should be exited on arrow up if there is no node before it.\n * @default true\n */\n exitOnArrowUp: boolean | null | undefined\n /**\n * The default language.\n * @default null\n * @example 'js'\n */\n defaultLanguage: string | null | undefined\n /**\n * Enable tab key for indentation in code blocks.\n * @default false\n */\n enableTabIndentation: boolean | null | undefined\n /**\n * The number of spaces to use for tab indentation.\n * @default 4\n */\n tabSize: number | null | undefined\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 exitOnArrowUp: true,\n defaultLanguage: null,\n enableTabIndentation: false,\n tabSize: DEFAULT_TAB_SIZE,\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: this.options.defaultLanguage,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n\n if (!languageClassPrefix) {\n return null\n }\n\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 markdownTokenName: 'code',\n\n parseMarkdown: (token, helpers) => {\n if (\n token.raw?.startsWith('```') === false &&\n token.raw?.startsWith('~~~') === false &&\n token.codeBlockStyle !== 'indented'\n ) {\n return []\n }\n\n return helpers.createNode(\n 'codeBlock',\n { language: token.lang || null },\n token.text ? [helpers.createTextNode(token.text)] : [],\n )\n },\n\n renderMarkdown: (node, h) => {\n let output = ''\n const language = node.attrs?.language || ''\n\n if (!node.content) {\n output = `\\`\\`\\`${language}\\n\\n\\`\\`\\``\n } else {\n const lines = [`\\`\\`\\`${language}`, h.renderChildren(node.content), '```']\n output = lines.join('\\n')\n }\n\n return output\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes =>\n ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes =>\n ({ 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 // handle tab indentation\n Tab: ({ editor }) => {\n if (!this.options.enableTabIndentation) {\n return false\n }\n\n const tabSize = this.options.tabSize ?? DEFAULT_TAB_SIZE\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if ($from.parent.type !== this.type) {\n return false\n }\n\n const indent = ' '.repeat(tabSize)\n\n if (empty) {\n return editor.commands.insertContent(indent)\n }\n\n return editor.commands.command(({ tr }) => {\n const { from, to } = selection\n const text = state.doc.textBetween(from, to, '\\n', '\\n')\n const lines = text.split('\\n')\n const indentedText = lines.map(line => indent + line).join('\\n')\n\n tr.replaceWith(from, to, state.schema.text(indentedText))\n return true\n })\n },\n\n // handle shift+tab reverse indentation\n 'Shift-Tab': ({ editor }) => {\n if (!this.options.enableTabIndentation) {\n return false\n }\n\n const tabSize = this.options.tabSize ?? DEFAULT_TAB_SIZE\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if ($from.parent.type !== this.type) {\n return false\n }\n\n if (empty) {\n return editor.commands.command(({ tr }) => {\n const { pos } = $from\n const codeBlockStart = $from.start()\n const codeBlockEnd = $from.end()\n\n const allText = state.doc.textBetween(codeBlockStart, codeBlockEnd, '\\n', '\\n')\n const lines = allText.split('\\n')\n\n let currentLineIndex = 0\n let charCount = 0\n const relativeCursorPos = pos - codeBlockStart\n\n for (let i = 0; i < lines.length; i += 1) {\n if (charCount + lines[i].length >= relativeCursorPos) {\n currentLineIndex = i\n break\n }\n charCount += lines[i].length + 1\n }\n\n const currentLine = lines[currentLineIndex]\n const leadingSpaces = currentLine.match(/^ */)?.[0] || ''\n const spacesToRemove = Math.min(leadingSpaces.length, tabSize)\n\n if (spacesToRemove === 0) {\n return true\n }\n\n let lineStartPos = codeBlockStart\n for (let i = 0; i < currentLineIndex; i += 1) {\n lineStartPos += lines[i].length + 1\n }\n\n tr.delete(lineStartPos, lineStartPos + spacesToRemove)\n\n const cursorPosInLine = pos - lineStartPos\n if (cursorPosInLine <= spacesToRemove) {\n tr.setSelection(TextSelection.create(tr.doc, lineStartPos))\n }\n\n return true\n })\n }\n\n return editor.commands.command(({ tr }) => {\n const { from, to } = selection\n const text = state.doc.textBetween(from, to, '\\n', '\\n')\n const lines = text.split('\\n')\n const reverseIndentText = lines\n .map(line => {\n const leadingSpaces = line.match(/^ */)?.[0] || ''\n const spacesToRemove = Math.min(leadingSpaces.length, tabSize)\n return line.slice(spacesToRemove)\n })\n .join('\\n')\n\n tr.replaceWith(from, to, state.schema.text(reverseIndentText))\n return true\n })\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 up if there is no node before it\n ArrowUp: ({ editor }) => {\n if (!this.options.exitOnArrowUp) {\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 if ($from.parentOffset !== 0) {\n return false\n }\n\n const before = $from.before()\n\n if (before > 0) {\n return false\n }\n\n return editor.commands.insertDefaultBlock({ pos: before })\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 editor.commands.command(({ tr }) => {\n tr.setSelection(Selection.near(doc.resolve(after)))\n return true\n })\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(\n TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))),\n )\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","import { CodeBlock } from './code-block.js'\n\nexport * from './code-block.js'\n\nexport default CodeBlock\n"],"mappings":"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"}

@@ -1,68 +0,68 @@

import { Node } from '@tiptap/core';
import { Node } from "@tiptap/core";
//#region src/code-block.d.ts
interface CodeBlockOptions {
/**
* Adds a prefix to language classes that are applied to code tags.
* @default 'language-'
*/
languageClassPrefix: string | null | undefined;
/**
* Define whether the node should be exited on triple enter.
* @default true
*/
exitOnTripleEnter: boolean | null | undefined;
/**
* Define whether the node should be exited on arrow down if there is no node after it.
* @default true
*/
exitOnArrowDown: boolean | null | undefined;
/**
* Define whether the node should be exited on arrow up if there is no node before it.
* @default true
*/
exitOnArrowUp: boolean | null | undefined;
/**
* The default language.
* @default null
* @example 'js'
*/
defaultLanguage: string | null | undefined;
/**
* Enable tab key for indentation in code blocks.
* @default false
*/
enableTabIndentation: boolean | null | undefined;
/**
* The number of spaces to use for tab indentation.
* @default 4
*/
tabSize: number | null | undefined;
/**
* Custom HTML attributes that should be added to the rendered HTML tag.
* @default {}
* @example { class: 'foo' }
*/
HTMLAttributes: Record<string, any>;
/**
* Adds a prefix to language classes that are applied to code tags.
* @default 'language-'
*/
languageClassPrefix: string | null | undefined;
/**
* Define whether the node should be exited on triple enter.
* @default true
*/
exitOnTripleEnter: boolean | null | undefined;
/**
* Define whether the node should be exited on arrow down if there is no node after it.
* @default true
*/
exitOnArrowDown: boolean | null | undefined;
/**
* Define whether the node should be exited on arrow up if there is no node before it.
* @default true
*/
exitOnArrowUp: boolean | null | undefined;
/**
* The default language.
* @default null
* @example 'js'
*/
defaultLanguage: string | null | undefined;
/**
* Enable tab key for indentation in code blocks.
* @default false
*/
enableTabIndentation: boolean | null | undefined;
/**
* The number of spaces to use for tab indentation.
* @default 4
*/
tabSize: number | null | undefined;
/**
* Custom HTML attributes that should be added to the rendered HTML tag.
* @default {}
* @example { class: 'foo' }
*/
HTMLAttributes: Record<string, any>;
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
codeBlock: {
/**
* Set a code block
* @param attributes Code block attributes
* @example editor.commands.setCodeBlock({ language: 'javascript' })
*/
setCodeBlock: (attributes?: {
language: string;
}) => ReturnType;
/**
* Toggle a code block
* @param attributes Code block attributes
* @example editor.commands.toggleCodeBlock({ language: 'javascript' })
*/
toggleCodeBlock: (attributes?: {
language: string;
}) => ReturnType;
};
}
interface Commands<ReturnType> {
codeBlock: {
/**
* Set a code block
* @param attributes Code block attributes
* @example editor.commands.setCodeBlock({ language: 'javascript' })
*/
setCodeBlock: (attributes?: {
language: string;
}) => ReturnType;
/**
* Toggle a code block
* @param attributes Code block attributes
* @example editor.commands.toggleCodeBlock({ language: 'javascript' })
*/
toggleCodeBlock: (attributes?: {
language: string;
}) => ReturnType;
};
}
}

@@ -82,3 +82,4 @@ /**

declare const CodeBlock: Node<CodeBlockOptions, any>;
export { CodeBlock, type CodeBlockOptions, backtickInputRegex, CodeBlock as default, tildeInputRegex };
//#endregion
export { CodeBlock, CodeBlock as default, CodeBlockOptions, backtickInputRegex, tildeInputRegex };
//# sourceMappingURL=index.d.cts.map

@@ -1,68 +0,68 @@

import { Node } from '@tiptap/core';
import { Node } from "@tiptap/core";
//#region src/code-block.d.ts
interface CodeBlockOptions {
/**
* Adds a prefix to language classes that are applied to code tags.
* @default 'language-'
*/
languageClassPrefix: string | null | undefined;
/**
* Define whether the node should be exited on triple enter.
* @default true
*/
exitOnTripleEnter: boolean | null | undefined;
/**
* Define whether the node should be exited on arrow down if there is no node after it.
* @default true
*/
exitOnArrowDown: boolean | null | undefined;
/**
* Define whether the node should be exited on arrow up if there is no node before it.
* @default true
*/
exitOnArrowUp: boolean | null | undefined;
/**
* The default language.
* @default null
* @example 'js'
*/
defaultLanguage: string | null | undefined;
/**
* Enable tab key for indentation in code blocks.
* @default false
*/
enableTabIndentation: boolean | null | undefined;
/**
* The number of spaces to use for tab indentation.
* @default 4
*/
tabSize: number | null | undefined;
/**
* Custom HTML attributes that should be added to the rendered HTML tag.
* @default {}
* @example { class: 'foo' }
*/
HTMLAttributes: Record<string, any>;
/**
* Adds a prefix to language classes that are applied to code tags.
* @default 'language-'
*/
languageClassPrefix: string | null | undefined;
/**
* Define whether the node should be exited on triple enter.
* @default true
*/
exitOnTripleEnter: boolean | null | undefined;
/**
* Define whether the node should be exited on arrow down if there is no node after it.
* @default true
*/
exitOnArrowDown: boolean | null | undefined;
/**
* Define whether the node should be exited on arrow up if there is no node before it.
* @default true
*/
exitOnArrowUp: boolean | null | undefined;
/**
* The default language.
* @default null
* @example 'js'
*/
defaultLanguage: string | null | undefined;
/**
* Enable tab key for indentation in code blocks.
* @default false
*/
enableTabIndentation: boolean | null | undefined;
/**
* The number of spaces to use for tab indentation.
* @default 4
*/
tabSize: number | null | undefined;
/**
* Custom HTML attributes that should be added to the rendered HTML tag.
* @default {}
* @example { class: 'foo' }
*/
HTMLAttributes: Record<string, any>;
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
codeBlock: {
/**
* Set a code block
* @param attributes Code block attributes
* @example editor.commands.setCodeBlock({ language: 'javascript' })
*/
setCodeBlock: (attributes?: {
language: string;
}) => ReturnType;
/**
* Toggle a code block
* @param attributes Code block attributes
* @example editor.commands.toggleCodeBlock({ language: 'javascript' })
*/
toggleCodeBlock: (attributes?: {
language: string;
}) => ReturnType;
};
}
interface Commands<ReturnType> {
codeBlock: {
/**
* Set a code block
* @param attributes Code block attributes
* @example editor.commands.setCodeBlock({ language: 'javascript' })
*/
setCodeBlock: (attributes?: {
language: string;
}) => ReturnType;
/**
* Toggle a code block
* @param attributes Code block attributes
* @example editor.commands.toggleCodeBlock({ language: 'javascript' })
*/
toggleCodeBlock: (attributes?: {
language: string;
}) => ReturnType;
};
}
}

@@ -82,3 +82,4 @@ /**

declare const CodeBlock: Node<CodeBlockOptions, any>;
export { CodeBlock, type CodeBlockOptions, backtickInputRegex, CodeBlock as default, tildeInputRegex };
//#endregion
export { CodeBlock, CodeBlock as default, CodeBlockOptions, backtickInputRegex, tildeInputRegex };
//# sourceMappingURL=index.d.ts.map

@@ -1,345 +0,248 @@

// src/code-block.ts
import { mergeAttributes, Node, textblockTypeInputRule } from "@tiptap/core";
import { Node, mergeAttributes, textblockTypeInputRule } from "@tiptap/core";
import { Plugin, PluginKey, Selection, TextSelection } from "@tiptap/pm/state";
var DEFAULT_TAB_SIZE = 4;
var backtickInputRegex = /^```([a-z]+)?[\s\n]$/;
var tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/;
var CodeBlock = Node.create({
name: "codeBlock",
addOptions() {
return {
languageClassPrefix: "language-",
exitOnTripleEnter: true,
exitOnArrowDown: true,
exitOnArrowUp: true,
defaultLanguage: null,
enableTabIndentation: false,
tabSize: DEFAULT_TAB_SIZE,
HTMLAttributes: {}
};
},
content: "text*",
marks: "",
group: "block",
code: true,
defining: true,
addAttributes() {
return {
language: {
default: this.options.defaultLanguage,
parseHTML: (element) => {
var _a;
const { languageClassPrefix } = this.options;
if (!languageClassPrefix) {
return null;
}
const classNames = [...((_a = element.firstElementChild) == null ? void 0 : _a.classList) || []];
const languages = classNames.filter((className) => className.startsWith(languageClassPrefix)).map((className) => className.replace(languageClassPrefix, ""));
const language = languages[0];
if (!language) {
return null;
}
return language;
},
rendered: false
}
};
},
parseHTML() {
return [
{
tag: "pre",
preserveWhitespace: "full"
}
];
},
renderHTML({ node, HTMLAttributes }) {
return [
"pre",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
[
"code",
{
class: node.attrs.language ? this.options.languageClassPrefix + node.attrs.language : null
},
0
]
];
},
markdownTokenName: "code",
parseMarkdown: (token, helpers) => {
var _a, _b;
if (((_a = token.raw) == null ? void 0 : _a.startsWith("```")) === false && ((_b = token.raw) == null ? void 0 : _b.startsWith("~~~")) === false && token.codeBlockStyle !== "indented") {
return [];
}
return helpers.createNode(
"codeBlock",
{ language: token.lang || null },
token.text ? [helpers.createTextNode(token.text)] : []
);
},
renderMarkdown: (node, h) => {
var _a;
let output = "";
const language = ((_a = node.attrs) == null ? void 0 : _a.language) || "";
if (!node.content) {
output = `\`\`\`${language}
\`\`\``;
} else {
const lines = [`\`\`\`${language}`, h.renderChildren(node.content), "```"];
output = lines.join("\n");
}
return output;
},
addCommands() {
return {
setCodeBlock: (attributes) => ({ commands }) => {
return commands.setNode(this.name, attributes);
},
toggleCodeBlock: (attributes) => ({ commands }) => {
return commands.toggleNode(this.name, "paragraph", attributes);
}
};
},
addKeyboardShortcuts() {
return {
"Mod-Alt-c": () => this.editor.commands.toggleCodeBlock(),
// remove code block when at start of document or code block is empty
Backspace: () => {
const { empty, $anchor } = this.editor.state.selection;
const isAtStart = $anchor.pos === 1;
if (!empty || $anchor.parent.type.name !== this.name) {
return false;
}
if (isAtStart || !$anchor.parent.textContent.length) {
return this.editor.commands.clearNodes();
}
return false;
},
// handle tab indentation
Tab: ({ editor }) => {
var _a;
if (!this.options.enableTabIndentation) {
return false;
}
const tabSize = (_a = this.options.tabSize) != null ? _a : DEFAULT_TAB_SIZE;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if ($from.parent.type !== this.type) {
return false;
}
const indent = " ".repeat(tabSize);
if (empty) {
return editor.commands.insertContent(indent);
}
return editor.commands.command(({ tr }) => {
const { from, to } = selection;
const text = state.doc.textBetween(from, to, "\n", "\n");
const lines = text.split("\n");
const indentedText = lines.map((line) => indent + line).join("\n");
tr.replaceWith(from, to, state.schema.text(indentedText));
return true;
});
},
// handle shift+tab reverse indentation
"Shift-Tab": ({ editor }) => {
var _a;
if (!this.options.enableTabIndentation) {
return false;
}
const tabSize = (_a = this.options.tabSize) != null ? _a : DEFAULT_TAB_SIZE;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if ($from.parent.type !== this.type) {
return false;
}
if (empty) {
return editor.commands.command(({ tr }) => {
var _a2;
const { pos } = $from;
const codeBlockStart = $from.start();
const codeBlockEnd = $from.end();
const allText = state.doc.textBetween(codeBlockStart, codeBlockEnd, "\n", "\n");
const lines = allText.split("\n");
let currentLineIndex = 0;
let charCount = 0;
const relativeCursorPos = pos - codeBlockStart;
for (let i = 0; i < lines.length; i += 1) {
if (charCount + lines[i].length >= relativeCursorPos) {
currentLineIndex = i;
break;
}
charCount += lines[i].length + 1;
}
const currentLine = lines[currentLineIndex];
const leadingSpaces = ((_a2 = currentLine.match(/^ */)) == null ? void 0 : _a2[0]) || "";
const spacesToRemove = Math.min(leadingSpaces.length, tabSize);
if (spacesToRemove === 0) {
return true;
}
let lineStartPos = codeBlockStart;
for (let i = 0; i < currentLineIndex; i += 1) {
lineStartPos += lines[i].length + 1;
}
tr.delete(lineStartPos, lineStartPos + spacesToRemove);
const cursorPosInLine = pos - lineStartPos;
if (cursorPosInLine <= spacesToRemove) {
tr.setSelection(TextSelection.create(tr.doc, lineStartPos));
}
return true;
});
}
return editor.commands.command(({ tr }) => {
const { from, to } = selection;
const text = state.doc.textBetween(from, to, "\n", "\n");
const lines = text.split("\n");
const reverseIndentText = lines.map((line) => {
var _a2;
const leadingSpaces = ((_a2 = line.match(/^ */)) == null ? void 0 : _a2[0]) || "";
const spacesToRemove = Math.min(leadingSpaces.length, tabSize);
return line.slice(spacesToRemove);
}).join("\n");
tr.replaceWith(from, to, state.schema.text(reverseIndentText));
return true;
});
},
// exit node on triple enter
Enter: ({ editor }) => {
if (!this.options.exitOnTripleEnter) {
return false;
}
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) {
return false;
}
const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2;
const endsWithDoubleNewline = $from.parent.textContent.endsWith("\n\n");
if (!isAtEnd || !endsWithDoubleNewline) {
return false;
}
return editor.chain().command(({ tr }) => {
tr.delete($from.pos - 2, $from.pos);
return true;
}).exitCode().run();
},
// exit node on arrow up if there is no node before it
ArrowUp: ({ editor }) => {
if (!this.options.exitOnArrowUp) {
return false;
}
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) {
return false;
}
if ($from.parentOffset !== 0) {
return false;
}
const before = $from.before();
if (before > 0) {
return false;
}
return editor.commands.insertDefaultBlock({ pos: before });
},
// exit node on arrow down
ArrowDown: ({ editor }) => {
if (!this.options.exitOnArrowDown) {
return false;
}
const { state } = editor;
const { selection, doc } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) {
return false;
}
const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2;
if (!isAtEnd) {
return false;
}
const after = $from.after();
if (after === void 0) {
return false;
}
const nodeAfter = doc.nodeAt(after);
if (nodeAfter) {
return editor.commands.command(({ tr }) => {
tr.setSelection(Selection.near(doc.resolve(after)));
return true;
});
}
return editor.commands.exitCode();
}
};
},
addInputRules() {
return [
textblockTypeInputRule({
find: backtickInputRegex,
type: this.type,
getAttributes: (match) => ({
language: match[1]
})
}),
textblockTypeInputRule({
find: tildeInputRegex,
type: this.type,
getAttributes: (match) => ({
language: match[1]
})
})
];
},
addProseMirrorPlugins() {
return [
// this plugin creates a code block for pasted content from VS Code
// we can also detect the copied code language
new Plugin({
key: new PluginKey("codeBlockVSCodeHandler"),
props: {
handlePaste: (view, event) => {
if (!event.clipboardData) {
return false;
}
if (this.editor.isActive(this.type.name)) {
return false;
}
const text = event.clipboardData.getData("text/plain");
const vscode = event.clipboardData.getData("vscode-editor-data");
const vscodeData = vscode ? JSON.parse(vscode) : void 0;
const language = vscodeData == null ? void 0 : vscodeData.mode;
if (!text || !language) {
return false;
}
const { tr, schema } = view.state;
const textNode = schema.text(text.replace(/\r\n?/g, "\n"));
tr.replaceSelectionWith(this.type.create({ language }, textNode));
if (tr.selection.$from.parent.type !== this.type) {
tr.setSelection(
TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2)))
);
}
tr.setMeta("paste", true);
view.dispatch(tr);
return true;
}
}
})
];
}
//#region src/code-block.ts
const DEFAULT_TAB_SIZE = 4;
/**
* Matches a code block with backticks.
*/
const backtickInputRegex = /^```([a-z]+)?[\s\n]$/;
/**
* Matches a code block with tildes.
*/
const tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/;
/**
* This extension allows you to create code blocks.
* @see https://tiptap.dev/api/nodes/code-block
*/
const CodeBlock = Node.create({
name: "codeBlock",
addOptions() {
return {
languageClassPrefix: "language-",
exitOnTripleEnter: true,
exitOnArrowDown: true,
exitOnArrowUp: true,
defaultLanguage: null,
enableTabIndentation: false,
tabSize: DEFAULT_TAB_SIZE,
HTMLAttributes: {}
};
},
content: "text*",
marks: "",
group: "block",
code: true,
defining: true,
addAttributes() {
return { language: {
default: this.options.defaultLanguage,
parseHTML: (element) => {
var _element$firstElement;
const { languageClassPrefix } = this.options;
if (!languageClassPrefix) return null;
const language = [...((_element$firstElement = element.firstElementChild) === null || _element$firstElement === void 0 ? void 0 : _element$firstElement.classList) || []].filter((className) => className.startsWith(languageClassPrefix)).map((className) => className.replace(languageClassPrefix, ""))[0];
if (!language) return null;
return language;
},
rendered: false
} };
},
parseHTML() {
return [{
tag: "pre",
preserveWhitespace: "full"
}];
},
renderHTML({ node, HTMLAttributes }) {
return [
"pre",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
[
"code",
{ class: node.attrs.language ? this.options.languageClassPrefix + node.attrs.language : null },
0
]
];
},
markdownTokenName: "code",
parseMarkdown: (token, helpers) => {
var _token$raw, _token$raw2;
if (((_token$raw = token.raw) === null || _token$raw === void 0 ? void 0 : _token$raw.startsWith("```")) === false && ((_token$raw2 = token.raw) === null || _token$raw2 === void 0 ? void 0 : _token$raw2.startsWith("~~~")) === false && token.codeBlockStyle !== "indented") return [];
return helpers.createNode("codeBlock", { language: token.lang || null }, token.text ? [helpers.createTextNode(token.text)] : []);
},
renderMarkdown: (node, h) => {
var _node$attrs;
let output = "";
const language = ((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.language) || "";
if (!node.content) output = `\`\`\`${language}\n\n\`\`\``;
else output = [
`\`\`\`${language}`,
h.renderChildren(node.content),
"```"
].join("\n");
return output;
},
addCommands() {
return {
setCodeBlock: (attributes) => ({ commands }) => {
return commands.setNode(this.name, attributes);
},
toggleCodeBlock: (attributes) => ({ commands }) => {
return commands.toggleNode(this.name, "paragraph", attributes);
}
};
},
addKeyboardShortcuts() {
return {
"Mod-Alt-c": () => this.editor.commands.toggleCodeBlock(),
Backspace: () => {
const { empty, $anchor } = this.editor.state.selection;
const isAtStart = $anchor.pos === 1;
if (!empty || $anchor.parent.type.name !== this.name) return false;
if (isAtStart || !$anchor.parent.textContent.length) return this.editor.commands.clearNodes();
return false;
},
Tab: ({ editor }) => {
var _this$options$tabSize;
if (!this.options.enableTabIndentation) return false;
const tabSize = (_this$options$tabSize = this.options.tabSize) !== null && _this$options$tabSize !== void 0 ? _this$options$tabSize : DEFAULT_TAB_SIZE;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if ($from.parent.type !== this.type) return false;
const indent = " ".repeat(tabSize);
if (empty) return editor.commands.insertContent(indent);
return editor.commands.command(({ tr }) => {
const { from, to } = selection;
const indentedText = state.doc.textBetween(from, to, "\n", "\n").split("\n").map((line) => indent + line).join("\n");
tr.replaceWith(from, to, state.schema.text(indentedText));
return true;
});
},
"Shift-Tab": ({ editor }) => {
var _this$options$tabSize2;
if (!this.options.enableTabIndentation) return false;
const tabSize = (_this$options$tabSize2 = this.options.tabSize) !== null && _this$options$tabSize2 !== void 0 ? _this$options$tabSize2 : DEFAULT_TAB_SIZE;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if ($from.parent.type !== this.type) return false;
if (empty) return editor.commands.command(({ tr }) => {
var _currentLine$match;
const { pos } = $from;
const codeBlockStart = $from.start();
const codeBlockEnd = $from.end();
const lines = state.doc.textBetween(codeBlockStart, codeBlockEnd, "\n", "\n").split("\n");
let currentLineIndex = 0;
let charCount = 0;
const relativeCursorPos = pos - codeBlockStart;
for (let i = 0; i < lines.length; i += 1) {
if (charCount + lines[i].length >= relativeCursorPos) {
currentLineIndex = i;
break;
}
charCount += lines[i].length + 1;
}
const leadingSpaces = ((_currentLine$match = lines[currentLineIndex].match(/^ */)) === null || _currentLine$match === void 0 ? void 0 : _currentLine$match[0]) || "";
const spacesToRemove = Math.min(leadingSpaces.length, tabSize);
if (spacesToRemove === 0) return true;
let lineStartPos = codeBlockStart;
for (let i = 0; i < currentLineIndex; i += 1) lineStartPos += lines[i].length + 1;
tr.delete(lineStartPos, lineStartPos + spacesToRemove);
if (pos - lineStartPos <= spacesToRemove) tr.setSelection(TextSelection.create(tr.doc, lineStartPos));
return true;
});
return editor.commands.command(({ tr }) => {
const { from, to } = selection;
const reverseIndentText = state.doc.textBetween(from, to, "\n", "\n").split("\n").map((line) => {
var _line$match;
const leadingSpaces = ((_line$match = line.match(/^ */)) === null || _line$match === void 0 ? void 0 : _line$match[0]) || "";
const spacesToRemove = Math.min(leadingSpaces.length, tabSize);
return line.slice(spacesToRemove);
}).join("\n");
tr.replaceWith(from, to, state.schema.text(reverseIndentText));
return true;
});
},
Enter: ({ editor }) => {
if (!this.options.exitOnTripleEnter) return false;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) return false;
const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2;
const endsWithDoubleNewline = $from.parent.textContent.endsWith("\n\n");
if (!isAtEnd || !endsWithDoubleNewline) return false;
return editor.chain().command(({ tr }) => {
tr.delete($from.pos - 2, $from.pos);
return true;
}).exitCode().run();
},
ArrowUp: ({ editor }) => {
if (!this.options.exitOnArrowUp) return false;
const { state } = editor;
const { selection } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) return false;
if ($from.parentOffset !== 0) return false;
const before = $from.before();
if (before > 0) return false;
return editor.commands.insertDefaultBlock({ pos: before });
},
ArrowDown: ({ editor }) => {
if (!this.options.exitOnArrowDown) return false;
const { state } = editor;
const { selection, doc } = state;
const { $from, empty } = selection;
if (!empty || $from.parent.type !== this.type) return false;
if (!($from.parentOffset === $from.parent.nodeSize - 2)) return false;
const after = $from.after();
if (after === void 0) return false;
if (doc.nodeAt(after)) return editor.commands.command(({ tr }) => {
tr.setSelection(Selection.near(doc.resolve(after)));
return true;
});
return editor.commands.exitCode();
}
};
},
addInputRules() {
return [textblockTypeInputRule({
find: backtickInputRegex,
type: this.type,
getAttributes: (match) => ({ language: match[1] })
}), textblockTypeInputRule({
find: tildeInputRegex,
type: this.type,
getAttributes: (match) => ({ language: match[1] })
})];
},
addProseMirrorPlugins() {
return [new Plugin({
key: new PluginKey("codeBlockVSCodeHandler"),
props: { handlePaste: (view, event) => {
if (!event.clipboardData) return false;
if (this.editor.isActive(this.type.name)) return false;
const text = event.clipboardData.getData("text/plain");
const vscode = event.clipboardData.getData("vscode-editor-data");
const vscodeData = vscode ? JSON.parse(vscode) : void 0;
const language = vscodeData === null || vscodeData === void 0 ? void 0 : vscodeData.mode;
if (!text || !language) return false;
const { tr, schema } = view.state;
const textNode = schema.text(text.replace(/\r\n?/g, "\n"));
tr.replaceSelectionWith(this.type.create({ language }, textNode));
if (tr.selection.$from.parent.type !== this.type) tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))));
tr.setMeta("paste", true);
view.dispatch(tr);
return true;
} }
})];
}
});
//#endregion
//#region src/index.ts
var src_default = CodeBlock;
//#endregion
export { CodeBlock, backtickInputRegex, src_default as default, tildeInputRegex };
// src/index.ts
var index_default = CodeBlock;
export {
CodeBlock,
backtickInputRegex,
index_default as default,
tildeInputRegex
};
//# sourceMappingURL=index.js.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/code-block.ts","../src/index.ts"],"sourcesContent":["import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, Selection, TextSelection } from '@tiptap/pm/state'\n\nconst DEFAULT_TAB_SIZE = 4\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 | null | undefined\n /**\n * Define whether the node should be exited on triple enter.\n * @default true\n */\n exitOnTripleEnter: boolean | null | undefined\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 | null | undefined\n /**\n * Define whether the node should be exited on arrow up if there is no node before it.\n * @default true\n */\n exitOnArrowUp: boolean | null | undefined\n /**\n * The default language.\n * @default null\n * @example 'js'\n */\n defaultLanguage: string | null | undefined\n /**\n * Enable tab key for indentation in code blocks.\n * @default false\n */\n enableTabIndentation: boolean | null | undefined\n /**\n * The number of spaces to use for tab indentation.\n * @default 4\n */\n tabSize: number | null | undefined\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 exitOnArrowUp: true,\n defaultLanguage: null,\n enableTabIndentation: false,\n tabSize: DEFAULT_TAB_SIZE,\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: this.options.defaultLanguage,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n\n if (!languageClassPrefix) {\n return null\n }\n\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 markdownTokenName: 'code',\n\n parseMarkdown: (token, helpers) => {\n if (\n token.raw?.startsWith('```') === false &&\n token.raw?.startsWith('~~~') === false &&\n token.codeBlockStyle !== 'indented'\n ) {\n return []\n }\n\n return helpers.createNode(\n 'codeBlock',\n { language: token.lang || null },\n token.text ? [helpers.createTextNode(token.text)] : [],\n )\n },\n\n renderMarkdown: (node, h) => {\n let output = ''\n const language = node.attrs?.language || ''\n\n if (!node.content) {\n output = `\\`\\`\\`${language}\\n\\n\\`\\`\\``\n } else {\n const lines = [`\\`\\`\\`${language}`, h.renderChildren(node.content), '```']\n output = lines.join('\\n')\n }\n\n return output\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes =>\n ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes =>\n ({ 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 // handle tab indentation\n Tab: ({ editor }) => {\n if (!this.options.enableTabIndentation) {\n return false\n }\n\n const tabSize = this.options.tabSize ?? DEFAULT_TAB_SIZE\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if ($from.parent.type !== this.type) {\n return false\n }\n\n const indent = ' '.repeat(tabSize)\n\n if (empty) {\n return editor.commands.insertContent(indent)\n }\n\n return editor.commands.command(({ tr }) => {\n const { from, to } = selection\n const text = state.doc.textBetween(from, to, '\\n', '\\n')\n const lines = text.split('\\n')\n const indentedText = lines.map(line => indent + line).join('\\n')\n\n tr.replaceWith(from, to, state.schema.text(indentedText))\n return true\n })\n },\n\n // handle shift+tab reverse indentation\n 'Shift-Tab': ({ editor }) => {\n if (!this.options.enableTabIndentation) {\n return false\n }\n\n const tabSize = this.options.tabSize ?? DEFAULT_TAB_SIZE\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if ($from.parent.type !== this.type) {\n return false\n }\n\n if (empty) {\n return editor.commands.command(({ tr }) => {\n const { pos } = $from\n const codeBlockStart = $from.start()\n const codeBlockEnd = $from.end()\n\n const allText = state.doc.textBetween(codeBlockStart, codeBlockEnd, '\\n', '\\n')\n const lines = allText.split('\\n')\n\n let currentLineIndex = 0\n let charCount = 0\n const relativeCursorPos = pos - codeBlockStart\n\n for (let i = 0; i < lines.length; i += 1) {\n if (charCount + lines[i].length >= relativeCursorPos) {\n currentLineIndex = i\n break\n }\n charCount += lines[i].length + 1\n }\n\n const currentLine = lines[currentLineIndex]\n const leadingSpaces = currentLine.match(/^ */)?.[0] || ''\n const spacesToRemove = Math.min(leadingSpaces.length, tabSize)\n\n if (spacesToRemove === 0) {\n return true\n }\n\n let lineStartPos = codeBlockStart\n for (let i = 0; i < currentLineIndex; i += 1) {\n lineStartPos += lines[i].length + 1\n }\n\n tr.delete(lineStartPos, lineStartPos + spacesToRemove)\n\n const cursorPosInLine = pos - lineStartPos\n if (cursorPosInLine <= spacesToRemove) {\n tr.setSelection(TextSelection.create(tr.doc, lineStartPos))\n }\n\n return true\n })\n }\n\n return editor.commands.command(({ tr }) => {\n const { from, to } = selection\n const text = state.doc.textBetween(from, to, '\\n', '\\n')\n const lines = text.split('\\n')\n const reverseIndentText = lines\n .map(line => {\n const leadingSpaces = line.match(/^ */)?.[0] || ''\n const spacesToRemove = Math.min(leadingSpaces.length, tabSize)\n return line.slice(spacesToRemove)\n })\n .join('\\n')\n\n tr.replaceWith(from, to, state.schema.text(reverseIndentText))\n return true\n })\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 up if there is no node before it\n ArrowUp: ({ editor }) => {\n if (!this.options.exitOnArrowUp) {\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 if ($from.parentOffset !== 0) {\n return false\n }\n\n const before = $from.before()\n\n if (before > 0) {\n return false\n }\n\n return editor.commands.insertDefaultBlock({ pos: before })\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 editor.commands.command(({ tr }) => {\n tr.setSelection(Selection.near(doc.resolve(after)))\n return true\n })\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(\n TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))),\n )\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","import { CodeBlock } from './code-block.js'\n\nexport * from './code-block.js'\n\nexport default CodeBlock\n"],"mappings":"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","names":["_a"]}
{"version":3,"file":"index.js","names":[],"sources":["../src/code-block.ts","../src/index.ts"],"sourcesContent":["import { mergeAttributes, Node, textblockTypeInputRule } from '@tiptap/core'\nimport { Plugin, PluginKey, Selection, TextSelection } from '@tiptap/pm/state'\n\nconst DEFAULT_TAB_SIZE = 4\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 | null | undefined\n /**\n * Define whether the node should be exited on triple enter.\n * @default true\n */\n exitOnTripleEnter: boolean | null | undefined\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 | null | undefined\n /**\n * Define whether the node should be exited on arrow up if there is no node before it.\n * @default true\n */\n exitOnArrowUp: boolean | null | undefined\n /**\n * The default language.\n * @default null\n * @example 'js'\n */\n defaultLanguage: string | null | undefined\n /**\n * Enable tab key for indentation in code blocks.\n * @default false\n */\n enableTabIndentation: boolean | null | undefined\n /**\n * The number of spaces to use for tab indentation.\n * @default 4\n */\n tabSize: number | null | undefined\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 exitOnArrowUp: true,\n defaultLanguage: null,\n enableTabIndentation: false,\n tabSize: DEFAULT_TAB_SIZE,\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: this.options.defaultLanguage,\n parseHTML: element => {\n const { languageClassPrefix } = this.options\n\n if (!languageClassPrefix) {\n return null\n }\n\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 markdownTokenName: 'code',\n\n parseMarkdown: (token, helpers) => {\n if (\n token.raw?.startsWith('```') === false &&\n token.raw?.startsWith('~~~') === false &&\n token.codeBlockStyle !== 'indented'\n ) {\n return []\n }\n\n return helpers.createNode(\n 'codeBlock',\n { language: token.lang || null },\n token.text ? [helpers.createTextNode(token.text)] : [],\n )\n },\n\n renderMarkdown: (node, h) => {\n let output = ''\n const language = node.attrs?.language || ''\n\n if (!node.content) {\n output = `\\`\\`\\`${language}\\n\\n\\`\\`\\``\n } else {\n const lines = [`\\`\\`\\`${language}`, h.renderChildren(node.content), '```']\n output = lines.join('\\n')\n }\n\n return output\n },\n\n addCommands() {\n return {\n setCodeBlock:\n attributes =>\n ({ commands }) => {\n return commands.setNode(this.name, attributes)\n },\n toggleCodeBlock:\n attributes =>\n ({ 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 // handle tab indentation\n Tab: ({ editor }) => {\n if (!this.options.enableTabIndentation) {\n return false\n }\n\n const tabSize = this.options.tabSize ?? DEFAULT_TAB_SIZE\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if ($from.parent.type !== this.type) {\n return false\n }\n\n const indent = ' '.repeat(tabSize)\n\n if (empty) {\n return editor.commands.insertContent(indent)\n }\n\n return editor.commands.command(({ tr }) => {\n const { from, to } = selection\n const text = state.doc.textBetween(from, to, '\\n', '\\n')\n const lines = text.split('\\n')\n const indentedText = lines.map(line => indent + line).join('\\n')\n\n tr.replaceWith(from, to, state.schema.text(indentedText))\n return true\n })\n },\n\n // handle shift+tab reverse indentation\n 'Shift-Tab': ({ editor }) => {\n if (!this.options.enableTabIndentation) {\n return false\n }\n\n const tabSize = this.options.tabSize ?? DEFAULT_TAB_SIZE\n const { state } = editor\n const { selection } = state\n const { $from, empty } = selection\n\n if ($from.parent.type !== this.type) {\n return false\n }\n\n if (empty) {\n return editor.commands.command(({ tr }) => {\n const { pos } = $from\n const codeBlockStart = $from.start()\n const codeBlockEnd = $from.end()\n\n const allText = state.doc.textBetween(codeBlockStart, codeBlockEnd, '\\n', '\\n')\n const lines = allText.split('\\n')\n\n let currentLineIndex = 0\n let charCount = 0\n const relativeCursorPos = pos - codeBlockStart\n\n for (let i = 0; i < lines.length; i += 1) {\n if (charCount + lines[i].length >= relativeCursorPos) {\n currentLineIndex = i\n break\n }\n charCount += lines[i].length + 1\n }\n\n const currentLine = lines[currentLineIndex]\n const leadingSpaces = currentLine.match(/^ */)?.[0] || ''\n const spacesToRemove = Math.min(leadingSpaces.length, tabSize)\n\n if (spacesToRemove === 0) {\n return true\n }\n\n let lineStartPos = codeBlockStart\n for (let i = 0; i < currentLineIndex; i += 1) {\n lineStartPos += lines[i].length + 1\n }\n\n tr.delete(lineStartPos, lineStartPos + spacesToRemove)\n\n const cursorPosInLine = pos - lineStartPos\n if (cursorPosInLine <= spacesToRemove) {\n tr.setSelection(TextSelection.create(tr.doc, lineStartPos))\n }\n\n return true\n })\n }\n\n return editor.commands.command(({ tr }) => {\n const { from, to } = selection\n const text = state.doc.textBetween(from, to, '\\n', '\\n')\n const lines = text.split('\\n')\n const reverseIndentText = lines\n .map(line => {\n const leadingSpaces = line.match(/^ */)?.[0] || ''\n const spacesToRemove = Math.min(leadingSpaces.length, tabSize)\n return line.slice(spacesToRemove)\n })\n .join('\\n')\n\n tr.replaceWith(from, to, state.schema.text(reverseIndentText))\n return true\n })\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 up if there is no node before it\n ArrowUp: ({ editor }) => {\n if (!this.options.exitOnArrowUp) {\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 if ($from.parentOffset !== 0) {\n return false\n }\n\n const before = $from.before()\n\n if (before > 0) {\n return false\n }\n\n return editor.commands.insertDefaultBlock({ pos: before })\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 editor.commands.command(({ tr }) => {\n tr.setSelection(Selection.near(doc.resolve(after)))\n return true\n })\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(\n TextSelection.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2))),\n )\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","import { CodeBlock } from './code-block.js'\n\nexport * from './code-block.js'\n\nexport default CodeBlock\n"],"mappings":";;;AAGA,MAAM,mBAAmB;;;;AAqEzB,MAAa,qBAAqB;;;;AAKlC,MAAa,kBAAkB;;;;;AAM/B,MAAa,YAAY,KAAK,OAAyB;CACrD,MAAM;CAEN,aAAa;EACX,OAAO;GACL,qBAAqB;GACrB,mBAAmB;GACnB,iBAAiB;GACjB,eAAe;GACf,iBAAiB;GACjB,sBAAsB;GACtB,SAAS;GACT,gBAAgB,CAAC;EACnB;CACF;CAEA,SAAS;CAET,OAAO;CAEP,OAAO;CAEP,MAAM;CAEN,UAAU;CAEV,gBAAgB;EACd,OAAO,EACL,UAAU;GACR,SAAS,KAAK,QAAQ;GACtB,YAAW,YAAW;;IACpB,MAAM,EAAE,wBAAwB,KAAK;IAErC,IAAI,CAAC,qBACH,OAAO;IAOT,MAAM,WAHY,CADE,KAAA,wBAAI,QAAQ,uBAAA,QAAA,0BAAA,KAAA,IAAA,KAAA,IAAA,sBAAmB,cAAa,CAAC,CACtC,CAAC,CACzB,QAAO,cAAa,UAAU,WAAW,mBAAmB,CAAC,CAAC,CAC9D,KAAI,cAAa,UAAU,QAAQ,qBAAqB,EAAE,CACpC,CAAC,CAAC;IAE3B,IAAI,CAAC,UACH,OAAO;IAGT,OAAO;GACT;GACA,UAAU;EACZ,EACF;CACF;CAEA,YAAY;EACV,OAAO,CACL;GACE,KAAK;GACL,oBAAoB;EACtB,CACF;CACF;CAEA,WAAW,EAAE,MAAM,kBAAkB;EACnC,OAAO;GACL;GACA,gBAAgB,KAAK,QAAQ,gBAAgB,cAAc;GAC3D;IACE;IACA,EACE,OAAO,KAAK,MAAM,WACd,KAAK,QAAQ,sBAAsB,KAAK,MAAM,WAC9C,KACN;IACA;GACF;EACF;CACF;CAEA,mBAAmB;CAEnB,gBAAgB,OAAO,YAAY;;EACjC,MAAA,aACE,MAAM,SAAA,QAAA,eAAA,KAAA,IAAA,KAAA,IAAA,WAAK,WAAW,KAAK,OAAM,WAAA,cACjC,MAAM,SAAA,QAAA,gBAAA,KAAA,IAAA,KAAA,IAAA,YAAK,WAAW,KAAK,OAAM,SACjC,MAAM,mBAAmB,YAEzB,OAAO,CAAC;EAGV,OAAO,QAAQ,WACb,aACA,EAAE,UAAU,MAAM,QAAQ,KAAK,GAC/B,MAAM,OAAO,CAAC,QAAQ,eAAe,MAAM,IAAI,CAAC,IAAI,CAAC,CACvD;CACF;CAEA,iBAAiB,MAAM,MAAM;;EAC3B,IAAI,SAAS;EACb,MAAM,aAAA,cAAW,KAAK,WAAA,QAAA,gBAAA,KAAA,IAAA,KAAA,IAAA,YAAO,aAAY;EAEzC,IAAI,CAAC,KAAK,SACR,SAAS,SAAS,SAAS;OAG3B,SAAS;GADM,SAAS;GAAY,EAAE,eAAe,KAAK,OAAO;GAAG;EACvD,CAAC,CAAC,KAAK,IAAI;EAG1B,OAAO;CACT;CAEA,cAAc;EACZ,OAAO;GACL,eACE,gBACC,EAAE,eAAe;IAChB,OAAO,SAAS,QAAQ,KAAK,MAAM,UAAU;GAC/C;GACF,kBACE,gBACC,EAAE,eAAe;IAChB,OAAO,SAAS,WAAW,KAAK,MAAM,aAAa,UAAU;GAC/D;EACJ;CACF;CAEA,uBAAuB;EACrB,OAAO;GACL,mBAAmB,KAAK,OAAO,SAAS,gBAAgB;GAGxD,iBAAiB;IACf,MAAM,EAAE,OAAO,YAAY,KAAK,OAAO,MAAM;IAC7C,MAAM,YAAY,QAAQ,QAAQ;IAElC,IAAI,CAAC,SAAS,QAAQ,OAAO,KAAK,SAAS,KAAK,MAC9C,OAAO;IAGT,IAAI,aAAa,CAAC,QAAQ,OAAO,YAAY,QAC3C,OAAO,KAAK,OAAO,SAAS,WAAW;IAGzC,OAAO;GACT;GAGA,MAAM,EAAE,aAAa;;IACnB,IAAI,CAAC,KAAK,QAAQ,sBAChB,OAAO;IAGT,MAAM,WAAA,wBAAU,KAAK,QAAQ,aAAA,QAAA,0BAAA,KAAA,IAAA,wBAAW;IACxC,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,cAAc;IACtB,MAAM,EAAE,OAAO,UAAU;IAEzB,IAAI,MAAM,OAAO,SAAS,KAAK,MAC7B,OAAO;IAGT,MAAM,SAAS,IAAI,OAAO,OAAO;IAEjC,IAAI,OACF,OAAO,OAAO,SAAS,cAAc,MAAM;IAG7C,OAAO,OAAO,SAAS,SAAS,EAAE,SAAS;KACzC,MAAM,EAAE,MAAM,OAAO;KAGrB,MAAM,eAFO,MAAM,IAAI,YAAY,MAAM,IAAI,MAAM,IAClC,CAAC,CAAC,MAAM,IACA,CAAC,CAAC,KAAI,SAAQ,SAAS,IAAI,CAAC,CAAC,KAAK,IAAI;KAE/D,GAAG,YAAY,MAAM,IAAI,MAAM,OAAO,KAAK,YAAY,CAAC;KACxD,OAAO;IACT,CAAC;GACH;GAGA,cAAc,EAAE,aAAa;;IAC3B,IAAI,CAAC,KAAK,QAAQ,sBAChB,OAAO;IAGT,MAAM,WAAA,yBAAU,KAAK,QAAQ,aAAA,QAAA,2BAAA,KAAA,IAAA,yBAAW;IACxC,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,cAAc;IACtB,MAAM,EAAE,OAAO,UAAU;IAEzB,IAAI,MAAM,OAAO,SAAS,KAAK,MAC7B,OAAO;IAGT,IAAI,OACF,OAAO,OAAO,SAAS,SAAS,EAAE,SAAS;;KACzC,MAAM,EAAE,QAAQ;KAChB,MAAM,iBAAiB,MAAM,MAAM;KACnC,MAAM,eAAe,MAAM,IAAI;KAG/B,MAAM,QADU,MAAM,IAAI,YAAY,gBAAgB,cAAc,MAAM,IACtD,CAAC,CAAC,MAAM,IAAI;KAEhC,IAAI,mBAAmB;KACvB,IAAI,YAAY;KAChB,MAAM,oBAAoB,MAAM;KAEhC,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;MACxC,IAAI,YAAY,MAAM,EAAE,CAAC,UAAU,mBAAmB;OACpD,mBAAmB;OACnB;MACF;MACA,aAAa,MAAM,EAAE,CAAC,SAAS;KACjC;KAGA,MAAM,kBAAA,qBADc,MAAM,iBACO,CAAC,MAAM,KAAK,OAAA,QAAA,uBAAA,KAAA,IAAA,KAAA,IAAA,mBAAI,OAAM;KACvD,MAAM,iBAAiB,KAAK,IAAI,cAAc,QAAQ,OAAO;KAE7D,IAAI,mBAAmB,GACrB,OAAO;KAGT,IAAI,eAAe;KACnB,KAAK,IAAI,IAAI,GAAG,IAAI,kBAAkB,KAAK,GACzC,gBAAgB,MAAM,EAAE,CAAC,SAAS;KAGpC,GAAG,OAAO,cAAc,eAAe,cAAc;KAGrD,IADwB,MAAM,gBACP,gBACrB,GAAG,aAAa,cAAc,OAAO,GAAG,KAAK,YAAY,CAAC;KAG5D,OAAO;IACT,CAAC;IAGH,OAAO,OAAO,SAAS,SAAS,EAAE,SAAS;KACzC,MAAM,EAAE,MAAM,OAAO;KAGrB,MAAM,oBAFO,MAAM,IAAI,YAAY,MAAM,IAAI,MAAM,IAClC,CAAC,CAAC,MAAM,IACK,CAAC,CAC5B,KAAI,SAAQ;;MACX,MAAM,kBAAA,cAAgB,KAAK,MAAM,KAAK,OAAA,QAAA,gBAAA,KAAA,IAAA,KAAA,IAAA,YAAI,OAAM;MAChD,MAAM,iBAAiB,KAAK,IAAI,cAAc,QAAQ,OAAO;MAC7D,OAAO,KAAK,MAAM,cAAc;KAClC,CAAC,CAAC,CACD,KAAK,IAAI;KAEZ,GAAG,YAAY,MAAM,IAAI,MAAM,OAAO,KAAK,iBAAiB,CAAC;KAC7D,OAAO;IACT,CAAC;GACH;GAGA,QAAQ,EAAE,aAAa;IACrB,IAAI,CAAC,KAAK,QAAQ,mBAChB,OAAO;IAGT,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,cAAc;IACtB,MAAM,EAAE,OAAO,UAAU;IAEzB,IAAI,CAAC,SAAS,MAAM,OAAO,SAAS,KAAK,MACvC,OAAO;IAGT,MAAM,UAAU,MAAM,iBAAiB,MAAM,OAAO,WAAW;IAC/D,MAAM,wBAAwB,MAAM,OAAO,YAAY,SAAS,MAAM;IAEtE,IAAI,CAAC,WAAW,CAAC,uBACf,OAAO;IAGT,OAAO,OACJ,MAAM,CAAC,CACP,SAAS,EAAE,SAAS;KACnB,GAAG,OAAO,MAAM,MAAM,GAAG,MAAM,GAAG;KAElC,OAAO;IACT,CAAC,CAAC,CACD,SAAS,CAAC,CACV,IAAI;GACT;GAGA,UAAU,EAAE,aAAa;IACvB,IAAI,CAAC,KAAK,QAAQ,eAChB,OAAO;IAGT,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,cAAc;IACtB,MAAM,EAAE,OAAO,UAAU;IAEzB,IAAI,CAAC,SAAS,MAAM,OAAO,SAAS,KAAK,MACvC,OAAO;IAGT,IAAI,MAAM,iBAAiB,GACzB,OAAO;IAGT,MAAM,SAAS,MAAM,OAAO;IAE5B,IAAI,SAAS,GACX,OAAO;IAGT,OAAO,OAAO,SAAS,mBAAmB,EAAE,KAAK,OAAO,CAAC;GAC3D;GAGA,YAAY,EAAE,aAAa;IACzB,IAAI,CAAC,KAAK,QAAQ,iBAChB,OAAO;IAGT,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,WAAW,QAAQ;IAC3B,MAAM,EAAE,OAAO,UAAU;IAEzB,IAAI,CAAC,SAAS,MAAM,OAAO,SAAS,KAAK,MACvC,OAAO;IAKT,IAAI,EAFY,MAAM,iBAAiB,MAAM,OAAO,WAAW,IAG7D,OAAO;IAGT,MAAM,QAAQ,MAAM,MAAM;IAE1B,IAAI,UAAU,KAAA,GACZ,OAAO;IAKT,IAFkB,IAAI,OAAO,KAEjB,GACV,OAAO,OAAO,SAAS,SAAS,EAAE,SAAS;KACzC,GAAG,aAAa,UAAU,KAAK,IAAI,QAAQ,KAAK,CAAC,CAAC;KAClD,OAAO;IACT,CAAC;IAGH,OAAO,OAAO,SAAS,SAAS;GAClC;EACF;CACF;CAEA,gBAAgB;EACd,OAAO,CACL,uBAAuB;GACrB,MAAM;GACN,MAAM,KAAK;GACX,gBAAe,WAAU,EACvB,UAAU,MAAM,GAClB;EACF,CAAC,GACD,uBAAuB;GACrB,MAAM;GACN,MAAM,KAAK;GACX,gBAAe,WAAU,EACvB,UAAU,MAAM,GAClB;EACF,CAAC,CACH;CACF;CAEA,wBAAwB;EACtB,OAAO,CAGL,IAAI,OAAO;GACT,KAAK,IAAI,UAAU,wBAAwB;GAC3C,OAAO,EACL,cAAc,MAAM,UAAU;IAC5B,IAAI,CAAC,MAAM,eACT,OAAO;IAIT,IAAI,KAAK,OAAO,SAAS,KAAK,KAAK,IAAI,GACrC,OAAO;IAGT,MAAM,OAAO,MAAM,cAAc,QAAQ,YAAY;IACrD,MAAM,SAAS,MAAM,cAAc,QAAQ,oBAAoB;IAC/D,MAAM,aAAa,SAAS,KAAK,MAAM,MAAM,IAAI,KAAA;IACjD,MAAM,WAAA,eAAA,QAAA,eAAA,KAAA,IAAA,KAAA,IAAW,WAAY;IAE7B,IAAI,CAAC,QAAQ,CAAC,UACZ,OAAO;IAGT,MAAM,EAAE,IAAI,WAAW,KAAK;IAK5B,MAAM,WAAW,OAAO,KAAK,KAAK,QAAQ,UAAU,IAAI,CAAC;IAIzD,GAAG,qBAAqB,KAAK,KAAK,OAAO,EAAE,SAAS,GAAG,QAAQ,CAAC;IAEhE,IAAI,GAAG,UAAU,MAAM,OAAO,SAAS,KAAK,MAE1C,GAAG,aACD,cAAc,KAAK,GAAG,IAAI,QAAQ,KAAK,IAAI,GAAG,GAAG,UAAU,OAAO,CAAC,CAAC,CAAC,CACvE;IAMF,GAAG,QAAQ,SAAS,IAAI;IAExB,KAAK,SAAS,EAAE;IAEhB,OAAO;GACT,EACF;EACF,CAAC,CACH;CACF;AACF,CAAC;;;AC7fD,IAAA,cAAe"}
{
"name": "@tiptap/extension-code-block",
"version": "3.30.2",
"version": "3.30.3",
"description": "code block extension for tiptap",

@@ -42,12 +42,12 @@ "keywords": [

"devDependencies": {
"@tiptap/core": "^3.30.2",
"@tiptap/pm": "^3.30.2"
"@tiptap/core": "^3.30.3",
"@tiptap/pm": "^3.30.3"
},
"peerDependencies": {
"@tiptap/core": "3.30.2",
"@tiptap/pm": "3.30.2"
"@tiptap/core": "3.30.3",
"@tiptap/pm": "3.30.3"
},
"scripts": {
"build": "tsup"
"build": "vp pack"
}
}