Migration: v0.2 → v0.3

v0.3 introduces a headless architecture with MentionController — a framework-agnostic state machine. The <Mentions> component and compound component APIs remain the same.

What Changed

Drop-in Component — No Changes

If you use the <Mentions> component, no migration needed:

// Works the same in v0.2 and v0.3
<Mentions
  triggers={[{ char: "@", data: users }]}
  onChange={(markup, plainText) => save(markup)}
/>

Compound Components — No Changes

<Mentions.Editor>, <Mentions.List>, <Mentions.Item>, etc. all work the same.

Headless Hook — Minor Changes

// Before (v0.2):
const { handleInput, buildHTML, ... } = useMentions(opts);
<div onInput={handleInput} ... />

// After (v0.3):
const { _handleInput, _buildHTML, ... } = useMentions(opts);
<div onInput={_handleInput} ... />

The underscore prefix signals these are internal plumbing. For most custom editors, you only need editorRef, inputProps, listProps, getItemProps, and the state accessors.

Using MentionController Directly

v0.3 exposes the core controller for building adapters in other frameworks:

import { MentionController, connect } from "@skyastrall/mentions-core";

const ctrl = new MentionController({
  triggers: [{ char: "@", data: users }],
  callbacks: {
    onChange: (markup, plainText) => console.log(markup),
    onSelect: (item, trigger) => console.log(item),
  },
});

// Subscribe to state changes
const unsub = ctrl.subscribe(() => {
  const state = ctrl.getState();
  const aria = connect(state, "my-id");
  // Update your UI...
});

// Feed input changes
ctrl.handleInputChange(markup, plainText, cursor);

// Keyboard
const result = ctrl.handleKeyDown("ArrowDown");
if (result.handled) preventDefault();

// Cleanup
ctrl.destroy();

New Core Exports

// DOM utilities (for building framework adapters)
import {
  getPlainTextFromDOM,
  getMarkupFromDOM,
  getCursorOffset,
  getCaretRect,
  insertTextAtCursor,
  buildMentionHTML,
} from "@skyastrall/mentions-core";