Plugins

The markdown editor of Agit CMS (Codemirror) can be customized by creating your own plugins.

Agit CMS evaluate javascript files in ~/.agitcms/plugins as plugins when boot, so you can place your own javascript file here to create a plugin. Name of the file does not matter.

Creating your own plugin requires a bit of knowledge of codemirror. If you don’t know any, Examples is helpful.

There’s two types of plugins.

  • Toolbar Item: manually invoked
  • TransactionFilter: automatically invoked

Toolbar Item

To create Toolbar Item plugin, create a new instance of ToolbarItem class and provide a valid config as a first argument.

If you don’t want your plugin to show up in the toolbar, set config.initialChar to empty. That way you can only call plugin via config.keyAlias

See table plugin for an exmaple.

Transaction Filter

Every time editor updates its document, this type of plugins, i.e., Transaction Filter plugin is called. Unlike ToolbarItem plugin, Transaction Filter does not modify,

To create Transaction Filter plugin, create a new instance of Transaction Filter class and provide a valid config as a first argument.

Examples

table plugin

A plugin that inserts markdown table syntax when clicked on the toolbar, or when ctrl + shift + t is pressed.

 1//put this in ~/.agitcms/plugins
 2new ToolbarItem({
 3  initialChar: "T",
 4  keyAlias: "Ctrl-T",
 5  run: (editorView) => {
 6    const from =
 7      editorView.state.selection.ranges[editorView.state.selection.mainIndex]
 8        .to;
 9    const insert = "|  |  |\n|---|---|\n";
10
11    editorView.dispatch({
12      changes: { from, insert },
13      selection: { anchor: from + 2 }, //puts cursor to from+2
14    });
15  },
16});

shortcode snippet plugin

This is a plugin that works as a snippet. If you are using Hugo for example, you might want to add shortcode snippets.

The plugin below inserts {{<alert `\n\n` [info] >}} to editor and sets cursor at position between two \n.
Also note that this plugin is activated for sites that has name "0xsuk's blog". (Name of the site is the one you set in the home)

 1const sitesToEnablePlugin = ["0xsuk's blog"];
 2new ToolbarItem({
 3  initialChar: "A",
 4  weight: 5,
 5  run: (editorView) => {
 6    const from =
 7      editorView.state.selection.ranges[editorView.state.selection.mainIndex]
 8        .to;
 9    const insert = "{{<alert `\n\n` info>}}";
10
11    editorView.dispatch({
12      changes: { from, insert },
13      selection: { anchor: from + 11 },
14    });
15
16    editorView.focus();
17  },
18  isActive: (siteConfig) => {
19    if (sitesToEnablePlugin.includes(siteConfig.name)) {
20      return true;
21    }
22    return false;
23  },
24});

autoclosebracket plugin

 1function handleAutoclose(transactionSpecList, transaction) {
 2  return function forBracket(opening, closing) {
 3    transaction.changes.iterChanges((fromA, _, fromB, toB) => {
 4      const newChars = transaction.newDoc.sliceString(fromB, toB);
 5      if (newChars === opening) {
 6        transactionSpecList.push({
 7          changes: { from: fromA, insert: opening + closing },
 8          selection: { anchor: fromA + 1 },
 9          filter: false, //do not filter transaction for this update to prevent unintended behaviour
10        });
11      }
12      if (newChars === closing) {
13        //if the next char is also closing bracket, move cursor right
14        const nextChar = transaction.startState.sliceDoc(fromA, fromA + 1);
15        if (nextChar === closing) {
16          transactionSpecList.push({
17            selection: { anchor: fromA + 1 },
18            filter: false,
19          });
20        }
21      }
22    });
23  };
24}
25new TransactionFilter({
26  fn: (transaction) => {
27    const transactionSpecList = [];
28    const forBracket = handleAutoclose(transactionSpecList, transaction);
29    forBracket("[", "]");
30    forBracket("(", ")");
31
32    return transactionSpecList;
33  },
34});

japanese keymap

1new TransactionFilter({
2  map: new Map([
3    ["#", "#"], //mapping japanese # to english #
4    [" ", " "], //mapping japanese space to english space
5  ]),
6});

Comments