You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I think I have the exact gesture, and it is worth separating from the version it landed in — this predates v0.9.0, so the tabs merge is not the cause.
The gesture is a triple-click, not a double-click
There are two selection states in a cell, and they behave differently:
gesture
selection
result of typing
double-click
"Test" — end = #text:4
replaces the cell text, table intact
triple-click
"Test\n" — end = P:0
cell boundary destroyed
The triple-click selection includes a trailing newline and its end anchor sits outside the text node, in the following block. Replacing it therefore eats the cell separator along with the text.
Starting from:
| Test | Test-2 || --- | --- || Test-3 | Test-4 |
triple-click Test, type Wrong:
before: [["Test","Test-2"],["Test-3","Test-4"]]
after : [["WrongTest-2"],["Test-3","Test-4"]] cell count changed
Row 1 is left with a single cell while row 2 still has two — a malformed table, which is also why undo cannot put it back cleanly. The original report is the same thing seen from the last column, where the next cell in document order is the next row's first column, exactly as described.
It is not from the tabs merge
Same script, same build process, against v0.8.1 — byte-identical:
The issue was filed about an hour after v0.9.0 shipped, so the tabs merge is an understandable first suspect — it is not this. For what it is worth, the tabs PR touched no kernel files at all, and the only kernel lines in any of my merged PRs are the two matchesLetterKey shortcut tests.
Suggested framing
The selection endpoint is the thing: a triple-click inside a cell resolves to a range whose end is a block boundary rather than the cell's text end, and the replace path then removes the boundary. Anchoring the replacement to the cell's own text extent would leave the grid intact.
Measured in WebKit via Playwright against the built static export; only macOS was exercised.
Unrelated but found alongside it: ⌘A with the caret in a table selects everything except the table when the table is the document's first block. That one is new in v0.9.0 — filed separately as #43 so it does not get tangled with this.
fcfbfed fixes the reported gesture — I re-measured it on WebKit and a triple-click now selects "Test" with end = #text:4, replacing the cell cleanly with the grid intact.
The corruption underneath it is still reachable from the keyboard, though. Double-click a word in a cell, extend with Shift+Right or Shift+Down, type:
Same outcome as the original report: row one keeps a single cell while row two still has two.
Not a regression from the fix — it behaves identically on v0.9.0 and v0.8.1. The triple-click was one entry point to it; the boundary-crossing replace is the thing itself, and handleMouseDown_ can only close the mouse route.
Opened #44 with a fix at the edit layer: applySelectEdit_ clamps a cross-cell selection to the cell it starts in, so the | separator is never inside the replaced span. Paragraph merges are untouched — between paragraphs that merge is the correct outcome, which is why the guard is scoped to cells.
It ships scripts/verify-table-cell-guard, and the part worth mentioning: with the clamp disabled 5 of its 9 assertions fail, including the exact | WrongTest-2 | collapse. The harness sets the cursor pair directly rather than through setSelection, because setSelection refuses a range straddling the separator (applied:false, reason:"invalid") — which is precisely why only a DOM selection could ever reach this, and why it never showed up in a headless suite.
Activity
Systemofdumb commentedon Sep 14, 2026
I think I have the exact gesture, and it is worth separating from the version it landed in — this predates v0.9.0, so the tabs merge is not the cause.
The gesture is a triple-click, not a double-click
There are two selection states in a cell, and they behave differently:
"Test"—end = #text:4"Test\n"—end = P:0The triple-click selection includes a trailing newline and its end anchor sits outside the text node, in the following block. Replacing it therefore eats the cell separator along with the text.
Starting from:
triple-click
Test, typeWrong:Row 1 is left with a single cell while row 2 still has two — a malformed table, which is also why undo cannot put it back cleanly. The original report is the same thing seen from the last column, where the next cell in document order is the next row's first column, exactly as described.
It is not from the tabs merge
Same script, same build process, against v0.8.1 — byte-identical:
The issue was filed about an hour after v0.9.0 shipped, so the tabs merge is an understandable first suspect — it is not this. For what it is worth, the tabs PR touched no kernel files at all, and the only kernel lines in any of my merged PRs are the two
matchesLetterKeyshortcut tests.Suggested framing
The selection endpoint is the thing: a triple-click inside a cell resolves to a range whose end is a block boundary rather than the cell's text end, and the replace path then removes the boundary. Anchoring the replacement to the cell's own text extent would leave the grid intact.
Measured in WebKit via Playwright against the built static export; only macOS was exercised.
Unrelated but found alongside it:
⌘Awith the caret in a table selects everything except the table when the table is the document's first block. That one is new in v0.9.0 — filed separately as #43 so it does not get tangled with this.fix(selection): keep triple-click selection inside one block (#42)
Systemofdumb commentedon Sep 16, 2026
fcfbfedfixes the reported gesture — I re-measured it on WebKit and a triple-click now selects"Test"withend = #text:4, replacing the cell cleanly with the grid intact.The corruption underneath it is still reachable from the keyboard, though. Double-click a word in a cell, extend with
Shift+RightorShift+Down, type:Same outcome as the original report: row one keeps a single cell while row two still has two.
Not a regression from the fix — it behaves identically on v0.9.0 and v0.8.1. The triple-click was one entry point to it; the boundary-crossing replace is the thing itself, and
handleMouseDown_can only close the mouse route.Opened #44 with a fix at the edit layer:
applySelectEdit_clamps a cross-cell selection to the cell it starts in, so the|separator is never inside the replaced span. Paragraph merges are untouched — between paragraphs that merge is the correct outcome, which is why the guard is scoped to cells.It ships
scripts/verify-table-cell-guard, and the part worth mentioning: with the clamp disabled 5 of its 9 assertions fail, including the exact| WrongTest-2 |collapse. The harness sets the cursor pair directly rather than throughsetSelection, becausesetSelectionrefuses a range straddling the separator (applied:false,reason:"invalid") — which is precisely why only a DOM selection could ever reach this, and why it never showed up in a headless suite.fix(selection): a selection edit may not delete a table cell boundary
fix(selection): a selection edit may not delete a table cell boundary (…