Repository navigation
Follow-up to #248: Affinity Debug toggle broken + reversed carets; group-row aria-expanded on mobile #254
Description
Activity
- addedbugSomething isn't workingSomething isn't workingtype:bugSomething brokenSomething broken
on Oct 5, 2026 Plan — CS-pve-agent3 #254
Branch
codex/issue-254-affinity-toggle-mobile-ariafromorigin/masterc12aa60b. Tests first (red), then the fix. One draft PR, "Relates to #254".1. Affinity Debug toggle (
public/nodes.js:920)- Move the card markup out of the
loadFullNodetemplate into a small pure renderer,renderAffinityDebugCard(). The heading holds a real<button type="button" class="affinity-debug-toggle" aria-expanded="false" aria-controls="affinityDebugBody">. The body getsid="affinityDebugBody"and thehiddenattribute instead ofstyle="display:none". - Caret per the fix(packets): collapsed groups show a right caret, expanded a down caret (#189) #248 convention: collapsed
ph-caret-right, expandedph-caret-down, from one helper (affinityDebugCaretHtml(expanded)), so the initial render and the toggle cannot disagree. toggleAffinityDebug(btn, doc)flipsaria-expanded, the body'shiddenand the caret. One delegated click listener on#nodeFullBodyis registered once ininit(). It is not registered inloadFullNode, which re-runs ontheme-refreshand would stack listeners. The<button>gives Enter and Space for free.- No inline handler, no nested markup in an attribute. The button style resets to the heading look with
inherit(font,color), plus acursorrule instyle.css. No new colours. - The fetch to
/api/debug/affinityand its rendering are unchanged.
2. Mobile group rows: render the row as
select-hashunder the mobile breakpointChoice: option 2, render the row as
select-hashon mobile, rather than only dropping the attribute.buildGroupRowHtmlderivesdata-actionandaria-expandedfrom one boolean,toggles = !isSingle && !selectsOnActivate().aria-expandedis present if and only if the row's action istoggle-select.selectsOnActivate()asksmobile-page-actions.js, which owns the Tufte v2: mobile packets view — kill chrome, kill duplication (follow-up to #1458 / #1459) Kpa-clawbot/CoreScope#1461 API: harden configured-scope contract for Local Mesh consumers #7 redirect and the 600 px breakpoint. A newwindow.MobilePageActions = { isMobile }export avoids a second copy of the breakpoint. If the module is not loaded, there is no redirect, so the row toggles.
Why not just drop
aria-expanded: the Kpa-clawbot#1461 #7 redirect only catchesclick. On mobile with a keyboard, Enter/Space still go throughtoggle-selectand expand the group. Dropping only the attribute would leave a row that expands with no announced state. Renderingselect-hashmakes every activation path (tap, screen-reader activation, Enter, Space) select, which matches what a mobile tap already does today.- Tufte v2: mobile packets view — kill chrome, kill duplication (follow-up to #1458 / #1459) Kpa-clawbot/CoreScope#1461 API: harden configured-scope contract for Local Mesh consumers #7 stays as it is. The intercept is untouched, and it still covers a row rendered before a breakpoint crossing.
- Breakpoint crossing (for example, rotating a phone from 390 to 844 px): a debounced
resizelistener, registered ininitand removed indestroy, re-renders only the visible rows (renderVisibleRowswith the range reset), and only when the mode actually flips. Cost: one visible-slice rebuild (tens of rows) per crossing, nothing per packet. - Desktop is unchanged from fix(packets): collapsed groups show a right caret, expanded a down caret (#189) #248:
toggle-select, caret andaria-expandedas before.
3. Tests and mutants
- Unit, new
test-issue-254-affinity-debug-toggle.js(vm sandbox on the realnodes.js, registered intest-all.sh):- the card renders a button with
aria-expanded="false",aria-controlspointing at the hidden body,caret-rightand noonclick; - the toggle opens (
true, body shown,caret-down) and closes again.
- the card renders a button with
- Unit,
test-packets.js:- at
innerWidth390 withMobilePageActionsstubbed, a multi-observation row isselect-hashwithoutaria-expanded; - at 1400 it stays
toggle-selectwitharia-expanded; - single-observation rows are unchanged.
- at
- E2E, new
test-issue-254-affinity-toggle-mobile-aria-e2e.js(registered indeploy.ymlnext to the test: 13 red orphan unit tests + 4 orphan E2E files left after #187; collapsed packet groups show an up-caret #189 line):- Node page with
meshcore-affinity-debug=true, and/api/debug/affinitymocked throughpage.route(no request reaches the server and no API key is used). The card is visible, the body is hidden, then click → open/caret-down/true, click → closed/caret-right/false, then keyboard Enter. - The seeded Packets page collapse button in the left column of the table opens the dialog. Kpa-clawbot/CoreScope#1486 group at 1400 px:
toggle-select+aria-expanded. - The same group at 390 px (touch):
select-hash, noaria-expanded, and a tap opens the detail instead of expanding. - A 1400 → 390 → 1400 resize updates the row.
- Node page with
- Mutants (each against the unit tests and the E2E, then restored):
- M1, the old inline
onclickmarkup; - M2, reversed carets;
- M3,
aria-expandedback on mobile; - M4,
aria-expandeddropped on desktop; - plus the resize re-render removed.
- M1, the old inline
- Suites:
sh test-all.sh,node test-frontend-helpers.js,node test-packets.js;test-issue-189-group-caret-e2e.js,#1486,#1461and the other packets and nodes E2Es against a local Go server one2e-fixture.db, prepared as in CI. - Gates:
scripts/check-xss-sinks.sh --diff, no hardcoded colours, fork guards stay 9 + 1.
Browser validation
Screenshots of the Affinity Debug card collapsed and expanded in light and dark, and of the packets list at 390 px.
Customizer
No new configurable values.
- Move the card markup out of the
- added a commit that references this issue
on Oct 5, 2026 Fixed by #255 (merged as
3bb2cb89).
Follow-up from the review of #248 (#189 group caret, merged as
c12aa60b). Both items were outside that PR's scope. Line numbers refer to masterc12aa60b.1. Node page "Affinity Debug" toggle cannot open, and its carets are reversed
public/nodes.js:920: the<h4>heading of the#node-affinity-debugcard has an inlineonclick. Thatonclickis a double-quoted attribute, but it contains the Phosphor sprite markup with its own double quotes (class="ph-icon",aria-hidden="true"). The browser therefore cuts the attribute off at the first inner". The handler that remains is not valid JavaScript (reported as "Invalid or unexpected token"), so clicking the heading does nothing, and the debug body stays hidden.The break came with the emoji-to-Phosphor migration (
30627454, upstream#1648M2), the same change that caused #189.The carets also contradict the convention #248 chose (collapsed =
caret-right, expanded =caret-down): the toggle showscaret-downwhile collapsed andcaret-upwhile open.The card is only shown when
debugAffinityis enabled (client config or the local-storage flag), which is probably why nobody noticed.Expected:
caret-rightand expanded showscaret-down;onclickwith nested markup;<button>witharia-expanded, or follows the same pattern as the packets group rows.Tests:
2. Packets group rows announce
aria-expandedon mobile, but activation selects insteadpublic/packets.js:2444(from #248) setsaria-expandedon multi-observation group rows. On mobile,public/mobile-page-actions.js(upstream#1461#7) rewritesdata-action="toggle-select"toselect-hashon click, and the expand column is hidden. A screen-reader user therefore hears "collapsed" on a row that opens the detail pane instead of expanding.This is a minor a11y issue and not a blocker. Options:
aria-expanded(or render the row withselect-hash) under the mobile breakpoint, so the announced state matches what activation does;Tests: a narrow-viewport E2E (or a unit test with
isMobile()stubbed) asserting that the row has noaria-expandedwhen activation selects.Not in scope
Evidence
c12aa60b. The fix(packets): collapsed groups show a right caret, expanded a down caret (#189) #248 review also parsed the line in Chromium; the attribute ends after 234 characters, and the handler then throws.mobile-page-actions.js:94-105,packets.js:2444) and has not been tested at mobile width.