Repository navigation
Description
Found while fixing #244 (PR #252); the reviewer confirmed it there. The default column widths of resizable tables come from a one-shot measurement that can go wrong in two ways. On the packets table, the Details column then becomes so narrow that some advert names are entirely on the hidden, clamped second line, so only the icon shows.
Cause
makeColumnsResizable() in public/app.js (master 310c501a, around line 2254):
- It measures only once. It sets
table.dataset.resizable = '1'and sizes the columns from the first ≤ 30 body rows present at that moment. If the first render is empty or nearly empty, the widths come from the header alone and stay that way. On packets, that happens when the default 15-minute window has few rows. - Colspan rows are credited to column 0. The measurement reads
row.children[i]per column index. A row with one full-widthcolspancell, such as a spacer, empty-state or group-detail row, therefore puts its whole content width into the first column. The extra width then stays with that column (expandon packets).
Observed in #252 at 1200 px (reported by the author and the reviewer, [T] there):
| expand | Details | |
|---|---|---|
| fresh fixture (first render has 29 rows) | 15 % | 15 % (178 px) |
| aged fixture (empty first window) | 43.5 % | 5.4 % (64 px) |
When colspan rows are skipped, the review measured Details at 301 px with the fresh fixture. CSS can't fix it: width: 32px !important on th.col-expand had no effect, because Chrome's auto layout still gives the leftover width to that column.
Saved widths in localStorage (meshcore-pkt-col-widths etc.) bypass the measurement. Users who resized a column once keep their own widths.
Expected
- Skip rows with a
colspancell, or any row whererow.children.length !== ths.length, when measuring. - Do not lock widths from an empty or near-empty body. Measure again once real rows exist, but at most once and only while the user has no saved widths, so there is no per-render cost.
- Keep the behaviour for the other callers:
nodes.js,observers.js,analytics.js,gps-sanity.js,new-nodes.js,node-changes.jsandposition-gaps.js. - Update the
pinned.truncatedguard intest-issue-1122-details-row-clamp-e2e.jsin the same change. The test(packets): make the Details advert-link E2E independent of fixture age (#244) #252 review notes that it assumes today's narrow Details column and will fail once the widths are fixed.
Tests
- A unit test (vm context) with a colspan row and with an empty first body.
- An E2E on packets at 1200 and 900 px with an aged fixture, which checks the Details width and that long advert names show text, not only the icon.
- A mutant check: put the colspan rows back into the measurement, and the test must fail.
Perf: the measurement runs once per table per page load (at most twice with the re-measure). There is no per-row or per-render work.
Activity
adminopenclaw8-sketch commentedon Oct 5, 2026
Plan — CS-Macmini #258
Branch
codex/issue-258-column-widthsfromorigin/master3bb2cb89. Tests first (red), then the fix. One draft PR, "Relates to #258".1. Skip colspan rows (
public/app.js)columnMeasureRows(tbody, colCount, limit), returns at most 30 body rows that have exactly one cell per header cell and no cell withcolSpan > 1.makeColumnsResizable()measures only those rows plus the header.2. Do not lock widths from an (almost) empty body
COL_MEASURE_MIN_ROWS = 5usable rows.MutationObserveron thattbody(childListonly) then waits for real rows.tbody.rows.lengthis below the threshold, it returns.thwidths, so the old percentages do not feed back.3. Saved widths
meshcore-*-col-widthsentry is applied exactly as today, with no measurement and no observer.onUpsaves the widths as today, and the observer then disconnects without re-measuring.4. The 8 callers
table-layoutand fitting logic. I'll extract the fit step into a pure helper without changing what it does.5.
test-issue-1122-details-row-clamp-e2e.js6. Tests and mutants
test-issue-258-column-widths.js(vm context on the realapp.js, with a minimal fake table DOM; registered intest-all.sh):test-issue-258-column-widths-e2e.js(registered indeploy.ymlafter the bug(packets): filter UX disaster — help panel overlaps table, toolbar chaotic, path chips spill rows Kpa-clawbot/CoreScope#1122 Details clamp line):page.clock, so that the fixture's effective age is 20 and 120 min. The offset is computed from the newest packet, so the CI age does not matter.sh test-all.shandnode test-frontend-helpers.js;e2e-fixture.dbprepared as in CI, fresh and aged (DB shifted 20 and 120 min), and with the instrumented frontend if possible.Gates
scripts/check-xss-sinks.sh --diff origin/masterclean.deploy.yml.Customizer
COL_MEASURE_MIN_ROWS(5) is an internal layout heuristic, not a user setting, so it gets no customizer entry.Merge pull request #268 from dborup/codex/issue-258-column-widths
dborup commentedon Oct 6, 2026
Fixed by #268 (merged as
406f8481).Merge branch 'master' into codex/issue-259-followups-255