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

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