-
Notifications
You must be signed in to change notification settings - Fork 2
Plugin API
Extend Neiki's Editor with custom functionality using the Plugin API. Plugins can add toolbar buttons, hook into initialization, and interact with the editor programmatically.
Plugins are registered globally before or after editor initialization. Each plugin can optionally:
- Add a toolbar button with a custom icon
- Run an action when the button is clicked
- Run an init function when the editor starts
Note
Plugins are registered globally via NeikiEditor.registerPlugin() and are available to all editor instances on the page.
NeikiEditor.registerPlugin({
name: 'my-plugin',
icon: '<svg viewBox="0 0 24 24">...</svg>',
tooltip: 'My Custom Action',
action: function(editor) {
// Called when toolbar button is clicked
},
init: function(editor) {
// Called once when editor initializes
}
});| Property | Type | Required | Description |
|---|---|---|---|
name |
string |
✅ | Unique identifier for the plugin |
icon |
string |
❌ | SVG icon markup for the toolbar button |
tooltip |
string |
❌ | Tooltip text shown on hover |
action |
function(editor) |
❌ | Handler called on toolbar button click |
init |
function(editor) |
❌ | Handler called once during editor initialization |
Important
The name must be unique across all registered plugins. If you register two plugins with the same name, the second will overwrite the first.
A simple plugin that shows a word count alert:
NeikiEditor.registerPlugin({
name: 'word-counter',
icon: '<svg viewBox="0 0 24 24"><path d="M3 18h12v-2H3v2zM3 6v2h18V6H3zm0 7h18v-2H3v2z"/></svg>',
tooltip: 'Show Word Count',
action: function(editor) {
const text = editor.getContent().replace(/<[^>]*>/g, '');
const words = text.trim().split(/\s+/).filter(Boolean).length;
const chars = text.length;
alert(`Words: ${words}\nCharacters: ${chars}`);
}
});Insert the current date and time at the cursor position:
NeikiEditor.registerPlugin({
name: 'timestamp',
icon: '<svg viewBox="0 0 24 24"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z"/></svg>',
tooltip: 'Insert Timestamp',
action: function(editor) {
const now = new Date();
const formatted = now.toLocaleString();
editor.insertHTML(`<span style="color:#6b7280;font-size:0.9em">[${formatted}]</span> `);
}
});A plugin that automatically capitalizes the first letter of every heading on init:
NeikiEditor.registerPlugin({
name: 'auto-capitalize',
init: function(editor) {
editor.contentArea.addEventListener('input', function() {
const headings = editor.contentArea.querySelectorAll('h1, h2, h3, h4, h5, h6');
headings.forEach(function(h) {
if (h.textContent.length > 0) {
const first = h.textContent.charAt(0);
if (first !== first.toUpperCase()) {
// Use selection-safe approach
h.textContent = first.toUpperCase() + h.textContent.slice(1);
}
}
});
});
}
});Caution
Be careful with init plugins that modify content on every input event — they can interfere with the user's typing and cursor position. Always test thoroughly.
NeikiEditor.registerPlugin({
name: 'styled-divider',
icon: '<svg viewBox="0 0 24 24"><path d="M2 12h4v1H2v-1zm6 0h4v1H8v-1zm6 0h4v1h-4v-1zm6 0h2v1h-2v-1z"/></svg>',
tooltip: 'Insert Styled Divider',
action: function(editor) {
editor.insertHTML(
'<div style="text-align:center;margin:1.5em 0;color:#d1d5db;letter-spacing:0.5em;font-size:1.2em">• • •</div>'
);
}
});A cleanup plugin that strips all inline styles and extra attributes:
NeikiEditor.registerPlugin({
name: 'clean-paste',
icon: '<svg viewBox="0 0 24 24"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>',
tooltip: 'Clean All Formatting',
action: function(editor) {
const text = editor.contentArea.innerText;
editor.setContent('<p>' + text.split('\n\n').join('</p><p>') + '</p>');
}
});Retrieve all registered plugins:
const plugins = NeikiEditor.getPlugins();
plugins.forEach(function(plugin) {
console.log('Plugin:', plugin.name);
});graph TD
A[NeikiEditor Instance] --> B[Toolbar Area]
A --> C[Content Area]
A --> D[Status Bar]
B --> E[Plugin Buttons]
E --> F[Word Counter]
E --> G[Timestamp]
E --> H[Divider]
A --> I[Plugin Init Hooks]
I --> J["auto-capitalize #8594; addEventListener"]
Tip
Follow these guidelines for reliable plugins:
-
Use unique names — prefix with your project name if needed (e.g.,
myapp-timestamp) - Keep actions focused — one plugin, one responsibility
-
Don't modify
contentAreadirectly ininit— use editor API methods when possible - Test with both themes — ensure your plugin UI works in Light and Dark mode
- Handle edge cases — check for empty selection before operating on it
- ⚙️ Configuration — Editor configuration options
- 🔧 Toolbar Reference — Built-in toolbar buttons
- 🧩 Advanced Features — Tables, images, themes
Getting Started
Reference
Extending
Integration
Features & UI
Project