docs/solutions/logic-errors/2026-03-24-table-selection-helpers-must-track-row-transitions-and-cell-derived-table-paths.md
Direct table helper coverage exposed two quiet bugs in the selection layer.
getSelectionWidth(...) dropped the first cell when the selected area crossed into a new row.
getTableGridByRange(...) could derive the table path from the raw selection anchor path, which is too deep when the range lives on text leaves.
Both bugs were easy to miss because common single-row and simple range cases still looked fine.
getSelectionWidth(...) reset total to 0 on row change before continuing, so the first cell in the new row never got counted.
getTableGridByRange(...) used at.anchor.path.slice(0, -2) to find the table entry. That assumes the incoming range path shape already points at the cell layer. For real text selections, it often points deeper.
For width counting:
total to that span instead of 0For grid lookup:
These checks passed:
bun test packages/table/src
pnpm test:profile -- --top 20 packages/table/src
pnpm test:slowest -- --top 20 packages/table/src
pnpm turbo build --filter=./packages/table
pnpm turbo typecheck --filter=./packages/table
For table helpers, keep direct deterministic specs alongside transform coverage.
When selection code climbs through table structure, resolve from real node entries first. Raw Slate paths are shape-dependent and will happily lie to you.