Fix drag ghost, image positioning, and tab indentation (#2545 #2570 #2609)#2776
Fix drag ghost, image positioning, and tab indentation (#2545 #2570 #2609)#2776mkcash wants to merge 5 commits into
Conversation
|
@mkcash is attempting to deploy a commit to the TypeCell Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughThree targeted fixes: updated JSDoc for a deprecated tab option, improved block-hit testing near the editor's right edge, and safer side-menu drag handling (preview size cap and fuller drag cleanup). ChangesSide Menu Drag Improvements and Documentation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 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 unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
packages/core/src/extensions/SideMenu/SideMenu.tsParsing error: Identifier expected. 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 |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/core/src/editor/BlockNoteEditor.ts`:
- Around line 279-280: Update the incomplete JSDoc bullet points in
BlockNoteEditor.ts so each bullet shows the actual option value identifier
before the colon (for example the boolean/enum/string option names used by the
method or property being documented) instead of just "- :"; locate the
surrounding docblock in the BlockNoteEditor class (the comment near the option
descriptions) and replace the two bullets "- : Indents the block." and "- :
(default) Focuses the toolbar if open, otherwise does nothing." with the correct
option tokens used by the code (e.g., the exact literal values or enum
identifiers referenced by the method/parameter) so the mapping between option
value and behavior is explicit.
In `@packages/core/src/extensions/SideMenu/dragging.ts`:
- Around line 124-130: The code uses dragImageElement (typed Element |
undefined) and then reads scrollWidth/scrollHeight and style properties; narrow
and guard it to HTMLElement first. Add an explicit runtime/type check such as if
(dragImageElement && dragImageElement instanceof HTMLElement) { const el =
dragImageElement; const maxWidth = 300; const maxHeight = 200; if
(el.scrollWidth > maxWidth || el.scrollHeight > maxHeight) { el.style.maxWidth =
...; el.style.maxHeight = ...; el.style.overflow = ...; } } so all uses of
scrollWidth/scrollHeight/style operate on a confirmed HTMLElement (referencing
dragImageElement in dragging.ts).
In `@packages/core/src/extensions/SideMenu/SideMenu.ts`:
- Around line 770-773: The code in blockDragEnd accesses the private
SideMenuView.hoveredBlock directly causing a TypeScript access error; add a
public mutator on SideMenuView (e.g., clearHoveredBlock) that sets
this.hoveredBlock = undefined, update blockDragEnd to call
view.clearHoveredBlock() instead of reading/assigning view.hoveredBlock, and
remove direct references to the private hoveredBlock outside the class.
- Around line 119-123: getBlockFromMousePos currently redeclares const
editorBoundingBox (duplicate of the earlier variable) and blockDragEnd illegally
touches a private member view.hoveredBlock; to fix, remove the duplicate
declaration in getBlockFromMousePos and reuse the existing editorBoundingBox
variable already declared earlier in that function (or rename the earlier one if
needed), and stop assigning view.hoveredBlock directly in blockDragEnd—instead
call a public API on SideMenuView (e.g., add/use a method like
clearHoveredBlock() or setHoveredBlock(undefined)) to clear the hovered state;
update blockDragEnd to call that public method (reference symbols:
getBlockFromMousePos, editorBoundingBox, blockDragEnd, SideMenuView,
view.hoveredBlock).
🪄 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: 6ca9ae62-221b-440c-9e29-cf2428f70d4f
📒 Files selected for processing (3)
packages/core/src/editor/BlockNoteEditor.tspackages/core/src/extensions/SideMenu/SideMenu.tspackages/core/src/extensions/SideMenu/dragging.ts
| * - : Indents the block. | ||
| * - : (default) Focuses the toolbar if open, otherwise does nothing. |
There was a problem hiding this comment.
Fix incomplete bullet point descriptions.
The bullet points are missing the option value identifiers before the colons, making it unclear which behavior corresponds to which option value.
📝 Proposed fix to complete the bullet descriptions
- * - : Indents the block.
- * - : (default) Focuses the toolbar if open, otherwise does nothing.
+ * - `"prefer-indent"`: Indents the block.
+ * - `"prefer-navigate-ui"`: (default) Focuses the toolbar if open, otherwise does nothing.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * - : Indents the block. | |
| * - : (default) Focuses the toolbar if open, otherwise does nothing. | |
| * - `"prefer-indent"`: Indents the block. | |
| * - `"prefer-navigate-ui"`: (default) Focuses the toolbar if open, otherwise does nothing. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/core/src/editor/BlockNoteEditor.ts` around lines 279 - 280, Update
the incomplete JSDoc bullet points in BlockNoteEditor.ts so each bullet shows
the actual option value identifier before the colon (for example the
boolean/enum/string option names used by the method or property being
documented) instead of just "- :"; locate the surrounding docblock in the
BlockNoteEditor class (the comment near the option descriptions) and replace the
two bullets "- : Indents the block." and "- : (default) Focuses the toolbar if
open, otherwise does nothing." with the correct option tokens used by the code
(e.g., the exact literal values or enum identifiers referenced by the
method/parameter) so the mapping between option value and behavior is explicit.
| // Constrain drag preview to prevent oversized ghost for media blocks | ||
| const maxWidth = 300; | ||
| const maxHeight = 200; | ||
| if (dragImageElement.scrollWidth > maxWidth || dragImageElement.scrollHeight > maxHeight) { | ||
| dragImageElement.style.maxWidth = maxWidth + "px"; | ||
| dragImageElement.style.maxHeight = maxHeight + "px"; | ||
| dragImageElement.style.overflow = "hidden"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify declaration type and style usage
rg -n -C2 'let dragImageElement: Element|dragImageElement\.style' packages/core/src/extensions/SideMenu/dragging.tsRepository: TypeCellOS/BlockNote
Length of output: 564
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="packages/core/src/extensions/SideMenu/dragging.ts"
# Show declaration + where it's assigned
rg -n "dragImageElement" "$FILE"
echo "----"
# Show the key region around the assignment(s) and around the problematic access
sed -n '1,220p' "$FILE" | nl -ba | sed -n '1,220p'Repository: TypeCellOS/BlockNote
Length of output: 1007
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="packages/core/src/extensions/SideMenu/dragging.ts"
echo "=== Lines 1-80 ==="
sed -n '1,80p' "$FILE" | cat -n
echo "=== Lines 80-170 ==="
sed -n '80,170p' "$FILE" | cat -nRepository: TypeCellOS/BlockNote
Length of output: 8198
Critical: Narrow dragImageElement to HTMLElement before using .style (and scrollWidth/scrollHeight)
dragImageElement is typed as Element | undefined (line ~19), but Lines ~128–130 access dragImageElement.style.*, and the condition also reads dragImageElement.scrollWidth/scrollHeight. This requires an HTMLElement (or an explicit type guard).
🛠️ Proposed fix
-let dragImageElement: Element | undefined;
+let dragImageElement: HTMLElement | undefined;
@@
- const parentClone = view.domAtPos(from).node.cloneNode(true) as Element;
+ const parentClone = view.domAtPos(from).node.cloneNode(true) as HTMLElement;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/core/src/extensions/SideMenu/dragging.ts` around lines 124 - 130,
The code uses dragImageElement (typed Element | undefined) and then reads
scrollWidth/scrollHeight and style properties; narrow and guard it to
HTMLElement first. Add an explicit runtime/type check such as if
(dragImageElement && dragImageElement instanceof HTMLElement) { const el =
dragImageElement; const maxWidth = 300; const maxHeight = 200; if
(el.scrollWidth > maxWidth || el.scrollHeight > maxHeight) { el.style.maxWidth =
...; el.style.maxHeight = ...; el.style.overflow = ...; } } so all uses of
scrollWidth/scrollHeight/style operate on a confirmed HTMLElement (referencing
dragImageElement in dragging.ts).
| const editorBoundingBox = ( | ||
| view.dom.firstChild as HTMLElement | ||
| ).getBoundingClientRect(); | ||
| const nearRightEdge = | ||
| referenceBlocksBoundingBox.right >= editorBoundingBox.right - 20; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify duplicate declaration in function scope
rg -n -C2 'const editorBoundingBox' packages/core/src/extensions/SideMenu/SideMenu.tsRepository: TypeCellOS/BlockNote
Length of output: 452
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="packages/core/src/extensions/SideMenu/SideMenu.ts"
echo "=== Context around first editorBoundingBox (approx 70-100) ==="
sed -n '60,105p' "$FILE" | cat -n
echo "=== Context around second editorBoundingBox (approx 105-135) ==="
sed -n '105,150p' "$FILE" | cat -n
echo "=== Find hoveredBlock usages ==="
rg -n "hoveredBlock" "$FILE"Repository: TypeCellOS/BlockNote
Length of output: 3814
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="packages/core/src/extensions/SideMenu/SideMenu.ts"
sed -n '730,800p' "$FILE" | cat -nRepository: TypeCellOS/BlockNote
Length of output: 2534
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="packages/core/src/extensions/SideMenu/SideMenu.ts"
echo "=== Declaration(s) of outer `view` variable(s) ==="
rg -n --hidden "(^|\s)(let|var|const)\s+view\b" "$FILE"
echo "=== Context for `private hoveredBlock` ==="
rg -n "private hoveredBlock" "$FILE"
START=130
END=180
sed -n "${START},${END}p" "$FILE" | cat -nRepository: TypeCellOS/BlockNote
Length of output: 1984
Fix TypeScript compile errors in SideMenu.ts (duplicate const + private access)
getBlockFromMousePosredeclaresconst editorBoundingBoxat ~line 119 even though it’s already declared in the same function at ~line 83 (duplicate block-scoped declaration).blockDragEndassignsview.hoveredBlock = undefined, buthoveredBlockisprivateonSideMenuView(illegal private-member access outside the class).
🛠️ Proposed fix (remove duplicate `editorBoundingBox`)
- const editorBoundingBox = (
- view.dom.firstChild as HTMLElement
- ).getBoundingClientRect();
const nearRightEdge =
referenceBlocksBoundingBox.right >= editorBoundingBox.right - 20;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/core/src/extensions/SideMenu/SideMenu.ts` around lines 119 - 123,
getBlockFromMousePos currently redeclares const editorBoundingBox (duplicate of
the earlier variable) and blockDragEnd illegally touches a private member
view.hoveredBlock; to fix, remove the duplicate declaration in
getBlockFromMousePos and reuse the existing editorBoundingBox variable already
declared earlier in that function (or rename the earlier one if needed), and
stop assigning view.hoveredBlock directly in blockDragEnd—instead call a public
API on SideMenuView (e.g., add/use a method like clearHoveredBlock() or
setHoveredBlock(undefined)) to clear the hovered state; update blockDragEnd to
call that public method (reference symbols: getBlockFromMousePos,
editorBoundingBox, blockDragEnd, SideMenuView, view.hoveredBlock).
| // Clear any stale drag references | ||
| if (view.hoveredBlock) { | ||
| view.hoveredBlock = undefined; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify private declaration and external access
rg -n -C2 'private hoveredBlock|view\.hoveredBlock' packages/core/src/extensions/SideMenu/SideMenu.tsRepository: TypeCellOS/BlockNote
Length of output: 417
Fix TypeScript private access to hoveredBlock in blockDragEnd
SideMenu.ts declares hoveredBlock as private (line 151), but blockDragEnd clears it by reading/assigning view.hoveredBlock (lines 771–773), which will be a TypeScript access error.
🛠️ Proposed fix
- if (view.hoveredBlock) {
- view.hoveredBlock = undefined;
- }
+ view.clearHoveredBlock();// Add inside SideMenuView class
public clearHoveredBlock = () => {
this.hoveredBlock = undefined;
};🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/core/src/extensions/SideMenu/SideMenu.ts` around lines 770 - 773,
The code in blockDragEnd accesses the private SideMenuView.hoveredBlock directly
causing a TypeScript access error; add a public mutator on SideMenuView (e.g.,
clearHoveredBlock) that sets this.hoveredBlock = undefined, update blockDragEnd
to call view.clearHoveredBlock() instead of reading/assigning view.hoveredBlock,
and remove direct references to the private hoveredBlock outside the class.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/core/src/extensions/SideMenu/SideMenu.ts`:
- Around line 817-831: The Tab keyboard handler contains invalid selection
access and wrong block API usage and the drag-end code touches a private field:
replace the parse-error sel..node()/sel..before() with proper ProseMirror
selection access (use selection.node if it's a NodeSelection or
selection.$anchor.node()/selection.$anchor.before() / selection.$from if you
need start; reference the selection from editor.prosemirrorView.state.selection
in the Tab handler), then actually invoke the block indentation API using a
valid BlockIdentifier (do not pass a raw numeric PM position to editor.getBlock;
instead resolve a block id or pass { id: <blockId> } to editor.getBlock and call
the editor method that performs indentation/indentBlock after focusing), and
finally stop assigning to the private hoveredBlock on SideMenuView in
blockDragEnd—use a public setter or method on SideMenuView (e.g.,
setHoveredBlock/clearHoveredBlock) to clear it instead of directly mutating the
private field.
🪄 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: 3401eca5-5910-42ed-9b27-a14f254032d2
📒 Files selected for processing (1)
packages/core/src/extensions/SideMenu/SideMenu.ts
| keyboardShortcuts: { | ||
| Tab: ({ editor }) => { | ||
| const sel = editor.prosemirrorView.state.selection; | ||
| const node = sel..node(); | ||
| // If the selected block has no content (image, video, file, divider), | ||
| // trigger indentation via the block API | ||
| if (node && node.type.spec.content === "none" && editor.isEditable) { | ||
| const blockInfo = editor.getBlock(sel..before()); | ||
| if (blockInfo) { | ||
| editor.focus(); | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
ProseMirror NodeSelection $from node() method
💡 Result:
In ProseMirror, NodeSelection doesn’t have a “$from node” method. The relevant constructors are: 1) new NodeSelection($pos: ResolvedPos) - This is the “$from” style: you pass a ResolvedPos (typically called $from in code). It creates a node selection pointing at the node after that resolved position (it uses $pos.nodeAfter and the nodeSize to compute the selection range) [1][2]. - It “does not verify the validity of its argument,” so you must ensure the resolved position actually points to a node you can select [1][2]. 2) NodeSelection.create(doc: Node, from: number) - This creates a node selection from a non-resolved position (a numeric position in a given document) [1][2]. How from/to/anchor/head are set - A node selection targets a single node; in such a selection, from and to point directly before and after the selected node, anchor equals from, and head equals to [1][2]. Common pitfall (why you can end up selecting the “wrong” node) - Creating a node selection “from a resolved position” can select an unexpected node depending on which resolved position boundary you used; community discussion notes this confusion and that you usually need to use the correct position for the target node [3]. Example (select a node using a ResolvedPos) - If you have a ResolvedPos named $from that resolves to the position before the node you want, you can do: const sel = new NodeSelection($from) // selects the nodeAfter of $from [1][2] If you meant a specific API like NodeSelection.$from or $from.node, please paste the exact snippet you saw—then I can map it to the correct ProseMirror API surface.
Citations:
- 1: https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.ts
- 2: https://prosemirror.net/docs/ref/
- 3: https://discuss.prosemirror.net/t/creating-a-node-selection-from-resolved-position-is-selecting-a-child-node/4852
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the SideMenu file and inspect around the Tab handler region
sed -n '760,880p' packages/core/src/extensions/SideMenu/SideMenu.ts
# Find how getBlock is implemented/used and what arguments it expects
rg -n "getBlock\\(" packages/core/src | head
rg -n "nestBlock\\(" packages/core/src | head
rg -n "indent" packages/core/src/extensions/SideMenu/SideMenu.ts packages/core/src | head -n 50
# Inspect editor/block API definitions (files that likely declare getBlock or nestBlock)
# Using fd to find likely candidates by filename
fd -a "getBlock" packages/core/src || true
fd -a "nestBlock" packages/core/src || true
# Broader: find the symbol definitions by searching for "getBlock" and "nestBlock" declarations
rg -n "function getBlock|getBlock\\s*:\\s*\\(|getBlock\\s*\\(" packages/core/src | head -n 50
rg -n "nestBlock\\s*:\\s*\\(|function nestBlock|nestBlock\\s*\\(" packages/core/src | head -n 50
# Search for hoveredBlock access in SideMenu
rg -n "hoveredBlock" packages/core/src/extensions/SideMenu/SideMenu.tsRepository: TypeCellOS/BlockNote
Length of output: 20040
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the Tab handler with correct line context
sed -n '800,850p' packages/core/src/extensions/SideMenu/SideMenu.ts
# Inspect SideMenu view class field for hoveredBlock and where blockDragEnd is defined
sed -n '120,320p' packages/core/src/extensions/SideMenu/SideMenu.ts
sed -n '740,790p' packages/core/src/extensions/SideMenu/SideMenu.ts
# Inspect editor/block APIs: getBlock signature and nestBlock implementation
sed -n '880,970p' packages/core/src/editor/BlockNoteEditor.ts
sed -n '180,270p' packages/core/src/editor/managers/BlockManager.ts
sed -n '1220,1285p' packages/core/src/editor/BlockNoteEditor.ts
sed -n '1,260p' packages/core/src/api/blockManipulation/commands/nestBlock/nestBlock.ts
# Inspect getBlock-from-pos helpers (argument types)
sed -n '1,220p' packages/core/src/api/blockManipulation/getBlock/getBlock.ts
sed -n '1,170p' packages/core/src/api/getBlockInfoFromPos.ts
sed -n '1,170p' packages/core/src/schema/blocks/internal.tsRepository: TypeCellOS/BlockNote
Length of output: 36982
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "export type BlockIdentifier" packages/core/src | head -n 50
rg -n "type BlockIdentifier" packages/core/src/schema packages/core/src | head -n 50
# Show getBlockManager.nestBlock implementation and/or nestBlock command
sed -n '1,120p' packages/core/src/editor/managers/BlockManager.ts
sed -n '180,260p' packages/core/src/editor/managers/BlockManager.ts
sed -n '1,260p' packages/core/src/api/blockManipulation/commands/nestBlock/nestBlock.ts
# Inspect canNestBlock or related usage maybe helps selection-based nesting
sed -n '260,360p' packages/core/src/api/blockManipulation/commands/nestBlock/nestBlock.ts
# Inspect getBlockFromPos helper used in SideMenu or elsewhere
sed -n '1,120p' packages/core/src/extensions/SideMenu/SideMenu.ts
sed -n '240,320p' packages/core/src/extensions/SideMenu/SideMenu.ts
# Inspect getBlockInfoFromSelection helpers which likely use ResolvedPos / selection
sed -n '200,320p' packages/core/src/api/getBlockInfoFromPos.ts
rg -n "getBlockInfoFromSelection\\(" packages/core/src | head -n 50
# Confirm getBlockInfoFromSelection signature and how to get beforePos etc.Repository: TypeCellOS/BlockNote
Length of output: 25763
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect Tab handling in KeyboardShortcutsExtension.ts
sed -n '880,1040p' packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts
# Search for correct ProseMirror NodeSelection usage patterns in this repo
rg -n "NodeSelection" packages/core/src | head -n 50
rg -n "selection\\.node\\b|\\bsel\\.node\\b|\\.node\\(\\)" packages/core/src/extensions packages/core/src/editor packages/core/src/api packages/core/src/blocks | head -n 50
rg -n "\\$from\\b|\\$anchor\\b|\\.before\\(" packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts packages/core/src/api packages/core/src/editor packages/core/src/extensions | head -n 100
# Look for existing tab indentation logic that likely nests the selected block
rg -n "tabForEmptyBlocks|prefer-indent|nestBlock\\(" packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts
rg -n "nestBlock\\(this\\.options\\.editor\\)|editor\\.nestBlock\\(" packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts packages/core/src/extensions/SideMenu/SideMenu.ts packages/core/src | head -n 50
# Inspect selection-to-block helper usage patterns
rg -n "getBlockInfoFromSelection\\(state\\)|getBlockInfoFromSelection\\(view\\.state\\)" packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts | head -n 50Repository: TypeCellOS/BlockNote
Length of output: 11170
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show SideMenu.ts Tab handler with a bit more context (to see intended selection type / surrounding logic)
sed -n '800,840p' packages/core/src/extensions/SideMenu/SideMenu.ts
# Inspect how SideMenu dragging treats selection types (NodeSelection / MultipleNodeSelection)
sed -n '1,120p' packages/core/src/extensions/SideMenu/dragging.ts
sed -n '160,230p' packages/core/src/extensions/SideMenu/dragging.ts
# Inspect MultipleNodeSelection implementation to understand what $anchor/$head/$from/$to expose
sed -n '1,220p' packages/core/src/extensions/SideMenu/MultipleNodeSelection.ts
# Inspect any usage of selection.$anchor/$head in repo for node/position access
rg -n "selection\\.node\\b|\\.selection\\.node\\b|\\bsel\\.node\\b|\\$anchor\\.node\\(|\\$head\\.node\\(" packages/core/src | head -n 50
# Inspect getBlockInfoFromSelection usage for indentation logic
rg -n "getBlockInfoFromSelection\\(.*\\).*nestBlock|nestBlock\\(" packages/core/src/extensions packages/core/src/api packages/core/src/editor | head -n 50Repository: TypeCellOS/BlockNote
Length of output: 11541
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the exact locations + context for blockDragEnd (where view.hoveredBlock is touched)
nl -ba packages/core/src/extensions/SideMenu/SideMenu.ts | sed -n '680,840p'
# Show where SideMenuView class starts and hoveredBlock is declared
nl -ba packages/core/src/extensions/SideMenu/SideMenu.ts | sed -n '140,340p'
# Show BlockNoteEditor.getBlock / nestBlock signatures to ensure correct calls
nl -ba packages/core/src/editor/BlockNoteEditor.ts | sed -n '880,980p'
nl -ba packages/core/src/editor/BlockNoteEditor.ts | sed -n '1220,1280p'Repository: TypeCellOS/BlockNote
Length of output: 107
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Print with line numbers via cat -n (nl isn't available in sandbox)
sed -n '140,340p' packages/core/src/extensions/SideMenu/SideMenu.ts | cat -n
sed -n '680,840p' packages/core/src/extensions/SideMenu/SideMenu.ts | cat -n
sed -n '880,980p' packages/core/src/editor/BlockNoteEditor.ts | cat -n
sed -n '1220,1280p' packages/core/src/editor/BlockNoteEditor.ts | cat -nRepository: TypeCellOS/BlockNote
Length of output: 19478
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect SideMenu.ts imports (to see whether NodeSelection/ResolvedPos are imported)
sed -n '1,120p' packages/core/src/extensions/SideMenu/SideMenu.ts
# Look for selection.$anchor usage patterns in SideMenu.ts
rg -n "selection\\.|\\.\\$anchor|\\$anchor\\.node\\(|\\.\\$from|\\$from\\.before\\(|\\.before\\(" packages/core/src/extensions/SideMenu/SideMenu.ts
# Look for other incorrect patterns sel..before / sel..node in repo
rg -n "sel\\.\\.|\\$anchor\\.\\.|\\.\\.node\\(|\\.\\.before\\(" packages/core/src/extensions/SideMenu/SideMenu.ts packages/core/src | head -n 50
# Inspect nearby helper methods for indentation via block API (if any) in SideMenu.ts
rg -n "nestBlock\\(|unnestBlock\\(|nestBlock" packages/core/src/extensions/SideMenu/SideMenu.ts
# Inspect getBlock usage around line 820 for expected argument type
sed -n '780,840p' packages/core/src/extensions/SideMenu/SideMenu.tsRepository: TypeCellOS/BlockNote
Length of output: 3702
Fix invalid selection access and missing nesting in SideMenu Tab shortcut
- Lines 820 & 824:
sel..node()/sel..before()contain invalid..syntax (won’t parse). Use the ProseMirror selection APIs instead (e.g.,selection.nodeforNodeSelection, orselection.$anchor.node()/selection.$anchor.before()). - The handler never actually indents: it only calls
editor.focus()aftereditor.getBlock(...). Also,editor.getBlockexpects aBlockIdentifier(string or{ id }), not a numeric PM position. blockDragEndassignsview.hoveredBlock = undefined, buthoveredBlockis declaredprivateonSideMenuView; TypeScript will reject this—use a public method/setter instead.
🧰 Tools
🪛 Biome (2.4.15)
[error] 820-820: Expected an identifier but instead found '.'.
(parse)
[error] 824-824: Expected an identifier but instead found '.'.
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/core/src/extensions/SideMenu/SideMenu.ts` around lines 817 - 831,
The Tab keyboard handler contains invalid selection access and wrong block API
usage and the drag-end code touches a private field: replace the parse-error
sel..node()/sel..before() with proper ProseMirror selection access (use
selection.node if it's a NodeSelection or
selection.$anchor.node()/selection.$anchor.before() / selection.$from if you
need start; reference the selection from editor.prosemirrorView.state.selection
in the Tab handler), then actually invoke the block indentation API using a
valid BlockIdentifier (do not pass a raw numeric PM position to editor.getBlock;
instead resolve a block id or pass { id: <blockId> } to editor.getBlock and call
the editor method that performs indentation/indentBlock after focusing), and
finally stop assigning to the private hoveredBlock on SideMenuView in
blockDragEnd—use a public setter or method on SideMenuView (e.g.,
setHoveredBlock/clearHoveredBlock) to clear it instead of directly mutating the
private field.
Summary
Fixes three drag-and-drop and keyboard issues.
#2609 - Drag ghost issues
isDragOrigin,menuFrozen,hoveredBlock,view.dragginginblockDragEnd()to prevent blocks becoming undraggable#2570 - Image drag positioning at end of line
#2545 - Tab indentation for image blocks
tabForEmptyBlocksconfig option for blocks with no text content (image, video)Files changed
Summary by CodeRabbit
New Features
Bug Fixes
Documentation