Migration guide: v1 → v2
on-codemerge 2.x rewrites the editor around a virtual JSON document (on-codemerge/kernel), an SDK surface (on-codemerge/sdk), and plugins that never own DOM.
See this migration guide for the v1 → v2 delta.
Quick map
| v1 | v2 |
|---|---|
HTMLEditor | Editor |
new ToolbarPlugin() class | ToolbarPlugin() factory (mark buttons only) |
| Plugin owns toolbar DOM | Core ToolbarPanel via ctx.toolbar.add / defineMenu |
PopupManager / createElement in plugins | ctx.popup / ctx.menu / ViewSpec + portals |
| HTML as source of truth | JSON: getJSON / setJSON (HTML is a boundary) |
plugins/FooPlugin/style.css CDN | on-codemerge/index.css + public.css (no per-plugin CSS export) |
| Jest | Vitest (+ Playwright / untestutils for e2e) |
Product surfaces (v2)
Eight Editor entries share kernel + SDK — pick by document shape in Editors:
| Package | Role |
|---|---|
on-codemerge | WYSIWYG prose |
on-codemerge/json | JSON Tree + Raw |
on-codemerge/markdown | Dual-pane Markdown |
on-codemerge/code | Source buffer |
on-codemerge/forms | Form studio |
on-codemerge/charts | Chart studio |
on-codemerge/calendar | Calendar studio |
on-codemerge/tables | Grid JSON studio |
Scoped packages also published: @codemerge/kernel, @codemerge/view, @codemerge/mermaid (Mermaid subset — Mermaid), @codemerge/sdk, @codemerge/hunspell, @codemerge/collaboration-server.
Install
bash
pnpm add on-codemerge
# workspace packages are re-exported from the main packagets
import 'on-codemerge/index.css';
import 'on-codemerge/public.css';
import { Editor, createDefaultPlugins, insertText } from 'on-codemerge';
// Individual plugins: import { TablePlugin } from 'on-codemerge/plugins'
const editor = new Editor(host, { plugins: createDefaultPlugins() });
editor.run(insertText('Hello'));Use createCorePlugins() for a lean bar, or pass an explicit factory list.
Plugins
ts
import { definePlugin, core, insertAtomAfter } from 'on-codemerge/sdk';
// or from 'on-codemerge'
export function MyPlugin() {
return definePlugin({
name: 'my-plugin',
commands: {
insertThing: insertAtomAfter('myAtom', {}),
},
setup(ctx) {
// Top-level bar
ctx.toolbar.add({
id: 'my-bar',
label: 'My',
group: 'format',
order: 20,
onClick: () => ctx.editor.command('insertThing'),
});
// Or into a named overflow menu (Insert / Review / Tools)
ctx.toolbar.add({
id: 'my-insert',
label: 'Thing',
menu: 'insert',
order: 80,
onClick: () => ctx.editor.command('insertThing'),
});
},
});
}Rules:
- Import UI/helpers from
on-codemerge/sdk(oron-codemerge) — noton-codemerge/kernelfrom a plugin. - No
document.createElement/.innerHTML =outside SDKforeign(...)/ widgets. - Overlays go through portals (
popup/menu/notify) — see Plugin guide.
Toolbar chrome vs ToolbarPlugin
- Chrome (the sticky bar) is always created by
Editor→ SDKToolbarPanel. ToolbarPlugin()only registers Bold / Italic / Underline / Strike.- Overflow menus come from
Editortoolbar.menus(omit → Insert / Review / Tools;{ menus: [] }→ flat bar). Plugins place items withmenu: '…'/PluginToolbarOpts; missing menu id → bar button. - Separators come from toolbar
groupboundaries (noToolbarDividerPlugin). - There is no
ToolbarMenusPlugin/flattenOverflowMenus— removed in favor of declarativetoolbar.menus.
Persistence
ts
// Prefer JSON
const json = editor.getJSON();
editor.setJSON(json);
// HTML when integrating with legacy CMS
const html = editor.getHTML();
editor.setHTML(html);Removed / renamed APIs
editor.showToolbar/hideToolbar/addToolbarTool— gone; useeditor.toolbar.add.editor.remove(plugin)/getPlugins()— not onEditor; dispose via plugin scope / recreate editor.createToolbarButton— deleted; usectx.toolbar.add.- Per-plugin CSS package exports — use bundled
index.css/public.css.
Testing
Unit: Vitest (pnpm test:unit). Browser: untestutils + Playwright (pnpm test:e2e).