Skip to content

feat: reusable React API for source blocks with previews#2883

Open
YousefED wants to merge 1 commit into
code-block-previewsfrom
source-preview-react-api
Open

feat: reusable React API for source blocks with previews#2883
YousefED wants to merge 1 commit into
code-block-previewsfrom
source-preview-react-api

Conversation

@YousefED

@YousefED YousefED commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

TLDR: Separates some of the react components and introduces reusable <PreviewWithSourcePopup> component. (Is not a decision yet on what to expose or vanilla vs. react)


Summary

Builds on #2857: extracts the math block's React popup UI into a reusable API in @blocknote/react for any block or inline content that renders a preview of its source (math today; mermaid, graphviz, etc. tomorrow), and fixes a few bugs found along the way.

Rationale

In #2857 the React math block and inline math each hand-rolled the full preview/popup DOM, handlers, and store wiring. That made "build your own preview block" (or "customize the math block") mean copying ~100+ lines of internals. This PR makes the consumer story: config + preview + optionally a popup hook — the math renders themselves are now ~25-line compositions and serve as the copy-me template.

Changes

@blocknote/react — new blocks/SourceWithPreview/

  • PreviewWithSourcePopup — presentational shell (block + inline via an inline prop). The error prop accepts arbitrary elements, so consumers can render actions (e.g. a "fix with AI" button) alongside the error message.
  • block/SourceBlockWithPreview & inlineContent/SourceInlineContentWithPreview — wired wrappers; callers only supply source, preview, and optional error.
  • block/useSourceBlockPreviewPopup & inlineContent/useSourceInlineContentPreviewPopup — the public popup-state API. Both return the shared SourcePreviewPopup shape ({ isOpen, isSelected, open, close }), so consumers never touch useExtension or raw extension stores. Split per kind deliberately: the block popup is an explicit toggle while the inline popup is selection-driven (isOpen === isSelected), and a unified hook meant type-erasing casts and interleaved branches.
  • AddSourceButton moved here from @blocknote/math-block (it was generic).

@blocknote/core

  • SourceInlineContentWithPreviewExtension moved from math-block to core, next to its block counterpart, after extracting the math-specific input rules. The two extensions intentionally stay separate — the block/inline UX models differ enough that merging them only hid the differences behind indirection.

@blocknote/math-block

  • New MathInlineInputRulesExtension ($...$ and \(...\)), mirroring MathBlockInputRulesExtension.
  • Block & inline React renders are now thin compositions over the new wrappers.

Bug fixes

  • Exported/serialized inline math rendered with data-open="true" and ProseMirror-selectednode: outside the editor getPos() returns undefined, which matched the store's initial undefined state. The hooks now guard against this; the inlineMath/basic snapshot reflects the corrected output.
  • Inline popup mouse handlers captured getPos() once at render time; positions shift without node views re-rendering, so actions could target stale positions. Position is now read fresh at action time.
  • Regenerated two stale schema snapshots (blocks.json, inlinecontent.json) that were already failing on the base branch.

Impact

No behavior changes to the math block or code block beyond the export fix. Spec-facing APIs are unchanged; the math package no longer exports AddSourceButton or the inline preview extension (now in core).

Testing

  • Existing suites pass: math-block (29), react unit (107, includes the inline popup keyboard/click behaviors driven through the new wrappers & hooks), core export (500).
  • Manually verified in the playground: popup open/close via click/Enter/Escape/OK, in-place preview updates while typing, KaTeX error display, and both inline input rules.

Additional Notes

  • The #2857 TODO about copy/paste while a block popup is closed remains open; the suggested direction is content-layer protection (a ProseMirror plugin via handleTextInput/handlePaste) instead of the current capture-phase keydown suppression, which also misses drop/IME/autocorrect.
  • Known follow-up: getExtension(factory) returns only the last-registered instance per factory, so block + inline (or code block + math) preview extensions can't be looked up by factory — the new hooks avoid this, but a keyed lookup (e.g. getExtension(factory, key)) is the eventual fix.

🤖 Generated with Claude Code

Extracts the math block's React popup UI into reusable components and
hooks in @blocknote/react: the PreviewWithSourcePopup shell, the
SourceBlockWithPreview/SourceInlineContentWithPreview wrappers, and the
useSourceBlockPreviewPopup/useSourceInlineContentPreviewPopup hooks.
Moves the generic SourceInlineContentWithPreviewExtension to core after
splitting its math-specific input rules into a new
MathInlineInputRulesExtension. Also fixes exported inline math HTML
rendering as selected with an open popup, and reads getPos() fresh in
popup actions instead of capturing it at render time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 71b6c56e-2878-4641-aac9-70bafefb793c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch source-preview-react-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blocknote Ready Ready Preview Jul 3, 2026 12:57pm
blocknote-website Ready Ready Preview Jul 3, 2026 12:57pm

Request Review

@pkg-pr-new

pkg-pr-new Bot commented Jul 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/ariakit@2883

@blocknote/code-block

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/code-block@2883

@blocknote/core

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/core@2883

@blocknote/mantine

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/mantine@2883

@blocknote/math-block

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/math-block@2883

@blocknote/react

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/react@2883

@blocknote/server-util

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/server-util@2883

@blocknote/shadcn

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/shadcn@2883

@blocknote/xl-ai

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-ai@2883

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-docx-exporter@2883

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-email-exporter@2883

@blocknote/xl-multi-column

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-multi-column@2883

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-odt-exporter@2883

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/TypeCellOS/BlockNote/@blocknote/xl-pdf-exporter@2883

commit: 3eeb901

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://TypeCellOS.github.io/BlockNote/pr-preview/pr-2883/

Built to branch gh-pages at 2026-07-03 13:05 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@matthewlipski matthewlipski left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, few comments

* Ref for the element holding the editable source content.
*/
contentRef: (node: HTMLElement | null) => void;
onPreviewMouseDown?: MouseEventHandler;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of passing individual event handlers like this. I would rather pass a setSelected and setPopupOpen then have PreviewWithSourcePopup create the mouse down handlers. Alternatively, if we really want to make PreviewWithSourcePopup as "dumb" as possible, I would still rather make it possible to pass all props rather than picking out just the mouse down handlers (previewContainerProps: HTMLAttributes<HTMLElement>; okButtonProps: HTMLAttributes<HTMLButtonElement>;).

/**
* Whether the source popup is shown.
*/
popupOpen: boolean;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should standardize the naming of popupOpen & selected to isOpen & isSelected.

import { useSourceInlineContentPreviewPopup } from "./useSourceInlineContentPreviewPopup.js";

export type SourceInlineContentWithPreviewProps = {
editor: BlockNoteEditor<any, any, any>;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be picked out from InlineContentImplementation instead of manually re-typed. Same for the block equivalent.

SourceInlineContentWithPreviewExtension({
key: INLINE_MATH_PREVIEW_KEY,
inlineContentType: "inlineMath",
inlineContentType: mathInlineContentConfig.type,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're changing this here we should probably do the same for the math block

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants