> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lumen.bjanczak.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugin API: Extending Lumen Editor with Custom Plugins

> The EditorPlugin type signature, what plugins can access, and how to install them via the plugins constructor option or the editor.use() method.

Plugins are the primary extension point for Lumen Editor. A plugin is a plain function that receives the fully initialized editor instance — nothing more, nothing less. Because every editor method and event is accessible from inside a plugin, you can build anything from a word-count sidebar to a collaborative cursor indicator without touching editor internals.

## Plugin Signature

A plugin conforms to the `EditorPlugin` type:

```ts theme={null}
type EditorPlugin = (editor: Editor) => void;
```

The function receives the `Editor` instance and should perform all its setup work synchronously — subscribing to events, appending UI elements, patching methods, etc. The return value is ignored.

```ts theme={null}
import { Editor, EditorPlugin } from 'lumen-editor';

const myPlugin: EditorPlugin = (editor) => {
  // Set up your plugin here
};
```

## What Plugins Can Do

Because plugins receive the full editor instance, they can:

* **Subscribe to events** using `editor.on()` and clean up with `editor.off()` to react to content changes, errors, view switches, and more.
* **Read and write content** by calling `editor.getHTML()`, `editor.getText()`, and `editor.setHTML()`.
* **Append UI to the editor root** by accessing `editor.root` — the underlying `contenteditable` element — and inserting sibling or wrapper nodes.
* **Call any public method** including `editor.focus()`, `editor.blur()`, `editor.setTheme()`, and `editor.use()` to compose behaviour across plugins.
* **Add side effects** such as starting a `MutationObserver`, registering keyboard shortcuts via `document.addEventListener`, or initializing third-party libraries that operate on the editor's DOM.

## Installing a Plugin

### Via the constructor

Pass an array of plugin functions to the `plugins` option. Plugins run synchronously in array order immediately after the editor mounts:

```js theme={null}
import { Editor } from 'lumen-editor';
import 'lumen-editor/theme.css';

function analyticsPlugin(editor) {
  editor.on('change', () => {
    analytics.track('editor_change');
  });
}

function autoLinkPlugin(editor) {
  editor.on('change', (html) => {
    // auto-link detection logic …
  });
}

const editor = new Editor('#editor', {
  plugins: [analyticsPlugin, autoLinkPlugin],
});
```

### Via `editor.use()`

Install a plugin at any point after initialization using `editor.use()`. This is useful for lazy-loading plugins or for conditionally applying them based on runtime state:

```js theme={null}
import { wordCountPlugin } from './plugins/wordCount';

// Install immediately
editor.use(wordCountPlugin);

// Or install after an async check
const flags = await fetchFeatureFlags();
if (flags.enableWordCount) {
  editor.use(wordCountPlugin);
}
```

<Note>
  Plugins run synchronously — whether registered in the `plugins` constructor option or installed via `editor.use()`. All setup code inside a plugin executes before the next JavaScript statement after the constructor or `use()` call returns.
</Note>

## Full Example: Word Count Plugin

The following plugin appends a live word count badge below the editor and keeps it updated on every content change:

```js theme={null}
function wordCountPlugin(editor) {
  // Create the badge element
  const badge = document.createElement('div');
  badge.className = 'wc';

  // Append it inside the editor root
  editor.root.appendChild(badge);

  // Keep it in sync with content changes
  editor.on('change', () => {
    badge.textContent =
      editor.getText().trim().split(/\s+/).filter(Boolean).length + ' words';
  });
}

// Install via options
const editor = new Editor('#editor', {
  plugins: [wordCountPlugin],
});

// — or install after the fact —
editor.use(wordCountPlugin);
```

## Accessing the Editor Root

`editor.root` is the `contenteditable` `HTMLElement` that hosts the document. You can use it as an anchor for additional DOM nodes, event delegation, or position-aware UI like floating toolbars:

```js theme={null}
function floatingMenuPlugin(editor) {
  const menu = document.createElement('div');
  menu.className = 'floating-menu';
  menu.hidden = true;

  // Position the menu relative to the editor root
  document.body.appendChild(menu);

  editor.on('selectionchange', () => {
    const sel = window.getSelection();
    if (!sel || sel.isCollapsed) {
      menu.hidden = true;
      return;
    }

    const range = sel.getRangeAt(0);
    const rect = range.getBoundingClientRect();

    menu.style.setProperty('--menu-top', `${rect.top + window.scrollY - 40}px`);
    menu.style.setProperty('--menu-left', `${rect.left}px`);
    menu.hidden = false;
  });
}
```

<Tip>
  If your plugin appends DOM nodes, consider removing them inside a cleanup handler. While Lumen Editor does not provide a formal plugin teardown hook, you can subscribe to a signal of your own (e.g. a custom event or a shared `AbortSignal`) to remove nodes and unsubscribe event listeners before calling `editor.destroy()`.
</Tip>
