0.5.7 • Published 7 months ago

prosemirror-codemirror-block v0.5.7

Weekly downloads
-
License
ISC
Repository
gitlab
Last release
7 months ago

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
  • Custom themes

How to use

import { schema as BasicSchema } from "prosemirror-schema-basic";
import { EditorState } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { exampleSetup } from "prosemirror-example-setup";
import {
  codeMirrorBlockPlugin,
  defaultSettings,
  languageLoaders,
  codeBlockKeymap,
  legacyLanguageLoaders,
  CodeBlockNodeName,
} from "prosemirror-codemirror-block";
import { undo, redo } from "prosemirror-history";
import { Schema } from "prosemirror-model";
import { keymap } from "prosemirror-keymap";

const codeBlockSpec = BasicSchema.spec.nodes.get(CodeBlockNodeName);

export const schema = new Schema({
  nodes: BasicSchema.spec.nodes.update(CodeBlockNodeName, {
    ...(codeBlockSpec || {}),
    attrs: { ...codeBlockSpec?.attrs, lang: { default: null } },
  }),
  marks: BasicSchema.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: CodeBlockNodeName,
    },
  ],
  type: "doc",
};

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

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

Configuration

CodeBlockSettings

Interface for the settings used by this plugin.

nametypedescription
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?LanguageLoadersRecord 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) => voidUndo provided by prosemirror-history. YJS uses a different one!
redo(state: EditorState, dispatch: (tr: Transaction) => void) => voidRedo provided by prosemirror-history. YJS uses a different one!
themeExtension[]Insert codemirror theme here. Or any other extension you want!
stopEvent(e: Event) => booleanCan be used to override stopEvent in NodeView. Can be used for custom drag handles for ex.
readOnlybooleanRead only editor mode. Defaults to false
themesArray<{ extension: Extension; name: string }>Editor themes
getCurrentTheme() => stringSets the current theme when creating a new code block

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;
}

Using and Customizing Themes

  • create your own themes, or use one from npm
import { gruvboxDark } from "cm6-theme-gruvbox-dark";
import { basicLight } from "cm6-theme-basic-light";

const themes = [
  {
    extension: gruvboxDark,
    name: "Dark",
  },
  {
    extension: basicLight,
    name: "Light",
  },
];

export const CodeBlockExtension = Extension.create({
  name: "codeBlock",
  addProseMirrorPlugins: () => [
    codeMirrorBlockPlugin({
      ...defaultSettings,
      languageLoaders: { ...languageLoaders },
      undo,
      redo,
      // add themes
      themes,
      // tell the nodeView which theme should be used when adding a new code block
      getCurrentTheme: () => {
        const content = document.getElementById("html-root");
        return content?.classList.contains("darkMode") ? "Dark" : "Light";
      },
    }),
    keymap(codeBlockKeymap),
  ],
});
  • use updateTheme to update the code blocks' theme
import { updateTheme } from "prosemirror-codemirror-block";

// const updateTheme: (theme: string) => void;
updateTheme("Dark")

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