Skip to content

Plugin Overview

import { LinkCard, CardGrid } from ‘@astrojs/starlight/components’;

notectl ships with 23 built-in plugins. Every editor feature — from bold text to tables — is implemented as a plugin. You can use all of them, a subset, or build your own.

Editor with full plugin set

PluginIDDescriptionKeyboard Shortcuts
TextFormattingPlugintext-formattingBold, italic, underlineCtrl+B, Ctrl+I, Ctrl+U
HeadingPluginheadingHeading levels 1-6, Title, SubtitleCtrl+Shift+1-6
ListPluginlistBullet, ordered, and checklistTab, Shift+Tab
LinkPluginlinkHyperlinksCtrl+K
TablePlugintableTables with cell selectionTab, Enter
CodeBlockPlugincode-blockFenced code blocks with syntax highlighting``` (input rule)
BlockquotePluginblockquoteBlock quotesCtrl+Shift+>
ImagePluginimageImage blocks with upload support-
FontPluginfontCustom font families-
FontSizePluginfontSizeFont size controlCtrl+Shift++/-
TextColorPlugintextColorText color picker-
AlignmentPluginalignmentStart, center, end, justify (logical values for RTL support)Ctrl+Shift+L/E/R/J
StrikethroughPluginstrikethroughStrikethrough textCtrl+Shift+X
SuperSubPluginsuper-subSuperscript & subscriptCtrl+., Ctrl+,
HighlightPluginhighlightText highlight (background color)-
HorizontalRulePluginhorizontal-ruleHorizontal divider linesCtrl+Shift+H
HardBreakPluginhard-breakLine breaks within a blockShift+Enter
PrintPluginprintPrint with clean output and HTML exportCtrl+P
ToolbarPlugintoolbarToolbar UI (auto-created)-
CaretNavigationPlugincaret-navigationPlatform-aware keyboard navigation keymapsArrow keys, word/line/document movement
TextDirectionPlugintext-directionRTL language support with block direction and inline bidi isolationCtrl+Shift+D, Ctrl+Shift+B
SmartPastePluginsmart-pasteAuto-detect and format structured content on paste-
GapCursorPlugingap-cursorVirtual cursor at void-block boundariesArrow keys (when gap cursor active)

Each plugin implements the Plugin interface and registers its capabilities during init():

interface Plugin {
/** Unique identifier. */
readonly id: string;
/** Human-readable name. */
readonly name: string;
/** Plugin initialization order (lower = first). */
readonly priority?: number;
/** Required plugin IDs that must be loaded first. */
readonly dependencies?: readonly string[];
/** Register schema, commands, keymaps, toolbar items. */
init(context: PluginContext): void | Promise<void>;
/** Clean up when the editor is destroyed. */
destroy?(): void | Promise<void>;
/** Called on every state change (for reactive updates). */
onStateChange?(oldState: EditorState, newState: EditorState, tr: Transaction): void;
/** Called when configurePlugin() is used at runtime. */
onConfigure?(config: TConfig): void;
/** Called once after all plugins are initialized. */
onReady?(): void | Promise<void>;
/** Called when the editor's read-only mode changes. */
onReadOnlyChange?(readonly: boolean): void;
/** Returns decorations for the current state. */
decorations?(state: EditorState, tr?: Transaction): DecorationSet;
}

Plugins register through the PluginContext:

MethodWhat it doesExample
registerNodeSpec()New block typesheading, list_item, table, blockquote
registerMarkSpec()New inline marksbold, italic, link, textColor
registerInlineNodeSpec()New inline elementshard_break, emoji, mention
registerCommand()Named commandstoggleBold, insertTable, alignCenter
registerKeymap()Keyboard shortcutsMod-B for bold, Mod-K for link
registerInputRule()Text pattern transforms# to heading, --- to horizontal rule
registerToolbarItem()Toolbar buttons/dropdownsBold button, heading dropdown, color picker
registerBlockTypePickerEntry()Block type dropdown entriesHeading levels, paragraph, title
registerNodeView()Custom block renderersCode block header, image upload UI, table controls
registerMiddleware()Transaction interceptorsPreserve alignment on block type change
registerService()Typed servicesToolbarService, TableSelectionService
registerPasteInterceptor()Paste content transformersSmart paste, markdown conversion
registerFileHandler()File paste/drop handlersImage upload on drag-and-drop
registerStyleSheet()Inject plugin CSSTable grid styles, code block theme
announce()Screen reader announcements”Image resized”, “Entered code block”
executeCommand()Execute a registered commandTrigger toggleBold from keyboard handler
getEventBus()Access typed event busEmit/subscribe to plugin events
isReadOnly()Check read-only stateSkip mutations in read-only mode
updateConfig()Push runtime config updatesDynamic plugin reconfiguration

The fastest way to get all plugins is with createFullPreset():

import { createEditor } from '@notectl/core';
import { createFullPreset } from '@notectl/core/presets';
const editor = await createEditor({
...createFullPreset(),
placeholder: 'Start typing...',
});

This gives you all standard plugins organized into 8 toolbar groups. Override individual plugin configs as needed:

const editor = await createEditor({
...createFullPreset({ list: { interactiveCheckboxes: true } }),
});

See the Plugin Presets guide for details.

Use plugins with the toolbar config for a visual toolbar:

const editor = await createEditor({
toolbar: [
[new TextFormattingPlugin()], // Group 1: B I U
[new HeadingPlugin()], // Group 2: Heading dropdown
[new ListPlugin(), new BlockquotePlugin()], // Group 3: Lists + blockquote
[new LinkPlugin(), new TablePlugin()], // Group 4: Link + table
[new CodeBlockPlugin()], // Group 5: Code blocks
],
});

Or use plugins for headless mode (no toolbar):

const editor = await createEditor({
plugins: [
new TextFormattingPlugin(),
new HeadingPlugin(),
],
});

TextFormattingPlugin, CaretNavigationPlugin, and GapCursorPlugin are auto-registered with default settings if not explicitly provided. All other plugins must be added manually.