prosemirror-codemirror-block

ProseMirror CodeMirror code block plugin

Usage no npm install needed!

<script type="module">
  import prosemirrorCodemirrorBlock from 'https://cdn.skypack.dev/prosemirror-codemirror-block';
</script>

README

prosemirror-codemirror-block

alt text

Sponsored by Skiff - a private, end-to-end encrypted, and decentralized workspace.

By Viktor Váczi at Emergence Engineering

Try it out at https://emergence-engineering.com/blog/prosemirror-codemirror-block

Features

  • CodeMirror 6 code_block in ProseMirror
  • Customizable language selector
  • Lazy-loaded language support
  • Legacy ( CodeMirror 5 ) language support trough @codemirror/legacy-modes

How to use

import { schema } from "prosemirror-schema-basic";
import { EditorState } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { exampleSetup } from "prosemirror-example-setup";
import {
    codeMirrorBlockPlugin,
    defaultSettings,
    languageLoaders,
    codeBlockArrowHandlers,
    legacyLanguageLoaders,
} from "prosemirror-codemirror-block";
import { undo, redo } from "prosemirror-history";


const codeBlockSpec = schema.spec.nodes.get("code_block");

export default new Schema({
    nodes: schema.spec.nodes.update("code_block", {
        ...(codeBlockSpec || {}),
        attrs: { ...codeBlockSpec?.attrs, lang: { default: null } },
    }),
    marks: schema.spec.marks,
});


const codeBlockDoc = {
    content: [
        {
            content: [
                {
                    text: "prosemirror-codemirror-block",
                    type: "text",
                },
            ],
            type: "paragraph",
        },
        {
            content: [
                {
                    text: "const jsFun = (arg) => {\n  console.log(arg); \n}",
                    type: "text",
                },
            ],
            attrs: {
                lang: "javascript",
            },
            type: "code_block",
        },
    ],
    type: "doc",
};


const state = EditorState.create<typeof schema>({
    doc: schema.nodeFromJSON(codeBlockDoc),
    plugins: [
        ...exampleSetup({
            schema,
        }),
        codeMirrorBlockPlugin({
            ...defaultSettings,
            languageLoaders: { ...languageLoaders, ...legacyLanguageLoaders },
            undo,
            redo,
        }),
        keymap(codeBlockArrowHandlers),
    ],
});

const view: EditorView = new EditorView(document.getElementById("editor"), {
  state,
});

Configuration

CodeBlockSettings

Interface for the settings used by this plugin.

name type description
createSelect (settings: CodeBlockSettings, dom: HTMLElement, node: ProseMirrorNode, view: EditorView, getPos: (() => number) | boolean) => ()=> void) Callback to create lanaguge selector. Returns function that is called when the NodeView is cleaned up. Has default.
updateSelect (settings: CodeBlockSettings, dom: HTMLElement, node: ProseMirrorNode, view: EditorView, getPos: (() => number) | boolean, oldNode: ProseMirrorNode) => ()=> void) Called when the codeblock node is updated. Should update the select value to reflect the lang property of the node.
languageLoaders ?LanguageLoaders Record of functions which return a code extension for a given language.
languageNameMap ?Record<string, string> Can be used to give aliases to languages in the selector.
languageWhitelist ?string[] List of used languages.
undo (state: EditorState, dispatch: (tr: Transaction) => void) => void Undo provided by prosemirror-history. YJS uses a different one!
redo (state: EditorState, dispatch: (tr: Transaction) => void) => void Redo provided by prosemirror-history. YJS uses a different one!
theme Extension[] Insert codemirror theme here. Or any other extension you want!
stopEvent (e: Event) => boolean Can be used to override stopEvent in NodeView. Can be used for custom drag handles for ex.
readOnly boolean Read only editor mode. Defaults to false

CSS & Styles

The following is a good starter style for the language selector:

.codeblock-select {
    position: absolute;
    right: 0;
    z-index: 100;
    opacity: 0;
    transition: all 0.3s ease;
    margin: 6px 14px;
}
.codeblock-root {
    position: relative;
}

.codeblock-root:hover .codeblock-select {
    opacity: 1;
}

About us

Emergence Engineering is dev shop from the EU: https://emergence-engineering.com/

We're looking for work, especially with ProseMirror ;)

Feel free to contact me at viktor.vaczi@emergence-engineering.com