Repository navigation
Releases
v0.8.0
Page tree operations (@slash-editor/core, @slash-editor/react):
- Optional
PageStore.setTrashed(ids, trashed)andPageStore.move(id, { parentId, index }). Trash/restore pages and re-parent or reorder them. The library never calls them; they are the contract for your page-tree UI and foronSubPagesDetached/onSubPagesAttached.listChildrenis now documented as returning siblings in order (trashed included), whichusePageTreerenders as-is.move'sindexcounts amonglistChildren(parentId)without the moved page, clamped; moving a page into itself or a descendant is a no-op. - The registry
createDemoPageStoreimplements both and persists sibling order.
v0.7.0
Emoji and highlighted code blocks move to their own entries (@slash-editor/core, @slash-editor/react):
- Breaking:
@slash-editor/core/emojiand@slash-editor/core/code-blocksubpaths.Emoji,emoji,searchEmojisand theEmojiItem/EmojiMenuState/EmojiMenuStorage/EmojiOptionstypes now import from@slash-editor/core/emoji;CodeBlock,codeBlockand theCodeBlockOptions/Lowlighttypes from@slash-editor/core/code-block. The root entry no longer exports them. - Breaking:
createBlockKittakes the built node.emojiandcodeBlockaccept the extension instead of its options (codeBlockstill acceptsfalsefor no code block), so the kit never imports the emoji dataset or lowlight.createBlockKit()drops from ~323 KB to ~198 KB gzipped (minified browser bundle, dependencies included) when neither is used. "sideEffects": falsein both packages, so bundlers can drop unused modules.
Migration:
// Before
import { CodeBlock, createBlockKit } from "@slash-editor/core";
createBlockKit({ emoji: { limit: 24 }, codeBlock: { lowlight } });
createBlockKit({ codeBlock: false, extend: [CodeBlock.extend({ addNodeView })] });
// After
import { createBlockKit } from "@slash-editor/core";
import { CodeBlock, codeBlock } from "@slash-editor/core/code-block";
import { emoji } from "@slash-editor/core/emoji";
createBlockKit({ emoji: emoji({ limit: 24 }), codeBlock: codeBlock({ lowlight }) });
createBlockKit({ codeBlock: CodeBlock.extend({ addNodeView }) });Media upload result mapping and display URLs (@slash-editor/core, @slash-editor/react):
toAttrsoption onimage,file, andvideo. Maps the adapter's full upload result onto node attrs (default{ src: result.url }) on the first success and on every retry, merged over the existing attrs — persist a server's canonical name/size/mime or id without reimplementingretryImage/retryFile/retryVideo. Also reachable throughcreateBlockKit({ image | file | video: { toAttrs } }).- Display-only
resolveSrcoption onimage,file,video, andembed(embedreceives its storedurl). Rewrites the URL a node view displays — signed URLs, files behind auth — whilegetHTML(), markdown, and JSON keep the canonical stored value. NewResolveSrcandUploadToAttrstypes. useResolvedSrc(src, node, resolveSrc)hook: sync results on the same render, async results once settled (stale results for a changedsrcare discarded), errors fall back to the stored URL. The registrynode-viewsimage/file/video/embed views apply it.
starterKit option on createBlockKit. Options are forwarded to Tiptap's StarterKit, so createBlockKit({ starterKit: { underline: false } }) drops the underline mark (and Mod-u / ++text++ markdown) without reconfiguring the returned extension by hand. Keys the kit owns (heading, undoRedo, link, codeBlock, blockquote, gapcursor) are excluded from the BlockKitStarterKitOptions type; use headingLevels, history, link, and codeBlock for those.
Keep the block handle inside the hover zone. BlockDrag now measures gutterWidth from the hovered block's left edge (or the editor box, whichever is further left) and defaults it to the new BLOCK_GUTTER_WIDTH (80px), which the registry BlockHandle lays itself out in — so the handle no longer vanishes on the way to it in an editor with little or no left padding. The geometry is exported as the pure resolveHoverRect.
unsetComment(threadId) now removes the thread's anchors across the whole document instead of only the selection (new pure removeCommentThread helper), so useComments().removeAnchor works from a sidebar; it no longer focuses the editor.
Markdown import reads a mention or page link that starts a line as inline content. Such a paragraph (including one holding only the mention, or a list item, blockquote or callout line starting with one) used to import as literal comment text or be dropped entirely, so serializeMarkdown output with a leading mention did not round-trip.
Bind Mod-k to openLinkEditor, matching the bubble toolbar's advertised ⌘K. The key falls through when there is nothing to link; change or remove it with linkEditor: { shortcut: "…" | false }.
v0.6.0
Syntax-highlighted code blocks (@slash-editor/core):
codeBlockoption.createBlockKit({ codeBlock: {} })swaps StarterKit's plain code block for@tiptap/extension-code-block-lowlightunder the samecodeBlockname, so block types, slash items, and markdown are unchanged. Opt-in; passlowlightto choose grammars (default: lowlight'scommonset).codeBlock: falseleaves it to aCodeBlock.extend(...)viaextend.CodeBlock/codeBlock()exports, plus thecode-block-node-viewregistry item: a language selector andhljs-*token CSS themed from shadcn tokens.
Emoji picker (@slash-editor/core, @slash-editor/react):
emojiextension. Wraps@tiptap/extension-emoji(MIT) with a:-triggered picker that exposes the same subscribe/state shape asmention.:shortcode:input and paste rules, unicode-to-node conversion, and markdown (:shortcode:, unknown shortcodes stay text) are included. Opt-in:createBlockKit({ emoji: {} })./emojislash item. Shown only while the node is registered; types the:that opens the picker.useEmojihook andemoji-menuregistry item. Command-in-Popover surface that follows themention-menupattern. Included inslash-editor-kit.
Localization (@slash-editor/core, @slash-editor/react):
messagesoption oncreateBlockKit. Overrides slash item, block type, and AI action text by id (title,description,aliases,keywords), slash menu group headings, the slash hint, empty-block placeholders, block menu titles, and bubble toolbar labels. Partial translations keep English for everything left out; the existingslash.hintandplaceholder.textoptions still win.localizeItems,localizeBlockMenuItems,localizeBubbleToolbarItemshelpers, themessagesextension, andeditor.storage.messages.useBlockMenureturns translated titles. The registryblock-handleandbubble-toolbarnow read block types fromuseBlockTypesso translated titles reach their menus.- Pages and uploads.
untitledPageanduploadFailedmessages;pageTitle,pagesandwithPageMentionstake an optionaluntitledargument./pageand/link to pageare translated throughitems/groups.
Pages (@slash-editor/core, @slash-editor/react):
subPageblock andpageLinkinline node. Each carries only apageId; titles, icons and the tree live in a host-ownedPageStore. Opt-in:createBlockKit({ pages: { store, currentPageId, onNavigate } }). Adds/pageand/link to page, and merges page results into the@menu (MentionItem.kind: "page").- Detach/attach reporting.
onSubPagesDetached/onSubPagesAttachedreport local deletes, undo and paste; moves and remote edits stay silent. Duplicating or pasting a sub-page owned elsewhere becomes apageLink. - Helpers.
collectPageRefs,getPagesOptions,pageTitle, anodeViewsoption for UI layers, and markdown export/import of both nodes. - React hooks.
usePage,usePageTree,useBreadcrumbanduseBacklinksread aPageStorewith no editor.
v0.5.1
Notion-style block editor for React: a headless Tiptap v3 core, React hooks, and a shadcn registry UI you own. MIT end to end.
Fixes
- SSR-safe
@slash-editor/react. Importing the package no longer throwsDOMRect is not definedduring SSR/prerender. - Self-contained registry installs.
shadcn add @slash-editor/slash-editor-kitnow works in an app that never ranshadcn init:popover/dropdown-menudeclare@base-ui/reactandcn.
New
- Next.js starter with one-click Deploy to Vercel
- Docs: Comparison and Migrating guides
bun add @slash-editor/core @slash-editor/react
bunx --bun shadcn@latest add @slash-editor/slash-editor-kitPlayground: https://slasheditor.dev/playground · Docs: https://slasheditor.dev/docs