Skip to content

feat(core): built-in table drag visualization - #2963

Open
nperez0111 wants to merge 5 commits into
mainfrom
table-reordering-visualization
Open

feat(core): built-in table drag visualization#2963
nperez0111 wants to merge 5 commits into
mainfrom
table-reordering-visualization

Conversation

@nperez0111

@nperez0111 nperez0111 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Moves table drag source highlighting, drop cursor decorations, and a floating drag preview into TableHandlesExtension so they work out of the box — no extensions or hooks needed.
  • Adds an example (21-table-reordering-visualization) that reskins the built-in drag affordances via CSS custom properties (with dark-mode support) for a Microsoft Loop-style look.
  • Adds unit tests for the new dragDecorations and dragPreview modules, plus e2e screenshot tests for the drag-in-progress state.

Test plan

  • Unit tests pass (vp run test)
  • E2e tests pass (vp run e2e)
  • Open the table reordering example in the playground and verify:
    • Dragging a row/column shows a tinted highlight on the source
    • A floating snapshot follows the cursor
    • A drop indicator marks the target position
    • Reorder completes correctly on drop
  • Verify dark mode styling works in the example

Summary by CodeRabbit

  • New Features

    • Added table row and column drag-and-drop interactions with clearer source highlights, drop cursors, and visual drag previews.
    • Added a new table reordering visualization example with customizable light and dark styling.
    • New tables inserted from the slash menu now include a header row by default.
  • Documentation

    • Documented table dragging styles, visual behavior, implementation details, and accessibility considerations.

mustafa-yilmaz and others added 5 commits August 11, 2026 17:41
Ports the enhanced table drag-and-drop feedback originally built as a
customization on top of La Suite Docs into a standalone BlockNote.js
example, using only public BlockNote/ProseMirror APIs and plain colors
(no external design-token dependency):

- Restyled tables: rounded card look, muted header row, hairline
  borders, row-hover highlight.
- Drag source highlight: the row/column being dragged is tinted and
  outlined via a ProseMirror decoration (survives redraws, unlike a
  direct DOM class mutation).
- Colored drop-position indicator.
- Floating drag image: a real snapshot of the row/column follows the
  cursor, replacing BlockNote's default hidden native drag image.
- New tables via "/table" now default to a header row, so the header
  styling is visible immediately instead of requiring a manual toggle.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds e2e coverage for the parts the new example actually changes:
- source-highlight + colored drop-cursor appearance during row/column drags
- per-cell tinting for column drags
- cleanup after a cancelled (Escape) drag
- dragging a row with rich inline content
- dragging a column across a merged (rowspan) cell
- the /table slash command defaulting new tables to a header row

Also documents the interaction model and known limitations (no
keyboard/touch reordering, no focus-restoration path, merged-cell
index fidelity, and stale-snapshot behavior on concurrent edits
mid-drag) in the example's README, since those are pre-existing
characteristics of BlockNote's own table-drag implementation that this
example doesn't introduce or change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- vite.config.ts: fix the local-source alias path (was 2 levels up,
  needed 3 to actually reach packages/core|react/src - tsconfig.json
  already had the correct depth, so this was a silent no-op before,
  always falling back to node_modules resolution)
- index.html: add missing <!doctype html>, move the generator marker
  comment out of the <script> tag
- tableDragSourceExtension.ts: guard the decoration's position
  resolution against a stale/out-of-range tablePos instead of letting
  it throw, since nothing remaps tablePos across later transactions
- useTableDragImage.ts: resolve the table's DOM node deterministically
  via its stable block ID instead of elementFromPoint hit-testing
  (which silently fails if any overlay covers that pixel); position
  the drag-image clone on-screen-but-invisible instead of far
  off-screen, since some browsers skip rasterizing elements placed
  well outside the viewport; wrap the append/setDragImage/cleanup in
  try/finally so cleanup always runs
- tableReorderingVisualization.test.tsx: wrap the post-drop decoration
  cleanup assertions in vi.waitFor instead of asserting immediately
  after mouseup, since cleanup isn't necessarily synchronous with it

Verified all fixes against the actual dev server (not just the test
suite, since the vite.config.ts alias fix specifically changes that
path) and re-ran the full test file across chromium/firefox/webkit
after each change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Addresses review comment on #2920 (r3651925104): apply() cast any object
transaction meta straight to DragSourceMeta without checking tablePos/
originalIndex were actually numbers, and never remapped a stored tablePos
across later transactions.

- Validate the meta shape before accepting it, matching the suggested fix.
- When a transaction changes the document without setting our meta (a
  concurrent local or collaborative edit while a drag is in progress),
  remap the stored tablePos through tr.mapping instead of leaving it
  stale.

