Smart typography while you type New
The editor now recognises five common typing patterns and substitutes the Unicode typographic glyph automatically — skipped inside <code>, <pre>, and <kbd> blocks so code samples stay verbatim.
- Auto-link on space — URLs,
www.hosts, and email addresses wrap into<a>the moment you press space.javascript:,vbscript:, and non-imagedata:URIs are blocked. - Em-dash —
--becomes —. - Ellipsis —
...becomes …. - Smart quotes — straight
"x"becomes “x”;it'sbecomes it’s (letter-aware apostrophe). - Trademarks —
(c),(r),(tm)become ©, ®, ™.
Opt-out master switch: config.smartTypography = false. Per-feature flags also available.
23 keyboard shortcuts at Google Docs / Word parity New
Every shortcut shows in toolbar tooltips (Mac auto-detects & uses ⌘ ⇧ ⌥). Press Ctrl+/ anywhere in the editor for the full cheat-sheet modal.
- Headings:
Ctrl+Alt+1..6, normal paragraphCtrl+Alt+0 - Lists: numbered
Ctrl+Shift+7, bulletCtrl+Shift+8, blockquoteCtrl+Shift+9 - Align:
Ctrl+Shift+L/E/R/J - Strike / Sub / Super / Clear:
Ctrl+Shift+X,Ctrl+,,Ctrl+.,Ctrl+\ - Insert: link
Ctrl+K, horizontal ruleCtrl+Shift+- - Paste plain:
Ctrl+Shift+V - Highlight (yellow
<mark>):Ctrl+Shift+H - Duplicate block:
Ctrl+Shift+D— Move block up / down:Alt+↑ / ↓ - Cycle case (UPPER → lower → Title):
Ctrl+Shift+K - Sort lines (alphabetical):
Ctrl+Shift+Alt+S - Reading mode (distraction-free):
Ctrl+Shift+M— persists across reloads via localStorage - Word count details:
Ctrl+Shift+C - Cheat sheet:
Ctrl+/
Auto-generated heading anchor IDs + copy-link on hover New
Every h1–h6 automatically receives a stable slug ID derived from its text (NFKD-normalised, lower-case, hyphenated, deduped). Hover a heading and a small 🔗 chip appears; click it to copy the deep-link (current URL plus the slug) to your clipboard. TOC plugins, share-link patterns, and screen-reader navigation all work without manual ID assignment.
Smart paste from Word + Google Docs Improved
Two paste-cleanup paths landed:
- Word internal-bookmark anchors (
<a name="OLE_LINK1">,<a name="_Toc...">etc.) are stripped on paste so the text doesn’t arrive as blue-underlined links. Anchors with a realhrefsurvive unchanged. - Google Docs wrapper (
<b id="docs-internal-guid-...">that makes everything bold) gets unwrapped, leaving only the genuine formatting.
When either path fires, a brief toast appears bottom-centre: “Cleaned from Word — Undo: Ctrl+Z”. Opt-out via config.smartPasteToast = false.
Restricted editing templates New
Word-style “protect document with editable regions.” Pass restrictedEditingMode: true to lock the document; mark fillable spans at runtime with editor.restrictedEditing.markSelection(). Use it for contracts, forms, and fillable templates — only the marked regions accept input. Tab between regions via goToNext() / goToPrev(); regions get a soft green outline for discoverability. Full TypeScript declarations shipped in index.d.ts.
Markdown round-trip New
Two new instance methods: editor.toMarkdown() (HTML → CommonMark subset) and editor.fromMarkdown(md, { apply }) (CommonMark → HTML). Round-trips through headings, bold / italic / strike / code, links, images, lists with proper indented nesting, blockquotes, horizontal rules, fenced code blocks, and pipe-tables. Round-trip-stable with the existing markdown-shortcut parser.
Smart-find live highlight + match count New
editor.openFindDialog() shows a small floating panel with a live count badge (“3 of 12”), Prev / Next navigation that auto-scrolls to the active hit, Replace single + Replace all. Every match is wrapped in <mark class="rte-find-hit"> so you can see the matches at a glance. Programmatic: editor.findHighlight(query, { caseSensitive }) returns { matches }.
Auto-save with status New
Pass config.autoSave = { onSave: function(html) {...} } and the editor debounces saves, hashes content to skip no-op writes, and renders “Saving…” → “✓ Saved at HH:MM” in the status bar. Promise-aware (waits for your save to resolve before flipping to ✓); failure-aware (shows ⚠ on rejected promise / thrown error). Programmatic: editor.autoSave.trigger(), isDirty(), lastSavedAt().
Auto-grow, sticky toolbar, reading mode, block drag handles New
Four composable mode switches for layout flexibility.
config.autoGrow = true— editor expands to fit content, no inner scrollbar.config.stickyToolbar = true— toolbar sticks to top of viewport on long pages.editor.toggleReadingMode()+Ctrl+Shift+M— distraction-free, hides chrome.config.blockDragHandles = true— Notion-style ⁞ handle appears on hover; drag to reorder paragraphs/lists/tables.
Editing-flow fixes that just match expectations UX
- Enter on an empty
<li>exits the list (no more chains of empty bullets). - Enter on an empty blockquote line exits the blockquote.
- Backspace at column 0 of an
<li>outdents one level instead of merging into the previous paragraph. - Backspace at column 0 of a blockquote exits the blockquote.
- Tab in a list nests; Shift+Tab outdents.
- Image upload inserts inline at the caret — no more forced centring.
- Insert table defaults to
width:100%; cells get visible borders (dotted in toggle-border mode) and minimum dimensions so empty tables don’t vanish. - Snippets: type
:date/:time/:datetime/:hr+ Tab; register your own witheditor.snippets.register(":sig", "<p>...</p>", { isHtml: true }).
Selection chrome polish UX
- Inline link URL preview — clickable URL appears in the float toolbar when caret is in an
<a>; opens in a new tab safely withrel="noopener noreferrer". - Image hover quick-action bar — floating Alt / Replace / × chips appear when hovering an
<img>; Replace opens a file picker, × deletes the image. - Inline alt-text editor — images missing
altget a dashed amber outline; click to open an inline popup that sets the alt attribute. - Video (IFRAME) float toolbar — gets the same handles + action chips as images.
- Table column & row resize handles — hover near a border, cursor changes to resize, drag to resize.
- Float toolbar icons enlarged 22 → 30 px button / 14 → 18 px SVG, all toolbars.
Print preview + PDF export New
editor.printPreview() opens a clean print-ready window; editor.exportPdf({ title }) additionally auto-triggers the print dialog so the user can pick “Save as PDF” as the destination — browser-native, no library required. A @media print stylesheet hides all toolbar / chrome / handles so only the document content prints.
Math markup hook New
editor.applyMathMarkup() wraps every $x^2$ run in <span class="rte-math-inline" data-tex="x^2">; editor.renderMath() additionally detects KaTeX or MathJax v3+ on the host page and dispatches to it. No bundling — you bring your own renderer, the editor provides stable markup.
Right-click “Ask AI” context menu AI
When a text selection is non-empty and the AI Toolkit plugin is wired, right-click shows a compact menu: ✨ Ask AI to explain, ✏️ Rewrite, 📝 Summarize, 🌐 Translate. Each entry calls editor.aiToolkit.openDialog({ mode }). Disable with config.aiContextMenu = false.
DOCX import endpoint Server
New: POST /richtextbox/import/docx on the ASP.NET Core RichTextBox.AspNetCore package. Accepts multipart/form-data with a .docx file; returns { html, mode, fileName }. Client wrapper: editor.aiToolkit.importDocx({ url, file, mode }) — opens a file picker if file is omitted, then applies via insertHTML or setHTMLCode. Uses DocumentFormat.OpenXml; conservative HTML subset (p, strong, em, u, h1–h6, ul / ol / li, br, table / tr / td) that round-trips with the existing DOCX export.
Robustness fixes Bug fix
Five real-world regressions resolved this wave:
- Chrome iframe document-swap recovery. Chrome fires a deferred
about:blankload AFTER our synchronouscontentDocument.open/write/close, replacing the document. Closure refs (editdoc/editable/editsel) now reseat to the live document; all 18 iframe listeners re-bind. Fixes “editor renders but typing does nothing” on multi-editor pages. - Toolbar null-guard. A single unknown button command in the toolbar config no longer crashes the toolbar build half-way through, leaving the editor visually-rendered-but-non-editable.
- Image insertion at caret. Persistent
__lastInEditorRangetracker ensures the file-picker flow inserts the upload at the original caret position even if focus shifted in between (reported by Nathan Allen / Kenneth Chen). - Plugin-button crash on empty selection.
_editsel_getRange()synthesises a collapsed range at end-of-content when callers (emoji panel, gallery, image dialog) fire from outside the iframe and the editor has never been focused. - NuGet
DocumentFormat.OpenXmlnow declared as aPackageReferencein the generated demo csproj so trial download bundles include the dependency (reported by Kenneth Chen).