UNPKG

@tiptap/extension-code-block

Version:

code block extension for tiptap

230 lines (227 loc) 7.32 kB
"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 }); module.exports = __toCommonJS(index_exports); // src/code-block.ts var import_core = require("@tiptap/core"); var import_state = require("@tiptap/pm/state"); 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, defaultLanguage: null, 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; 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 ] ]; }, 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; }, // 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 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; } } }) ]; } }); // 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