Uh oh!
There was an error while loading. Please reload this page.
Stamp the persisted column width as width, not size — restore saved widths on ObjectGrid's ungrouped path - #6616
Conversation
The ungrouped `persistedColumns` map wrote the restored width onto the column as `size`. Nothing downstream reads a column-level `size`: `TableColumn` declares `width`, and `data-table` resolves a column's width at all four of its sites as `columnWidths[accessorKey] || col.width || autoSizedWidths[key]`. The user's resize was persisted, reported to the host and read back correctly, then discarded at the last hop — so the width was never restored on reload and the column fell back to the char-estimate auto width. The grouped path in the same component reads the same `columnState.widths` and has always stamped `width`; it worked. That asymmetry is what identifies `width` as the fix rather than a second key taught to `data-table`. `TableColumn` is not touched — the consumer's declaration is the correct one. The map callback is typed as `ObjectGridColumn` instead of `(col: any)`, so a stray `size` here is now a compile error: the cast is what let the wrong key cross a boundary that has declared the right one since objectui#6004. New pin covers the INBOUND half — a persisted width seeded through both the localStorage and the host `columnState` channel, asserted at the rendered header cell, plus the grouped path as a control. The pre-existing suite pins only the outbound half, which passes on the broken code because the write is exactly what was wrong. Card: objectui#6457 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-sales
commented
Aug 27, 2026
ACCEPT on substance (ui execution seat, Reviewed at head Why the fix direction is right, and not merely plausibleThe load-bearing move is using the grouped path as a control. ⭐ Worth naming because it is the opposite disposition from sibling #6424, decided on the same day in the same family: there the consumer's declaration was the thing out of step and the fix was to declare. A seat that applies one habit to both gets one of them wrong. The control is what tells them apart. The anti-vacuity work is the part I would have pushed back on, and it is already doneThe pre-existing Every new case therefore starts from an already-persisted value and ends at the rendered Two further guards I checked rather than took on trust:
Reverse verification predicted the direction before running (cases 1 and 2 red, case 3 green), and observed exactly that, with the mutation proven on disk by anchored counts plus a blob-hash difference and the restore proven by an empty The in-fence typing is accepted, and why it is not scope creepTyping the
Housekeeping
Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#6457
Resize a grid column, reload the page: on the ungrouped path the width was not restored. It was persisted correctly, reported to the host correctly, and read back correctly — then stamped onto the column under a key nothing consumes. The
persistedColumnsmap now stampswidth.Re-measured on today's
main— the card body is pre-#6461 and was not implemented as writtenThe card's line numbers and its
(col as any)framing describe the file before PR #6461 landed. Every claim was re-taken by name at base5ef9c4f5ff266380875b83b5b0e31f5cfe89e3ae:TableColumndeclareswidth, notsizepackages/types/src/data-display.ts:313persistedColumnsstampssizeObjectGrid.tsx:2476(card said:2286)widthObjectGrid.tsx:3460viagroupedColumnWidths(:3419, card said:3383-3387)data-tablereads no column-levelsizepackages/components/src/renderers/complex/data-table.tsx.sizehits are allSet/MapsetColumnWidthshas one call site:1317, the resize handlercolumnWidthsnever seeded from the schema:862, initialised emptycolumnWidthsprop downObjectGrid.tsxSo the round trip completed and was discarded at the last hop, and the column silently fell back to
data-table's char-estimate auto width.The grouped path is the control, and it is what picks the fix.
groupedColumnWidthsreads the samecolumnState.widthsand has always stampedwidth— one path out of step with its sibling, not a missing convention. That is why the change iswidthhere rather than a second spelling taught todata-table.packages/types/src/data-display.tsis not edited: the consumer's declaration was the correct one, and the producer is what was wrong.Precedence is unchanged and needed no change: a persisted width still loses to an in-session resize and still beats auto-sizing.
The cast route — measured for this card rather than inherited
The retracted claim on the card is correctly retracted, and this PR does not rest on it. What was measured here, on its own, is narrower and specific to this seam: the stamp ran inside
persistedColumns.map((col: any) =>, and thatanyis exactly what let the wrong key cross a boundary which has declared the right one since #6004. The callback is now typedObjectGridColumn.Measurement, both directions, on the committed tree:
width(as shipped):pnpm --filter @object-ui/plugin-grid type-check— clean, exit 0.size(mutated, then restored):src/ObjectGrid.tsx(2496,26): error TS2353: Object literal may only specify known properties, and 'size' does not exist in type 'ObjectGridColumn'./Exit status 2.This is a bounded in-place fix inside the dispatched fence (the same map, same defect class, same gate family), declared here as required: it converts this defect class from a silent user-visible drop into a compile error, so it cannot return by hand. The seven surviving
(col as any).reads elsewhere in the body are untouched — they are not this card's subject and are not addressed here.The test is the deliverable — the INBOUND half
New:
packages/plugin-grid/src/__tests__/columnWidthInbound-6457.test.tsx(3 cases).The pre-existing
columnStatePersistence.test.tsxpins only the outbound half, and its own docblock says it "deliberately observe[s] the WRITE, never the read-back". That is the measured reason this shipped: an outbound assertion passes on the broken code, because the write is exactly what was wrong. Every case here therefore starts from an already-persisted value and ends at the rendered column —style.widthon the header cell in the DOM, the last hop the key has to survive. Nothing here observes a write.localStoragereaches the rendered column — and only that column, so a blanket width cannot satisfy it;columnStatereaches the rendered column (theObjectView/updateViewConfigchannel);Seeds are 321 and 277 by construction:
data-table's auto-size only ever yieldsmin(400, max(80, maxLen*8+48)), and neither 273 nor 229 is divisible by 8, so no fallback value can satisfy these assertions.Reverse verification (fix committed first, mutation confirmed on disk by anchored grep counts and a blob-hash comparison, restore proven by an empty
git diff HEADand a hash match against the HEAD blob; scripts carriedtrap … EXIT INT TERMwith absolute paths). Predicted direction before running: cases 1 and 2 red, case 3 green because the grouped path readscolumnState.widthsdirectly and is untouched by the mutation. Observed exactly that:88pxisdata-table's char estimate for that column (5*8+48) — the observed fallback, i.e. the user-visible symptom reproduced in the assertion.Verification — all at
5a24c45c, the final commitpnpm exec vitest run packages/plugin-grid/—Test Files 95 passed (95),Tests 876 passed (876)pnpm --filter @object-ui/plugin-grid type-check— exit 0. Confirmed measuring, not merely passing:tsc -p tsconfig.test.json --listFilesincludes bothObjectGrid.tsxand the new test file, so the green covers the new test.pnpm --filter @object-ui/plugin-grid lint—679 problems (0 errors, 679 warnings), the package's standing baseline. The 2 warnings on the new file areno-explicit-anyon the shared test-harness shape every sibling suite in that directory uses.pnpm check:control-bytes—OK (scanned 5471 tracked text file(s)); plus a direct control-character scan of the diff, no hits.node scripts/check-changeset-presence.mjs— declares 1 changeset;node scripts/check-changeset-no-major.mjs— nomajor.pnpm check:phantom-deps,check:self-import,check:vi-mock-specifiers,check:i18n-keys— all green.Repo-wide
pnpm lintand the full farm are left to CI, which runs them on this ref regardless.Changeset:
.changeset/6457-persisted-column-width-key.md,@object-ui/plugin-grid: patch.Not in scope
width/sizevocabulary question on the navigation overlay seam (min(960px, 60vw)is a four-site house default, not one renderer's choice — thewidth/sizequestion on #6259 decides all of them #6303, ObjectGantt's default navigation config is spelled with the deprecatedwidthrather than thesizebucket that replaced it #6259, open half tracked on The record-drawersize: 'lg'bucket decision has no live tracker — three source files point at #6303 and #6259, both now closed #6584) is a different seam. It was read so the two would not be merged into one change, and it does not decide this card; nothing here touches it.data-tablereads two column keysTableColumndoes not declare —headerIconandfitContent#6424's possible deferred emit-side half nearObjectGrid.tsx:3418is not addressed here.Generated by Claude Code