Skip to content

fix: action buttons of left column in toggle block hover (#2526)#2671

Open
yamiletpineda wants to merge 3 commits intoTypeCellOS:mainfrom
yamiletpineda:fix/side-menu-toggle-column
Open

fix: action buttons of left column in toggle block hover (#2526)#2671
yamiletpineda wants to merge 3 commits intoTypeCellOS:mainfrom
yamiletpineda:fix/side-menu-toggle-column

Conversation

@yamiletpineda
Copy link
Copy Markdown

@yamiletpineda yamiletpineda commented Apr 22, 2026

Summary

Fixes #2526. Action buttons of the left column in a multi-column block not appearing or being clickable when column block is nested in a toggle list block.

Rationale

When a multi-column block is nested inside a toggle list block, the side menu action buttons of the leftmost column were not appearing or clickable on hover. This was due to incorrect column detection logic and a hardcoded x-coordinate offset in SideMenu.ts.

Changes

  • Changed column selector in getBlockFromCoords from [data-node-type=columnList] to [data-node-type=column].
  • Replaced the hardcoded left: coords.left + 50 offset with a bounds-aware clamp using the column's actual getBoundingClientRect().
  • Added explicit column bounds check in getBlockFromMousePos to handle columns nested inside toggle list blocks.
  • Modified onMouseMove to return early if the cursor is over a .bn-side-menu element, preventing the side menu from disappearing when hovered.
  • Simplified the reference x-coordinate in updateStateFromMousePos to use column.getBoundingClientRect().x directly.

Impact

Side menu action buttons now correctly appear and are clickable when hovering over columns nested inside toggle blocks.

Testing

  • Manually verified fix on Chrome and Firefox.
  • Added Playwright test in tests/src/end-to-end/draghandle/draghandle.test.ts: "Hovering over column nested in toggle block should show draghandle."

Screenshots/Video

multi-column-toggle-side-menu.copy.mov

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

The added test is a Playwright end-to-end test, not a unit test.

Summary by CodeRabbit

  • New Features
    • Added a "Select All" keyboard shortcut (Mod‑A) to select the entire document.
  • Bug Fixes
    • Improved side menu positioning and stopped hover updates when interacting with the side menu.
    • Fixed draghandle behavior for blocks nested inside toggle/list columns.
    • Improved copy-to-clipboard handling for selections that contain nested block groups.
  • Tests
    • Added an end-to-end test covering draghandle interactions in nested columns.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

@Rn123456789 is attempting to deploy a commit to the TypeCell Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

Column detection and side-menu positioning logic were changed to recognize data-node-type=column, clamp X coordinates to the column bounding rect, and avoid updating hover state when the cursor is over the side menu. A new E2E test verifies draghandle behavior inside toggle-list columns. Clipboard selection serialization and a keyboard shortcut for "Select All" were also added.

Changes

Cohort / File(s) Summary
Side Menu: column detection & hover
packages/core/src/extensions/SideMenu/SideMenu.ts
Detect columns via data-node-type=column, re-resolve reference block when inside a column, compute clamped X using columnRect.left/right + padding (instead of fixed offset), use column element's bounding box for menu positioning, and skip mousemove handling when cursor is over .bn-side-menu.
End-to-end draghandle test
tests/src/end-to-end/draghandle/draghandle.test.ts
Add E2E test that creates a toggle with columns, types content, hovers the first column, asserts draghandle appears and opens its menu.
Clipboard: selection serialization
packages/core/src/api/clipboard/toClipboard/copyExtension.ts
Serialize a narrowed mutable selectedFragment (unwrap single blockGroup to its content) and construct Slice(selectedFragment, 0, 0) for HTML clipboard output instead of serializing view.state.selection.content() directly.
Keyboard shortcuts: select-all
packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts
Add "Mod-a" handler that sets a ProseMirror AllSelection on the document via tr.setSelection(new AllSelection(doc)) and dispatches it through the view. Updated imports accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant Mouse
    participant EditorView as Editor View
    participant SideMenu as SideMenu Extension
    participant DOM as Column DOM Element

    Mouse->>EditorView: mousemove at (x,y)
    EditorView->>SideMenu: onMouseMove event
    SideMenu->>DOM: elementFromPoint(x,y) -> check `.bn-side-menu`
    alt cursor over side menu
        SideMenu-->>EditorView: bail out (no update)
    else not over side menu
        SideMenu->>DOM: find nearest column element (`data-node-type=column`)
        SideMenu->>EditorView: resolve referenceBlock from (clampedX, y)
        EditorView-->>SideMenu: referenceBlock
        SideMenu->>SideMenu: compute menu position using columnRect.left/right
        SideMenu-->>EditorView: update side-menu position / show hover
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • nperez0111

Poem

🐰
Columns tucked inside a fold,
I hop and clamp where stories are told.
Hover stays steady, menus in place,
Draghandles land with gentle grace.
Hooray — a rabbit's tidy chase! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Out of Scope Changes check ❓ Inconclusive Most changes directly address #2526, but the Mod-a (Select All) keyboard shortcut addition appears potentially unrelated to the column hover issue and may require clarification. Clarify whether the Select All keyboard shortcut addition is necessary for this fix or should be in a separate PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: action buttons of left column in toggle block hover (#2526)' accurately describes the main change: fixing side-menu button visibility/clickability for columns nested in toggle blocks.
Description check ✅ Passed The PR description covers all required sections: Summary, Rationale, Changes, Impact, Testing, and Checklist. While some checklist items are incomplete, the description is comprehensive with clear explanation of the fix.
Linked Issues check ✅ Passed All coding requirements from issue #2526 are met: side-menu button visibility is fixed for columns nested in toggle blocks through column detection logic updates, bounds-aware x-offset calculation, and early return on side-menu hover.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Actionable comments posted: 3

🤖 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/extensions/SideMenu/SideMenu.ts`:
- Around line 109-122: The current early return when a column is found uses
getBlockFromCoords with a fixed left offset and skips the nested-block
correction logic, causing parent blocks to be selected instead of indented
children; replace the early return with assigning the result of
getBlockFromCoords to a local variable (e.g., candidate =
getBlockFromCoords(view, {...})) and then fall through to the existing
nested-block correction logic that appears after this block so that
indented/nested blocks inside a column are still resolved (use candidate only if
the nested/block-correction step doesn't find a better match). Ensure you
reference the same symbols (referenceBlock, view, columnRect,
getBlockFromCoords) and preserve the original coordinate calculation but do not
return before running the nested resolution.
- Around line 626-630: The code in SideMenu.ts currently casts event.target to
HTMLElement | null and calls target.closest(".bn-side-menu"), which can throw if
event.target is a non-Element (e.g., Text node); modify the logic in the event
handler where `const target = event.target as HTMLElement | null` and the
subsequent `target?.closest(...)` check to first guard with `if (!(event.target
instanceof Element)) return;` or similar, then assign a properly typed `const
target: Element = event.target` and call `target.closest(".bn-side-menu")`—this
ensures `closest` is only invoked on an Element and prevents runtime TypeErrors.

In `@tests/src/end-to-end/draghandle/draghandle.test.ts`:
- Around line 185-196: The test currently grabs the first global
".bn-block-column" which can pass even if the columns are siblings of the
toggle; change the locator/assertion so the column is proved to be inside the
toggle before hovering. Specifically, in the "Hovering over column nested in
toggle block should show draghandle" test (the test function name) either scope
leftColumn to a toggle ancestor (e.g., find the toggle block created by
executeSlashCommand("togglelist") and then use its
locator().locator(".bn-block-column").first()) or add an explicit assertion that
the selected leftColumn is a descendant of the toggle (using a toggle locator
and an .contains/.getBy* or assertion that
toggle.locator(".bn-block-column").count() > 0) before calling
moveMouseOverElement, then proceed to waitForSelector(DRAG_HANDLE_SELECTOR) and
click as before.
🪄 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: 6bbe7cf1-5d9e-4289-97dc-5da44f7d7758

📥 Commits

Reviewing files that changed from the base of the PR and between 666ee1e and 3aef93b.

📒 Files selected for processing (2)
  • packages/core/src/extensions/SideMenu/SideMenu.ts
  • tests/src/end-to-end/draghandle/draghandle.test.ts

Comment on lines +109 to +122
const column = referenceBlock.node.closest("[data-node-type=column]");

if (column) {
const columnRect = column.getBoundingClientRect();

return getBlockFromCoords(
view,
{
left: Math.min(columnRect.left + 20, columnRect.right - 10),
top: mousePos.y,
},
false,
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve nested-block resolution inside columns.

This early return skips the nested-block correction below Line 124. For indented/nested blocks inside a column, probing at columnRect.left + 20 can resolve the parent block instead of the nested block under the cursor.

Suggested fix
   if (column) {
     const columnRect = column.getBoundingClientRect();
+    const referenceBlocksBoundingBox =
+      referenceBlock.node.getBoundingClientRect();
 
     return getBlockFromCoords(
       view,
       {
-        left: Math.min(columnRect.left + 20, columnRect.right - 10),
+        left: Math.min(
+          Math.max(referenceBlocksBoundingBox.right - 10, columnRect.left + 10),
+          columnRect.right - 10,
+        ),
         top: mousePos.y,
       },
       false,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/extensions/SideMenu/SideMenu.ts` around lines 109 - 122,
The current early return when a column is found uses getBlockFromCoords with a
fixed left offset and skips the nested-block correction logic, causing parent
blocks to be selected instead of indented children; replace the early return
with assigning the result of getBlockFromCoords to a local variable (e.g.,
candidate = getBlockFromCoords(view, {...})) and then fall through to the
existing nested-block correction logic that appears after this block so that
indented/nested blocks inside a column are still resolved (use candidate only if
the nested/block-correction step doesn't find a better match). Ensure you
reference the same symbols (referenceBlock, view, columnRect,
getBlockFromCoords) and preserve the original coordinate calculation but do not
return before running the nested resolution.

Comment on lines +626 to +630
const target = event.target as HTMLElement | null;

if (target?.closest(".bn-side-menu")) {
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "SideMenu.ts" -type f | head -20

Repository: TypeCellOS/BlockNote

Length of output: 115


🏁 Script executed:

cat -n ./packages/core/src/extensions/SideMenu/SideMenu.ts | sed -n '610,640p'

Repository: TypeCellOS/BlockNote

Length of output: 1208


🌐 Web query:

DOM event.target type MouseEvent .closest method Element

💡 Result:

In the DOM, for a MouseEvent (e.g., from a 'click' handler), event.target is an EventTarget referencing the innermost element that dispatched the event, typically an Element like an input or button. Since Element.closest(selectors) is a standard method that traverses up the DOM tree to find the nearest ancestor (or self) matching the CSS selectors, you can safely call event.target.closest(selectors) after ensuring it's an Element. This pattern is commonly used for event delegation: attach a single listener to a parent, then use event.target.closest to identify the relevant child element. Example: document.addEventListener('click', function(event) { const button = (event.target as Element).closest('button.special'); if (button) { // Handle click on special button or its children } }); TypeScript requires casting event.target to Element or HTMLElement since EventTarget lacks closest. MouseEvent generics specify the expected target type (e.g., MouseEvent). No changes or deprecations as of 2026 per MDN docs.

Citations:


🌐 Web query:

Text node Comment node .closest() method throws TypeError

💡 Result:

The .closest method throws a TypeError when called on a Text node or Comment node because it is defined only on the Element interface, not on Node. Text nodes (nodeType 3) and Comment nodes (nodeType 8) are not Elements, so they do not have the closest method. To find the closest ancestor element matching a selector from a Text or Comment node, first access its parent Element using node.parentElement (or node.parentNode if you need to check the type), then call closest on that Element. Example: const textNode = someElement.firstChild; // Text node if (textNode.nodeType === Node.TEXT_NODE) { const closestDiv = textNode.parentElement.closest('div'); } This is a current DOM specification behavior as of 2026; an open WHATWG issue proposes extending closest to more node types like Text, but it remains Element-only.

Citations:


Narrow event.target before calling .closest.

The cast to HTMLElement | null does not guarantee the runtime target is an Element. Since event.target has type EventTarget, it could be a non-Element node (e.g., Text or Comment), and calling .closest() on such nodes throws a TypeError. Use an instanceof Element guard to safely narrow the type.

Suggested fix
-    const target = event.target as HTMLElement | null;
+    const target = event.target;
 
-    if (target?.closest(".bn-side-menu")) {
-        return;
+    if (target instanceof Element && target.closest(".bn-side-menu")) {
+      return;
     }
📝 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.

Suggested change
const target = event.target as HTMLElement | null;
if (target?.closest(".bn-side-menu")) {
return;
}
const target = event.target;
if (target instanceof Element && target.closest(".bn-side-menu")) {
return;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/extensions/SideMenu/SideMenu.ts` around lines 626 - 630,
The code in SideMenu.ts currently casts event.target to HTMLElement | null and
calls target.closest(".bn-side-menu"), which can throw if event.target is a
non-Element (e.g., Text node); modify the logic in the event handler where
`const target = event.target as HTMLElement | null` and the subsequent
`target?.closest(...)` check to first guard with `if (!(event.target instanceof
Element)) return;` or similar, then assign a properly typed `const target:
Element = event.target` and call `target.closest(".bn-side-menu")`—this ensures
`closest` is only invoked on an Element and prevents runtime TypeErrors.

Comment on lines +185 to +196
test("Hovering over column nested in toggle block should show draghandle", async () => {
await executeSlashCommand(page, "togglelist");
await page.keyboard.type("Toggle heading");
await page.keyboard.press("Enter");
await executeSlashCommand(page, "two");
await page.keyboard.type("Left column content");
const leftColumn = await page.locator(".bn-block-column").first();
await moveMouseOverElement(page, leftColumn);
await page.waitForSelector(DRAG_HANDLE_SELECTOR);
await page.click(DRAG_HANDLE_SELECTOR);
await page.waitForSelector(DRAG_HANDLE_MENU_SELECTOR);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Make the regression test prove the column is inside the toggle.

Right now the test selects the first column globally, so it can still pass if Enter creates the columns as a sibling of the toggle instead of a child. Scope the locator to a toggle ancestor, or assert that relationship before hovering.

Suggested test hardening
-    const leftColumn = await page.locator(".bn-block-column").first();
+    const leftColumn = page
+      .locator('[data-node-type*="toggle" i] [data-node-type="column"]')
+      .first();
+    await expect(leftColumn).toBeVisible();
     await moveMouseOverElement(page, leftColumn);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/src/end-to-end/draghandle/draghandle.test.ts` around lines 185 - 196,
The test currently grabs the first global ".bn-block-column" which can pass even
if the columns are siblings of the toggle; change the locator/assertion so the
column is proved to be inside the toggle before hovering. Specifically, in the
"Hovering over column nested in toggle block should show draghandle" test (the
test function name) either scope leftColumn to a toggle ancestor (e.g., find the
toggle block created by executeSlashCommand("togglelist") and then use its
locator().locator(".bn-block-column").first()) or add an explicit assertion that
the selected leftColumn is a descendant of the toggle (using a toggle locator
and an .contains/.getBy* or assertion that
toggle.locator(".bn-block-column").count() > 0) before calling
moveMouseOverElement, then proceed to waitForSelector(DRAG_HANDLE_SELECTOR) and
click as before.

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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/core/src/api/clipboard/toClipboard/copyExtension.ts (1)

132-135: Nit: formatting / scope of unwrap.

Indentation of the if block is off (extra indent inside the brace), and the unwrap triggers on any single-child blockGroup, including when an explicit NodeSelection of a blockGroup is copied. If that case should retain the outer wrapper, guard with e.g. !("node" in view.state.selection). Otherwise safe given doc's content spec is a single blockGroup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/api/clipboard/toClipboard/copyExtension.ts` around lines
132 - 135, The if-block wrongly indents and unconditionally unwraps a
single-child blockGroup, which also unwraps when the user has a NodeSelection of
that blockGroup; fix by normalizing indentation for the if and add a
selection-type guard so you only unwrap when the current selection is not a node
selection (e.g. check that !"node" in view.state.selection or similar) before
assigning selectedFragment = selectedFragment.firstChild.content; references:
selectedFragment, blockGroup, and view.state.selection/NodeSelection.
packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts (1)

956-966: Scope: unrelated to PR objective.

This Mod-a shortcut and the clipboard Slice changes in copyExtension.ts are unrelated to the stated fix (side-menu action buttons on toggle-nested columns, issue #2526). Consider splitting them into a separate PR to keep the change set focused and simplify review/revert.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts`
around lines 956 - 966, The Mod-a keyboard shortcut handler (the "Mod-a" case in
KeyboardShortcutsExtension.ts that sets an AllSelection) and the clipboard Slice
changes in copyExtension.ts are unrelated to the side-menu toggle-nested columns
fix; revert or remove these unrelated edits from this branch/commit and put them
into a separate focused PR: locate the "Mod-a" case in
KeyboardShortcutsExtension.ts and restore the previous behavior (or remove the
added AllSelection logic), and revert the Slice/clipboard changes in
copyExtension.ts, then create a new branch/PR containing only those two changes
so this PR only contains the toggle-nested columns fixes.
🤖 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/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts`:
- Around line 956-966: The "Mod-a" key handler currently uses optional chaining
on this.options.editor.prosemirrorView so it may return true even when no
dispatch occurs; replace the direct dispatch with the TipTap command pattern to
ensure consistent pipeline and only swallow the event when dispatch happened:
inside the "Mod-a" handler call this.editor.commands.command(({ tr, dispatch })
=> { const { doc } = this.options.editor.prosemirrorState; if (dispatch)
dispatch(tr.setSelection(new AllSelection(doc))); return !!dispatch; }); or
alternatively use this.editor.view.dispatch if you must dispatch directly, but
ensure you check view exists and return false when no dispatch occurred. Ensure
references include the existing AllSelection,
this.options.editor.prosemirrorState, and the handler name "Mod-a".

---

Nitpick comments:
In `@packages/core/src/api/clipboard/toClipboard/copyExtension.ts`:
- Around line 132-135: The if-block wrongly indents and unconditionally unwraps
a single-child blockGroup, which also unwraps when the user has a NodeSelection
of that blockGroup; fix by normalizing indentation for the if and add a
selection-type guard so you only unwrap when the current selection is not a node
selection (e.g. check that !"node" in view.state.selection or similar) before
assigning selectedFragment = selectedFragment.firstChild.content; references:
selectedFragment, blockGroup, and view.state.selection/NodeSelection.

In
`@packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts`:
- Around line 956-966: The Mod-a keyboard shortcut handler (the "Mod-a" case in
KeyboardShortcutsExtension.ts that sets an AllSelection) and the clipboard Slice
changes in copyExtension.ts are unrelated to the side-menu toggle-nested columns
fix; revert or remove these unrelated edits from this branch/commit and put them
into a separate focused PR: locate the "Mod-a" case in
KeyboardShortcutsExtension.ts and restore the previous behavior (or remove the
added AllSelection logic), and revert the Slice/clipboard changes in
copyExtension.ts, then create a new branch/PR containing only those two changes
so this PR only contains the toggle-nested columns fixes.
🪄 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: 4d2607dd-15ae-4deb-bfed-7465630e8cb6

📥 Commits

Reviewing files that changed from the base of the PR and between 3aef93b and efc1016.

📒 Files selected for processing (2)
  • packages/core/src/api/clipboard/toClipboard/copyExtension.ts
  • packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts

Comment on lines +956 to +966
// Forces AllSelection from pos 0 to include non-editable blocks (e.g. images) that
// TextSelection would skip.
"Mod-a": () => {
const { doc } = this.options.editor.prosemirrorState;
this.options.editor.prosemirrorView?.dispatch(
this.options.editor.prosemirrorState.tr.setSelection(
new AllSelection(doc)
)
);
return true;
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Mod-a returns true even when dispatch is skipped.

prosemirrorView is accessed via optional chaining, so if it's undefined the selection is never updated but the handler still returns true, swallowing the shortcut and preventing the browser's native Select-All fallback. Also, dispatching directly bypasses the TipTap command pipeline used elsewhere in this file — prefer this.editor.commands.command(({ tr, dispatch }) => { ... }) or this.editor.view.dispatch for consistency with the rest of the keymap (which relies on this.editor, not this.options.editor.prosemirrorView).

♻️ Suggested change
-      "Mod-a": () => {
-        const { doc } = this.options.editor.prosemirrorState;
-        this.options.editor.prosemirrorView?.dispatch(
-          this.options.editor.prosemirrorState.tr.setSelection(
-            new AllSelection(doc)
-          )
-        );
-        return true;
-      },
+      "Mod-a": () => {
+        const view = this.options.editor.prosemirrorView;
+        if (!view) {
+          return false;
+        }
+        view.dispatch(
+          view.state.tr.setSelection(new AllSelection(view.state.doc)),
+        );
+        return true;
+      },
📝 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.

Suggested change
// Forces AllSelection from pos 0 to include non-editable blocks (e.g. images) that
// TextSelection would skip.
"Mod-a": () => {
const { doc } = this.options.editor.prosemirrorState;
this.options.editor.prosemirrorView?.dispatch(
this.options.editor.prosemirrorState.tr.setSelection(
new AllSelection(doc)
)
);
return true;
},
// Forces AllSelection from pos 0 to include non-editable blocks (e.g. images) that
// TextSelection would skip.
"Mod-a": () => {
const view = this.options.editor.prosemirrorView;
if (!view) {
return false;
}
view.dispatch(
view.state.tr.setSelection(new AllSelection(view.state.doc)),
);
return true;
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts`
around lines 956 - 966, The "Mod-a" key handler currently uses optional chaining
on this.options.editor.prosemirrorView so it may return true even when no
dispatch occurs; replace the direct dispatch with the TipTap command pattern to
ensure consistent pipeline and only swallow the event when dispatch happened:
inside the "Mod-a" handler call this.editor.commands.command(({ tr, dispatch })
=> { const { doc } = this.options.editor.prosemirrorState; if (dispatch)
dispatch(tr.setSelection(new AllSelection(doc))); return !!dispatch; }); or
alternatively use this.editor.view.dispatch if you must dispatch directly, but
ensure you check view exists and return false when no dispatch occurred. Ensure
references include the existing AllSelection,
this.options.editor.prosemirrorState, and the handler name "Mod-a".

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.

Action buttons of left column in toggle block hover

2 participants