Arkpad
Extensions

Extension Overview

The definitive map of the Arkpad extension system.

Core File Map

The Foundation (packages/core/src)

FilePurpose
editor.tsThe brain of Arkpad — editor initialization and React communication
CommandManager.tsThe command executor — command routing and interception
schema-builder.tsThe compiler — merges nodes and marks into the final schema
types.tsThe contract — TypeScript types and interfaces

The Logic Layer (packages/core/src/commands)

FileLogic TypeUse Case
toggleBlock.tsStructural changesConvert paragraph to heading or blockquote
toggleMark.tsInline stylingBold, Italic, Link, or any text-wrapper style
toggleList.tsList managementNesting/un-nesting bullet and ordered lists
updateAttributes.tsData updatesUpdate node metadata (image URL, table width)

The Feature Layer (packages/core/src/extensions)

FileFeatureUse Case
unique-id.tsBlock TrackingSnapshots, collaboration, AI ghost-text
base.tsCore DefaultsTrailing paragraph (trailingNode)
Extension.tsThe APILifecycle hooks like onTransaction
index.tsThe BundleAdd new extensions to the Engine

How to Build a New Extension

Step 1: Choose Your Type

  • Node Extension — For whole blocks (Tables, Images, AI Suggestions)
  • Mark Extension — For inline styles (Highlighter, Comment, Superscript)
  • Functional Extension — For logic without a node (Character Count, Telemetry)

Step 2: Use the Extension Template

import { Extension } from "./Extension";

export const Snapshots = Extension.create({
  name: "snapshots",

  addOptions() {
    return { maxSnapshots: 10 };
  },

  addStorage() {
    return { history: new Map() };
  },

  addCommands() {
    return {
      saveSnapshot:
        (name: string) =>
        ({ editor }) => {
          const json = editor.getJSON();
          this.storage.history.set(name, json);
          return true;
        },
    };
  },

  onTransaction({ transaction }) {
    if (transaction.docChanged) {
      // Logic to run on every change
    }
  },
});

V1 Checklist

When creating a new extension, ask yourself:

  1. Does it have Telemetry? — Does it log why it failed?
  2. Does it use Unique IDs? — Can we track nodes across snapshots?
  3. Is it "Plugin-less"? — Does it avoid raw ProseMirror boilerplate?

Quick Reference

  • Need a new button? → Add command in /commands, add button in @arkpad/react
  • Need a new shortcut? → Add mapping in extensions/keymap.ts
  • Need a new block type? → Create file in extensions/ inheriting from Node
  • Need to intercept data? → Add interceptor in ExtensionManager.ts