Changelog
0.14.0 (2026-07-26)
Section titled “0.14.0 (2026-07-26)”Features
Section titled “Features”- feat(extension-block-controls): new
addBlockMenuItems()hook. Any extension can contribute entries to the block handle menu the same wayaddToolbarItems()already works: an item declares a group (primary,colors,turnInto,collaboration), an optional order, and may report itself unavailable or disabled with a reason. Contributed entries keep the menu’srole="menuitem", roving tabindex and arrow-key navigation, a disabled item renders inert witharia-disabledand its reason as the title rather than disappearing, and a contributor that throws is skipped instead of taking the menu down.ExtensionConfigBaseis exported from@domternal/coreso the declaration merge resolves in a consuming package. (#140) - feat(core): read-only editors now refuse every editing affordance and command entry point. The toolbar hides behind a new
allowReadOnlyflag on toolbar items, block handles and table chrome are hidden, and the bubble menu, floating menu, heading shortcut, table dropdowns, details toggle and image resizing are all gated, closing the holes where a read-only document could still be edited. (#138) - feat(core):
tablejoins the defaultUniqueIDtypes, so a table is addressable by id like every other block. Naming the type is inert when the table extension is absent, exactly asimagealready was; without it a table was the one block the block menu could not Copy link and no id-anchored feature could name. (#140)
- fix(core): block ids now survive undo, moves and duplicates. The startup id sweep stays out of the undo stack, so the first undo no longer strips every id and lets the next sweep mint different ones; a block dragged within the document keeps its id, because
transformPastedruns on the dragged slice before the source is deleted and made a plain move look like a paste; and when two nodes momentarily hold the same id the node that already had it keeps it, instead of the first in document order winning and renaming the original in favour of a copy pasted above it. Every id consumer benefits:#hashanchors, table-of-contents deep links and the block menu’s Copy link all break silently when an id changes underneath them. (#140) - fix(core): the slash, emoji and mention menus shrink to the available viewport space instead of flipping over the content they were triggered from. (#134)
- fix(extension-block-controls): a block handle whose hovered block is deleted now retracts, instead of freezing the next hover and handle click on a block that is gone. (#139)
0.13.0 (2026-07-19)
Section titled “0.13.0 (2026-07-19)”Features
Section titled “Features”- feat(core): the Notion-mode text bubble menu now leads with an
aiitem (Notion’s “Ask AI”) followed by a separator; likemathInline, it is silently skipped along with its separator when the Pro AI extension is not loaded. (#131) - feat(core): new
announce(view, message)utility: a shared politearia-livestatus region inside the editor for screen-reader feedback on actions with no other non-visual signal (WCAG 4.1.3). Used by the Pro columns keyboard commands; reusable by any extension. (#130) - feat(extension-block-controls):
DropZoneQuerypassed todropZoneProvidersnow carriesdraggedTo(the dragged range’s exclusive end) alongsidedraggedFrom, so providers read a range instead of assuming a single block; groundwork for multi-block drags while the API is still experimental. (#130) - feat(theme): the block drop indicator stays visible under Windows High Contrast (forced colors) by repainting with the OS
Highlightcolor. (#130) - feat(extension-block-controls): experimental
dropZoneProvidersoption on BlockHandle. Providers claim pointer positions during a handle drag; a claimed position hides the built-in drop indicator and turns the release into a no-op for BlockHandle, so a higher-priority plugin can own its own drop zones (custom indicators and transactions). With no providers registered, behavior is unchanged. (#130) - feat(extension-block-controls): experimental
nested.anchorContainersoption on BlockHandle. Blocks inside listed side-by-side containers (e.g. acolumnnode) get Notion-style per-block handles: the cursor’s horizontal position picks the container, hover resolution is scoped to that container’s subtree, and the handle anchors to the container’s left edge instead of the editor gutter. The gap between containers belongs to the container whose handle floats in it, so hovering the gap summons the neighbour block’s handle instantly (Notion behavior); margins resolve into the nearest container. Pair it with adropZoneProvidersentry that claims drops back into the container; without one, moves out of it are irreversible. Default off; behavior is unchanged without the option. (#130)
- fix(extension-emoji): typing emoticons in a row (e.g.
xD xD,:) :)) now converts every one; the rule was swallowing the space that triggered the conversion and then rejecting an emoticon left flush against the resulting emoji atom, so every other emoticon stayed as literal text. It now keeps the trailing space and treats an atom node as a valid leading boundary. (#132) - fix(extension-table): a table whose columns have no explicit widths is now floored at
defaultCellMinWidthper column, so inside a narrow container (e.g. a layout column) the cells stay readable and the.tableWrapperscrolls horizontally instead of crushing them, matching Notion’s tables. Tables with resized columns already behaved this way. (#130) - fix(extension-block-controls,theme): the block handle now matches Notion’s control layout: the + button sits left of the drag grip (the grip is adjacent to the block it moves), the two buttons sit flush instead of 2px apart, and the 40px cluster ends 4px before the text instead of 10px. Anchored handles inside side-by-side containers now fit a 46px gutter without overhanging the left neighbor. (#130)
- fix(extension-block-controls): a collaborator deleting the dragged block mid-drag no longer makes the drop move whichever block now occupies the stale source position; once the drag source is committed to plugin state, its mapped (or deleted) position is the only truth and the drop aborts cleanly. (#130)
- fix(core):
setContentnow ends with the caret at the LAST TEXT position of the new document instead of leaving the mapped selection wherever the replace strands it; with a document ending in an atom (e.g. block math) that stranded selection was a NodeSelection on the trailing atom, which disabled every mark command until the user clicked into the editor. (#130) - fix(react):
useEditorno longer echoes a full-documentsetContenton every mount; the content-sync effect reacts only to acontentprop value the current editor does not already carry. (#130) - fix(extension-block-controls): dragging the last real block out of a generic
block+container that also holds an empty placeholder paragraph no longer swallows the container and the placeholder with it; the filler-paragraph collapse rule is scoped to list items, its documented purpose. (#130) - fix(extension-block-controls): the drop zone now extends 80px past the editor’s right edge, mirroring the left gutter, so a release in the page’s right margin drops instead of dying at the content edge. This also gives
dropZoneProviderssymmetric room for right-edge side zones. (#130) - fix(core): the caret-follow placeholder (
showOnlyCurrent) renders only while the editor is focused; after a load or tab switch the preserved selection no longer shows a misplaced hint on an unfocused editor. The empty-document placeholder still renders without focus. (#130) - fix(extension-block-controls): dragging a native text selection is no longer swallowed by the block drop handler; drops that do not come from a handle drag fall through to ProseMirror’s default move. (#130)
- fix(extension-block-controls): the drop indicator hides as soon as the dragged block is deleted mid-drag (e.g. by a collaborator), instead of advertising a landing line for a guaranteed no-op release. (#130)
0.12.1 (2026-07-11)
Section titled “0.12.1 (2026-07-11)”- fix(theme): long block (display) equations now scroll horizontally instead of being clipped by the editor, and the LaTeX edit popover is bounded to the viewport with a scrolling preview instead of stretching to the formula’s width. Inline math keeps its overflow visible so fractions, integrals, and roots are never vertically clipped. (#128)
0.12.0 (2026-07-10)
Section titled “0.12.0 (2026-07-10)”Features
Section titled “Features”- feat(extension-markdown): new
@domternal/extension-markdownpackage: GitHub-flavored Markdown import and export for the full schema. Markdown-looking plain-text pastes convert to rich content (opt-out),insertMarkdownandsetMarkdownContentcommands plus a headless parser/serializer API cover programmatic use, and serialization reports fidelity losses through a warnings channel. Tables, task lists, math, and fenced code round-trip; a currency guard keeps$5 and $10from parsing as math. (#125) - feat(core): plugin views can dispatch transactions while the editor is constructed, so collaborative bindings apply their initial sync immediately. The framework wrappers gain a
history: falseoption for editors that bring their own undo,onErroris wired before extension setup so construction-time errors reach it, and the newExtensionConfigurationErrorescapes extension error isolation for fatal misconfiguration. (#124)
Chores
Section titled “Chores”- All packages now ship a LICENSE file in the npm tarball, and the repository gains issue forms, a pull request template, a code of conduct, and a security policy. (#123)
0.11.2 (2026-07-01)
Section titled “0.11.2 (2026-07-01)”- fix(theme): the muted and placeholder text color now meets WCAG AA contrast on both themes. The light muted token moves from
#999999(2.85:1) to#6b7280, and the dark token from#777777(3.72:1) to#9ca3af, so placeholder text, disclosure toggles, and menu hints stay legible. (#119, #120)
- Every package now ships a package-specific README with a real description, install, and usage section, replacing the previous shared boilerplate. The four per-framework StackBlitz links are consolidated into a single “Live examples” link to https://domternal.dev/examples. (#119, #120)
0.11.1 (2026-06-30)
Section titled “0.11.1 (2026-06-30)”- fix(extension-details): the disclosure toggle stays open on click. A DOMObserver flush triggered right after the pointer click (for example moving the selection out of the details) no longer redraws the node view and discards the DOM-only open state. (#117)
0.11.0 (2026-06-25)
Section titled “0.11.0 (2026-06-25)”API cleanup
Section titled “API cleanup”- Removed the never-emitted
paste,drop,delete, andunmounteditor events and their prop types (PasteEventProps,DropEventProps,DeleteEventProps). They were exported but never fired, soeditor.on('paste', ...)and imports of those types will no longer compile.mount/MountEventPropsare unchanged. (#115)
Features
Section titled “Features”- feat(extension-image): edit alt text on existing images. Selecting an image adds an “Edit alt text” bubble action that opens an alt-only menu pre-filled with the current alt, and the action shows active when the image already has alt text. (#115)
- fix(angular, react, vue, vanilla): the editor honors the
skipUpdatemeta, so a programmaticsetContent(content, false)no longer echoes throughonUpdate/ the Angular control-value accessor (it no longer marks reactive forms dirty onwriteValue). (#115) - fix(vue):
immediatelyRender: trueno longer renders a blank editor (the editor DOM is re-parented on mount), and changing the extensions array no longer leaks an orphan clone into the container. (#115) - fix(vanilla): the toolbar no longer throws when keyboard navigation lands on an out-of-range button index. (#115)
- fix(core): a heading configured with an empty
levelsoption falls back to level 1 instead of rendering<hundefined>;unsetTextColor/unsetHighlightalso clear the named color/highlight token so the “Default” swatch resets token-based colors;insertContent([])returns false instead of deleting the selection, and malformed JSON/HTML returns false instead of throwing; the input-rulecompositionendhandler guards against a destroyed view. (#115) - fix(theme): the light theme resets every dark-only token, so a
.dm-theme-lightregion nested inside a dark context no longer leaks dark block colors, scrollbars, and shadows. (#115) - fix(extension-toc): the floating table-of-contents card collapses on scroll inside a container (
activeScrollParent), not only on window scroll. (#115) - fix(extension-details): the disclosure toggle exposes
aria-expandedandaria-controlsso assistive technology can announce its open / closed state. (#115) - fix(extension-image): the node view no longer writes the literal string
"null"to an image’salt/titlewhen those attributes are absent. (#115)
0.10.0 (2026-06-21)
Section titled “0.10.0 (2026-06-21)”Packages
Section titled “Packages”- New:
@domternal/extension-math- LaTeX math (inline + block) with a pluggable renderer (KaTeX). ShipsMathInline,MathBlock, the sharedMathEditingedit popover, and aMathRendererinterface. (#110) - Renamed:
@domternal/extension-block-menuis now@domternal/extension-block-controls. The old package becomes a thin re-export shim, so existing imports keep working unchanged; it is deprecated and will be removed in v1.0.0. Switch your imports to@domternal/extension-block-controls. (#111)
Features
Section titled “Features”- feat(extension-math): inline (
$...$) and block ($$) authoring via slash menu, toolbar, input rules, and the text bubble menu, with a distinct radical icon for inline equations and turning a text selection into an equation. The shared edit popover has a live preview and is keyboard accessible (Enter on a selected equation opens it, WCAG 2.1.1), and rendered math is RTL-isolated. (#110, #112) - feat(core): a command run from a toolbar or menu now returns focus to the editor on the next frame, yielding when the command opened a popover input (such as the math editor) so that field keeps focus. This keeps the selection highlight after keyboard activation of a toolbar button. (#112)
0.9.1 (2026-06-17)
Section titled “0.9.1 (2026-06-17)”- fix(core): outdenting a list item into a list of a different kind keeps its own kind and checked state (a to-do outdented next to bullets stays a to-do), instead of taking on the surrounding list’s type. (#108)
- fix(core): pressing Enter at the end of a list item that has nested children adds a new sibling and leaves the children under the original item; Enter on an empty item with children no longer spawns a stray empty item. (#108)
- fix(core): the ordered-list
startattribute is clamped to a valid positive integer, so malformed markup no longer writesNaN/nullinto the document. (#108) - fix(core): GitHub-style (
contains-task-list) markdown task lists import as real to-do lists with their checked state preserved, and a single<ul>that mixes to-do and plain items no longer fabricates a leading empty placeholder item. (#108) - fix(theme): list indentation now scales with the editor font size, a colored task item keeps its checkbox aligned with its label, and list markers / checkboxes mirror correctly in RTL; a task list nested inside a bullet or ordered list no longer inherits an extra children-zone indent. (#108)
0.9.0 (2026-06-15)
Section titled “0.9.0 (2026-06-15)”Features
Section titled “Features”- feat(core): the floating menu plugin now lives in
@domternal/core, and@domternal/extension-block-menuis no longer a peer dependency of the framework wrappers. The Angular/React/Vue/Vanilla wrappers need only@domternal/core+@domternal/theme; add@domternal/extension-block-menuexplicitly when you want the block handle, slash menu, or drag-and-drop. (#104) - feat(react):
immediatelyRendercreates the editor synchronously on the first render instead of after mount. (#104) - feat(extension-block-menu): per-item “Turn into” for lists. The slash command and block menu convert one list item at a time via the new
turnIntoBulletList/turnIntoOrderedList/turnIntoTaskListcommands, while the toolbar and keyboard shortcut still convert the whole list. (#105) - feat(theme): Notion-style list spacing. In notion mode, adjacent lists of different type chunk together at the within-item gap instead of the wider container gap; in both modes a nested list sits tightly under its parent label. (#105)
- fix(core): the placeholder paints on the initial draw of an empty editor instead of waiting for the first transaction. (#104)
- fix(extension-mention, extension-emoji): suggestion items select on
mousemove, so a resting pointer no longer overrides arrow-key navigation. (#104) - fix(theme): content clips on the editor’s inner wrapper so popups can escape short editors; added the
--dm-editor-padding-topvariable for two-value padding shorthands. (#104) - fix(core, extension-table):
positionFloatinggained a flip-boundary option so the table cell toolbar stays inside the editor. (#104) - fix(core, extension-block-menu): a list item keeps its kind (bullet / ordered / to-do) across drag, paste, and turn-into; split numbering restarts and container drags close both ways. (#105)
- fix(extension-block-menu): no-op block drops are suppressed. The drop indicator hides and the drop is ignored when the release target is the block’s own slot. (#105)
- fix(core): Shift+Tab outdents only the targeted children-zone block, splitting the list and keeping the parent item intact (Notion parity). (#105)
0.8.0 (2026-06-09)
Section titled “0.8.0 (2026-06-09)”Features
Section titled “Features”- feat(extension-block-menu): reworked block drag-and-drop onto a unified gap-first drop model. The drop indicator now snaps to the nearest gap between blocks and the pointer’s horizontal position chooses the nesting depth, with Y and X dead-bands so the line no longer flickers between gaps or levels. Drag right to nest a block deeper, left to outdent it across ancestor levels. (#101, #102)
- feat(extension-block-menu): position-aware nested drop. A dragged block can now land as the first, in-between, or last child of a list item, not just as a sibling. (#101)
- feat(extension-block-menu): dropping a non-list block into a list keeps the block’s own type and splits the list around it, instead of wrapping it in a bullet. A list item dropped into a list of the other kind keeps its kind too (a to-do dropped among bullets stays a to-do). (#102)
- feat(extension-block-menu): two same-type lists rejoin into one when the block separating them is dragged out from between them, healing the ordered-list numbering. (#102)
- feat(react):
ReactNodeViewRenderernode-view types now match ProseMirror’sNodeViewConstructor(getPos,decorations,ignoreMutation), soaddNodeViewno longer needs a cast. (#102)
Internal
Section titled “Internal”- test(demos): cross-wrapper functional parity for drag-and-drop, node views, and form integration. Added Angular
ngModel, React node-view, and React compound demos with their e2e, and backported heading Notion-Enter and task-checkbox coverage to vanilla/react/vue. (#102)
0.7.5 (2026-06-03)
Section titled “0.7.5 (2026-06-03)”Changes
Section titled “Changes”- fix(core):
StarterKitmakesListIndentopt-in (off by default). Tab on a paragraph that merely follows a list used to capture focus and pull the paragraph into the list; now Tab moves focus to the next field, which suits embedded / form usage. Opt back in withStarterKit.configure({ listIndent: true }). In-list Tab/Shift-Tab and block-menu drag-to-nest are unchanged. (#98)
- fix(core): extension instances are now cloned per editor, so several editors on one page no longer clobber each other. Previously creating a second editor repointed the first editor’s node types at its own schema, and list
Enteron the earlier editors dropped an indented child paragraph instead of a new list item. (#91) - fix(core):
SelectionDecorationno longer keeps a ghost range when focus moves from one editor to another on the same page. The “editor UI” blur check is now scoped to the editor that lost focus, so a click into a different editor collapses its selection.
0.7.4 (2026-05-29)
Section titled “0.7.4 (2026-05-29)”- fix(extension-table): table control dropdowns (row/column handles, cell color and alignment menus) now render inside the editor container instead of
document.body, so they display correctly when the editor lives inside a modal or<dialog>. They are positioned withfixedso the editor’soverflow: hiddencan no longer clip them. (#93) - fix(core): converting a nested list item via a slash or menu command (Heading, Code block, Quote, Details) now keeps it indented as a children-zone block of its parent (Notion-style “Turn into”) instead of silently doing nothing. Top-level items still dissolve to a top-level block. (#94)
- fix(extension-table): the Table extension now pulls in Gapcursor, so a table at the end of the document is no longer a caret trap. ArrowDown or a click below the table drops a gap cursor and lets you type a new paragraph. (#94)
- fix(core): list markdown shortcuts (
-,*,+,1.,[ ],[x]) now also join with the following same-type list, not just the preceding one. Creating a list item on a line between two lists merges them into a single list instead of orphaning the trailing one. (#95)
0.7.3 (2026-05-24)
Section titled “0.7.3 (2026-05-24)”- fix(theme): link and image popovers were invisible on light theme. Popovers mount to
document.bodyso the--dm-*design tokens defined inside.dm-editornever cascade to them;background: var(--dm-bg)resolved to the CSS initial value (transparent) and the popover blended into the toolbar. Hoisted token fallbacks to SCSS variables in_link-popover.scssand_image.scss, each carrying both the cascade lookup and a literal fallback. Also strengthened the default--dm-popover-shadowso popovers lift visibly off the toolbar. (#88)
0.7.2 (2026-05-24)
Section titled “0.7.2 (2026-05-24)”Features
Section titled “Features”- feat(extension-toc):
FloatingTocOutline.activeScrollParentoption enablesscroll-mode='container'. The outline pins to the host viewport’s vertical middle via CSS sticky (zero JS during scroll) and the scroll-spy follows the host scroll instead of the window. Pair with a fixed-height wrapper for Notion-style scrollable editors. See Table of Contents. (#86) - feat(extension-toc,theme): tick column caps at ~50% of the host viewport with
overflow: hidden(matches Notion behaviour); the hover card matches that height and scrolls its rows internally. New--dm-toc-ticks-max-htoken controls the cap. (#86) - feat(theme): new
--dm-editor-padding-top-extratoken adds a comfortable gap between the editor’s top edge and the first row. Defaults to1remand bumps automatically in Notion mode where there is no toolbar above the column. (#86) - feat(demo-vanilla,demo-react,demo-vue,demo-angular): “Notion scrollable” demo mode. Editor sits inside a fixed-height wrapper that scrolls internally; the TOC scroll-spy follows the host scroll, not the window. Full e2e parity across all four demos. (#86)
- fix(theme,extension-block-menu): BlockHandle
dragstartnow hides the native dropcursor element correctly. The previous selector targeted.ProseMirror-dropcursor, a classprosemirror-dropcursorv1.8+ does not emit, so both the custom and native indicators rendered at once during a handle drag. The hide rule now targetsprosemirror-dropcursor-blockandprosemirror-dropcursor-inline. E2E regression coverage added across all four demos. (#86) - fix(extension-toc):
FloatingTocOutlinedefaultminHeadingslowered from 2 to 1 so the outline appears on a single-heading document (matches Notion). Click-to-scroll no longer bubbles to the window whenactiveScrollParentis set. (#86) - fix(extension-toc,theme): zero-jitter container scroll. Absolute UI children (BlockHandle, BubbleMenu, FloatingMenu) are pinned at
top: 0so the hidden box doesn’t inflate the host’sscrollHeightand create a ghost scroll. (#86) - fix(theme): tokenised slim scrollbar on the TOC card and Notion scrollable container so contrast holds in dark mode. (#86)
- fix(theme): suppressed the sharp 2px accent border on
li.ProseMirror-selectednode::afterwhen BlockHandle is active. The translucent halo is now the single source of truth for selection feedback so dragging a list item no longer shows a doubled frame. (#86)
Internal
Section titled “Internal”- refactor(extension-toc): extracted shared
getHeadingLabelandsetActiveMarkerhelpers used by bothFloatingTocOutlineandTableOfContentsBlock. (#86) - refactor(extension-block-menu): extracted
clearTriggeredFlaghelper inFloatingMenu(deduplicates the dismiss and update branches). (#86) - refactor(demos): standardised
TOAST_MS[kind]access pattern across all four demos; Angular’s copy-link listeners switched toAbortControllerfor parity with the other three. (#86)
0.7.1 (2026-05-23)
Section titled “0.7.1 (2026-05-23)”Features
Section titled “Features”- feat(ci): layered safety nets to prevent silent surface regressions. Consumer type-surface test exercises every
RawCommandsaugmentation through the dist; per-package API surface snapshots; CSS variable references validated against theme definitions; gzipped bundle size budgets per package; grep guard forbidding relative-path module augmentations;publint --strict. (#84) - feat(theme): tables render as a “data view” globally - 15px font, 1.5 line-height, tight cell padding. Easier to scan than body-scale tables. Override on
.dm-editor .ProseMirror tablefor body-scale. See Table styling. (#84) - feat(theme): Notion mode suppresses the slash-command placeholder inside narrow
<td>/<th>cells so long hints likePress '/' for commandsdon’t wrap onto two lines. See Notion mode placeholder. (#84) - feat(core): new
copyThemeClass(view, target)utility - copies the editor’sdm-theme-*class onto a floating element portaled todocument.bodyso the dark/light cascade reaches it. (#84)
- fix(core):
RawCommandsmodule augmentations now reach external consumers. 30+ source files switched fromdeclare module '../types/Commands.js'todeclare module '@domternal/core'; tsup + rollup-plugin-dts dropped relative-path augmentations during bundling, silently breakingeditor.commands.focus()and the rest of the command surface in any consumer of@domternal/[email protected]. (#84) - fix(theme): table dropdown text rendered dark-on-dark in dark mode because portaled popovers couldn’t read
--dm-button-color. Added fallback to--dm-text. (#84) - fix(core,extension-image,theme):
LinkPopoverand image popover rewritten to read theme tokens instead of hardcoded hex values; removed their dark-theme override blocks. Both popovers now adapt automatically to light/dark via the standard cascade. (#84) - fix(extension-block-menu,theme): native
prosemirror-dropcursorno longer doubles up with BlockHandle’s own.dm-block-drop-indicatorduring a handle drag. BlockHandle adds adm-block-handle-draggingclass for the drag’s duration; the theme hides the native cursor only while that class is present. Non-handle drags (text selection, external file drops) keep the native cursor. See Dropcursor interaction. (#84) - fix(extension-toc):
Table of contentsslash menu item moved fromBasicgroup (priority 600, first slot) toAdvanced(priority 90, last slot). Sits next toToggle blockat the end of the menu instead of dominating the first position. (#84) - fix(theme): Notion mode body and h1 / h2 / h3
font-sizeoverrides removed. Both modes now share the base text scale, so toggling between classic and Notion shifts layout (narrow column, gutter, tight rhythm) without a text-size jump. (#84) - fix(theme): Notion mode table cells match classic-mode cell height. Higher-specificity rule keeps
td > p/th > pmargin at 0 against the Notion paragraph-margin rule that was puffing cells. (#84)
0.7.0 (2026-05-19)
Section titled “0.7.0 (2026-05-19)”Breaking changes
Section titled “Breaking changes”listItem/taskItemschema is now Notion-strict (paragraph block*). Existing content where the first child is not a paragraph may need migration. See List Item and Task Item for migration patterns.
Packages
Section titled “Packages”- New:
@domternal/vanilla- framework-free DOM wrapper for Astro, Svelte, Solid, plain HTML, and Web Components. Class-based API (new DomternalEditor(host, opts)+.destroy()+ setters), ESM-only with subpath exports, SSR-safe. ShipsDomternalEditor,DomternalToolbar,DomternalBubbleMenu,DomternalFloatingMenu,DomternalEmojiPicker,DomternalNotionColorPicker. See Vanilla guide. - New:
@domternal/extension-block-menu- Notion-style block UX:BlockHandle(hover gutter with drag and plus button),BlockContextMenu(Delete / Duplicate / Turn into / Colors / Copy link),KeyboardReorder(Mod-Shift-Up/Down),SlashCommand,SmartPaste. See Block Menu. - New:
@domternal/extension-toc- Notion-style Table of Contents. ShipsTableOfContents(heading observer +scrollToHeadingcommand),FloatingTocOutline(sticky outline with IntersectionObserver active tracking and hover-expanded card),TableOfContentsBlock(inline/tocatom node). See Table of Contents.
Features
Section titled “Features”- feat(core):
FloatingMenuitems API with controller (role=menu, Alt-F10 / Mod-/ keymap, click-outside, roving tabindex). Default items contributed by Heading, Lists, Blockquote, CodeBlock, HorizontalRule, Image, Table, Details viaaddFloatingMenuItemshook. See Floating Menu. (#75) - feat(angular,react,vue): render
FloatingMenuvia controller with role=menu, groups, roving-tabindex keyboard nav. (#75) - feat(core):
NotionColorPickerextension with named-token color attrs (colorToken,backgroundColorToken) ontextStylemark, hex/token mutual exclusion, last-action-wins for inline color conflicts. See Notion Color Picker. (#80) - feat(angular,react,vue,vanilla):
DomternalNotionColorPickercomponent - circular 5x2 grid, dark palette, persistent picker, A trigger glyph with slash. (#80) - feat(core): Notion-style list/task UX. Strict
paragraph block*schema with children-zone indent,ListIndentextension (Tab/Shift-Taboutside list items indents as nested child), Enter on empty children-zone paragraph inserts sibling inside the li, non-empty Enter splits in place, Backspace at offset 0 lifts as top-level paragraph. See List Indent. (#77) - feat(core): Notion-style Enter on heading - end-of-heading inserts paragraph below, empty heading converts in place. (#77)
- feat(core): Notion-style
/h1in a list-item label dissolves the item (setBlockType lift fallback). (#77) - feat(core):
TaskItemcheckbox click toggles checked state via NodeView and applies strikethrough; Enter on a checked task spawns an unchecked sibling. (#80) - feat(extension-block-menu): drag-to-reorder with custom drop indicator (sibling/nested modes via X-threshold over list items), auto-scroll near viewport edges, cross-list-type drop auto-conversion, wrap dropped block into listItem when target is a list. (#76, #77)
- feat(extension-block-menu): nested drag handle for inner blocks of list/task items via
BlockHandle.nested(configurable allowed nodes, deepest-block-at-Y resolution). (#77) - feat(extension-block-menu):
BlockContextMenuColors picker, Copy link via newwriteToClipboardutility (async Clipboard API + execCommand fallback) with split success/error events. Turn into routes wrapper commands and accepts wrapper sources (list-type swap from list-item drag handle). (#76, #80) - feat(extension-block-menu):
SlashCommandworks in a list-item label and cooperates with other overlays viadm:dismiss-overlays. (#76, #77, #78) - feat(extension-toc):
FloatingTocOutlineeditor anchor mode with middle/center/frozen state machine, expanded card scroll-closes and viewport-clamps, level-encoded width buttons, hover-expanded card with text labels and per-level indent. (#78, #80) - feat(extension-toc): inline
/tocatom node with reactive heading list, initial-load#hashauto-scroll opens collapsed details ancestors. (#78) - feat(angular,react,vue,vanilla):
iconsprop / input onDomternalBubbleMenu(parity withDomternalToolbar). (#81) - feat(extension-block-menu,angular,theme): Notion-style empty-paragraph placeholder and
requireExplicitTriggeron FloatingMenu (opens only on the+button). (#76, #80) - feat(theme):
.dm-notion-modeopt-in class with task checkbox token overrides, Notion-style centered content + side gutter for block handle (no text shift on color toggle), task list aligns with bullet list. (#78, #80) - feat(demos): Notion mode in all four demo apps (default/custom/notion toggle) with the full extension stack. (#75, #76, #77, #80)
- feat(angular,theme): text-align dropdown in Notion bubble menu with dynamic icon, Escape close, cross-overlay dismissal, scoped active highlight. (#80)
- feat(core):
BlockColorextension - globalbgColorandtextColorblock attrs preserved acrossturnIntoBlock. See Block Color. (#76) - feat(core):
UniqueIDextension renames duplicate ids onsetContent. (#80) - feat(emoji): toolbar option to hide the emoji button from the toolbar (default:
true). (#71)
- fix(angular,react,vue): show image bubble menu by default when the
Imageextension is loaded. (#71) - fix(core): Backspace on an empty paragraph after a list deletes it and places the caret at the end of the last item, instead of wrapping it back as a list item. (#78)
- fix(core):
toggleListin a children-zone paragraph wraps that paragraph in a fresh list, instead of converting the ancestor. (#78) - fix(core): Backspace on an empty paragraph between two lists of the same type joins them back into one. (#78)
- fix(extension-block-menu): slash menu activates only on a real typing event of the trigger char (not pure selection changes or bulk inserts); query tracks only typed chars. (#78)
- fix(extension-block-menu):
SmartPastehandles same-type slice paste (fixes heading-into-heading shred bug) and trailing hard-break (Shift+Enter). (#76, #80) - fix(extension-block-menu): Delete on a list item no longer kills the whole list. (#76)
- fix(extension-block-menu):
turnIntoBlockpreserves global attrs (bgColor,textColor,id) across block type change. (#76) - fix(extension-block-menu): drag and drop edge cases - PM dispatch deferred out of
dragstarttick, drop works in handle gutter and side margins, fast-drag race betweendragstartanddrophandled, dragging the only nested list item collapses its wrapper. (#76, #77) - fix(extension-block-menu):
SlashCommandmisposition and page scroll on open. (#76) - fix(extension-block-menu): hover detection on the editor parent so the block handle surfaces in the side gutter (Notion centered layout). (#76)
- fix(extension-block-menu): virtual-ref preserves color picker and context-menu position when the bubble menu rebuilds its anchor. (#80)
- fix(extension-toc): active heading tracks viewport-top crossing and always keeps one tick lit. (#80)
- fix(angular,core):
LinkPopovertriggered from the bubble menu anchors to the Link button (bottom-start) and reparents into.dm-editor. (#80) - fix(core): bubble menu hides A and
...triggers on node selection (image);...disabled on multi-block selection. (#80) - fix(theme): notion-demo page background and border follow the dark theme. (#78)
- fix(theme): scope task-item checked strikethrough to the label paragraph so nested children stay unstruck. (#80)
- fix(react): migrate to React 19
RefObject; runtime guard foruseEditorStatemode. (#74) - fix(angular): resolve ESLint errors in wrapper components; raise bundle size budgets for demo and example apps. (#74)
- fix(extension-block-menu): security + correctness review fixes (XSS, empty-doc guard, position mapping,
duplicateBlockmarks). (#76)
Accessibility
Section titled “Accessibility”role="menu"+aria-label="Floating menu"on FloatingMenu with roving tabindex. (#75)BlockContextMenu:aria-activedescendant, arrow nav, Esc close, focus return on outside-click. (#76)DomternalNotionColorPicker:aria-haspopup,aria-modal="false", disconnected-anchor defense, keyboard arrow nav across the swatch grid. (#80)prefers-reduced-motionguards on TOC tick highlight, card slide, drop-indicator transition. (#78, #80)- Forced-colors mode guard on TOC outline. (#78)
Tests / CI
Section titled “Tests / CI”- Codecov integration with per-package flags and coverage badge.
publint+arethetypeswrongvalidation extended to React, Vue, Vanilla, extension-block-menu, and extension-toc. (#72, #80) - E2E retries set to
2in all demo Playwright configs. (#74) - Demo-vanilla: 56 e2e spec files (41 shared + 15 Notion). Demo-angular full Notion mode e2e coverage. Demo-react / demo-vue Notion-specific specs.
- Coverage raised across packages:
extension-table95.56% statements,extension-block-menu~89% lines,core94.42% (all files ≥80%).FloatingMenuController100%. (#73, #75)
0.6.2 (2026-04-16)
Section titled “0.6.2 (2026-04-16)”- fix(vue):
Domternalcompound subcomponents (Domternal.Toolbar,Domternal.BubbleMenu, etc.) were tree-shaken away in production builds due tosideEffects: false. Moved assignments intoDomternal.tsso bundlers can’t drop them. - fix(vue):
editableprop on<Domternal>and<DomternalEditor>was not syncing at runtime. Added reactivewatchsinceuseEditor’s internal watcher can’t track plain object props. - fix(react,vue): toolbar keyboard activation (Enter/Space) did not work in
EditorContent. Addeddata-dm-editor-uiattribute soSelectionDecorationpreserves selection on toolbar focus.
0.6.1 (2026-04-16)
Section titled “0.6.1 (2026-04-16)”- fix(angular):
@domternal/[email protected]was published without compiled output (#66)
Improvements
Section titled “Improvements”- chore: add automatic
pnpm buildtoprepublishOnlyhook in all packages to prevent publishing without dist
0.6.0 (2026-04-15)
Section titled “0.6.0 (2026-04-15)”Features
Section titled “Features”- feat(vue): add
@domternal/vuewrapper withDomternalcompound component,useEditor/useEditorStatecomposables,DomternalEditor(v-model),DomternalToolbar,DomternalBubbleMenu,DomternalFloatingMenu,DomternalEmojiPicker, andVueNodeViewRenderer(Vue 3.3+) (#64) - feat(vue):
useCurrentEditor()inject for descendant components with VueappContextforwarding into ProseMirror node views - feat(vue):
VueNodeViewRendererwith reactive node/selected props,NodeViewWrapper,NodeViewContent, drag handle, and nested editable content
- fix(core): add
Backspacehandler toTaskItem- pressing Backspace at start of first task item now lifts it out of the task list (parity withBulletList/OrderedList)
- 2014 E2E tests for Vue demo app: 1923 ported from demo-react + 91 Vue-specific tests covering v-model two-way binding, compound component,
useCurrentEditor()inject chain, selector mode, andVueNodeViewRenderer
Packages
Section titled “Packages”- New:
@domternal/vue- Vue 3 wrapper with composable components, composables, and Vue node view renderer withappContextchain forwarding
0.5.1 (2026-04-14)
Section titled “0.5.1 (2026-04-14)”- fix(core):
SelectionDecorationpreserves selection when focus moves to toolbar or editor UI - fix(angular): ArrowDown dropdown trigger detection uses
document.activeElementinstead ofcontroller.focusedIndex - fix(angular,react): toolbar refocuses editor after keyboard-activated commands to preserve
::selectionhighlight - fix(angular,react): arrow keys enter emoji grid when focus is on grid container
- fix(angular,react): selecting emoji category tab via keyboard focuses first emoji in that category
- fix(theme): table dropdown hover fallback for dark mode
- 26 new E2E tests (13 Angular + 13 React) for toolbar dropdown keyboard navigation
0.5.0 (2026-04-13)
Section titled “0.5.0 (2026-04-13)”Features
Section titled “Features”- feat(core): add
SelectionDecorationto StarterKit (opt-out viaselectionDecoration: false), collapses range selection on blur to prevent ghost selections - feat(core): add
ariaLabeloption toEditorOptionsfor configurable editor label - feat(core): editor element now has
role="textbox",aria-multiline="true", andaria-labelby default - feat(core): dynamic
aria-readonlyattribute synced withsetEditable()state - feat(core): floating menu sets default
role="toolbar"andaria-label="Floating menu" - feat(theme):
:focus-visibleindicators on 16 interactive element types (toolbar, emoji, table, popovers, details) - feat(theme):
prefers-reduced-motionmedia query disabling all animations and transitions - feat(angular): bubble menu ARIA parity with React (
role="toolbar",aria-label,aria-pressed,role="separator") - feat(angular,react): ArrowUp/ArrowDown keyboard navigation inside open toolbar dropdown menus
- feat(angular,react): emoji picker grid 2D keyboard navigation (arrows, Enter/Space to select)
- feat(angular,react): emoji picker tabs with
role="tab"andaria-selected
- fix(theme): move
prefers-reduced-motionblock to end of stylesheet to correctly override all animation/transition rules - fix(angular): add missing
tabindex="-1"on frequently used and category emoji swatches - fix(react): use
document.activeElementfor ArrowDown dropdown trigger detection instead ofcontroller.focusedIndex
Accessibility
Section titled “Accessibility”aria-label="URL"on link popover input,aria-label="Image URL"on image popover inputaria-label="Task status"on task item checkboxesaria-label="Search emoji"on emoji picker search inputaria-label="Emoji suggestions"andaria-label="Mention suggestions"on suggestion containers- Table cell toolbar:
role="toolbar"witharia-label="Cell formatting" - Table dropdowns:
role="menu"witharia-label,role="menuitem"on items,role="separator"on dividers - Dropdown menu items:
tabindex="-1"for keyboard focusability (Angular + React)
- 105 new E2E accessibility tests (56 Angular + 49 React)
- 10 new E2E tests for SelectionDecoration blur behavior (5 Angular + 5 React)
0.4.1 (2026-04-09)
Section titled “0.4.1 (2026-04-09)”- fix(angular,react): prevent page scroll when emoji picker opens by using
focus({ preventScroll: true }) - fix(react): replace wrapper
<div>with<Fragment>in emoji picker grid so categories render as rows instead of columns
0.4.0 (2026-04-09)
Section titled “0.4.0 (2026-04-09)”Features
Section titled “Features”- feat(react): add
@domternal/reactwrapper with hooks, composable components, toolbar, bubble menu, floating menu, emoji picker, and React node views (#54) - feat(react): scaffold React example app and demo app with full E2E test suite
- feat(core): export
NodeViewContextinterface for framework wrapper node view integration
- fix(react):
deleteNodeinReactNodeViewRendererusesnode.nodeSizeinstead of hardcoded1for correct deletion of nodes with content - fix(react):
useEditorStateskips expensivegetHTML()/getJSON()on selection-only transactions - fix(react):
DomternalEditorrenders children before editor div (toolbar above content) - fix(react): bubble menu
activeVersiontriggers re-renders for active/disabled state updates - fix(core): replace
AnyExtensionunion type with interface to fix generic variance issue withconfigure()
Accessibility
Section titled “Accessibility”- Bubble menu:
role="toolbar",aria-label,aria-pressedon buttons,role="separator"on dividers displayNameon allDomternalcompound subcomponents for React DevToolsDomternalEditorRefexposesisEditable
- 1856 E2E tests for React demo app (38 spec files covering all extensions, toolbar, bubble menu, emoji picker, tables, mentions, and more)
- 60 React-specific E2E tests: bubble menu a11y,
aria-pressedsync, active class updates,useEditorStatereactive output, dark theme toggle, toolbar layout switch, context-aware bubble menu filtering
Packages
Section titled “Packages”- New:
@domternal/react- React 18+ wrapper withDomternalcomposable component,useEditor,useEditorState,DomternalEditor,EditorContent,DomternalToolbar,DomternalBubbleMenu,DomternalFloatingMenu,DomternalEmojiPicker,ReactNodeViewRenderer
0.3.0 (2026-04-01)
Section titled “0.3.0 (2026-04-01)”Features
Section titled “Features”- feat(mention): add default mention suggestion renderer with keyboard navigation and dark mode support (#52)
- feat(core): custom inputRules plugin with Backspace undo for all input rules (blockquote, lists, headings, code blocks) (#51)
- feat(core): add input rule helper wrappers (wrappingInputRule, textblockTypeInputRule, nodeInputRule, textInputRule, markInputRule) with undoable option (#51)
- fix(core): HR input rule trailing paragraph and undo cursor position (#52)
- fix(core): use event.code for heading shortcuts to fix macOS Alt key issues (#51)
- fix(core): toggleWrap lifts all paragraphs when unwrapping with AllSelection (#51)
- fix(core): prevent list input rules from firing inside existing list items (#51)
- fix(core): flatten mixed list+paragraph selections into single flat list (#51)
- fix(mention): add code mark guard to suggestion plugin (#52)
- fix(mention): fix keydown handling in suggestion plugin (#52)
- fix(theme): remove browser-default CSS from content styles (#51)
- fix(theme): adjust blockquote spacing, remove link cursor override (#51)
- fix(theme): add dark mode styles for mention dropdown (#52)
- 195 new E2E tests: mention (81), horizontal rule, image (38), emoji (27), details (11), blockquote input rule, heading shortcuts, lists (#51, #52)
0.2.1 (2026-03-27)
Section titled “0.2.1 (2026-03-27)”- fix(theme,table): apply dark theme to table dropdowns appended to document.body (#49)
- fix(theme,table): improve syntax highlighting contrast ratios for WCAG AA (#49)
- fix(core,table): toolbar layout button name fixes (#48)
- Unified README across all 10 packages with badges, features, and documentation links (#48)
- Rewrite main README (#47)
0.2.0 (2026-03-21)
Section titled “0.2.0 (2026-03-21)”Initial public release.
Packages
Section titled “Packages”@domternal/core- Framework-agnostic editor engine (13 nodes, 9 marks, 25 extensions, 112+ chainable commands, SSR helpers, toolbar controller with 45 built-in icons)@domternal/pm- ProseMirror re-exports (12 subpath exports: state, view, model, transform, commands, keymap, history, tables, inputrules, dropcursor, gapcursor, schema-list)@domternal/theme- Light and dark themes with 160+ CSS custom properties@domternal/angular- 5 Angular components (editor, toolbar, bubble menu, floating menu, emoji picker)@domternal/extension-table- Tables with cell merging, column resize, row/column controls (18 commands)@domternal/extension-image- Image with paste/drop upload, URL input, XSS protection, bubble menu@domternal/extension-emoji- Emoji picker panel and:shortcode:autocomplete@domternal/extension-mention-@mentionautocomplete with multi-trigger and async support@domternal/extension-details- Collapsible details/accordion blocks@domternal/extension-code-block-lowlight- Syntax-highlighted code blocks powered by lowlight
Highlights
Section titled “Highlights”- Built on ProseMirror with clean extension API
- Headless core works with any framework or vanilla JS/TS
- First-class Angular support (17.1+) with signals, OnPush, reactive forms, zoneless-ready
- Tree-shakeable, fully typed, SSR-ready
- SSR helpers:
generateHTML,generateJSON,generateTextfor server-side rendering - Inline styles export:
getHTML({ styled: true })for email clients, CMS, and Google Docs - Input rules for markdown-style shortcuts (e.g.
**bold**,# heading,> quote,- list) - Toolbar controller with automatic active state tracking and 45 Phosphor icons
- All floating elements (bubble menu, floating menu, popovers) powered by
@floating-ui/dom