Skip to content

fix(ui): Affinity Debug toggle and mobile group-row aria-expanded (#254) - #255

Merged
dborup merged 3 commits into
masterfrom
codex/issue-254-affinity-toggle-mobile-aria
Oct 5, 2026
Merged

dborup merged 3 commits into
masterfrom
codex/issue-254-affinity-toggle-mobile-aria

Conversation

@dborup-agent

Copy link
Copy Markdown
Collaborator

Relates to #254

Follow-up to #248 (#189), findings F1 and F2 from its review. Plan: comment on #254.

1. Node page "Affinity Debug" toggle

Bug: the heading's inline onclick held the Phosphor sprite markup with unescaped double quotes. The browser cut the attribute short, the handler was a syntax error, and the body never opened. The carets were also reversed against the #189 convention (caret-down collapsed, caret-up open).

Fix (public/nodes.js, public/style.css):

  • The card comes from renderAffinityDebugCard(). The heading holds a <button type="button" class="affinity-debug-toggle" aria-expanded aria-controls="affinityDebugBody">. The body uses the hidden attribute.
  • Caret: caret-right collapsed, caret-down expanded, from one helper, so the render and the toggle cannot disagree.
  • toggleAffinityDebug(btn) flips aria-expanded, the body and the caret. One delegated click listener on #nodeFullBody is wired once in init(). It is not wired in loadFullNode, which re-runs on theme-refresh and would stack listeners. As a <button>, it handles Enter and Space natively.
  • No inline handler is left in the card. The button keeps the h4 look (font/color/letter-spacing/text-transform: inherit) and gets a 48 px min-height on coarse pointers. No new colours.
  • The /api/debug/affinity fetch and its rendering are unchanged.

2. Mobile group rows: select-hash, no aria-expanded

Bug: under the 600 px breakpoint, mobile-page-actions.js (Kpa-clawbot#1461 #7) rewrites a group-row click to select-hash, and the expand column is hidden. The row still announced aria-expanded="false". Also, Enter/Space on a focused row still went through toggle-select and expanded the group (the redirect only catches click). The E2E confirmed this on master.

Choice: render the row as select-hash under the breakpoint, rather than only dropping the attribute. buildGroupRowHtml derives data-action and aria-expanded from one flag:

  • aria-expanded is present if and only if the row toggles;
  • on mobile, tap, screen-reader activation, Enter and Space all select, which matches what a tap already did;
  • dropping only the attribute would have left a row that expands on Enter with no announced state.

Details:

Perf:

  • groupRowSelectsOnActivate() is one innerWidth comparison per rendered row, and only the visible slice is rendered (virtual scroll).
  • A breakpoint crossing costs one visible-slice rebuild (tens of rows). Nothing runs per packet, and there are no new API calls.

Tests

Tests come first: two red commits, then the fix.

  • test-issue-254-affinity-debug-toggle.js (new, in test-all.sh) runs the real nodes.js in a vm sandbox. It checks:
    • the card markup (button, aria-expanded="false", aria-controls on the hidden body, one caret-right, no on*= attribute);
    • the toggle opening and closing;
    • the delegated handler toggling, and ignoring other clicks.
  • test-packets.js gets 7 new cases:
  • test-issue-254-affinity-toggle-mobile-aria-e2e.js (new, in deploy.yml after the test: 13 red orphan unit tests + 4 orphan E2E files left after #187; collapsed packet groups show an up-caret #189 caret E2E):
    • node page with meshcore-affinity-debug=true, and /api/debug/affinity answered by page.route, so no request reaches the server and no API key is sent;
    • click, Enter and Space, in light and dark;
    • packets: 1400 px toggle; 1400 → 390 → 1400 resize; 390 px touch, where tap and Enter open the detail sheet and do not expand the group.

SCREENSHOT_DIR saves screenshots.

Gates

  • scripts/check-xss-sinks.sh --diff origin/master is clean.
  • git diff --check is clean.
  • No hardcoded colours.
  • Fork guards are unchanged: 9 in deploy.yml, 1 in release-fast-path.yml.

🤖 Generated with Claude Code

dborup and others added 3 commits October 5, 2026 12:09
…xpanded (#254)

test-issue-254-affinity-debug-toggle.js renders the node page's Affinity
Debug card from the real nodes.js and checks for a disclosure button
(aria-expanded, aria-controls on the hidden body, caret-right collapsed,
caret-down expanded) with no inline handler, and that the toggle and the
delegated click handler open and close it. Red on master: the card is
inline in loadFullNode, with a broken inline onclick.

test-packets.js: under the 600 px mobile breakpoint of
mobile-page-actions.js a multi-observation group row is select-hash
without aria-expanded; above it, it stays the #189 toggle. 3 cases red
on master.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…254)

Node page with meshcore-affinity-debug=true and /api/debug/affinity
answered by page.route (no request reaches the server, no API key): the
heading button opens and closes the body by click, Enter and Space, with
caret and aria-expanded following. Packets: the seeded Kpa-clawbot#1486 group is
the #189 toggle at 1400 px, select-hash without aria-expanded at 390 px
(tap and Enter open the detail sheet, the group does not expand), and a
1400 -> 390 -> 1400 resize re-renders it. 9 of 12 steps red on master.

Registered in deploy.yml after the #189 caret E2E.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…without aria-expanded (#254)

nodes.js: the Affinity Debug heading's inline onclick held the Phosphor
sprite markup with unescaped double quotes, so the attribute was cut
short and the handler was a syntax error; the body never opened. The
card now comes from renderAffinityDebugCard(): the heading holds a
<button aria-expanded aria-controls> for the body (hidden attribute),
with caret-right collapsed and caret-down expanded (the #189
convention). One delegated click listener on #nodeFullBody, wired once
in init() because loadFullNode re-renders on theme-refresh. style.css
keeps the h4 look for the button with inherited font and colour; no
new colours.

packets.js: under the mobile breakpoint mobile-page-actions.js (Kpa-clawbot#1461
#7) turns a group-row click into select-hash, but the row still said
aria-expanded, and Enter/Space still expanded it. buildGroupRowHtml now
derives data-action and aria-expanded from one flag: under the
breakpoint the row is select-hash with no aria-expanded, so tap, Enter
and Space all select. mobile-page-actions.js exports isMobile() as
window.MobilePageActions so the breakpoint stays in one place; its
redirect is unchanged. A debounced resize listener re-renders the
visible rows only when the breakpoint is crossed. Desktop rows are
unchanged from #248.

Also fixes the caret helper in test-issue-254-affinity-debug-toggle.js,
which kept the "ph-" prefix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dborup-agent

Copy link
Copy Markdown
Collaborator Author

Rapport — CS-pve-agent3 PR#255 #254 — head 6f09368

Status: Both #254 items are fixed on a draft PR. Every requirement has a unit and/or E2E test, all 8 mutants are killed, local suites are green and CI is green on attempt 1. The PR stays a draft.

Evidence tags: [T] test or run output, [A] assessment or inference, [K] checked in code, diff, git or CI.

Branch

codex/issue-254-affinity-toggle-mobile-aria from origin/master c12aa60b, three commits, test-first. All have author and committer dborup <kontakt@meshview.dk>. No rebase, amend or force-push; only explicit git add. [K]

  1. 883c80b3 unit tests (red on master).
  2. defbefaa E2E, registered in deploy.yml right after the test: 13 red orphan unit tests + 4 orphan E2E files left after #187; collapsed packet groups show an up-caret #189 caret E2E (red on master).
  3. 6f09368f the fix. It also fixes a helper in the new unit test that kept the ph- prefix.

The plan is posted on #254. Part 2 uses the "render select-hash on mobile" option. The reason, verified on master: the Kpa-clawbot#1461 #7 redirect only catches click, so Enter/Space on a mobile row still expanded the group. Dropping only aria-expanded would have left a row that expands with no announced state. [T][A]

Requirements

Requirement Test Mutant(s) Result
Card shown only with debugAffinity (flag meshcore-affinity-debug=true) unit: card renders display:none; E2E: shown with the flag — ✅ [T]
Heading folds the body in and out unit: toggle and delegated handler open and close; E2E: click, click, Enter, Space, light and dark M1, M6 ✅ [T]
caret-right collapsed, caret-down expanded unit: exactly one caret per state; E2E: caret per step M2, M2b ✅ [T]
aria-expanded follows the state; aria-controls names the body unit + E2E M1, M6 ✅ [T]
No inline handler with nested markup unit: no on*= attribute in the card; E2E: no on* attribute on any element in the card M1 ✅ [T]
No calls to /api/debug/affinity in tests, no API key E2E: page.route answers every request; asserts ≥1 request and an empty X-API-Key. The unit test never fetches — ✅ [T]
Mobile (≤ 600 px, touch): a group row that selects has no aria-expanded test-packets.js: 390 and 600 px, collapsed and expanded; E2E 390 px touch: tap and Enter open the detail sheet, the group does not expand M3, M3b ✅ [T]
Desktop keeps aria-expanded as in #248 test-packets.js: 601 and 1400 px; E2E 1400 px; #189 E2E 3/3 M4 ✅ [T]
Breakpoint crossing re-renders E2E: 1400 → 390 → 1400 resize M5 ✅ [T]
Kpa-clawbot#1461 #7 redirect intact the code is unchanged; test-issue-1461-mobile-page-actions.js 6/6 — ✅ [K][T]
test-issue-189-group-caret-e2e.js and test-packets.js stay green 3/3; 139 passed, 0 failed — ✅ [T]
XSS gate, no hardcoded colours, fork guards check-xss-sinks.sh --diff origin/master exit 0 locally and in CI; no hex or rgb() added; guards: 9 in deploy.yml, 1 in release-fast-path.yml — ✅ [T][K]
New E2E registered next to the #189 line deploy.yml line after test-issue-189-group-caret-e2e.js; test-test-all.js 8/8 — ✅ [K][T]

Red on master [T]

  • test-issue-254-affinity-debug-toggle.js: 1 passed, 8 failed.
  • test-packets.js: 136 passed, 3 failed (the 390 px and 600 px cases).
  • New E2E: 3 passed, 9 failed. On master, Enter on a 390 px group row expanded it (aria-expanded="true", 3 child rows).

Mutants [T]

Each mutant ran against both unit files, the new E2E and the #189 E2E, with a server restart per mutant. Each was then restored (git checkout -- public/, tree clean).

Mutant nodes unit test-packets.js #254 E2E #189 E2E
M1 old inline onclick markup back 7 failed green 5 failed green
M2 carets swapped (down collapsed / right expanded) 3 failed green 5 failed¹ green
M2b master's down/up carets 3 failed green 5 failed¹ green
M3 aria-expanded back on mobile rows green 3 failed 3 failed green
M3b toggle-select back on mobile rows green 3 failed 4 failed¹ green
M4 aria-expanded dropped on desktop green 4 failed 2 failed 3 failed
M5 resize re-render removed green green 1 failed green
M6 delegated listener not wired in init() green green 3 failed green

All 8 are killed by at least one layer. ¹ The log line was truncated, so the count is the ✗ markers visible.

Suites (local Go server on a copy of e2e-fixture.db, prepared as in CI: freshen, seed SQL, corescope-migrate, seeds 2073 and 199; stopped by port) [T]

Suite Result
sh test-all.sh 219/219 files
node test-frontend-helpers.js 707 passed, 0 failed
node test-packets.js 139 passed, 0 failed
New #254 E2E 12/12
Packets E2Es: #189, Kpa-clawbot#1486, Kpa-clawbot#1122 Details clamp + filter UX, Kpa-clawbot#1128 layout + multi-viewport, #96, #147, #180, Kpa-clawbot#1692, slide-over Kpa-clawbot#1056 / Kpa-clawbot#1168, filter UX, Kpa-clawbot#1758, touch gestures, packet-trace alignment, mobile VCR Kpa-clawbot#1267 all exit 0
Nodes E2Es: Kpa-clawbot#2073, Kpa-clawbot#1147, #199, nodes export, favourite re-render, node reach, Kpa-clawbot#2027, node liveness all exit 0
Touch targets (test-touch-targets.js, Kpa-clawbot#2052) exit 0, 11/11
test-a11y-axe-1668.js 0 violations, 120 cells
test-e2e-playwright.js 132/135, 3 skipped

Screenshots (local, Chromium, SCREENSHOT_DIR; not attached, because the CLI cannot upload images) [T]

  • Light, 1400 px, collapsed: the card heading "AFFINITY DEBUG" keeps the muted uppercase h4 look, with a single right caret before the magnifying glass and no body below.
  • Light and dark, expanded: one down caret. The body shows the mocked data ("No affinity edges for this node", Graph Stats, "Cache age: E2E-254 mock"). The dark theme uses the card's normal tokens, and no colour is added.
  • 390 px (touch): the seeded group row has no expand column. A tap opens the bottom detail sheet ("Observations (3)"), and the row stays collapsed.

CI [K]

Run on head 6f09368f, attempt 1, conclusion success. No reruns.

Neither known flake fired (#244 Details clamp, #250 TestStatsFileHasNoCredentials_118).

Remainder

@adminopenclaw8-sketch

Copy link
Copy Markdown
Collaborator

Review — CS-Macmini PR#255 affinity-toggle — head 6f09368

Dom: APPROVE med nits

Independent, read-only review. I made no changes to the PR. Local runs used git archive of the head, of origin/master d05b0db5 and of the merged tree (git merge-tree --write-tree origin/master 6f09368f… → dcec26c5, no conflicts), in scratch directories. git ls-remote showed the head at 6f09368f before and after the review. During the review, master moved to 310c501a (#252). That change touches only test-issue-1122-details-row-clamp-e2e.js, which does not overlap this PR, and merge-tree against 310c501a is also clean. I did not rerun the suites on it. [K]

Evidence tags: [T] test or run output, [A] assessment or inference, [K] checked in code, diff, git or CI.

Findings

# Severity Finding Evidence
F1 nit (test gap) The new unit test does not prove the card actually uses the renderer. It exercises renderAffinityDebugCard() in isolation. My mutant M1, which puts master's inline onclick card back into the loadFullNode template and leaves the renderer exported but unused, passes test-issue-254-affinity-debug-toggle.js 9/9. Only the E2E kills it (5 failed). Relatedly, "no inline event handler anywhere in the card" is green on master (1 of the 9 master cases passes), because the renderer is missing there and the test scans an empty string. CI runs the E2E, so this is a gap in the fast layer only. A one-line source assertion would close it: loadFullNode's template contains ${renderAffinityDebugCard()}, or nodes.js has no onclick= near node-affinity-debug. [T] M1 · [K]
F2 nit, pre-existing (follow-up) An expanded group that crosses to ≤ 600 px stays expanded, with no state and no collapse path. At 1400 px I expanded the seeded group, then resized to 390 px. The row re-rendered as select-hash without aria-expanded, but it keeps class="expanded" and its 3 child rows stay visible. The expand column is hidden, so there is no way to collapse it there. Before this PR the same row announced a stale aria-expanded="true", so this is no regression. The author lists it under "Remainder". A follow-up could collapse expandedHashes when the mode flips to mobile, or keep the children but drop them from the visible slice. [T] reviewer check · [K]
F3 nit (test gap) The E2E covers Enter on a mobile row but not Space. The handler treats both the same (packets.js, the keydown branch with Enter/' '). My own check pressed Space on the focused 390 px row: the sheet opened and the group did not expand. [T]
F4 info, pre-existing, out of scope nodes.js init() still adds a document-level keydown listener (nodesEsc) per node-page init. It is only removed when Escape is pressed, so navigating A → B → C stacks three. Escape then sets the same hash three times, which is harmless. This PR does not touch it, and the new #nodeFullBody click listener does not leak (see 1). [K]

No blocking findings.

1. Affinity Debug

  • Shown only with debugAffinity: [T]
    • With meshcore-affinity-debug=true the card shows.
    • Without the flag, the card stays display:none and no /api/debug/affinity request is made (reviewer check, route counter = 0).
    • The CLIENT_CONFIG.debugAffinity path is unchanged code. [K]
  • Click, Enter and Space toggle it: [T] The PR's E2E passes 12/12 locally and in CI. My own checks also cover a tap at 390 px with touch, in light and dark.
  • Exactly one caret per state: [T][K]
    • Collapsed shows ph-caret-right and expanded shows ph-caret-down. One <use> sits in .toggle-icon, from the single helper affinityDebugCaretHtml, so the first render and the toggle cannot disagree.
    • After a theme-refresh while the card is expanded, it re-renders consistently collapsed (false, hidden, caret-right).
  • aria-expanded / aria-controls: [T][K]
    • aria-expanded sits on the <button> and follows the body.
    • aria-controls="affinityDebugBody" names the body's id, and the body uses hidden.
    • There is one node page at a time, so the fixed id is unique (#nodeFullBody count = 1).
  • No inline on* handler: [T] The reviewer check found 0 on* attributes on the card subtree after several re-renders.
  • No listener leak (checked myself): [T] I wrapped EventTarget.prototype.addEventListener in an init script and recorded every registration of onFullBodyClick. Then I navigated node A → B → A → #/packets → A and fired theme-refresh three times. Results:
    • 4 registrations, one per init();
    • all on #nodeFullBody, click;
    • only one on a connected element, because the old bodies are discarded with app.innerHTML;
    • afterwards, one click opens the card and one click closes it.
  • Two reasons it cannot leak: [T][K]
    • init() rebuilds app.innerHTML, so every node page gets a fresh #nodeFullBody and the old element is discarded with its listener.
    • Within one page, theme-refresh re-runs loadFullNode, which replaces only the body's content, not the element. Even re-adding the same named handler would be de-duplicated (mutant M3).
  • Styling: [T][K]
    • The button inherits the h4 look: the computed color equals the h4 colour in both themes, and background is transparent.
    • On coarse pointers the button is 48 px tall.
    • Keyboard focus shows the global 2 px focus ring.
    • The light-blue flash right after a tap is Chromium's default -webkit-tap-highlight-color, the same as on the back button. It is gone after 2 s.
    • No new colour values.

2. Mobile: select-hash below the breakpoint

  • Is the choice right? Yes. [T][A]
  • Breakpoint: [K][T]
    • groupRowSelectsOnActivate() calls MobilePageActions.isMobile() (innerWidth <= 600).
    • The CSS that hides #pktTable td.col-expand is @media (max-width: 600px). Both include the scrollbar, so they agree.
    • Touch contexts at 600 / 601 px: 600 px is select-hash with the column hidden; 601 px is toggle-select with aria-expanded and the column visible.
    • Mutant M4 (own < 600 check) is killed by the unit test.
  • Breakpoint crossing: [T]
    • The PR's E2E covers 1400 → 390 → 1400.
    • My checks add a rotation from 390×844 to 844×390 and back. Each side renders correctly.
    • A fast 1400 → 390 → 1400 flip inside the 150 ms debounce also ends as the desktop row: the timer is cleared, and the mode compare is a no-op.
    • destroy removes the listener and clears the timer. [K]
  • Touch device wider than 600 px: consistent. [T]
    • Touch contexts at 601, 620, 768 (tablet portrait) and 1024 (tablet landscape) all render toggle-select with aria-expanded="false" and a visible expand column.
    • A tap on the caret expands (true, children shown), and a second tap collapses (false).
    • At 601–640 px the detail opens in the mobile sheet, and at 641–1023 px in the slide-over. Both cover the table until they are closed. This is unchanged from master and is not caused by this PR. [A]

3. Desktop is unchanged from #248

Reviewer check at 1400 px on the seeded group. After expanding it, aria-expanded stays "true" and class="expanded" stays, with the child rows present, through each of: [T]

  • theme-refresh (renderTableRows);
  • sorting by Time twice;
  • a filter expression that matches the group (type == GRP_TXT), and clearing it;
  • a simulated live update: a packet message for the same hash pushed through the page's WS listeners. Children went 3 → 4, and the row kept "true".

After collapsing, a further live update keeps "false".

test-issue-189-group-caret-e2e.js: 3/3 locally on the merged tree, and green in CI. [T]

4. Tests

Red on master, green on head/merged [T]

Test master d05b0db5 merged dcec26c5
test-issue-254-affinity-debug-toggle.js 1 passed, 8 failed 9/9
test-packets.js 136 passed, 3 failed (390 / 390-expanded / 600 px) 139/139
test-issue-254-affinity-toggle-mobile-aria-e2e.js 3 passed, 9 failed 12/12

Mutants

Each mutant is a copy of the merged tree's public/. For each one I ran the #254 unit test, test-packets.js, the #254 E2E and the #189 E2E against a fresh server.

Mutant #254 unit test-packets.js #254 E2E #189 E2E Reviewer checks Verdict
M1: master's inline onclick card put back into the loadFullNode template (renderer exported but unused) 9/9 green green 5 failed green no button, script aborts killed by the E2E only (F1)
M1b: renderAffinityDebugCard() returns master's inline markup 7 failed green 5 failed green aborts killed
M2: aria-expanded back on mobile group rows green 3 failed 3 failed green 4 failed killed
M3: delegated listener added in loadFullNode (every render) instead of init green green 12/12 green 1 failed (call count only) equivalent, see below
M4: own innerWidth < 600 check instead of MobilePageActions.isMobile() green 2 failed 12/12 green 1 failed (600 px touch) killed by the unit test
M5: resize listener not added in init green green 1 failed green 2 failed killed
M6: carets swapped 3 failed green 5 failed green aborts killed
M7: toggle flips aria-expanded and the caret but leaves the body hidden 2 failed green 3 failed green 1 failed killed
M8: resize re-render without resetting _lastVisibleStart / _lastVisibleEnd green green 1 failed green 2 failed killed

M3 is behaviourally equivalent. addEventListener with the same function reference, type and capture flag on the same element is de-duplicated by the DOM. So re-adding the named onFullBodyClick on every loadFullNode registers it once. My call-counting check saw 4 calls on the live #nodeFullBody, but one click still toggled exactly once. The code comment "would stack listeners" is therefore stricter than necessary for a named handler, though it would apply to an inline closure. Wiring it in init() is still the cleaner choice. [T][A]

All non-equivalent mutants are killed by the PR's own tests. M1 is killed only by the E2E (F1). [T]

5. Rules

Suites (merged tree, against origin/master d05b0db5)

Suite Result
sh test-all.sh 219/219 files [T]
node test-frontend-helpers.js 707 passed, 0 failed [T]
node test-packets.js 139 passed, 0 failed [T]
#254 E2E / #189 E2E / test-issue-1461-mobile-page-actions.js 12/12, 3/3, 6/6 [T]
Packets E2Es: Kpa-clawbot#1486, Kpa-clawbot#1122 clamp (18/18) + filter UX, Kpa-clawbot#1128 layout + multi-viewport, #96, #147, #180, Kpa-clawbot#1692, slide-over Kpa-clawbot#1056 / Kpa-clawbot#1168, filter UX, Kpa-clawbot#1758, touch-gestures coverage, packet-trace alignment all exit 0 [T]
Nodes E2Es: Kpa-clawbot#2073, Kpa-clawbot#1147, #199, nodes export, favourite re-render, node reach, Kpa-clawbot#2027, node liveness; touch targets, Kpa-clawbot#2052 all exit 0 [T]
test-e2e-1267-mobile-vcr.js not run: it needs /usr/bin/chromium, which this macOS host lacks [T]
test-a11y-axe-1668.js local net=1 (mobile light /observers colour contrast on "connected") and identical on master. CI on this head reports net=0. Local and time-dependent, not this PR. [T][K]
test-e2e-playwright.js local fail-fast at "Version info lives on Perf dashboard" (the #navStats wait times out), identical on master. It passes in CI on this head. [T][K]

The local Go server ran on a copy of e2e-fixture.db, prepared as in CI: freshen, the seed SQL from deploy.yml, corescope-migrate, then seeds 2073 and 199. I stopped it by port.

CI: run on head 6f09368f, attempt 1, conclusion success. The log shows the #254 E2E at 12/12, axe at net=0, and Version info ✅. [K]

Screenshots (local Chromium; described, not attached)

  • Light, 1400 px, expanded: "AFFINITY DEBUG" keeps the muted uppercase h4 look, with a single down caret before the magnifying glass. The body shows the mocked "No affinity edges for this node" and Graph Stats. [T]
  • Dark, 1400 px, collapsed: a single right caret, no body, and the card uses the normal dark card tokens. [T]
  • 390 px, touch, light and dark, expanded after a tap: the heading button spans the card at 48 px height with one down caret. The body text wraps cleanly, and nothing overflows horizontally. [T]
  • 390 px packets, light and dark: the seeded group row shows Time / Type / Details with no expand column. A tap opens the bottom sheet, and the row stays collapsed. [T]

Not verified

  • Firefox, Safari / WebKit, a real phone or tablet, and a real screen reader. In particular, I have not heard how aria-expanded on a role="row" in a plain table is announced; this is carried over from fix(packets): collapsed groups show a right caret, expanded a down caret (#189) #248.
  • A real WebSocket stream: the live update was injected through the page's WS listeners.
  • test-e2e-1267-mobile-vcr.js locally (no /usr/bin/chromium).
  • The full test-e2e-playwright.js locally (fail-fast on an env-only failure that also fails on master); I relied on CI for it.
  • Staging and production, by design.

@dborup
dborup marked this pull request as ready for review October 5, 2026 13:48
@dborup
dborup merged commit 3bb2cb8 into master Oct 5, 2026
6 checks passed
dborup added a commit that referenced this pull request Oct 6, 2026
fix(ui): follow-ups to #255 — mobile group dead end, nodesEsc leak, test gaps (#259)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants