fix(editor): persist a dragged table column width as a sidecar marker - #1949
Merged
Conversation
… markdown Red on both serializers. A table whose first column was dragged wider serializes to plain GFM, and the width is gone the next time the note is parsed. The same cases also pin the two ways a marker can disagree with the table it sits above, a column added and a column removed, so the degrade path is fixed before the marker exists.
A fourth entry in the marker registry, so both serializers pick it up with no change of their own. `extractTableLayout` writes only a table somebody has resized, which keeps every table already in a vault byte identical, and `applyTableLayout` reconciles against the parsed table's own column count so a marker that no longer fits pads or truncates instead of corrupting the note. BlockNote's table content has no row height, so column widths are the whole of what there is to store.
… vault Two corpus cases, one table carrying a width and one carrying cell colours as well, which pins the on-disk order of the two table markers on both pipelines at once. The golden vault fixture holds the same pair next to an untouched table, so a regression that starts writing a marker for every table fails on bytes.
Drives the width through the live editor, waits for the marker to land in the vault file, then reopens the note and relaunches the app. The last assertion measures the rendered header cell, so a width that reads back into the block model but never reaches the layout still fails.
Records the on-disk shape, the registry order against the cell colour marker, the pad and truncate rule, and that row heights are absent because the editor has no row height to store.
`editor:check` compares the packed bundle against a stamp over its sources, and the shared marker registry is one of them.
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
h4yfans
marked this pull request as ready for review
September 2, 2026 11:09
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1936. Part of #1934.
Why
Resizing a table column works, and nothing carries the result out of the session. BlockNote keeps a dragged width in the table content's
columnWidths, GFM has no syntax for it, andblocksToMarkdownLossywrites| a | b |, so the width is gone the next time the note is parsed. Markdown is the storage, so the width needs somewhere on disk to live.It goes next to the table as a comment line, the same shape the cell colour marker already uses. #1937 turned that pattern into a registry in
packages/shared/src/block-markers.ts, and both serializers read and write through it, so this is a fourth registry entry rather than a fourth pair of code paths. Living in the note body also means the width syncs with the note, with no new synced type and no device-local state to go stale.Scope
packages/shared/src/block-markers.tsgainsTableLayout,extractTableLayout,serializeTableLayoutMarker,parseTableLayoutMarkerandapplyTableLayout, plus the registry entry that puts the marker aftertable-colorsin on-disk order.One slot per column. A column nobody has resized is
null, and a table whose slots are all null writes no marker, so a table already in a vault keeps its bytes.On read the parsed table's own column count wins, not the marker's. A marker naming fewer columns is padded with the widths BlockNote chose, one naming more is truncated. A
columnWidthsthat is not an array of positive finite numbers or nulls is not claimed as a marker at all and stays a foreign comment.Row heights are out, and not by omission. BlockNote's table content holds
columnWidths,headerRowsandheaderCols, and nothing about row sizes, so there is no row height in the block model to persist. The issue title names both; only the column half exists to fix.Tradeoffs
The width is visible to anyone reading the raw
.md. The alternative, a per-note view-state record beside the note, keeps the file clean but makes the width device-local until it becomes a synced type, and leaves it to go stale when the table is edited. The marker travels with the note for free and reconciles against the table it sits above.An older app version reading a marked file drops the comment on its next save and shows the table at default widths, which is the same degrade the colour and alignment markers already have.
Blast Radius
Both serializers, for tables only. A table nobody has resized writes no marker, so no note in any existing vault changes bytes on its next save; the golden vault fixture holds an untouched table next to a marked one to keep that true. The registry entry is additive, so the colour and alignment markers keep their bytes and their order.
apps/mobile/src/editor/generated/editor-web-asset.tsis a rebuild, not an edit.editor:checkstamps the packed bundle against its sources and the shared registry is one of them.Verification
pnpm --filter @memry/desktop test:mainonblocknote-converter.test.ts, 6 of the 6 new#1936cases green. With the registry entry removed by hand, 5 of the 6 go red and the "leaves a table nobody has resized byte-identical" guard stays green, which is the shape the fix should have.Round-trip conformance and the golden vault on both pipelines:
blocknote-converter.roundtrip.test.tsandbyte-preservation.golden.test.ts131 passed, renderermarkdown-utils.test.tsandroundtrip-conformance.test.ts114 passed with the 2 pre-existing expected failures.--project shared2764 passed.E2E
table-column-widths-persist.e2e.tsgreen in 19.5s against a production build: drag the width, wait for the marker to reach the vault file, switch to another note and back, relaunch the app, then measure the rendered header cell so a width that reaches the block model but never reaches the layout still fails. This is the one check here that walks the whole user-facing path.pnpm typecheckandpnpm lintclean.pnpm docs:impact --base origin/main --strictreports covered,pnpm docs:buildbuilds.