Repository navigation
fix(ui): column widths ignore colspan rows and empty first renders (#258) - #268
Conversation
…rst renders (#258) makeColumnsResizable() credits full-width colspan rows (vscroll spacers, "No packets found") to column 0 and locks the widths from an empty or near-empty first body. The new vm-sandbox unit test runs the real app.js against a minimal fake table: colspan and short rows must not change any width, an (almost) empty body must be measured again once when rows arrive (and only then), saved widths must be applied as before. Red on master: 3 passed, 9 failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New test-issue-258-column-widths-e2e.js, registered after the Kpa-clawbot#1122 Details clamp E2E. It ages the fixture by moving the browser clock (20 and 120 min effective, offset from the newest packet), checks that the default window renders empty, then widens the window at 1200 and 900 px: Details >= 15% and expand <= 10% of the table, advert names show text (the long pinned name at least half), widths stay through later renders, saved widths apply unchanged and a resize handle still resizes and saves. Red on master: 9 passed, 9 failed. test-issue-1122-details-row-clamp-e2e.js: the pinned long advert name must be cut by its clip only on mobile (375 px). At 900/1200 px Details is sized from the real rows after the fix, so the name must show at least half of itself instead (finding 1 of the #252 review). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
) makeColumnsResizable() measured each column by row index, so a full-width colspan row (vscroll spacer, "No packets found") was credited to column 0, and it measured only once, from whatever the first render had. On packets with an aged fixture the default 15-min window is empty: expand got ~43% of the table and Details 65 px at 1200 px, hiding long advert names behind the #67 clamp. - columnMeasureRows() keeps only rows with one cell per header cell and no colspan. - With fewer than 5 usable rows the widths are provisional; one MutationObserver (childList on the tbody) measures again once real rows arrive, unless widths have been saved by then, and disconnects. A table measured from a full body gets no observer. - Saved widths are read by readSavedColumnWidths() and applied as before. - The measure and fit steps are extracted unchanged (measureColumnWidths, fitColumnWidths, applyMeasuredColumnWidths); the re-measure clears the old th widths first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…258) - A row with as many cells as the header, one of them a colspan, is not measured either. - A width the page's markup puts on a th (observers' compare column has style="width:32px") counts in the first measure, as on master, and again in the re-measure. Red on the previous commit, which cleared it before measuring and changed the observers table's widths slightly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first measure no longer clears the header cells' inline widths, so tables measured from a full body (nodes, observers, analytics, new-nodes) get exactly master's widths. The re-measure restores the widths the page's markup set before measuring, instead of the provisional percentages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- E2E: after the deferred re-measure, every column's share must equal (within 1 point) that of a page opened with the wide window right away. Red on the previous commit: at 900 px TableResponsive had hidden five header cells but not yet the new rows' cells, so the re-measure saw a skewed table (HB 12.2% instead of 3.8%); at 1200 px the resize handles, which stick out 4px past their th, widened the narrow columns. - Unit: columns TableResponsive hid are measured inside TableResponsive.unhidden (stubbed here), and a shown handle's 4px overhang does not count. - test-packets.js: the real TableResponsive.unhidden lifts col-hidden and the pills only while its callback runs, also when it throws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TableResponsive.unhidden(table, fn) (packets.js) runs fn with its own column hiding lifted (no col-hidden, no pill) and restores it. measureColumnWidths() measures inside it, so the re-measure, which runs before TableResponsive has marked the new rows' cells, sees the same columns as the first measure (before register()). Other CSS that hides a column still applies. - The resize handles are hidden while measuring; they did not exist at the first measure and their overhang inflated th.scrollWidth. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review — CS-pve-agent2 PR#268 — head da436cdDom: APPROVE med nits Independent, read-only review. Evidence tags: [T] I ran it myself, [A] code analysis, [K] from CI logs or the PR description. Trees tested:
Findings
No blocking findings. Answers to the review points1. Colspan rows not measured. Yes. [A] [T]
2. No locking from an empty or near-empty first render. Yes. [A] [T]
3. Saved widths and handles; the other callers. Unchanged. [T] [A]
4.
5. Column widths before and after. [T] Packets, local Go server with the CI-prepared fixture. Each cell gives the first render, then → after widening the window (24 h at 1200 px, 3 h at 900 px). "Pinned" is the visible/total px of
6. Mutant: colspan rows back in the measurement. Red. [T]
Always-checks
Tests runAll on the tree merged with
Not verified
|
#268 (issue #258, column widths) and this branch both appended a line to test-all.sh's runner list. Kept both, in issue order next to #254: run test-issue-258-column-widths.js run test-issue-259-nodes-esc-listener.js Everything else auto-merged and was verified to be the exact union of both sides: .github/workflows/deploy.yml keeps the #258 and #259 E2E lines, public/packets.js keeps #258's TableResponsive.unhidden() and #259's groupIsExpandedInView(), and test-packets.js keeps both sets of cases. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Relates to #258
Follow-up to #252 (#244). It addresses finding 1 and the "UX remainder" of the #252 review. Plan: comment on #258.
Problem
makeColumnsResizable()(public/app.js) sizes a table's columns from the header and the first body rows. Two things went wrong:On packets with an aged fixture, the default 15-min window is empty. At 1200 px the expand column got 43 % of the table and Details 65 px. Long advert names then sat entirely on the line the #67 clamp hides, and only the icon showed. Even with a fresh fixture, expand took 176 px for a chevron.
Fix
columnMeasureRows()keeps only rows with one cell per header cell and no colspan.MutationObserver(childListon the tbody) measures again once real rows arrive, then disconnects.TableResponsive.unhidden(table, fn)(packets.js) runs the measurement with its own column hiding lifted. Its tbody observer runs after ours, so the new rows' cells are not yet marked; without this, at 900 px HB came out at 12.2 % instead of 3.8 %.th.thare restored, for example observers'style="width:32px".meshcore-*-col-widths) are applied exactly as before, and the handles are unchanged.measureColumnWidths,fitColumnWidths,applyMeasuredColumnWidths,readSavedColumnWidths). The first measure is the same as master's except that colspan rows are skipped.Perf
childListmutation runs a guard:isConnected;localStorageread for the storage key;tbody.rows.length;unhidden: twoquerySelectorAllcalls and a class toggle on the hidden cells, only during a measurement.Tests
Test-first: each fix commit follows a red test commit.
test-issue-258-column-widths.js(new, intest-all.sh) runs the realapp.jsin a vm sandbox with a fake table DOM. Its cells report content width, assigned width and handle overhang. It covers:thwidth set by the page;unhidden;15 cases, 3/15 on master.
test-packets.js: the realTableResponsive.unhiddenlifts and restores, also when its callback throws. Both new cases are red on master.test-issue-258-column-widths-e2e.js(new, indeploy.ymlafter the bug(packets): filter UX disaster — help panel overlaps table, toolbar chaotic, path chips spill rows Kpa-clawbot/CoreScope#1122 Details clamp E2E). It ages the fixture by moving the browser clock to an effective 20 and 120 min, with the offset taken from the newest packet. At 1200 and 900 px it checks:It also checks that saved widths apply unchanged and that a handle drag resizes and saves. 10/22 on master (12 red).
test-issue-1122-details-row-clamp-e2e.js:Gates
scripts/check-xss-sinks.sh --diff origin/masteris clean.deploy.ymland 1 inrelease-fast-path.yml.deploy.yml.🤖 Generated with Claude Code