Repository navigation
makeColumnsResizable measures once and credits colspan rows to column 0 (packets Details column too narrow) #258
Copy link
Copy link
Closed
Labels
Description
Activity
- addedbugSomething isn't workingSomething isn't workingtype:bugSomething brokenSomething broken
on Oct 5, 2026 adminopenclaw8-sketch commented
on Oct 5, 2026 CollaboratorMore actionsPlan — 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)- A new helper,
columnMeasureRows(tbody, colCount, limit), returns at most 30 body rows that have exactly one cell per header cell and no cell withcolSpan > 1. - It skips the vscroll spacers, "No packets found", the group-detail rows and any row with a different cell count.
makeColumnsResizable()measures only those rows plus the header.
2. Do not lock widths from an (almost) empty body
- Threshold:
COL_MEASURE_MIN_ROWS = 5usable rows. - Below the threshold, the table gets provisional widths as today: the header plus the usable rows, with handles added. One
MutationObserveron thattbody(childListonly) then waits for real rows. - The callback is a cheap guard:
- if the table is gone or the storage key now holds saved widths, it disconnects;
- if
tbody.rows.lengthis below the threshold, it returns.
- Once enough usable rows exist, it disconnects first and then re-measures once. Before measuring it clears the
thwidths, so the old percentages do not feed back. - At or above the threshold on the first call, no observer is created. Today's path is unchanged, apart from the colspan skip.
- Cost: at most two measurements per table per page load. The guard runs only while the table is still provisional; after that there is no per-render or per-row work.
3. Saved widths
- A valid
meshcore-*-col-widthsentry is applied exactly as today, with no measurement and no observer. - If the user drags a handle while the table is provisional,
onUpsaves the widths as today, and the observer then disconnects without re-measuring. - The handles themselves are unchanged.
4. The 8 callers
- packets: the first render with the default 15-min window and an aged fixture has only colspan rows, so it gets the deferred measure.
- nodes, observers and gps-sanity, new-nodes, node-changes, position-gaps: they call after rendering rows. They change only when they render colspan rows or fewer than 5 rows first; then they also re-measure once later.
- analytics: small static tables never mutate, so the observer never fires and costs nothing. They keep today's widths.
- The measurement keeps today's
table-layoutand fitting logic. I'll extract the fit step into a pure helper without changing what it does. - Browser check: column widths of nodes, observers and one analytics table, master vs branch, with a fresh fixture.
5.
test-issue-1122-details-row-clamp-e2e.js- With the fix, Details at 1200 px is about 300 px, so the pinned 21-char name fits.
- Truncation is required only where it is real, at 375 px.
- At 900 and 1200 px the pinned long name must instead show its whole width. This is a stronger check, and it cannot pass vacuously, because the pinned row must still be on screen.
- I will base the exact rule on measured widths.
6. Tests and mutants
- Unit, new
test-issue-258-column-widths.js(vm context on the realapp.js, with a minimal fake table DOM; registered intest-all.sh):- a colspan spacer or empty-state row and a short group-detail row do not change any width;
- an empty first body is deferred and then re-measured once when rows arrive, after which the observer is disconnected;
- fewer than 5 rows are deferred;
- saved widths: no measure and no observer;
- saved widths that appear while the table is provisional: no re-measure;
- a full first body: no observer;
- idempotent re-call.
- E2E, new
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):- Setup: packets at 1200 and 900 px. The browser clock is moved forward with Playwright
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. - Empty first render: the default 15-min window renders with no data rows; this is asserted, so the test cannot pass vacuously.
- After switching to a wide window (24 h at 1200, 3 h at 900):
- Details has a minimum width;
- expand stays narrow;
- every on-screen advert link shows at least 12 px, and the long-name row shows its text, not only the icon;
- a further window change does not move the widths (only one re-measure);
- preset saved widths are applied unchanged.
- Setup: packets at 1200 and 900 px. The browser clock is moved forward with Playwright
- Mutants (at least 3):
- colspan rows back in the measurement;
- no deferral, i.e. today's one-shot behaviour;
- re-measure on every mutation (no disconnect);
- re-measure despite saved widths;
- the 1122 guard reverted.
- Suites:
sh test-all.shandnode test-frontend-helpers.js;- the bug(packets): filter UX disaster — help panel overlaps table, toolbar chaotic, path chips spill rows Kpa-clawbot/CoreScope#1122 Details clamp, bug(packets): layout chaos — path chips spill rows, +N popover overlaps, filter chips collide with toolbar, Saved dropdown bleeds through Kpa-clawbot/CoreScope#1128 layout and multi-viewport, test: 13 red orphan unit tests + 4 orphan E2E files left after #187; collapsed packet groups show an up-caret #189 and Follow-up to #248: Affinity Debug toggle broken + reversed carets; group-row aria-expanded on mobile #254 E2Es, plus the new one;
- all against a local Go server on
e2e-fixture.dbprepared as in CI, fresh and aged (DB shifted 20 and 120 min), and with the instrumented frontend if possible.
Gates
- No hardcoded colours.
scripts/check-xss-sinks.sh --diff origin/masterclean.- Fork guards stay 9 + 1.
- One new E2E line in
deploy.yml.
Customizer
COL_MEASURE_MIN_ROWS(5) is an internal layout heuristic, not a user setting, so it gets no customizer entry.- A new helper,
- added a commit that references this issue
on Oct 6, 2026 Fixed by #268 (merged as
406f8481).- added a commit that references this issue
on Oct 6, 2026
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()inpublic/app.js(master310c501a, around line 2254):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.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):
When colspan rows are skipped, the review measured Details at 301 px with the fresh fixture. CSS can't fix it:
width: 32px !importantonth.col-expandhad no effect, because Chrome's auto layout still gives the leftover width to that column.Saved widths in
localStorage(meshcore-pkt-col-widthsetc.) bypass the measurement. Users who resized a column once keep their own widths.Expected
colspancell, or any row whererow.children.length !== ths.length, when measuring.nodes.js,observers.js,analytics.js,gps-sanity.js,new-nodes.js,node-changes.jsandposition-gaps.js.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
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.