-
-
Notifications
You must be signed in to change notification settings - Fork 735
Fix drag ghost, image positioning, and tab indentation (#2545 #2570 #2609) #2776
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9523f06
2dfb11a
f611b73
8cae0c0
4c46d1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -112,10 +112,22 @@ function getBlockFromMousePos( | |
| */ | ||
| const referenceBlocksBoundingBox = | ||
| referenceBlock.node.getBoundingClientRect(); | ||
|
|
||
| // For blocks near the right edge of the editor (e.g. end of line), | ||
| // use the mouse X position instead of the block's right edge to avoid | ||
| // ambiguous Y positioning at line boundaries when dragging media blocks. | ||
| const editorBoundingBox = ( | ||
| view.dom.firstChild as HTMLElement | ||
| ).getBoundingClientRect(); | ||
| const nearRightEdge = | ||
| referenceBlocksBoundingBox.right >= editorBoundingBox.right - 20; | ||
|
Comment on lines
+119
to
+123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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)
🛠️ Proposed fix (remove duplicate `editorBoundingBox`)- const editorBoundingBox = (
- view.dom.firstChild as HTMLElement
- ).getBoundingClientRect();
const nearRightEdge =
referenceBlocksBoundingBox.right >= editorBoundingBox.right - 20;🤖 Prompt for AI Agents |
||
|
|
||
| return getBlockFromCoords( | ||
| view, | ||
| { | ||
| left: referenceBlocksBoundingBox.right - 10, | ||
| left: nearRightEdge | ||
| ? mousePos.x | ||
| : referenceBlocksBoundingBox.right - 10, | ||
| top: mousePos.y, | ||
| }, | ||
| false, | ||
|
|
@@ -749,9 +761,20 @@ export const SideMenuExtension = createExtension(({ editor }) => { | |
| * Handles drag & drop events for blocks. | ||
| */ | ||
| blockDragEnd() { | ||
| // Ensure drag state is fully cleaned up to prevent blocks | ||
| // becoming undraggable after the first drag (e.g. image blocks). | ||
| unsetDragImage(editor.prosemirrorView.root); | ||
| if (view) { | ||
| view.isDragOrigin = false; | ||
| view.menuFrozen = false; | ||
| // Clear any stale drag references | ||
| if (view.hoveredBlock) { | ||
| view.hoveredBlock = undefined; | ||
| } | ||
|
Comment on lines
+770
to
+773
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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
🛠️ Proposed fix- if (view.hoveredBlock) {
- view.hoveredBlock = undefined;
- }
+ view.clearHoveredBlock();// Add inside SideMenuView class
public clearHoveredBlock = () => {
this.hoveredBlock = undefined;
};🤖 Prompt for AI Agents |
||
| } | ||
| // Clear PM dragging state | ||
| if (editor.prosemirrorView.dragging !== null) { | ||
| editor.prosemirrorView.dragging = null; | ||
| } | ||
|
|
||
| editor.blur(); | ||
|
|
@@ -790,5 +813,22 @@ export const SideMenuExtension = createExtension(({ editor }) => { | |
| view!.emitUpdate(view!.state!); | ||
| } | ||
| }, | ||
|
|
||
| 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; | ||
| }, | ||
|
Comment on lines
+817
to
+831
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 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:
🏁 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
🧰 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 |
||
| }, | ||
| } as const; | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -121,6 +121,14 @@ function setDragImage(view: EditorView, from: number, to = from) { | |
| ) | ||
| .join(" "); | ||
|
|
||
| // 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"; | ||
|
Comment on lines
+124
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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
🛠️ 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 |
||
| } | ||
| dragImageElement.className = | ||
| dragImageElement.className + " bn-drag-preview " + inheritedClasses; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
📝 Committable suggestion
🤖 Prompt for AI Agents