While writing a regression test for this, dispatching an unrelated
transaction mid-drag surfaced a pre-existing bug in BlockNote's own
TableHandlesExtension: view.tablePos (used for its drop-cursor decoration)
has the same never-remapped issue, but throws a RangeError instead of
failing safely, since it's a plain instance property rather than plugin
state going through tr.mapping. That's out of scope for this example to
fix, so the test was dropped (it can't pass while core's own decorations()
throws first in the same view update) and the README's "Concurrent edits
mid-drag" section was corrected - it previously understated this as
"drops can overwrite a concurrent edit" when it can actually throw and
break the editor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Move drag source highlighting, drop cursor decorations, and the floating
drag preview from the example into TableHandlesExtension so they work
out of the box. The example now only reskins the built-in affordances
via CSS custom properties and dark-mode overrides.
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
blocknote Ready Ready Preview Aug 11, 2026 3:42pm
blocknote-website Error Error Aug 11, 2026 3:42pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds table row and column drag decorations, native drag previews, themed styling, a visualization example, documentation, unit tests, and browser-level visual tests.

Changes

Table drag visualization

Layer / File(s) Summary
Drag decorations and state wiring
packages/core/src/extensions/TableHandles/..., packages/core/src/editor/editor.css
Table dragging now highlights source rows or columns and shows validated drop cursors.
Native drag-preview lifecycle
packages/core/src/extensions/TableHandles/..., packages/core/src/editor/editor.css
Row and column previews are cloned from rendered cells, attached for native drag images, and removed after dragging.
Visualization example and documentation
examples/03-ui-components/21-table-reordering-visualization/*, docs/content/docs/react/styling-theming/overriding-css.mdx, playground/src/examples.gen.tsx
The example configures table features, custom styling, header-row insertion, metadata, and documentation for drag classes and preview scope.
Browser drag-visual validation
tests/src/end-to-end/tables/tableDragVisuals.test.tsx
Browser tests verify highlights, cursors, previews, invalid drops, cancellation, cleanup, and a drag-in-progress screenshot.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant TableHandlesView
  participant DragDecorations
  participant DragPreview
  participant DOM
  User->>TableHandlesView: start row or column drag
  TableHandlesView->>DragPreview: create table drag image
  DragPreview->>DOM: attach cloned preview
  TableHandlesView->>DragDecorations: update drag state
  DragDecorations->>DOM: render source highlight and drop cursor
  User->>TableHandlesView: end or cancel drag
  TableHandlesView->>DragPreview: remove active preview
  TableHandlesView->>DragDecorations: clear drag decorations
Loading

Possibly related PRs

Poem

A rabbit hops where table rows align,
Dashed cursors mark the drop-line.
Cloned cells glide in previews bright,
Headers stay bold in themed light.
Drag, release, and cleanup run—
The table’s tidy work is done!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the feature and test plan but omits most required template sections, including rationale, impact, screenshots, checklist, and additional notes. Add the missing template sections and record completed unit, end-to-end, manual, and dark-mode testing results.
Docstring Coverage ⚠️ Warning Docstring coverage is 47.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the primary change: built-in table drag visualization in the core package.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch table-reordering-visualization

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

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

@blocknote/code-block

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

@blocknote/core

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

@blocknote/mantine

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

@blocknote/react

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

@blocknote/server-util

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

@blocknote/shadcn

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

@blocknote/xl-ai

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

@blocknote/xl-docx-exporter

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

@blocknote/xl-email-exporter

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

@blocknote/xl-multi-column

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

@blocknote/xl-odt-exporter

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

@blocknote/xl-pdf-exporter

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

commit: fde31a6

@github-actions

github-actions Bot commented Aug 11, 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-2963/

Built to branch gh-pages at 2026-08-11 15:51 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (5)
tests/src/end-to-end/tables/tableDragVisuals.test.tsx (1)

23-42: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add drag-visual tests for non-plain table structures.

This fixture creates only plain text tableCell nodes. It does not exercise header cells, merged cells, or rich cell content.

Add row and column drag cases for these structures. Verify the source decorations, drop cursor, preview content, and cleanup for each case.

Also applies to: 175-183

🤖 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 `@tests/src/end-to-end/tables/tableDragVisuals.test.tsx` around lines 23 - 42,
Expand seedTable and the row/column drag tests to cover tableHeader cells,
merged cells with colspan/rowspan, and rich cell content in addition to plain
cells. For each structure, assert source decorations, drop cursor placement,
preview content, and cleanup after the drag completes, reusing the existing
drag-test helpers and fixture symbols.
packages/core/src/extensions/TableHandles/dragDecorations.ts (1)

122-166: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider a key on the drop-cursor widget spec.

dragOverHandler dispatches a dummy transaction on every index change. Without a key, ProseMirror cannot compare widget decorations and rebuilds the DOM for each cursor on each redraw. A key derived from the orientation and target index lets ProseMirror reuse the widget when nothing changed.

♻️ Proposed change
       Decoration.widget(decorationPos, () => {
         ...
         return widget;
-      }),
+      }, { key: `${DROP_CURSOR_CLASS}-${draggedCellOrientation}-${newIndex}` }),
🤖 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/TableHandles/dragDecorations.ts` around lines
122 - 166, Update the Decoration.widget specification in the widget creation
flow within dragOverHandler to include a stable key derived from
draggedCellOrientation and the target index, so ProseMirror can reuse the
drop-cursor DOM when the cursor position is unchanged.
packages/core/src/extensions/TableHandles/dragPreview.ts (1)

10-10: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Scope the preview to the editor view instead of a module singleton.

dragImageElement is module state shared by every editor on the page. TableHandlesView.destroy calls unsetTableDragImage() unconditionally, and dragEnd does the same. If a page mounts two editors and one unmounts or ends a drag, it clears the preview owned by the other editor. Keying the preview by view removes the cross-editor coupling.

♻️ Proposed change
-let dragImageElement: HTMLElement | undefined;
+const dragImageElements = new WeakMap<EditorView, HTMLElement>();

setTableDragImage then stores under view, and unsetTableDragImage(view) removes only that entry. Both call sites in TableHandles.ts already have the view available.

Also applies to: 151-158

🤖 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/TableHandles/dragPreview.ts` at line 10, Replace
the module-level dragImageElement singleton with view-scoped storage keyed by
the editor view. Update setTableDragImage to store the preview for its view,
change unsetTableDragImage to accept a view and remove only that view’s entry,
and update TableHandlesView.destroy and dragEnd in TableHandles.ts to pass their
available view.
packages/core/src/editor/editor.css (1)

65-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Expose the drag accent colour as a custom property.

The accent #adf is hard-coded in three rules: the drop cursor background, the source outline, and the preview cell border. Lines 89-90 already use var(--bn-colors-..., fallback). The PR describes an example that customizes these affordances with CSS custom properties. A single token keeps the three rules in sync and gives consumers one override point.

♻️ Proposed change
 .bn-table-drop-cursor {
   position: absolute;
   z-index: 20;
-  background-color: `#adf`;
+  background-color: var(--bn-table-drag-accent, `#adf`);
   border-radius: 2px;
   pointer-events: none;
 }

Apply the same token to the outline on line 79 and the border on line 100.

Also applies to: 79-79, 100-100

🤖 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/editor.css` at line 65, Define a shared CSS custom
property for the drag accent color with `#adf` as its fallback, then use that
token consistently for the drop cursor background, source outline, and preview
cell border rules in editor.css. Ensure all three affordances remain
synchronized while allowing consumers to override the single property.
packages/core/src/extensions/TableHandles/dragDecorations.test.ts (1)

1-11: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the redundant @vitest-environment jsdom docblocks. packages/core/vite.config.ts already sets the test environment to "jsdom" for this package.

🤖 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/TableHandles/dragDecorations.test.ts` around
lines 1 - 11, Remove the redundant `@vitest-environment` jsdom docblocks from
packages/core/src/extensions/TableHandles/dragDecorations.test.ts lines 1-11 and
packages/core/src/extensions/TableHandles/dragPreview.test.ts lines 1-14; rely
on the package-level jsdom configuration in packages/core/vite.config.ts, with
no other test changes.
🤖 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 `@examples/03-ui-components/21-table-reordering-visualization/index.html`:
- Line 1: Update the document header before the html element to include the
standard HTML5 doctype declaration, ensuring the page does not enter quirks
mode.

In `@examples/03-ui-components/21-table-reordering-visualization/package.json`:
- Line 10: Update the build:prod script to replace the direct tsc invocation
with vp run lint, preserving the subsequent vp build command and using only
supported vp commands for linting and type-checking.

In `@examples/03-ui-components/21-table-reordering-visualization/README.md`:
- Around line 19-21: Update the README wording around TableHandlesExtension to
say the example requires no additional extensions or event handling. Regenerate
playground/src/examples.gen.tsx so its generated example text reflects the
corrected README wording at the cited range.

In `@examples/03-ui-components/21-table-reordering-visualization/vite.config.ts`:
- Around line 16-27: Update the development aliases in the Vite configuration to
resolve both `@blocknote/core` and `@blocknote/react` from ../../../packages/...
instead of ../../packages/..., matching the local package paths used by
tsconfig.json and preserving live source loading.

In `@packages/core/src/editor/editor.css`:
- Around line 61-68: Add position: relative to the table cell CSS rule used by
the td/th elements targeted by getTableDragDecorations, so each inserted
.bn-table-drop-cursor is positioned relative to its containing cell rather than
.tableWrapper.

In `@packages/core/src/extensions/TableHandles/dragDecorations.ts`:
- Around line 60-99: Update TableHandlesView.update() to remap tablePos through
each transaction before using the refreshed block, then make resolveCell
validate row and column indices against the resolved table and row node bounds.
Return no cell when resolution fails, posAtIndex throws, or resolves to a
parent-end position, and guard both decoration paths so invalid cells are
skipped instead of producing decorations.

---

Nitpick comments:
In `@packages/core/src/editor/editor.css`:
- Line 65: Define a shared CSS custom property for the drag accent color with
`#adf` as its fallback, then use that token consistently for the drop cursor
background, source outline, and preview cell border rules in editor.css. Ensure
all three affordances remain synchronized while allowing consumers to override
the single property.

In `@packages/core/src/extensions/TableHandles/dragDecorations.test.ts`:
- Around line 1-11: Remove the redundant `@vitest-environment` jsdom docblocks
from packages/core/src/extensions/TableHandles/dragDecorations.test.ts lines
1-11 and packages/core/src/extensions/TableHandles/dragPreview.test.ts lines
1-14; rely on the package-level jsdom configuration in
packages/core/vite.config.ts, with no other test changes.

In `@packages/core/src/extensions/TableHandles/dragDecorations.ts`:
- Around line 122-166: Update the Decoration.widget specification in the widget
creation flow within dragOverHandler to include a stable key derived from
draggedCellOrientation and the target index, so ProseMirror can reuse the
drop-cursor DOM when the cursor position is unchanged.

In `@packages/core/src/extensions/TableHandles/dragPreview.ts`:
- Line 10: Replace the module-level dragImageElement singleton with view-scoped
storage keyed by the editor view. Update setTableDragImage to store the preview
for its view, change unsetTableDragImage to accept a view and remove only that
view’s entry, and update TableHandlesView.destroy and dragEnd in TableHandles.ts
to pass their available view.

In `@tests/src/end-to-end/tables/tableDragVisuals.test.tsx`:
- Around line 23-42: Expand seedTable and the row/column drag tests to cover
tableHeader cells, merged cells with colspan/rowspan, and rich cell content in
addition to plain cells. For each structure, assert source decorations, drop
cursor placement, preview content, and cleanup after the drag completes, reusing
the existing drag-test helpers and fixture symbols.
🪄 Autofix

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 Plus

Run ID: 9940a4be-55a6-4c80-a301-1a4eaad2cb94

📥 Commits

Reviewing files that changed from the base of the PR and between e0cce10 and fde31a6.

⛔ Files ignored due to path filters (3)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • tests/src/end-to-end/tables/__screenshots__/tableDragVisuals.test.tsx/tableRowDragInProgress-chromium-linux.png is excluded by !**/*.png
  • tests/src/end-to-end/tables/__screenshots__/tableDragVisuals.test.tsx/tableRowDragInProgress-webkit-linux.png is excluded by !**/*.png
📒 Files selected for processing (20)
  • docs/content/docs/react/styling-theming/overriding-css.mdx
  • examples/03-ui-components/21-table-reordering-visualization/.bnexample.json
  • examples/03-ui-components/21-table-reordering-visualization/README.md
  • examples/03-ui-components/21-table-reordering-visualization/index.html
  • examples/03-ui-components/21-table-reordering-visualization/main.tsx
  • examples/03-ui-components/21-table-reordering-visualization/package.json
  • examples/03-ui-components/21-table-reordering-visualization/src/App.tsx
  • examples/03-ui-components/21-table-reordering-visualization/src/tableStyles.css
  • examples/03-ui-components/21-table-reordering-visualization/src/vite-env.d.ts
  • examples/03-ui-components/21-table-reordering-visualization/tsconfig.json
  • examples/03-ui-components/21-table-reordering-visualization/vite-env.d.ts
  • examples/03-ui-components/21-table-reordering-visualization/vite.config.ts
  • packages/core/src/editor/editor.css
  • packages/core/src/extensions/TableHandles/TableHandles.ts
  • packages/core/src/extensions/TableHandles/dragDecorations.test.ts
  • packages/core/src/extensions/TableHandles/dragDecorations.ts
  • packages/core/src/extensions/TableHandles/dragPreview.test.ts
  • packages/core/src/extensions/TableHandles/dragPreview.ts
  • playground/src/examples.gen.tsx
  • tests/src/end-to-end/tables/tableDragVisuals.test.tsx

@@ -0,0 +1,14 @@
<html lang="en">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add the HTML doctype.

The missing doctype can enable quirks mode. Add <!doctype html> before the <html> element.

Proposed fix
+<!doctype html>
 <html lang="en">
📝 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
<html lang="en">
<!doctype html>
<html lang="en">
🧰 Tools
🪛 HTMLHint (1.9.2)

[error] 1-1: Doctype must be declared before any non-comment content.

(doctype-first)

🤖 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 `@examples/03-ui-components/21-table-reordering-visualization/index.html` at
line 1, Update the document header before the html element to include the
standard HTML5 doctype declaration, ensuring the page does not enter quirks
mode.

Source: Linters/SAST tools

"scripts": {
"start": "vp dev",
"dev": "vp dev",
"build:prod": "tsc && vp build",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Replace tsc with the supported type-check command.

Line 10 invokes tsc directly. Use vp run lint before vp build.

Proposed fix
-    "build:prod": "tsc && vp build",
+    "build:prod": "vp run lint && vp build",

As per coding guidelines, use only vp or pnpm commands and use vp run lint for linting and type-checking; do not use tsc or prettier.

📝 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
"build:prod": "tsc && vp build",
"build:prod": "vp run lint && vp build",
🤖 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 `@examples/03-ui-components/21-table-reordering-visualization/package.json` at
line 10, Update the build:prod script to replace the direct tsc invocation with
vp run lint, preserving the subsequent vp build command and using only supported
vp commands for linting and type-checking.

Source: Coding guidelines

Comment on lines +19 to +21
Everything here is CSS plus one slash-menu tweak - no extensions, no event
handling. BlockNote's `TableHandlesExtension` owns the whole drag lifecycle and
exposes it through classes you can target:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the extension wording.

TableHandlesExtension provides the built-in behavior. “No extensions” is incorrect. State that the example requires no additional extensions or event handling.

  • examples/03-ui-components/21-table-reordering-visualization/README.md#L19-L21: replace “no extensions” with “no additional extensions”.
  • playground/src/examples.gen.tsx#L918-L918: regenerate this generated file after correcting the source README.
📍 Affects 2 files
  • examples/03-ui-components/21-table-reordering-visualization/README.md#L19-L21 (this comment)
  • playground/src/examples.gen.tsx#L918-L918
🤖 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 `@examples/03-ui-components/21-table-reordering-visualization/README.md` around
lines 19 - 21, Update the README wording around TableHandlesExtension to say the
example requires no additional extensions or event handling. Regenerate
playground/src/examples.gen.tsx so its generated example text reflects the
corrected README wording at the cited range.

Comment on lines +16 to +27
!fs.existsSync(path.resolve(__dirname, "../../packages/core/src"))
? {}
: ({
// Comment out the lines below to load a built version of blocknote
// or, keep as is to load live from sources with live reload working
"@blocknote/core": path.resolve(
__dirname,
"../../packages/core/src/",
),
"@blocknote/react": path.resolve(
__dirname,
"../../packages/react/src/",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the development alias paths.

Lines 16, 23, and 27 resolve ../../packages under examples/packages. This does not match the local package references in tsconfig.json.

Use ../../../packages/... for both aliases. Otherwise, development loads the installed package instead of live source code.

Proposed fix
- !fs.existsSync(path.resolve(__dirname, "../../packages/core/src"))
+ !fs.existsSync(path.resolve(__dirname, "../../../packages/core/src"))
...
-              "../../packages/core/src/",
+              "../../../packages/core/src/",
...
-              "../../packages/react/src/",
+              "../../../packages/react/src/",
📝 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
!fs.existsSync(path.resolve(__dirname, "../../packages/core/src"))
? {}
: ({
// Comment out the lines below to load a built version of blocknote
// or, keep as is to load live from sources with live reload working
"@blocknote/core": path.resolve(
__dirname,
"../../packages/core/src/",
),
"@blocknote/react": path.resolve(
__dirname,
"../../packages/react/src/",
!fs.existsSync(path.resolve(__dirname, "../../../packages/core/src"))
? {}
: ({
// Comment out the lines below to load a built version of blocknote
// or, keep as is to load live from sources with live reload working
"`@blocknote/core`": path.resolve(
__dirname,
"../../../packages/core/src/",
),
"`@blocknote/react`": path.resolve(
__dirname,
"../../../packages/react/src/",
🤖 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 `@examples/03-ui-components/21-table-reordering-visualization/vite.config.ts`
around lines 16 - 27, Update the development aliases in the Vite configuration
to resolve both `@blocknote/core` and `@blocknote/react` from ../../../packages/...
instead of ../../packages/..., matching the local package paths used by
tsconfig.json and preserving live source loading.

Comment on lines +61 to 68
/* Marks the edge a dragged table row/column would be dropped at. */
.bn-table-drop-cursor {
position: absolute;
z-index: 20;
background-color: #adf;
border-radius: 2px;
pointer-events: none;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Look for position rules applied to table cells in the core styles.
rg -n -B 6 'position:\s*relative' packages/core/src/editor/editor.css
rg -n -C 6 'data-content-type="table"\].*\b(td|th)\b' packages/core/src --glob '*.css'

Repository: TypeCellOS/BlockNote

Length of output: 2371


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- dragDecorations.ts locations ---'
fd -i 'dragDecorations\.ts$' packages
printf '%s\n' '--- table wrapper and drop cursor references ---'
rg -n -C 12 'bn-table-drop-cursor|tableWrapper|drop cursor|dropCursor' packages/core/src --glob '*.ts' --glob '*.tsx' --glob '*.css'
printf '%s\n' '--- relevant editor.css ---'
sed -n '165,225p' packages/core/src/editor/editor.css

Repository: TypeCellOS/BlockNote

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- dragDecorations outline ---'
ast-grep outline packages/core/src/extensions/TableHandles/dragDecorations.ts
printf '%s\n' '--- decoration implementation ---'
sed -n '1,260p' packages/core/src/extensions/TableHandles/dragDecorations.ts
printf '%s\n' '--- table wrapper and cell DOM styles ---'
rg -n -C 8 'tableWrapper|table-widgets-container|position:|appendChild|createElement' packages/core/src/extensions/TableHandles packages/core/src/editor/editor.css --glob '*.ts' --glob '*.css'

Repository: TypeCellOS/BlockNote

Length of output: 28675


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- all repository rules for table cells and wrappers ---'
rg -n -C 5 '(\.tableWrapper|tableWrapper|data-content-type="table").*(td|th)|(td|th).*(position|tableWrapper)|position\s*:\s*(relative|absolute|fixed|sticky)' packages --glob '*.css' --glob '*.scss' --glob '*.less'
printf '%s\n' '--- widget position tests and DOM assumptions ---'
rg -n -C 10 'DROP_CURSOR_CLASS|bn-table-drop-cursor|decorationPos|Decoration\.widget|tableWrapper' packages/core/src/extensions/TableHandles --glob '*.ts' --glob '*.tsx'
printf '%s\n' '--- table node-view DOM construction ---'
rg -n -C 12 'tableWrapper|table-widgets-container|contentDOM|createElement\("table"|createElement\("td"|createElement\("th"' packages/core/src --glob '*.ts' --glob '*.tsx'

Repository: TypeCellOS/BlockNote

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- table-related source files ---'
fd -i 'table' packages/core/src/blocks packages/core/src/extensions packages/core/src/editor
printf '%s\n' '--- table node-view and wrapper references ---'
rg -n -C 6 'tableWrapper|table-widgets-container|contentDOM|tableHandles' packages/core/src/blocks packages/core/src/extensions/TableHandles --glob '*.ts' --glob '*.tsx'
printf '%s\n' '--- read-only containing-block verifier ---'
python3 - <<'PY'
from pathlib import Path
import re

css = Path("packages/core/src/editor/editor.css").read_text()
drag = Path("packages/core/src/extensions/TableHandles/dragDecorations.ts").read_text()

cell_rule = re.search(
    r'\[data-content-type="table"\]\s+(?:th|td)[^{]*,\s*'
    r'\[data-content-type="table"\]\s+(?:th|td)\s*\{([^}]*)\}',
    css,
    re.S,
)
wrapper_rule = re.search(
    r'\.tableWrapper\s*\{([^}]*)\}',
    css,
    re.S,
)

assert cell_rule, "table cell rule not found"
assert not re.search(r'\bposition\s*:', cell_rule.group(1)), (
    "table cell rule unexpectedly defines position"
)
assert wrapper_rule and re.search(r'\bposition\s*:\s*relative\s*;', wrapper_rule.group(1)), (
    "tableWrapper is not positioned"
)
assert 'Decoration.widget(decorationPos' in drag
assert 'widget.style.left = "0"' in drag
assert 'widget.style.right = "0"' in drag
assert 'widget.style.top = "0"' in drag
assert 'widget.style.bottom = "0"' in drag

print("cell_position_rule=absent")
print("tableWrapper_position=relative")
print("drop_cursor_widget=inserted_at_cell_boundary_with_edge_offsets")
print("nearest_positioned_ancestor=tableWrapper")
PY

Repository: TypeCellOS/BlockNote

Length of output: 43622


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- table node-view construction ---'
sed -n '190,265p' packages/core/src/blocks/Table/block.ts
printf '%s\n' '--- corrected read-only containing-block verifier ---'
python3 - <<'PY'
from pathlib import Path
import re

css = Path("packages/core/src/editor/editor.css").read_text()
drag = Path("packages/core/src/extensions/TableHandles/dragDecorations.ts").read_text()
table = Path("packages/core/src/blocks/Table/block.ts").read_text()

cell_rules = re.findall(
    r'\[data-content-type="table"\]\s+(?:th|td)\s*\{([^}]*)\}',
    css,
    re.S,
)
assert cell_rules, "table cell rules not found"
assert all(not re.search(r'\bposition\s*:', body) for body in cell_rules), (
    "a table cell rule defines position"
)

wrapper_rule = re.search(r'\.tableWrapper\s*\{([^}]*)\}', css, re.S)
assert wrapper_rule and re.search(
    r'\bposition\s*:\s*relative\s*;', wrapper_rule.group(1)
), "tableWrapper is not positioned"

assert 'Decoration.widget(decorationPos' in drag
for declaration in (
    'widget.style.left = "0"',
    'widget.style.right = "0"',
    'widget.style.top = "0"',
    'widget.style.bottom = "0"',
):
    assert declaration in drag, declaration

assert 'tableWrapper.appendChild(tableWrapperInner)' in table
assert 'tableWrapper.appendChild(floatingContainer)' in table

print("cell_position_rule=absent")
print("tableWrapper_position=relative")
print("drop_cursor_widget=inserted_at_cell_boundary_with_edge_offsets")
print("tableWrapper_contains_table_and_floating_container")
print("nearest_positioned_ancestor_for_cell_widget=tableWrapper")
PY

Repository: TypeCellOS/BlockNote

Length of output: 3250


Set each table cell as the drop cursor’s containing block.

getTableDragDecorations inserts the cursor inside each target td/th, but those cells are not positioned. The nearest positioned ancestor is .tableWrapper, so the cursor can span the full table instead of the target cell. Add position: relative to the table cell rule.

🤖 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/editor.css` around lines 61 - 68, Add position:
relative to the table cell CSS rule used by the td/th elements targeted by
getTableDragDecorations, so each inserted .bn-table-drop-cursor is positioned
relative to its containing cell rather than .tableWrapper.

Comment on lines +60 to +99
let tableResolvedPos: ResolvedPos;
try {
tableResolvedPos = doc.resolve(tablePos + 1);
} catch {
return [];
}
if (tableResolvedPos.node().type.name !== "table") {
return [];
}

// Resolves the relative indices returned by `getCellsAtRowHandle` /
// `getCellsAtColumnHandle` to a position inside that cell.
const resolveCell = ({ row, col }: RelativeCellIndices) => {
// Gets each row in the table.
const rowResolvedPos = doc.resolve(tableResolvedPos.posAtIndex(row) + 1);

// Gets the cell within the row.
return doc.resolve(rowResolvedPos.posAtIndex(col) + 1);
};

const decorations: Decoration[] = [];

const draggedCells =
draggedCellOrientation === "row"
? getCellsAtRowHandle(block, originalIndex)
: getCellsAtColumnHandle(block, originalIndex);

draggedCells.forEach((cell) => {
const cellResolvedPos = resolveCell(cell);
const cellStart = cellResolvedPos.before();

decorations.push(
Decoration.node(cellStart, cellStart + cellResolvedPos.node().nodeSize, {
class:
draggedCellOrientation === "row"
? DRAG_SOURCE_ROW_CLASS
: DRAG_SOURCE_COL_CLASS,
}),
);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect tablePos assignment and block refresh in TableHandles.
rg -n -C 5 'tablePos' packages/core/src/extensions/TableHandles/TableHandles.ts
rg -n -C 3 'posAtIndex' packages/core/src

Repository: TypeCellOS/BlockNote

Length of output: 9465


🏁 Script executed:

#!/bin/bash
set -e
ast-grep outline packages/core/src/extensions/TableHandles/TableHandles.ts
ast-grep outline packages/core/src/extensions/TableHandles/dragDecorations.ts
printf '%s\n' '--- update and tablePos lifecycle ---'
sed -n '250,340p' packages/core/src/extensions/TableHandles/TableHandles.ts
sed -n '640,725p' packages/core/src/extensions/TableHandles/TableHandles.ts
sed -n '850,925p' packages/core/src/extensions/TableHandles/TableHandles.ts
printf '%s\n' '--- drag event handlers ---'
rg -n -C 8 'mousemove|dragstart|drag|update\\(' packages/core/src/extensions/TableHandles/TableHandles.ts
printf '%s\n' '--- decoration implementation ---'
cat -n packages/core/src/extensions/TableHandles/dragDecorations.ts

Repository: TypeCellOS/BlockNote

Length of output: 9657


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- TableHandlesView methods and fields ---'
sed -n '180,330p' packages/core/src/extensions/TableHandles/TableHandles.ts
printf '%s\n' '--- decoration callback and handlers ---'
sed -n '640,725p' packages/core/src/extensions/TableHandles/TableHandles.ts
sed -n '725,850p' packages/core/src/extensions/TableHandles/TableHandles.ts
printf '%s\n' '--- selection path ---'
sed -n '875,925p' packages/core/src/extensions/TableHandles/TableHandles.ts
printf '%s\n' '--- event references ---'
rg -n -C 6 -e 'mousemove' -e 'dragstart' -e 'dragover' -e 'drop' -e 'update\\(' packages/core/src/extensions/TableHandles/TableHandles.ts
printf '%s\n' '--- decoration source ---'
sed -n '1,150p' packages/core/src/extensions/TableHandles/dragDecorations.ts

Repository: TypeCellOS/BlockNote

Length of output: 12239


🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- remaining mouseMoveHandler and update method ---'
sed -n '330,640p' packages/core/src/extensions/TableHandles/TableHandles.ts
printf '%s\n' '--- decoration source ---'
cat -n packages/core/src/extensions/TableHandles/dragDecorations.ts
printf '%s\n' '--- event references ---'
rg -n -C 5 'mousemove|dragstart|dragover|drop|update' packages/core/src/extensions/TableHandles/TableHandles.ts

Repository: TypeCellOS/BlockNote

Length of output: 24714


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- table helper implementations ---'
rg -n -C 12 'function getCellsAtRowHandle|function getCellsAtColumnHandle|getCellsAtRowHandle|getCellsAtColumnHandle' packages/core/src/api/blockManipulation/tables
printf '%s\n' '--- table helper file map ---'
fd -t f . packages/core/src/api/blockManipulation/tables
printf '%s\n' '--- positional API references ---'
rg -n -C 8 'posAtIndex\\(' packages/core/src packages/core/package.json pnpm-lock.yaml 2>/dev/null || true
printf '%s\n' '--- relevant types and dimensions ---'
rg -n -C 8 'type RelativeCellIndices|getDimensionsOfTable|canRowBeDraggedInto|canColumnBeDraggedInto' packages/core/src/api/blockManipulation/tables packages/core/src/extensions/TableHandles
printf '%s\n' '--- read-only behavioral model ---'
python3 - <<'PY'
class RangeError(Exception):
    pass

def pos_at_index(child_sizes, index):
    pos = 0
    for i in range(index):
        if i >= len(child_sizes):
            raise RangeError(f"Index {i} out of range")
        pos += child_sizes[i]
    return pos

cases = [
    ("valid row and cell", [[10, 10], [10, 10]], 0, 1),
    ("row index equal to row count", [[10, 10], [10, 10]], 2, 0),
    ("column index equal to cell count", [[10, 10], [10, 10]], 0, 2),
]
for name, rows, row, col in cases:
    try:
        row_offset = pos_at_index([sum(r) for r in rows], row)
        cell_offset = pos_at_index(rows[row], col)
        print(name, "would resolve", row_offset, cell_offset)
    except (IndexError, RangeError) as exc:
        print(name, "throws", type(exc).__name__, str(exc))
PY

Repository: TypeCellOS/BlockNote

Length of output: 50378


🌐 Web query:

ProseMirror prosemirror-model ResolvedPos.posAtIndex implementation out of range child index

💡 Result:

In ProseMirror, the ResolvedPos.posAtIndex(index, depth) method is designed to calculate the absolute position at a specific child index within a parent node at a given depth [1][2]. The implementation of posAtIndex does not explicitly validate whether the provided index is within the valid range (0 to childCount) of the target node, which can lead to unexpected results or logic errors if an out-of-range index is provided [1]. Key implementation details: 1. Depth Resolution: The method resolves the target depth using this.resolveDepth(depth), defaulting to the current position's depth if none is provided [1]. 2. Path Traversal: It retrieves the relevant ancestor node and starting position from the ResolvedPos path [1]. 3. Calculation: It iterates through the children of the target node, summing their nodeSizes up to the specified index to determine the absolute position [1]. If you provide an index that exceeds the childCount of the target node, the loop will simply sum the sizes of all available children and return the end position of that node [1]. If you provide a negative index, the loop will not execute, and the function will return the starting position of the node [1]. Users have previously noted confusion regarding the exact position returned (e.g., whether it points to the start of the child or elsewhere), but the method is intended to provide the position of the child at that index [3]. If you are encountering issues, ensure you are providing a valid index (0 <= index <= node.childCount) and checking the depth parameter to ensure you are targeting the expected ancestor level [1][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- exact row and column helper bodies ---'
sed -n '430,575p' packages/core/src/api/blockManipulation/tables/tables.ts
printf '%s\n' '--- dimensions implementation ---'
sed -n '324,365p' packages/core/src/api/blockManipulation/tables/tables.ts
printf '%s\n' '--- returned index construction ---'
sed -n '90,180p' packages/core/src/api/blockManipulation/tables/tables.ts

Repository: TypeCellOS/BlockNote

Length of output: 8514


🏁 Script executed:

#!/bin/bash
set -e
tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT
curl -fsSL 'https://raw.githubusercontent.com/ProseMirror/prosemirror-model/06a044abfe64d57e0d1c8c1a5a7a914386530b84/src/resolvedpos.ts' >"$tmp"
rg -n -C 8 'posAtIndex|child\\(index|maybeChild' "$tmp"

python3 - <<'PY'
class RangeError(Exception):
    pass

def child(children, index):
    if index < 0 or index >= len(children):
        raise RangeError("Index out of range")
    return children[index]

def pos_at_index(children, index):
    pos = 0
    for i in range(index):
        pos += child(children, i)["nodeSize"]
    return pos

rows = [{"nodeSize": 10}, {"nodeSize": 12}]
cells = [{"nodeSize": 4}, {"nodeSize": 6}]

for label, children, index in [
    ("valid child", rows, 1),
    ("first position after last child", rows, len(rows)),
    ("past last child", rows, len(rows) + 1),
    ("cell index at childCount", cells, len(cells)),
]:
    try:
        print(label, "=>", pos_at_index(children, index))
    except RangeError as exc:
        print(label, "=> throws:", exc)

print("strict row bound:", 1 < len(rows))
print("strict cell bound:", 2 < len(cells))
PY

Repository: TypeCellOS/BlockNote

Length of output: 209


🏁 Script executed:

#!/bin/bash
tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT
if curl -fsSL 'https://raw.githubusercontent.com/ProseMirror/prosemirror-model/06a044abfe64d57e0d1c8c1a5a7a914386530b84/src/resolvedpos.js' >"$tmp"; then
  rg -n -C 8 'posAtIndex|child\\(index|maybeChild' "$tmp"
else
  echo 'Could not fetch the cited ProseMirror source path'
fi

python3 - <<'PY'
class RangeError(Exception):
    pass

def child(children, index):
    if index < 0 or index >= len(children):
        raise RangeError("Index out of range")
    return children[index]

def pos_at_index(children, index):
    pos = 0
    for i in range(index):
        pos += child(children, i)["nodeSize"]
    return pos

rows = [{"nodeSize": 10}, {"nodeSize": 12}]
cells = [{"nodeSize": 4}, {"nodeSize": 6}]

for label, children, index in [
    ("valid child", rows, 1),
    ("first position after last child", rows, len(rows)),
    ("past last child", rows, len(rows) + 1),
    ("cell index at childCount", cells, len(cells)),
]:
    try:
        print(label, "=>", pos_at_index(children, index))
    except RangeError as exc:
        print(label, "=> throws:", exc)

print("strict row bound:", 1 < len(rows))
print("strict cell bound:", 2 < len(cells))
PY

Repository: TypeCellOS/BlockNote

Length of output: 434


Remap tablePos and guard cell resolution during drag.

TableHandlesView.update() refreshes state.block but not tablePos, and native drag events do not trigger mousemove. A transaction can therefore leave tablePos stale while the cell helpers use the current block. Remap tablePos through transactions, and skip cells whose row or column index is outside the resolved node’s strict bounds. Protect both decoration loops from failed cell resolution because posAtIndex can throw or return a parent-end position.

🤖 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/TableHandles/dragDecorations.ts` around lines 60
- 99, Update TableHandlesView.update() to remap tablePos through each
transaction before using the refreshed block, then make resolveCell validate row
and column indices against the resolved table and row node bounds. Return no
cell when resolution fails, posAtIndex throws, or resolves to a parent-end
position, and guard both decoration paths so invalid cells are skipped instead
of producing decorations.

Source: Learnings

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.

2 participants