Files
meshcore-analyzer/.github/workflows
Kpa-clawbot fb6bb085a5 fix(analytics): render Channels group-header sprites as HTML, not escaped text (#1657) (#1658)
Fixes #1657

## Bug

On `/analytics` → **Channels** tab, the "Channel Activity" table's
group-header rows ("My Channels", "Network", "Encrypted") rendered
literal HTML source text:

```
<SVG CLASS="PH-ICON" ARIA-HIDDEN="TRUE"><USE HREF="/ICONS/PHOSPHOR-SPRITE.SVG#PH-KEY"/></SVG> My Channels
```

instead of the actual Phosphor sprites. Per-row encrypted/lock icons
rendered fine — the bug was isolated to the group-header render path.

## Root cause

`public/analytics.js` `channelTbodyHtml` builds each group-section
header by wrapping the section label in `esc()`:

```js
esc(sections[si].label) + ' <span class="text-muted">(' + rows.length + ')</span>'
```

But the labels (`sections[].label`) are hardcoded sprite-bearing
strings:

```js
{ key: 'mine', label: '<svg class="ph-icon" aria-hidden="true"><use href="…#ph-key"/></svg> My Channels' },
```

`esc()` HTML-encoded the `<` / `>` so the browser displayed the source
text rather than rendering the sprite. Affects all 3 groups (and any
future group with a sprite).

## Fix

Drop the `esc()` wrap on the hardcoded label (single line change, same
pattern as M3 commit 4ca73ced for mobile channel avatars). The
`(<count>)` suffix is numeric and was always safe.

## Tests

New `test-issue-1657-analytics-channels-group-sprites-e2e.js` (mobile
375 viewport, matching the bug report):

- (1) at least one group-header row renders
- (2) every header row contains a real `<svg.ph-icon>` child
- (3) per-group sprite refs resolve (My Channels → `#ph-key`, Network →
`#ph-radio`, Encrypted → `#ph-lock`)
- (4) the Channel Activity table's `innerText` contains no literal
`<svg` substring (escape-leak gate)

Wired into the CI E2E lane (`.github/workflows/deploy.yml`) immediately
after the M4 icons E2E.

## TDD evidence

- Red commit: `8f8781c1` — test + CI wiring only, no production change.
Test asserts behavior that did not exist on master → CI fails on this
commit.
- Green commit: `8385fa54` — 1-line fix to `public/analytics.js`. Test
passes.

## Anti-tautology proof

Hot-patched staging with the `analytics.js` from the red commit
(pre-fix), reloaded `/analytics?tab=channels` at 375 viewport, and the
in-browser DOM probe returned:

```
headers[0].text = "<svg class=\"ph-icon\" aria-hidden=\"true\"><use href=\"/icons/phosphor-sprite.svg#ph…"
headers[0].svgs = 0           // (2) would fail
headers[1].svgs = 0           // (2) would fail
literalSvg     = true         // (4) would fail
```

Restored the fixed file; same probe returned `svgs=1`, correct `uses[]`
refs, `literalSvg=false`.

## Staging verification

Hot-patched `corescope-staging-go:/app/public/analytics.js` (no restart
needed — static file). Mobile dark @ 375 viewport shows Network → radio
sprite and Encrypted → lock sprite rendering correctly. (My Channels
group not present because the e2e fixture has no `mine`-tagged channels
— expected; the test skips that assertion when the row is absent.)

## Scope discipline

Touched only:
- `public/analytics.js` (1-line `esc()` removal + comment)
- `test-issue-1657-…-e2e.js` (new)
- `.github/workflows/deploy.yml` (1-line E2E wire)

No broadening, no helper renames, no related-but-different
escape-removal opportunism.

---------

Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-06-11 07:34:51 -07:00
..