Plugin Migration Guide from v5 to v6

New ipm CLI tool

In v6, there is a new CLI tool named ipm (Inkdrop Plugin Manager) for publishing plugins.

npm install -g @inkdropapp/ipm-cli

ipm configure

Check out the repository for more details.

New TypeScript definitions

TypeScript definitions for Inkdrop v6 are now available at @inkdropapp/types. You can install them to get type-checking and autocompletion for the Inkdrop API in your plugin:

npm install --save-dev @inkdropapp/types

@electron/remote is deprecated

remote.dialoginkdrop.dialog

Before:

const remote = require('@electron/remote')
const { dialog } = remote

return dialog.showOpenDialog(
  inkdrop.window,
  {
  ...
})

After:

return inkdrop.dialog.showOpenDialog({
  ...
})

inkdrop.window.on()

Before:

inkdrop.window.on('focus', this.handleAppFocus)

After:

const sub = inkdrop.window.onFocus(this.handleAppFocus)

// Unsubscribe
sub.dispose()

activate() receives the inkdrop environment

In v6, the plugin's activate() function is called with the inkdrop environment instance as the first argument, followed by the persisted package state.

Before:

module.exports = {
  activate() {
    inkdrop.components.registerClass(MyComponent)
  }
}

After:

import type { Environment } from '@inkdropapp/types'

module.exports = {
  activate(env: Environment) {
    env.components.registerClass(MyComponent)
  }
}

Since the inkdrop environment is now passed as an argument rather than read from the global scope, you need a way to share it across the other modules of your plugin. A common pattern is to capture the instance in a dedicated module that exposes a getter and setter:

import type { Environment } from '@inkdropapp/types'

/**
 * Captures the `Environment` instance handed to `activate()` so the plugin's
 * other modules can reach it without touching the (discouraged) global
 * `inkdrop` variable.
 */
let captured: Environment | undefined

export function setEnv(env: Environment | undefined): void {
  captured = env
}

export function getEnv(): Environment {
  if (!captured) {
    throw new Error('env accessed before activate()')
  }
  return captured
}

Then call setEnv from activate() so the rest of your plugin can retrieve the environment via getEnv(), and clear it in deactivate() to avoid holding a stale reference:

import type { Environment, IInkdropPlugin } from '@inkdropapp/types'
import { setEnv } from './env'

class YourPlugin implements IInkdropPlugin {
  activate(env: Environment) {
    setEnv(env)
    // initialize your plugin
  }

  deactivate(env: Environment) {
    // cleanup
    setEnv(undefined)
  }
}

export default new YourPlugin()

onEditorLoadensureEditorLoaded

In v6, you can use ensureEditorLoaded instead of onEditorLoad to run code with the active editor. onEditorLoad only fires when the editor component loads, so if the editor was already loaded by the time your plugin subscribed, you had to check for an active editor yourself and then also subscribe for future loads. ensureEditorLoaded handles both cases: it invokes the callback immediately if an editor is already loaded, and otherwise waits for the next one to load.

Before:

function extendEditor() {
  // ...
}

const editor = inkdrop.getActiveEditor()
if (editor) extendEditor()
inkdrop.onEditorLoad(() => {
  extendEditor()
})

After:

inkdrop.ensureEditorLoaded((editor: EditorView) => {
  // extend the editor
})

You no longer have to do the same dance of checking for an already-active editor before subscribing.

inkdrop.main.dataStore.getLocalDB()inkdrop.localDB

CSS selectors for keymaps and commands

  • .mde-preview.mde-preview-container

LESS is deprecated

The necessity of LESS has been less and less over the years, since CSS supports nested selectors and variables. In v6, the app has dropped support for LESS.

  • Rename .less to .css

Before:

@primary-color: #ff0000;
.my-plugin {
  .component {
    color: @primary-color;
  }
}

After:

:root {
  --primary-color: #ff0000;
}
.my-plugin {
  .component {
    color: var(--primary-color);
  }
}

Electron's clipboard is deprecated

Accessing the system clipboard through Electron's clipboard module is no longer supported. Use env.clipboard instead, which proxies the system clipboard over IPC.

Before:

const { clipboard } = require('electron')

const text = clipboard.readText()
clipboard.writeText('Hello, Inkdrop!')

After:

const text = env.clipboard.readText()
env.clipboard.writeText('Hello, Inkdrop!')

Syntax themes

Syntax themes (packages with "theme": "syntax" in package.json) styled CodeMirror 5 in v5. Inkdrop 6 runs on CodeMirror 6, so a theme moves from styling CodeMirror's DOM directly to setting CSS variables, and from CodeMirror 5 mode tokens (.cm-*) to CodeMirror 6 / Lezer highlight classes (.tok-*).

The stock default-light and default-dark themes are maintained as reference implementations. Like every other plugin, themes also drop LESS for plain CSS, and should bump engines.inkdrop to ^6.x.

Editor chrome: DOM selectors → CSS variables

You no longer style CodeMirror's DOM. Replace the old .CodeMirror-* rules with the corresponding --editor-* variable, set in :root:

v5 (CodeMirror 5)v6 (CSS variable)
.CodeMirror { background }--editor-background-color
.CodeMirror { color }--editor-foreground-color
.CodeMirror-cursor border / fat cursor--editor-caret-color
.CodeMirror-selected--editor-selection-background
.CodeMirror-focused .CodeMirror-selected--editor-focused-selection-background
.CodeMirror-gutters--editor-gutter-background-color, --editor-gutter-border-right
.CodeMirror-linenumber--editor-gutter-color
.CodeMirror-activeline-background--editor-active-line-background-color
.CodeMirror-matchingbracket--editor-matching-bracket-outline, --editor-matching-bracket-background-color

See the full list of --editor-* and --md-* variables at the top of the reference theme's styles/index.css.

Syntax tokens: .cm-*.tok-*

CodeMirror 5 mode tokens become CodeMirror 6 / Lezer highlight classes, scoped under .cm-editor (and .mde-preview .codeblock for rendered preview code blocks):

v5 (CodeMirror 5)v6 (Lezer highlight class)
.cm-keyword.tok-keyword
.cm-string.tok-string
.cm-comment.tok-comment
.cm-number.tok-number
.cm-variable.tok-variable-name
.cm-def.tok-name.tok-definition, .tok-variable-name.tok-function
.cm-type.tok-type-name, .tok-class-name
.cm-operator.tok-operator
.cm-tag.tok-tag-name
.cm-attribute.tok-attribute-name
.cm-atom.tok-atom, .tok-bool
.cm-meta.tok-meta

Before:

.cm-s-default {
  .cm-keyword {
    color: #c678dd;
  }
  .cm-string {
    color: #98c379;
  }
}

After:

.cm-editor,
.mde-preview .codeblock {
  .tok-keyword {
    color: #c678dd;
  }
  .tok-string {
    color: #98c379;
  }
}

Inkdrop ships a primitive color palette — Tailwind-style --hsl-<family>-<scale> HSL triplets — in the shared @inkdropapp/css package, loaded before your theme. Instead of hard-coding hsl(...) values, reference these tokens so your theme stays consistent with the rest of the app:

:root {
  --editor-background-color: hsl(var(--hsl-white));
  --editor-foreground-color: hsl(var(--hsl-slate-800));
  --editor-selection-background: hsl(var(--hsl-slate-500) / 40%);
}

The full list of primitive tokens is defined in tokens.css.

Can you help us improve the docs? 🙏

The source of these docs is here on GitHub. If you see a way these docs can be improved, please fork us!