@waveeditor/core
v1.0.0
Published
Self-hosted, white-labeled rich-text editor for the web. Accepts legacy editor config strings for easy migration — no external CDN, no GPL contagion.
Readme
🌊 WaveEditor
A self-hosted, white-labeled rich-text editor for modern web apps. No external CDN, no surprise bills.
🔑 WaveEditor uses a license-key model. Local development is exempt and mounts without a key. Production domains need a key — request one at waveeditor.co.in. See Licensing below.
What WaveEditor is
WaveEditor gives web developers a rich-text editor that is owned by the people who use it:
- ✅ Self-hosted — single JS + CSS pair, no external CDN, works offline and behind firewalls
- ✅ Legacy migration aid — accepts the config-string shape used by older WYSIWYG editors, so existing init() calls parse without changes
- ✅ MIT-licensed — no GPL contagion, no recurring fees
- ✅ Brandable — full white-label support via CSS variables; ship it under your own product name
Package
Published on npm as @waveeditor/core.
The unscoped waveeditor name is blocked by npm's anti-typosquatting policy (similarity to the unrelated wangeditor package), so we use the @waveeditor org scope — same convention as TipTap (@tiptap/core), Lexical (@lexical/core), CKEditor 5 (@ckeditor/*). Brand identity stays "WaveEditor" everywhere except the install command.
Licensing
WaveEditor uses a license-key model so we can identify legitimate installs and revoke individual keys if needed.
WaveEditor.init({
selector: '#editor',
license: 'YOUR-LICENSE-KEY', // request a key at https://waveeditor.co.in
plugins: 'bold italic | link image',
toolbar: 'bold italic | link image',
});Exempt hosts — local development is never blocked. The following hostnames mount without a license:
localhost · 127.0.0.1 · 0.0.0.0 · *.local · *.test · *.localhost
That covers Laragon, Valet, mDNS, and the typical dev stack. Production domains require a key.
Failure mode is strict. Without a valid key on a non-exempt host, WaveEditor.init() returns null and the target element is replaced by an inline error panel — no silent fallback. This is by design.
Need a key for your own domain? Visit waveeditor.co.in.
Quick start
Via npm
npm install @waveeditor/coreimport { WaveEditor } from '@waveeditor/core';
import '@waveeditor/core/dist/waveeditor.min.css';
WaveEditor.init({
selector: '#editor',
license: 'YOUR-LICENSE-KEY',
plugins:
'undo redo bold italic underline strikethrough subscript superscript ' +
'heading blockquote bullet-list ordered-list indent outdent ' +
'align-left align-center align-right align-justify ' +
'link unlink image clear-formatting horizontal-rule ' +
'source-view fullscreen word-count clipboard',
toolbar:
'undo redo | bold italic underline strikethrough | ' +
'heading blockquote | bullet-list ordered-list indent outdent | ' +
'align-left align-center align-right align-justify | ' +
'link unlink image | source-view fullscreen',
});Via CDN (drop-in, no build step)
<link rel="stylesheet" href="https://unpkg.com/@waveeditor/core/dist/waveeditor.min.css">
<script src="https://unpkg.com/@waveeditor/core/dist/waveeditor.min.js"></script>For a more compact, cool-blue look (smaller buttons, lighter toolbar), swap the stylesheet — JS is unchanged:
<link rel="stylesheet" href="https://unpkg.com/@waveeditor/core/dist/waveeditor.classic.min.css">
<div id="editor"></div>
<script>
WaveEditor.init({
selector: '#editor',
license: 'YOUR-LICENSE-KEY',
plugins: 'bold italic link image bullet-list',
toolbar: 'bold italic | link image | bullet-list',
});
</script>The config-string shape is shared with older WYSIWYG editors, so migrating an existing init() block is usually a find-and-replace.
Toolbar placement & overflow
The toolbar can be docked at the bottom of the editor, and wide toolbars get a configurable overflow strategy. Both options are opt-in — pass neither and you get the default top toolbar with wrapping rows.
WaveEditor.init({
selector: '#editor',
license: 'YOUR-LICENSE-KEY',
toolbar_position: 'bottom', // 'top' (default) | 'bottom'
toolbar_overflow: 'menu', // 'wrap' (default) | 'menu' | 'two-rows' | 'scroll' | 'expand'
plugins: 'bold italic | link image',
toolbar: 'bold italic | link image',
});toolbar_position—'top'(default) or'bottom'(docks below the content, above the status bar).toolbar_overflow— how a toolbar wider than its container behaves:'wrap'(default) — wraps to additional rows.'menu'— a single row plus a⋯More button; tools that don't fit move into a popover.'two-rows'— splits the groups across two organized rows.'scroll'— a single horizontally-scrolling row with edge-fade affordances.'expand'— keeps the basic tools visible with a More panel toggle for the advanced ones.- On mobile (<640px) every style collapses to a single scrolling row.
The font-size control is a compact [ − 16px + ] stepper that walks a fixed ladder (10·12·14·16·18·20·24·28·32).
Features
| Category | Features |
|---|---|
| Marks | bold · italic · underline · strikethrough · subscript · superscript · code |
| Typography | font-family · font-size · font-color · highlight · line-height |
| Blocks | heading (paragraph / H1–H6 dropdown) · blockquote · preformatted · bullet-list · ordered-list · horizontal-rule |
| Code blocks | code-block — Auto-detect + Plain + 12 languages (Bash / CSS / HTML / JS / JSON / Markdown / PHP / Python / SQL / TS / XML / YAML) via lowlight |
| Tables | table · table-ops (row + col ops, merge / split, toggle header, delete) · column resize · table-cell-color · table-cell-border |
| Alignment | align-left · align-center · align-right · align-justify (paragraphs + headings) |
| Operations | undo · redo · indent · outdent (lists) · clear-formatting · page-break · date-time (5 formats) |
| Editing aids | anchor (named jump target) · search-replace (decoration highlights + prev / next / replace / replace-all + case / whole-word) · charmap (~115 special characters with live search) · emoji (8 categories + search + recents) |
| Workflow | templates (config-driven snippet popover) · markdown-shortcuts (StarterKit input rules) · visual-blocks (debug-overlay toggle) |
| Dialogs | link (insert / edit / autoprefix https:// / new-tab / autolink on type) · unlink · image (Upload / URL, drag-drop + paste, server upload via images_upload_url / images_upload_handler; resize / align / caption / alt via a floating bubble) · media (YouTube / Vimeo auto-detect + configurable iframe allowlist) |
| Editor modes | source-view (HTML edit) · fullscreen (ESC exits) · word-count (debounced status bar) · autosave (debounced localStorage save + restore prompt + "Saved · HH:MM" indicator) |
| Security | clipboard (strips <script>, <iframe>, inline on*= handlers, javascript: URLs on paste) · paste-from-word (7-stage MSO scrub + pseudo-list recovery) · media_allowlist rejects clickjacking-prone iframe sources |
| Drag & drop | image (file → upload pipeline) · drag-drop (text / html / .txt with drop-zone outline) |
| Legacy aliases | numlist → ordered-list, bullist → bullet-list, alignleft → align-left, etc. — accept the config-string shape used by older WYSIWYG editors |
Examples
HTML demos in examples/ that you can open via npm run dev:
examples/basic.html— minimal hello-world drop-inexamples/full-toolbar.html— every shipped featureexamples/tinymce-migration.html— reference side-by-side comparison with a legacy editor, identical config stringsexamples/docs.html— designed developer guide with live demo
Testing
npm test # 861 unit tests
npm run e2e # Playwright e2e — chromium / firefox / webkit
npm run build # produces dist/ — waveeditor.min.{js,css}
# + waveeditor.classic.min.css
# + waveeditor.dark.min.css
npm run license:audit # fails on GPL/AGPL/SSPLBuilt on
- TipTap — editor engine (MIT)
- ProseMirror — underlying document model (MIT)
- Tabler Icons — icon set (MIT)
- Inter — chrome typeface (OFL)
- JetBrains Mono — monospace typeface (OFL)
Full third-party attributions: NOTICES.md.
Contributing
See CONTRIBUTING.md for the contribution workflow.
Security
Found a vulnerability? See SECURITY.md for the responsible disclosure process.
License
MIT — © 2026 Manish Patolia.
WaveEditor is built on MIT-licensed open-source components and is itself MIT-licensed.
