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.dialog → inkdrop.dialog
Before:
const remote = require('@electron/remote')
const { dialog } = remote
return dialog.showOpenDialog(
inkdrop.window,
{
...
})
After:
return inkdrop.dialog.showOpenDialog({
...
})
inkdrop.window argument is no longer required.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)
}
}
A future release will deprecate the global inkdrop reference for sandboxing,
so please update your plugin to use the argument form. The global inkdrop
reference still works for backward compatibility at the moment.
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()
onEditorLoad → ensureEditorLoaded
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
.lessto.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!')
env.clipboard also provides readHTML(), readImageAsDataURL(), writeImage(dataUrl), availableFormats(), write({ text, html }), and saveAsImageAttachment(). See the clipboard property on the Environment for details.
Themes are now a single, unified type
In Inkdrop 5, a visual theme was up to three separate packages — a UI theme, a syntax theme, and a preview theme — each declaring "theme": "ui" | "syntax" | "preview" in its package.json and selected independently from three drop-downs.
Inkdrop 6 merges them into one theme package:
package.jsonsets"theme": true— a boolean, not a string.- It ships up to three stylesheets —
ui.css,syntax.css,preview.css— one per area (app chrome, editor, preview), each wrapped in its own cascade layer. - A single
themeAppearance: "light" | "dark"describes the whole theme. - Preferences → Themes shows one Theme drop-down instead of three.
Before (three packages):
{
"theme": "ui",
"themeAppearance": "light",
"styleSheets": ["theme.css"]
}
After (one package):
{
"theme": true,
"themeAppearance": "light",
"styleSheets": ["ui.css", "syntax.css", "preview.css"]
}
The old string values are no longer recognized. If a package still
declares "theme": "ui" (or "syntax" / "preview"), Inkdrop logs a warning
— “<name> does not support v6. Please update the theme.” — and
does not load or activate it. Convert the package to "theme": true to
make it loadable again.
Wrap each stylesheet in its cascade layer
The app no longer wraps your theme CSS in an @layer for you — your stylesheets are now injected verbatim. Wrap each one in the matching layer (theme.ui, theme.syntax, or theme.preview) so its overrides land in the right place in the cascade:
@layer theme.ui {
:root {
--page-background: var(--color-neutral-50);
--sidebar-background: var(--color-neutral-100);
}
}
The app's built-in defaults live in the theme.ui.base / theme.ui.components, theme.syntax.base, and theme.preview.base sub-layers, and a layer's own rules outrank its sub-layers. So wrapping your overrides in @layer theme.ui (etc.) lets them win over the defaults reliably, without resorting to high-specificity selectors.
Merging three v5 themes into one
If you maintained separate -ui, -syntax, and -preview packages, combine them into a single repository:
- Keep one
package.jsonwith"theme": true, a singlethemeAppearance, and astyleSheetsarray. - Move each old package's CSS into
ui.css,syntax.css, andpreview.cssrespectively, each wrapped in its@layer. - If the three packages shared a color palette, keep those raw tokens in one shared file (e.g.
palette.css, wrapped in@layer theme) listed first instyleSheets, and reference them from the three layer files viavar(). Don't copy the color definitions into all three — define them once. See Sharing a palette across the three stylesheets. - List the files in
styleSheets(tokens first, then the layered stylesheets).
The official Solarized and Kanagawa themes were migrated exactly this way and make good references.
Update the manifest and tooling
Bump the engine, the dev-helpers (they provide the v6 dev-server and generate-palette binaries), and add a prepublishOnly script so palette.json stays current:
Before:
{
"theme": "ui",
"styleSheets": ["theme.css"],
"devDependencies": {
"@inkdropapp/theme-dev-helpers": "^0.3.6"
},
"engines": {
"inkdrop": "^5.9.0"
}
}
After:
{
"theme": true,
"styleSheets": ["ui.css", "syntax.css", "preview.css"],
"scripts": {
"prepublishOnly": "generate-palette"
},
"devDependencies": {
"@inkdropapp/theme-dev-helpers": "^0.6.0"
},
"engines": {
"inkdrop": "^6.0.0"
}
}
Like every other plugin, themes also drop LESS for plain CSS.
Use the v6 design tokens
The biggest thing to be aware of is that the shared color palette was overhauled. v6's @inkdropapp/css (loaded before your theme) provides a set of Tailwind-inspired design tokens to build on instead of hard-coding colors:
- A neutral grayscale ramp,
--hsl-neutral-50…--hsl-neutral-950(50= lightest,950= darkest) - Accent families at the same scale —
blue,cyan,green,yellow,orange,red,violet,pink— e.g.--hsl-blue-500,--hsl-red-600 --color-<family>-<step>wrappers (e.g.--color-neutral-50) that resolve the matching--hsl-*triplet to anhsl()color
:root {
--page-background: var(--color-neutral-50);
--text-color: var(--color-neutral-600);
--primary-color: hsl(var(--hsl-blue-500) / 90%);
}
If your v5 theme referenced palette tokens that no longer exist, repoint them at the tokens above. (For example, Solarized Light dropped its old base16-style grayscale and semantic aliases in favor of the neutral ramp, and switched its --hsl-magenta-* references to --hsl-pink-*.) The full set of color tokens is published in the @inkdropapp/css repository.
Generate palette.json
All v6 themes ship a palette.json: a JSON dump of the theme's variables, used for theme previews and (in the future) the mobile app. The prepublishOnly script above runs the generate-palette binary from @inkdropapp/theme-dev-helpers on every publish, so you don't need to generate or commit the file by hand. To inspect it beforehand, run it yourself:
npx generate-palette
See Creating a theme for the full dev loop (ipm init --type theme, npx dev-server, hot reloading, and the component preview).
The UI stylesheet (ui.css)
This stylesheet styles the app chrome — sidebar, note list, toolbar, drop-downs, modals — by overriding CSS variables in :root. That model is unchanged in v6: you still set the same component variables (--sidebar-background, --note-list-bar-background, --editor-background, …). What changed is the primitive color tokens those variables build on.
The stock Solarized Light and Solarized Dark themes have been updated for v6 and make good references.
Override the built-in variables
The app defines named CSS variables for nearly every UI surface, and your theme overrides just the ones you want to change. For the full list, refer to the source CSS files in @inkdropapp/css, which the app loads before your theme:
tokens.css— design tokensui.css— the main file that defines CSS variables for UI components (sidebar, note list, editor chrome, modals, …)status.css— note status colors (--note-status-*)tags.css— tag colors (--tag-*)task-progress.css— task progress view colors (--task-progress-view-*)
Optional: recolor by overriding the ramps
If your theme's palette is shaped like Inkdrop's own design tokens — Tailwind-style color ramps with numeric shades — you can recolor the whole UI from one place by redefining the primitive --hsl-* / --color-* ramps in a token file wrapped in @layer theme, rather than repointing every component variable by hand. The theme authoring guide covers this in detail — see recolor by overriding the ramps.
Move preview styling out
If your v5 UI theme styled preview content directly (e.g. .mde-preview h1, .mde-preview em), move those rules into your theme's preview stylesheet instead.
For any UI-level rule you keep, note that the container class was renamed .mde-preview → .mde-preview-container (see above).
The editor stylesheet (syntax.css)
This stylesheet styled CodeMirror 5 in v5.
Inkdrop 6 runs on CodeMirror 6, and the model shifts in two ways: the theme now (1) sets CSS variables instead of styling CodeMirror's DOM, and (2) sets --syntax-* color variables instead of writing per-token rules. The rules that paint the tokens — the CodeMirror 6 / Lezer .tok-* classes — now live in the shared @inkdropapp/css package and read your variables. So a v6 editor stylesheet is a single :root { … } block (wrapped in @layer theme.syntax) with no selectors of its own.
The base syntax theme now lives in @inkdropapp/css's syntax.css: it defines every --editor-*, --syntax-*, and --md-* variable with a default value (the built-in light and dark themes merged via light-dark()). The app loads it before your theme, so you set only the variables you want to change.
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 — with their default values — in @inkdropapp/css's syntax.css.
Syntax tokens: .cm-* → --syntax-* variables
CodeMirror 5 mode tokens map to v6 highlight tokens — but instead of writing rules for them, you set one --syntax-* color variable per token. @inkdropapp/css applies each variable to the matching .tok-* class it emits under .cm-editor and .mde-preview .codeblock (the latter for rendered preview code blocks):
| v5 (CodeMirror 5) | v6 (--syntax-* variable) |
|---|---|
.cm-keyword | --syntax-keyword-color |
.cm-string | --syntax-string-color |
.cm-comment | --syntax-comment-color |
.cm-number | --syntax-number-color |
.cm-variable | --syntax-variable-name-color |
.cm-def | --syntax-name-definition-color, --syntax-variable-name-function-color |
.cm-type | --syntax-class-name-color |
.cm-operator | --syntax-operator-color |
.cm-tag | --syntax-tag-name-color |
.cm-attribute | --syntax-attribute-name-color |
.cm-atom | --syntax-atom-color, --syntax-bool-color |
.cm-meta | --syntax-meta-color |
Before:
.cm-s-default {
.cm-keyword {
color: #c678dd;
}
.cm-string {
color: #98c379;
}
}
After:
@layer theme.syntax {
:root {
--syntax-keyword-color: #c678dd;
--syntax-string-color: #98c379;
}
}
A few conventions: each token also exposes -font-style / -font-weight / -text-decoration variants where relevant (--syntax-comment-font-style, --syntax-heading-font-weight, …); modifier variants default to their base token (--syntax-name-function-color falls back to --syntax-name-color); and any token you don't color falls back to --editor-foreground-color. @inkdropapp/css's syntax.css defines a value for every token — copy it as a starting point.
The token catalog is finer-grained than CodeMirror 5's modes (separate
variables for line vs. block comments, the six heading levels, each operator
kind, and so on). If you need to style something the variables don't cover,
you can still write an ordinary .tok-* rule scoped under
.cm-editor, .mde-preview .codeblock.
The preview stylesheet (preview.css)
This stylesheet styles the rendered Markdown preview — headings, body text, links, blockquotes, tables, code blocks, task lists, GFM alerts, and so on. This is the area that changed the most between v5 and v6.
In v5 a preview theme shipped the entire preview stylesheet: a fork of GitHub's github-markdown.css (usually with a separate github-markdown-dark.css for the dark UI), plus extra files such as gfm-alerts.css and metadata-section.css, often authored in LESS. Every rule hard-coded its own colors, so recoloring the preview meant editing hundreds of lines of CSS — and the theme had to be kept in sync with each change Inkdrop made to the preview markup.
In v6 those base styles are embedded in the app. Inkdrop ships them in @inkdropapp/css's markdown.css, loaded inside an @layer theme.preview.base, so your preview stylesheet no longer carries the full stylesheet — it only overrides CSS variables.
The base stylesheet drives every element through a family of --mde-preview-* variables, most of which default to the active theme's tokens (--text-color, --border-color, --link-color, …). So in v6 the preview matches the rest of your theme out of the box, and you set only the handful of --mde-preview-* variables you want to diverge on.
Ship full stylesheets → override variables
This is the breaking change. Delete your forked GitHub-markdown rules and replace them with variable overrides on :root, inside @layer theme.preview. Anything you don't override falls back to the app's base styles (which track the rest of your theme).
Before (v5 — styling elements directly):
.mde-preview {
color: #333;
}
.mde-preview a {
color: #4078c0;
}
.mde-preview code {
background-color: rgba(27, 31, 35, 0.05);
}
.mde-preview blockquote {
color: #777;
border-left: 0.25em solid #ddd;
}
After (v6 — overriding variables):
@layer theme.preview {
:root {
--mde-preview-link-color: hsl(var(--hsl-blue-500));
--mde-preview-heading-color: hsl(var(--hsl-slate-900));
--mde-preview-strong-color: hsl(var(--hsl-slate-900));
--mde-preview-em-color: hsl(var(--hsl-slate-700));
--mde-preview-inline-code-background-color: hsl(var(--hsl-slate-100));
--mde-preview-blockquote-text-color: hsl(var(--hsl-slate-500));
--mde-preview-blockquote-border-color: hsl(var(--hsl-slate-200));
}
}
Reach for the shared @inkdropapp/css palette (--hsl-<family>-<scale> / --color-<family>-<scale>) for values, the same way the UI and editor stylesheets do.
For non-color tweaks that no variable covers — font weight, letter-spacing, and the like — you can still write ordinary rules under .mde-preview:
@layer theme.preview {
.mde-preview {
strong {
font-weight: 800;
}
h1,
h2,
h3,
h4,
h5,
h6 {
letter-spacing: -0.02em;
}
}
}
The old gfm-alerts.css and metadata-section.css are now part of the embedded base, so you can drop them.
The variables
The full list lives in the :root block of markdown.css. The most commonly themed ones:
| Area | Variable(s) |
|---|---|
| Links | --mde-preview-link-color |
| Headings | --mde-preview-heading-color, --mde-preview-heading-border-color, --mde-preview-heading-muted-text-color |
| Strong / emphasis | --mde-preview-strong-color, --mde-preview-em-color |
| Blockquotes | --mde-preview-blockquote-text-color, --mde-preview-blockquote-border-color |
| Inline code | --mde-preview-inline-code-text-color, --mde-preview-inline-code-background-color, --mde-preview-inline-code-border-color / -border-width |
| Code blocks | --mde-preview-codeblock-background-color, --mde-preview-codeblock-border-color, --mde-preview-codeblock-text-color, --mde-preview-codeblock-meta-* |
| Tables | --mde-preview-table-head-*, --mde-preview-table-border-color, --mde-preview-table-row-* |
Highlight (==mark==) | --mde-preview-inline-mark-text-color, --mde-preview-inline-mark-background-color, --mde-preview-inline-mark-underline-color |
| Task lists | --mde-preview-task-list-checked-color |
| Footnotes | --mde-preview-footnote-* |
| Keyboard keys | --mde-preview-kbd-* |
| GFM alerts | --gfm-alert-note, --gfm-alert-tip, --gfm-alert-important, --gfm-alert-warning, --gfm-alert-caution |
Code blocks: frame vs. tokens
The preview stylesheet styles the code block frame — background, border, and the meta/title bar — through the --mde-preview-codeblock-* variables above. The syntax highlighting inside the block (the .tok-* classes under .mde-preview .codeblock) is driven by your theme's editor stylesheet, not the preview stylesheet.
Override the --mde-preview-* variables on :root by
default, the same as the UI and editor stylesheets. The
.mde-preview content root is only for the extra rules that the
variables don't cover — don't confuse it with
.mde-preview-container, the outer wrapper renamed for keymap and
command scoping.
Mermaid diagram colors
In v5, diagrams rendered by the mermaid plugin had their own theme / themeCSS / themeVariables plugin settings, separate from your theme's stylesheet — matching a diagram's colors to your theme meant configuring the plugin on top of it, if at all.
That plugin setting is gone in v6. Diagrams now always render with a built-in theme driven by CSS variables: @inkdropapp/css's mermaid.css defines a --mermaid-* token for every color Mermaid can draw, layered the same way as the rest of the preview — you override the ones you care about in preview.css, and everything else falls back to defaults that already track --text-color, --border-color, and the rest of your palette. See Diagram colors (Mermaid) in the theme guide for the token list and an example.
If your v5 theme shipped custom Mermaid CSS or documented the plugin's theme option for users, drop it — it no longer applies, and the --mermaid-* variables replace it.
See Creating a theme for the full dev loop (ipm init --type theme, ipm link --dev, and hot reloading).