fix(core): trigger codeblock input rule on Enter and place cursor inside#2686
fix(core): trigger codeblock input rule on Enter and place cursor inside#2686nperez0111 wants to merge 3 commits intomainfrom
Conversation
Fix the codeblock input rule (```ts + space) which had been broken by a two-transaction handler that read stale block info, then extend it so Enter triggers the same rule via a sidecar plugin in ExtensionManager. After conversion, place the cursor inside the new block instead of leaving it after. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds Vitest/JSDOM tests for Code block conversion from triple-backticks and language IDs, and modifies ExtensionManager input-rules to handle Enter keydown, compute block context from transactions, reposition the cursor after replacements, and make input-rule replacements undoable. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant View as ProseMirror View
participant KeyHandler as Enter Key Handler
participant InputRules as InputRules Plugin
participant RuleHandler as InputRule Handler
participant Doc as Document/Transaction
User->>View: Press Enter after typing ```lang
View->>KeyHandler: handleKeyDown(Enter)
KeyHandler->>InputRules: invoke handleTextInput("\\n") (synthetic)
InputRules->>InputRules: match input rules
InputRules->>RuleHandler: execute matched rule (replacement)
RuleHandler->>Doc: getBlockInfoFromTransaction(tr)
Doc-->>RuleHandler: block/container context
RuleHandler->>Doc: replace content with codeBlock
RuleHandler->>View: setTextCursorPosition inside new block
View-->>User: render new code block with cursor
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
@blocknote/ariakit
@blocknote/code-block
@blocknote/core
@blocknote/mantine
@blocknote/react
@blocknote/server-util
@blocknote/shadcn
@blocknote/xl-ai
@blocknote/xl-docx-exporter
@blocknote/xl-email-exporter
@blocknote/xl-multi-column
@blocknote/xl-odt-exporter
@blocknote/xl-pdf-exporter
commit: |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/core/src/blocks/Code/block.test.ts (1)
37-40:pressKeyskips the default Enter path.
view.someProp("handleKeyDown", ...)only invokes plugin handlers. When every handler returnsfalse, this helper does nothing, so the negative Enter tests still pass even if normal Enter behavior is broken or swallowed. Please capture the handled result and simulate/assert the fallback behavior too.One way to tighten the helper
function pressKey(editor: BlockNoteEditor, key: string) { const view = editor.prosemirrorView; const event = new KeyboardEvent("keydown", { key }); - view.someProp("handleKeyDown", (f) => f(view, event)); + const handled = view.someProp("handleKeyDown", (f) => f(view, event)); + return !!handled; }Then assert
pressKey(...)istruefor conversion cases, and add an explicit fallback-behavior assertion for non-conversion cases.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/blocks/Code/block.test.ts` around lines 37 - 40, The helper pressKey currently calls view.someProp("handleKeyDown", ...) but ignores the boolean return (so it never exercises the editor's default Enter fallback); update pressKey (used in the Code block tests) to capture the boolean handled result from someProp("handleKeyDown") and, if handlers return false, simulate/assert the editor's default Enter behavior (i.e., the fallback path that ProseMirror would run when unhandled). Return the handled boolean from pressKey so tests can assert pressKey(...) === true for conversion cases and add explicit assertions that fallback behavior occurred when pressKey(...) === false for non-conversion cases; reference pressKey, BlockNoteEditor, and view.someProp("handleKeyDown") to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/editor/managers/ExtensionManager/index.ts`:
- Around line 384-400: The Enter-handling branch in handleKeyDown currently
treats modified Enter keys the same as plain Enter; update handleKeyDown(view,
event) to first return false when any modifier is pressed (event.shiftKey ||
event.ctrlKey || event.metaKey || event.altKey) so the branch only runs for an
unmodified Enter, then continue calling
inputRules.props.handleTextInput?.call(...) as before; reference the
handleKeyDown function, the event object, and inputRules.props.handleTextInput
to locate and change the check.
---
Nitpick comments:
In `@packages/core/src/blocks/Code/block.test.ts`:
- Around line 37-40: The helper pressKey currently calls
view.someProp("handleKeyDown", ...) but ignores the boolean return (so it never
exercises the editor's default Enter fallback); update pressKey (used in the
Code block tests) to capture the boolean handled result from
someProp("handleKeyDown") and, if handlers return false, simulate/assert the
editor's default Enter behavior (i.e., the fallback path that ProseMirror would
run when unhandled). Return the handled boolean from pressKey so tests can
assert pressKey(...) === true for conversion cases and add explicit assertions
that fallback behavior occurred when pressKey(...) === false for non-conversion
cases; reference pressKey, BlockNoteEditor, and view.someProp("handleKeyDown")
to locate the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: dbaa54a9-e0d7-4b4f-9d88-994a4d92f193
📒 Files selected for processing (2)
packages/core/src/blocks/Code/block.test.tspackages/core/src/editor/managers/ExtensionManager/index.ts
… combos Skip the sidecar's input-rule trigger when Shift/Ctrl/Meta/Alt+Enter is pressed so soft-break, submit, and similar handlers can run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Fixes the broken
\``` codeblock input rule, then extends it so pressing Enter (in addition to space) triggers the same conversion, and lands the cursor inside the new code block instead of after it.Rationale
The space-trigger input rule had been silently broken: the handler called
state.trtwice (creating two separate transactions) and readblock.typefromeditor.getTextCursorPosition()which uses the editor's stale pre-insertion state, so language detection was off and the conversion path was unreliable. Beyond fixing that, users expected Enter to confirm the language and convert — the standard Tiptap codeblock UX — but BlockNote only fired on space. And after conversion the cursor landed after the new block, so the next keystroke went into the wrong block.Changes
ExtensionManagerto use a singlestate.tr, derive block info from the post-insertion transaction viagetBlockInfoFromTransaction(tr), and guard onisBlockContainerand inline content.Pluginnext toinputRulesPlugin({ rules })whosehandleKeyDownintercepts Enter and delegates to the inputRules plugin'shandleTextInputwithtext: "\n". The handlewithcare regex\s$already matches\n, so any rule fires for both space and Enter through one path. Bypassesview.somePropso other plugins don't observe the synthetic\n, and keeps undo metadata keyed to the same plugin instance Tiptap'scommands.undoInputRulereads from.updateBlockTr, callsetTextCursorPosition(tr, blockId, "start")so any rule whosereplaceclears content lands the caret inside the new block.packages/core/src/blocks/Code/block.test.ts(new) — 19 tests covering both space and Enter triggers,getLanguageIdalias resolution, negative cases (no trailing whitespace, two backticks, preceding text, in-codeblock retrigger), and cursor placement after each conversion path.Impact
The Enter trigger applies to every input rule registered through
ExtensionManager, so any\s$-anchored rule gets it for free — Divider's---rule now also fires on Enter, and any future block input rule benefits without per-block code. No public API change.Testing
block.test.tswith 19 tests; all 306 unit tests in@blocknote/corepass.pnpm --filter @blocknote/core buildpasses (TypeScript + Vite).Checklist
🤖 Generated with Claude Code
Summary by CodeRabbit
Tests
Bug Fixes
Supercedes #2345