Skip to content

fix: Image block selection clears on mouse leave in Safari#2613

Merged
matthewlipski merged 4 commits intomainfrom
safari-selection-mouse-move-bug
Apr 1, 2026
Merged

fix: Image block selection clears on mouse leave in Safari#2613
matthewlipski merged 4 commits intomainfrom
safari-selection-mouse-move-bug

Conversation

@matthewlipski
Copy link
Copy Markdown
Collaborator

@matthewlipski matthewlipski commented Mar 31, 2026

Summary

This PR fixes an issue specific to Safari where when a image/video/audio block is selected and be mouse moves off of it, the selection collapses to just before the block.

The surface level cause of this is the resize handles, which are added to & removed from the DOM when the mouse cursor enters & leaves the image/video/audio element. The DOM mutation triggers a selection change event in Safari only.

This is apparently a known issue in Safari, i.e. that DOM mutations within an active selection trigger selection updates, which does not happen in other browsers. This comment supports that reasoning, though after searching for this bug/issue in WebKit online, I couldn't find anything referring to this exact problem.

Either way, this PR fixes the issue by making the resize handles appear and disappear using CSS and be permanently attached to the DOM.

Closes #2541

Rationale

This is an issue especially because it makes the formatting toolbar inaccesssible using the mouse for media blocks.

Changes

See above.

Impact

N/A

Testing

Slight problem here - I tried adding an e2e test to ensure that moving the mouse off of an image block while it's selected keeps it selected. However, I couldn't reproduce the issue using Playwright mouse handling, so need to discuss this.

Screenshots/Video

N/A

Checklist

  • Code follows the project's coding standards.
  • Unit tests covering the new feature have been added.
  • All existing tests pass.
  • The documentation has been updated to reflect the new feature

Additional Notes

Summary by CodeRabbit

  • New Features

    • Audio, Image, and Video blocks now include file-type metadata for improved file handling and ordering.
  • Improvements

    • Resize handles for media/file blocks are now always present and use show/hide styling for smoother hover and resize interactions.
    • Handle visibility behavior refined for hover, resize, and mouse/touch end states.
  • Tests

    • Exported HTML snapshots updated to include hidden resize-handle elements.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 31, 2026

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

