Files
meshcore-analyzer/public/icons
Kpa-clawbotandopenclaw-bot 1116801b2f M5: emoji → Phosphor Icons — settings & customize (#1648) (#1653)
**Red commit:** `851cc8c3a024b1675558092d772444bf4f1ec625` — failing
test on a stub branch (will link CI run after PR opens).

Partial fix for #1648 (M5 of 6). **Do NOT close the tracking issue** —
M6 (server-side residual emoji sweep + lint gate) still pending.

## Per-file swap counts

| File | Phosphor `<use>` refs | Notes |
|---|---|---|
| `public/customize.js` | 20 | DEFAULTS → `ph:<name>` tokens; render
path keeps legacy emoji branch (back-compat) |
| `public/customize-v2.js` | 26 | same as v1; cv2 overrides path
unchanged |
| `public/home.js` | (helpers added) | `_renderHomeGlyph` /
`_renderHomeLabel` accept both `ph:<name>` and legacy emoji |
| `public/geofilter-builder.html` | 5 | clear / undo / save / load
buttons (+inline `.ph-icon` CSS) |
| `public/audio.js` | 1 | audio unlock prompt |
| `public/filter-ux.js` | 5 (3 new) | help popover star + close,
saved-filter delete |
| `public/style.css` | 0 | `#chList .ch-share-btn::before { content: '📤'
}` removed; JS now renders an inline sprite |
| `cmd/server/routes.go` | (6 `ph:` tokens) | onboarding home defaults
updated in lockstep with customize-v2.js |

## Operator config back-compat — PROMINENT

Per design call #1 (user-locked): existing operator-stored emoji values
in `config.json` / `localStorage` are **NOT** touched. The render path
supports both:

```js
function renderConfigGlyph(value) {
  var m = String(value || '').match(/^ph:([a-z][a-z0-9-]+)$/);
  if (m) return '<svg class="ph-icon"><use href="/icons/phosphor-sprite.svg#ph-' + m[1] + '"/></svg>';
  return esc(value);  // EMOJI-OK-LEGACY-RENDER — operator-stored emoji/text path
}
```

Defaults flipped to `ph:<name>` tokens, so new operators (and operators
who hit "Reset to Defaults") see Phosphor sprites. Operators with stored
emoji values continue to see their emoji exactly as before. Verified
end-to-end (see E2E (b) below).

## cmd/server/routes.go — changed in lockstep

Per design call #2: the home-defaults `steps` / `footerLinks` mirror the
JS DEFAULTS, so they MUST update together. routes.go now emits
`ph:<name>` tokens; the frontend home-render path resolves them.
Existing tests (`TestConfigThemeHomeDefaults`) still pass — they assert
structure, not glyph values.

## E2E assertions added

- `test-issue-1648-m5-emoji-scan.js` — per-file zero-emoji + ph-token
DEFAULTS + sprite presence
- `test-issue-1648-m5-icons-e2e.js`:
- (a) customize chrome — tabs/header rendered as sprites; chrome text
icon-free
- **(b) back-compat — injects fake `🐙` operator step into localStorage,
reloads, opens customize, asserts the emoji renders verbatim in both the
input value AND the live preview span; asserts the ph-token step renders
as a sprite** (design call #1 in action)
  - (c) `/channels` modal sprite count
  - (d) `/audio-lab` sprite presence
  - (e) `geofilter-builder.html` control buttons sprite-driven
  - (f) every `<use>` resolves to a defined symbol id

## Out of scope (M6 cleanup)

- cmd/server/routes.go residual server-rendered emoji **not** tied to
customize defaults (none found by my grep — file already audited)
- `make lint-no-emoji` CI grep gate (M6 owns it)
- `public/icons/README.md` workflow doc

cross-stack: justified — design call #2 requires Go + JS update
together.

---------

Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-06-11 05:04:29 -07:00
..

Phosphor Icon Sprite

This directory holds the vendored Phosphor Icons sprite used by the CoreScope frontend. We do not ship the Phosphor webfont (~150 KB) or fetch icons from a CDN at runtime — every icon used by the UI is bundled here.

File layout

  • phosphor-sprite.svg — single SVG sprite, one <symbol id="ph-NAME"> per icon (regular weight, viewBox="0 0 256 256" to match Phosphor's native grid).

Markup pattern

<svg class="ph-icon" aria-hidden="true" focusable="false">
  <use href="/icons/phosphor-sprite.svg#ph-magnifying-glass"></use>
</svg>

CSS helper (defined in public/style.css):

.ph-icon { width: 1em; height: 1em; vertical-align: -0.125em; fill: currentColor; }

Icons inherit color via currentColor and size via the surrounding font-size, so they re-theme automatically with light/dark mode and CSS variables.

Adding a new icon

  1. Pull the regular-weight SVG from https://cdn.jsdelivr.net/npm/@phosphor-icons/core@2.1.1/assets/regular/NAME.svg (or assets/fill/NAME.svg for the rare filled-circle / star-fill cases).
  2. Append a <symbol id="ph-NAME" viewBox="0 0 256 256">…</symbol> to phosphor-sprite.svg. Strip the outer <svg> wrapper and any fill= attrs on the inner <path> (we want currentColor from the parent).
  3. Reference it with <use href="/icons/phosphor-sprite.svg#ph-NAME"></use>.

Weight policy

Regular weight only, with two filled exceptions allowed for status dots and star-favorite (circle-fill, star-fill, square-fill). Bold/duotone are reserved for a future design pass — do not introduce them ad hoc.

Lint plan (M6)

A make lint-no-emoji target will grep public/** for codepoints in U+1F300–U+1FAFF, U+2600–U+27BF, U+2700–U+27BF and the misc-symbols set (◆●■▲★☆○✓✗⚠✉) outside an allowlist (channel-name strings, log/error text, test fixtures). Until that lands, run the audit script in scripts/audit-emoji.py (added in #1648).