Skip to content

fix(core): handle Enter on Android via beforeinput and keypress - #3031

Draft
YousefED wants to merge 4 commits into
mobile/link-popoverfrom
mobile/android-enter
Draft

fix(core): handle Enter on Android via beforeinput and keypress#3031
YousefED wants to merge 4 commits into
mobile/link-popoverfrom
mobile/android-enter

Conversation

@YousefED

@YousefED YousefED commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Top of the stack, on #3030. Fixes #3001.

The bug

On Android, prosemirror-view deliberately bails out of keydown handling (the IME reports composing keys as keyCode 229, so key identity can't be trusted) — Enter never reached the keymap. Its DOM-diffing fallback fails to recognize the split in BlockNote's nested block DOM and corrupts the document instead: Enter inserting a space, doing nothing, or breaking tables.

The fix

Two interception points in KeyboardShortcutsExtension, Android-only, sharing one dispatchSynthesizedEnter helper (which restores the pre-keydown DOM flush prosemirror-view's bail skips, so the keymap never runs against a stale selection):

  • beforeinput (insertParagraph / insertLineBreak): the IME path — the intent arrives unambiguously regardless of what the keyboard reports.
  • keypress: the hardware/synthetic keyboard path. prosemirror-view's own keypress handler cancels the browser default for cross-block selections without doing anything in their place, so Enter over a selection spanning blocks was a silent no-op.

Tests

  • androidEnter.test.tsx covers three routes: keypress, the synthetic beforeinput-without-keypress sequence (only a real IME produces it, so it's dispatched as a synthetic InputEvent — proven red with the interception removed), and cross-block selections.
  • This layer also widens the android instance to the behavioural suites (keyboardhandlers, copypaste, emojipicker, form) — held out of the test-infra layer precisely because, before this fix, every test that presses Enter failed under the emulation. Un-skipping Check Enter when selection is not empty there is the suite-level proof the keypress hole is closed.
  • Real-device: editing.device.test.ts runs typing + soft-keyboard Enter on the BrowserStack matrix.

@vercel

vercel Bot commented Aug 31, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
blocknote Error Error Sep 1, 2026 12:58am UTC
blocknote-website Error Error Sep 1, 2026 12:58am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

@pkg-pr-new

pkg-pr-new Bot commented Aug 31, 2026

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

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

@blocknote/code-block

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

@blocknote/core

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

@blocknote/diagram-block

npm i https://pkg.pr.new/@blocknote/diagram-block@3031

@blocknote/mantine

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

@blocknote/math-block

npm i https://pkg.pr.new/@blocknote/math-block@3031

@blocknote/react

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

@blocknote/server-util

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

@blocknote/shadcn

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

@blocknote/xl-ai

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

@blocknote/xl-docx-exporter

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

@blocknote/xl-email-exporter

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

@blocknote/xl-multi-column

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

@blocknote/xl-odt-exporter

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

@blocknote/xl-pdf-exporter

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

commit: b1ff1c7

@github-actions

github-actions Bot commented Aug 31, 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-3031/

Built to branch gh-pages at 2026-09-01 00:59 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@YousefED
YousefED force-pushed the mobile/android-enter branch from fd54794 to 9e471b2 Compare August 31, 2026 17:29
@YousefED
YousefED force-pushed the mobile/link-popover branch from 56c82a9 to 2b6ff77 Compare August 31, 2026 17:36
@YousefED
YousefED force-pushed the mobile/android-enter branch from 9e471b2 to 62b914e Compare August 31, 2026 17:36
@YousefED
YousefED force-pushed the mobile/link-popover branch from 2b6ff77 to d5df900 Compare August 31, 2026 17:42
@YousefED
YousefED force-pushed the mobile/android-enter branch from 62b914e to 4ad7e77 Compare August 31, 2026 17:42
@YousefED
YousefED force-pushed the mobile/link-popover branch from d5df900 to 9909733 Compare August 31, 2026 17:49
@YousefED
YousefED force-pushed the mobile/android-enter branch from 4ad7e77 to 581489e Compare August 31, 2026 17:49
@YousefED
YousefED force-pushed the mobile/link-popover branch from 9909733 to 72fae20 Compare August 31, 2026 17:51
@YousefED
YousefED force-pushed the mobile/android-enter branch from 581489e to 5a57b7c Compare August 31, 2026 17:51
@YousefED
YousefED force-pushed the mobile/link-popover branch from 72fae20 to dff52d6 Compare August 31, 2026 17:59
@YousefED
YousefED force-pushed the mobile/android-enter branch from 5a57b7c to d4e0efd Compare August 31, 2026 17:59
@YousefED
YousefED force-pushed the mobile/link-popover branch from dff52d6 to 45b00f6 Compare August 31, 2026 18:20
@YousefED
YousefED force-pushed the mobile/android-enter branch from d4e0efd to 010102c Compare August 31, 2026 18:20
@YousefED
YousefED force-pushed the mobile/link-popover branch from d10475e to 93e6389 Compare August 31, 2026 19:36
@YousefED
YousefED force-pushed the mobile/android-enter branch from b908209 to 5ae8d7d Compare August 31, 2026 19:36
@YousefED
YousefED force-pushed the mobile/link-popover branch from 93e6389 to 4176f2e Compare August 31, 2026 19:43
@YousefED
YousefED force-pushed the mobile/android-enter branch from 5ae8d7d to 2f1286f Compare August 31, 2026 19:44
@YousefED
YousefED force-pushed the mobile/link-popover branch from 4176f2e to 6aa2abb Compare August 31, 2026 19:44
@YousefED
YousefED force-pushed the mobile/android-enter branch from 2f1286f to f276b7c Compare August 31, 2026 19:44
@YousefED
YousefED force-pushed the mobile/link-popover branch from 6aa2abb to 01fecda Compare August 31, 2026 19:51
@YousefED
YousefED force-pushed the mobile/android-enter branch from f276b7c to c2ba353 Compare August 31, 2026 19:51
@YousefED YousefED closed this Aug 31, 2026
@YousefED YousefED reopened this Aug 31, 2026
@YousefED
YousefED force-pushed the mobile/link-popover branch from 01fecda to f5541a3 Compare August 31, 2026 20:07
@YousefED
YousefED force-pushed the mobile/android-enter branch from e20ec37 to 0f53ee7 Compare August 31, 2026 20:07
On Android, prosemirror-view deliberately bails out of its keydown
handling: the IME reports composing keys as keyCode 229, so the key
identity can't be trusted. Enter therefore never reached the keymap and
pressing it did nothing — no new block, no list continuation.

`beforeinput` carries the intent unambiguously (`insertParagraph` /
`insertLineBreak`) regardless of what the IME reports, so the shortcuts
extension intercepts it there and runs the same keymap command. Only on
Android, and only when not composing, so every other platform keeps the
existing path.

This also unblocks running the core behavioural suites under Android
emulation. They were held out of the android instance in the test-infra
change precisely because of this bug — every test that presses Enter to
make a second block failed there — so the instance's include list grows
here, where it can be green.
The beforeinput interception only covers the IME path. With a hardware or
synthetic keyboard, Enter arrives as a keypress instead — and
prosemirror-view's own keypress handler cancels the browser default for
cross-block selections without doing anything in their place (its
cross-parent branch skips newline characters), so Enter over a selection
spanning two blocks was a silent no-op.

Intercepting keypress too closes that hole, and the two paths now share one
`dispatchSynthesizedEnter` helper rather than repeating the flush-then-
synthesize sequence. The `domObserver` reach-through is typed against
`EditorView` instead of `typeof view`.

Test coverage goes from one path to three — keypress, beforeinput, and the
cross-block selection — and `Check Enter when selection is not empty` no
longer has to be skipped on the android instance, which is the suite-level
proof that the keypress hole is closed.

Also makes `Check Delete before shallower block` deterministic: it relied on
ArrowUp's goal-x landing on a particular side of a character boundary, which
varies with subpixel metrics and had been flaking across engines.
The popover form-submission tests exist because of Android bugs, yet only
ran on the desktop engines. The android instance is chromium, so even the
CDP composition tests run there. All 14 pass under the emulation.
…ening

These skips guard tests that drive selection/resizing with positional
mouse drags, which have no touch-emulation equivalent. They used to ship
with the test infrastructure; review pointed out they belong here, where
the android instance actually starts running the copypaste suite.
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.

Enter, newline, and down arrow broken on Android

1 participant