Project Deployment Actions Updated (UTC)
blocknote Ready Ready Preview Apr 1, 2026 0:08am
blocknote-website Ready Ready Preview Apr 1, 2026 0:08am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • packages/server-util/src/context/__snapshots__/ServerBlockNoteEditor.test.ts.snap is excluded by !**/*.snap

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 60ee30c7-a67c-451e-acf2-fa3f3864c6f2

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
📝 Walkthrough

Walkthrough

This PR adds file-type metadata to Image/Audio/Video React blocks and refactors resize-handle behavior by keeping handles in the DOM and toggling visibility via style.display instead of adding/removing them.

Changes

Cohort / File(s) Summary
Image Block
packages/react/src/blocks/Image/block.tsx
Added meta.fileBlockAccept: ["image/*"] and runsBefore: ["file"] to the ReactImageBlock spec.
Video Block
packages/react/src/blocks/Video/block.tsx
Added meta.fileBlockAccept: ["video/*"] and runsBefore: ["file"] to the ReactVideoBlock spec.
Audio Block
packages/react/src/blocks/Audio/block.tsx
Added meta.fileBlockAccept: ["audio/*"] to the ReactAudioBlock spec.
Core resize wrapper
packages/core/src/blocks/File/helpers/render/createResizableFileBlockWrapper.ts
Resizing handles are now always appended; code hides/shows handles via style.display instead of removing/adding DOM nodes or using containment checks.
React resize wrapper
packages/react/src/blocks/File/helpers/render/ResizableFileBlockWrapper.tsx
Always renders two .bn-resize-handle elements and controls visibility via inline style.display ("initial" or "none") instead of conditional rendering.
Block spec external HTML
packages/react/src/schema/ReactBlockSpec.tsx
toExternalHTML now passes isFileBlock to BlockContentWrapper when meta.fileBlockAccept is present.
Snapshots (tests)
tests/src/unit/.../blocknoteHTML/**/image*.html, .../video.html, .../reactImage/*.html
Exported HTML snapshots updated to include two hidden .bn-resize-handle divs (left/right) within .bn-visual-media-wrapper.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I’m a rabbit, quick and spry,
I kept the handles—hid, not fly,
No removals, just display,
File blocks now behave, hooray! 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main fix: preventing image block selection from clearing on mouse leave in Safari.
Description check ✅ Passed The description covers the issue, root cause, solution, and testing attempts. All major template sections are addressed with substantive content.
Linked Issues check ✅ Passed The PR successfully addresses issue #2541 by converting DOM-mutating resize handles to CSS-visibility-controlled elements, preventing Safari selection collapse.
Out of Scope Changes check ✅ Passed All changes are directly related to the Safari selection fix: resize handle rendering refactoring and metadata updates for media blocks.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch safari-selection-mouse-move-bug

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/react/src/schema/ReactBlockSpec.tsx (1)

320-347: isFileBlock prop missing in non-nodeView render path.

The nodeView render path (line 296) passes isFileBlock={!!blockImplementation.meta?.fileBlockAccept}, but the non-nodeView render path in this else block does not pass isFileBlock to BlockContentWrapper. This inconsistency means file blocks rendered via the non-nodeView path won't have the data-file-block attribute set.

If this path is exercised for file blocks, the CSS selectors targeting [data-file-block] won't apply correctly.

Proposed fix
               <BlockContentWrapper
                 blockType={block.type}
                 blockProps={block.props}
                 propSchema={blockConfig.propSchema}
                 domAttributes={this.blockContentDOMAttributes}
+                isFileBlock={!!blockImplementation.meta?.fileBlockAccept}
               >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react/src/schema/ReactBlockSpec.tsx` around lines 320 - 347, The
non-nodeView render branch in ReactBlockSpec (the else that builds BlockContent
via renderToDOMSpec) omits the isFileBlock prop on BlockContentWrapper; update
that BlockContentWrapper invocation to include
isFileBlock={!!blockImplementation.meta?.fileBlockAccept} (same value used in
the nodeView path where isFileBlock is already set) so the data-file-block
attribute gets applied for file blocks rendered by the non-nodeView path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/react/src/schema/ReactBlockSpec.tsx`:
- Around line 320-347: The non-nodeView render branch in ReactBlockSpec (the
else that builds BlockContent via renderToDOMSpec) omits the isFileBlock prop on
BlockContentWrapper; update that BlockContentWrapper invocation to include
isFileBlock={!!blockImplementation.meta?.fileBlockAccept} (same value used in
the nodeView path where isFileBlock is already set) so the data-file-block
attribute gets applied for file blocks rendered by the non-nodeView path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7061287-6828-470d-8e94-1df12a174f3a

📥 Commits

Reviewing files that changed from the base of the PR and between a850078 and 256af33.

📒 Files selected for processing (6)
  • packages/core/src/blocks/File/helpers/render/createResizableFileBlockWrapper.ts
  • packages/react/src/blocks/Audio/block.tsx
  • packages/react/src/blocks/File/helpers/render/ResizableFileBlockWrapper.tsx
  • packages/react/src/blocks/Image/block.tsx
  • packages/react/src/blocks/Video/block.tsx
  • packages/react/src/schema/ReactBlockSpec.tsx

Copy link
Copy Markdown
Contributor

@nperez0111 nperez0111 left a comment

Choose a reason for hiding this comment

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

Good, this is simpler

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 1, 2026

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/@blocknote/ariakit@2613

@blocknote/code-block

npm i https://pkg.pr.new/@blocknote/code-block@2613

@blocknote/core

npm i https://pkg.pr.new/@blocknote/core@2613

@blocknote/mantine

npm i https://pkg.pr.new/@blocknote/mantine@2613

@blocknote/react

npm i https://pkg.pr.new/@blocknote/react@2613

@blocknote/server-util

npm i https://pkg.pr.new/@blocknote/server-util@2613

@blocknote/shadcn

npm i https://pkg.pr.new/@blocknote/shadcn@2613

@blocknote/xl-ai

npm i https://pkg.pr.new/@blocknote/xl-ai@2613

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/@blocknote/xl-docx-exporter@2613

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/@blocknote/xl-email-exporter@2613

@blocknote/xl-multi-column

npm i https://pkg.pr.new/@blocknote/xl-multi-column@2613

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/@blocknote/xl-odt-exporter@2613

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/@blocknote/xl-pdf-exporter@2613

commit: 7913efa

@matthewlipski matthewlipski merged commit dd4b9a3 into main Apr 1, 2026
14 checks passed
@matthewlipski matthewlipski deleted the safari-selection-mouse-move-bug branch April 1, 2026 12:11
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.

Image toolbar disappears instantly in Safari when the mouse leaves the image area

2 participants