@get-set/gs-editor
v1.0.5
Published
Get-Set Editor — a dual-target (vanilla / jQuery / HTMLElement.prototype / React) model-based rich-text editor. No document.execCommand: a schema-validated document model is the source of truth, edited through transactions for reliable undo/redo, clean se
Downloads
60
Maintainers
Readme
@get-set/gs-editor
A model-based rich-text editor for the web — a serious, dependency-free alternative to CKEditor / TinyMCE / Quill, in the GetSet dual-target style (vanilla / jQuery / HTMLElement.prototype / React).
Why it's different
Most editors edit contentEditable directly through the deprecated, browser-inconsistent document.execCommand. That is the root of their well-known bugs. GSEditor never touches execCommand. Instead:
- a schema-validated document model is the single source of truth,
- the DOM is only a projection of that model,
- every keystroke/paste/shortcut is intercepted (
beforeinput, keymap, IME) and turned into a pure transform, - the view then reconciles the DOM and restores the caret from the model.
The payoff: deterministic cross-browser behaviour, clean serialized HTML, exact undo/redo, schema-safe paste, and a first-class plugin surface.
A strict schema usually means the editor discards whatever it cannot describe. GSEditor doesn't: markup with no place in the model — tables, <section>s, wrapper <div class="…">s, svg, comments, custom elements — is kept verbatim as an inert island and written back byte-for-byte, so setHTML(x) → getHTML() returns x. Hand-written HTML survives the Source ⇄ WYSIWYG switch intact; ordinary prose stays fully editable.
Install
npm install @get-set/gs-editorVanilla / native
<link rel="stylesheet" href="node_modules/@get-set/gs-editor/styles/GSEditor.css" />
<div id="editor"></div>
<script src="node_modules/@get-set/gs-editor/dist-js/bundle.js"></script>
<script>
const editor = GSEditor('#editor', {
placeholder: 'Write something…',
value: '<p>Hello <strong>world</strong></p>',
toolbar: true,
onChange: (html, json) => console.log(html)
});
editor.command('bold'); // run a command
editor.getHTML(); // clean HTML out
editor.getJSON(); // canonical JSON document
editor.setHTML('<h1>New</h1>'); // load content
</script>GSEditor(el, params) also works via element.GSEditor(params) and $('#editor').GSEditor(params) when jQuery is present.
React
import { useRef } from 'react';
import GSEditor, { GSEditorHandle } from '@get-set/gs-editor';
function App() {
const ref = useRef<GSEditorHandle>(null);
return (
<>
<GSEditor
ref={ref}
placeholder="Write something…"
value="<p>Hello <strong>world</strong></p>"
onChange={(html, json) => console.log(html)}
/>
<button onClick={() => ref.current?.command('bold')}>Bold</button>
</>
);
}Options (Params)
| Option | Type | Default | Description |
|---|---|---|---|
| value | string | '' | Initial content as HTML |
| json | EditorDoc | – | Initial content as a JSON document (wins over value) |
| placeholder | string | 'Start writing…' | Empty-state text |
| readonly | boolean | false | Static, selectable content |
| toolbar | boolean \| string \| string[] | 'full' | Preset name (minimal/basic/standard/full), or an explicit ordered list of item ids |
| toolbarPosition | 'top' \| 'bottom' \| 'none' | 'top' | Toolbar placement |
| sourceEditor | 'basic' \| 'monaco' | 'basic' | Source-view engine (Monaco lazy-loads from a CDN) |
| footer | boolean | true | Word/character-count footer |
| resizable | boolean | false | Let the user drag the editor's bottom edge to resize its height |
| onImageUpload | (file) => Promise<string> | – | Upload picked images to your server; the returned URL is stored (no base64) |
| inputRules | boolean | true | Markdown autoformat (## , - , > , ```) |
| keymap | boolean | true | Keyboard shortcuts |
| maxLength | number | 0 | Character cap (0 = unlimited) |
| minHeight / maxHeight | number | 160 / 0 | Surface sizing in px |
| theme | 'light' \| 'dark' \| 'auto' | 'auto' | Colour theme |
| variant | 'minimal' \| 'outlined' \| 'filled' | 'outlined' | Surface style |
| accentColor | string | '#2563eb' | Accent colour |
| onChange | (html, json) => void | – | Fires on every change |
| onSelectionChange | (state) => void | – | Active marks/block snapshot |
Instance API
Content — getHTML · getJSON · getText · setHTML · setJSON · setText · clear · insertHTML(html) · insertText(text) · isEmpty()
Selection — getSelectedText() · selectAll() · getActive() (marks/block/align/colors/link snapshot)
Find & replace (model-based, markup can never be corrupted) — countMatches(find, {caseSensitive, wholeWord}?) · replaceAll(find, replace, opts?) → count
History — undo() · redo() · canUndo() · canRedo()
Counts — getCounts() → { words, chars }
Commands & state — command(name, arg?) · can(name) · isActive(name)
Chrome — focus · blur · setReadonly(bool) · setTheme('light'|'dark'|'auto') · setAccent(color) · setPlaceholder(text) · setRtl(bool) · setResizable(bool)
Events — on(event, fn) · off(event, fn) (change, selectionchange, focus, blur) · destroy
Commands
bold italic underline strike code subscript superscript · color highlight fontSize fontFamily lineHeight changeCase · paragraph h1…h6 blockquote codeBlock · alignLeft alignCenter alignRight alignJustify · bulletList orderedList todoList indent outdent · link unlink setLink hardBreak hr · insertImage updateImage removeImage insertInlineImage insertGrid updateGrid deleteGrid · clear undo redo selectAll.
Shortcuts
Ctrl/Cmd+B/I/U marks · Ctrl/Cmd+K link · Ctrl/Cmd+Z / Ctrl/Cmd+Shift+Z undo/redo · Ctrl/Cmd+Alt+1…6 headings, …+0 paragraph · Tab / Shift+Tab list indent · Enter on an empty list item exits the list.
Architecture
See ARCHITECTURE.md. Core layers: schema → model (blocks + inline runs + marks) → transforms → state + snapshot history → view (contentEditable reconciler + beforeinput pipeline + selection mapping) → commands / keymap / input rules → serialization → plugin surface.
Roadmap
- P0 (shipped): marks, headings, quotes, code blocks, lists, links, undo/redo, autoformat, HTML+JSON serialization, toolbar, native + React.
- P1: tables, images/media, slash menu, bubble toolbar, find-&-replace, word count, autosave, markdown mode.
- P2: mentions, code syntax highlighting, track-changes/comments, real-time collaboration.
License
ISC © Get-Set
