From 89eade6e7b8e073c85e8b769faa323c500a29d4f Mon Sep 17 00:00:00 2001 From: Kpa-clawbot Date: Thu, 11 Jun 2026 05:44:37 -0700 Subject: [PATCH] =?UTF-8?q?M6:=20emoji=20=E2=86=92=20Phosphor=20=E2=80=94?= =?UTF-8?q?=20final=20sweep,=20lint=20gate,=20carry-forwards=20(#1648)=20(?= =?UTF-8?q?#1654)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Red commit: fe7468d473233f33ad67f3ca636100417153a60f (CI run: will appear in this PR's Checks tab — emoji lint test fails on the red commit, passes on green) **Fixes #1648.** Closes the 6-milestone emoji→Phosphor migration started in #1649. ## Sweep results (real UI icons swapped this PR) | File:line | Before | After | |---|---|---| | `public/index.html:140` | ⭐ | `ph-star-fill` | | `public/mobile-page-actions.js:154` | ⭐ | `ph-star-fill` | | `public/geofilter-builder.html:76` | ⬇ | `ph-download-simple` | | `public/analytics.js:2103` | ⏱️ | `ph-clock` | | `public/analytics.js:2288` | ⏰ | `ph-clock` | | `public/analytics.js:4080` | ⏰ | `ph-clock` | | `public/nodes.js:1066` | ⏰ | `ph-clock` | | `public/observer-detail.js:284` | ⏰ | `ph-clock` | | `public/channel-qr.js:133` | 📋 | `ph-clipboard-text` | | `public/channel-qr.js:139` | ✓ | `ph-check` | | `public/packets.js:1472` | ⏸ | `ph-pause` | ## Carry-forwards addressed - **M5 CDP — live `.cust-emoji-preview` re-render** — `public/customize-v2.js:2434` wires `renderConfigGlyph` to the `input` event so previews update without Save+reload. (commit `9e698a04`) - **M5 CDP — `.modal-close` 44×44 mobile** — `public/style.css` adds a ≤640px breakpoint bumping both `.modal-close` and `.ch-modal-close` to WCAG-minimum hit targets. (commit `9e698a04`) - **M4 CDP — route-hop fallback color** — `public/route-render.js` now reads `var(--status-info)` (new token added to `:root` and dark-mode blocks in `style.css`) instead of baked `#3b82f6`. (commit `9e698a04`) - M2 carry-forward set (⭐ favStar / ▾ More / ⚠️ clock / 🌱⚡ welcome cards) verified already addressed by re-running M3 emoji scan — all green. ## Lint gate (M6 headline) - Test: `test-issue-1648-m6-final-sweep.js` — full repo scan across `public/**.{js,html,css}` and `cmd/(server|ingestor|decrypt)/*.go`. - Self-test: `test-issue-1648-m6-lint-self.js` — exercises the lint engine + anti-tautology probe. - Allowlist: `tests/emoji-allowlist.txt`. Format: `path` (glob), `path:line`, `path:line:U+XXXX`, or `/regex/`. Add intentional emojis here with a `# why` comment. - Wired into `test-all.sh` alongside M1/M2/M3 scans. ## routes.go smoke check Server-side defaults in `cmd/server/routes.go:567-574` confirmed `ph:bluetooth`/`ph:radio`/`ph:broadcast`/`ph:repeat` (M5 landed). Operator-customized configs on staging/prod still carry their legacy emoji overrides — per M5 design call those are preserved and NOT touched by this PR. ## PR closing list Fixes #1648. M1 #1649 ✅, M2 #1650 ✅, M3 #1651 ✅, M4 #1652 ✅, M5 #1653 ✅, this PR ✅. --------- Co-authored-by: Bot --- public/analytics.js | 8 +- public/channel-qr.js | 11 +- public/customize-v2.js | 10 ++ public/geofilter-builder.html | 2 +- public/index.html | 2 +- public/mobile-page-actions.js | 2 +- public/nodes.js | 2 +- public/observer-detail.js | 2 +- public/packets.js | 2 +- public/route-render.js | 8 +- public/style.css | 19 +++ test-all.sh | 2 + test-issue-1648-m6-final-sweep.js | 203 ++++++++++++++++++++++++++++++ test-issue-1648-m6-lint-self.js | 139 ++++++++++++++++++++ tests/emoji-allowlist.txt | 99 +++++++++++++++ 15 files changed, 496 insertions(+), 15 deletions(-) create mode 100644 test-issue-1648-m6-final-sweep.js create mode 100644 test-issue-1648-m6-lint-self.js create mode 100644 tests/emoji-allowlist.txt diff --git a/public/analytics.js b/public/analytics.js index 300848f3..6769da3a 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -2100,7 +2100,7 @@
-
⏱️ Timeline
+
Timeline
First seen: ${data.firstSeen ? (typeof formatAbsoluteTimestamp === 'function' ? formatAbsoluteTimestamp(data.firstSeen) : new Date(data.firstSeen).toLocaleString()) : '—'}
Last seen: ${data.lastSeen ? (typeof formatAbsoluteTimestamp === 'function' ? formatAbsoluteTimestamp(data.lastSeen) : new Date(data.lastSeen).toLocaleString()) : '—'}
@@ -2222,7 +2222,7 @@ }).join('')} - ${myKeys.size ? `

⭐ My Claimed Nodes

+ ${myKeys.size ? `

My Claimed Nodes

@@ -2285,7 +2285,7 @@
NodeRolePacketsAvg SNRObserversLast Heard
-

⏰ Recently Active

+

Recently Active

@@ -4077,7 +4077,7 @@ function destroy() { _stopRolesRefresh(); _stopScopesRefresh(); _analyticsData = ''; }).join(''); - el.innerHTML = '

⏰ Clock Health

' + + el.innerHTML = '

Clock Health

' + filterHtml + '
NodeRoleLast HeardPackets TodayAnalytics
' + '' + diff --git a/public/channel-qr.js b/public/channel-qr.js index b4da84be..8ccaade3 100644 --- a/public/channel-qr.js +++ b/public/channel-qr.js @@ -130,14 +130,17 @@ const copyBtn = document.createElement('button'); copyBtn.type = 'button'; copyBtn.className = 'channel-qr-copy'; - copyBtn.textContent = '📋 Copy Key'; + // #1648 M6: Phosphor sprite for copy/copied state (was 📋 / ✓). + const ICON_COPY = ''; + const ICON_CHECK = ''; + copyBtn.innerHTML = ICON_COPY + ' Copy Key'; copyBtn.style.cssText = 'margin-top:6px;'; copyBtn.addEventListener('click', function () { const text = secretHex; const done = function () { - const orig = copyBtn.textContent; - copyBtn.textContent = '✓ Copied'; - setTimeout(function () { copyBtn.textContent = orig; }, 1200); + const orig = copyBtn.innerHTML; + copyBtn.innerHTML = ICON_CHECK + ' Copied'; + setTimeout(function () { copyBtn.innerHTML = orig; }, 1200); }; if (root.navigator && root.navigator.clipboard && root.navigator.clipboard.writeText) { root.navigator.clipboard.writeText(text).then(done, function () { diff --git a/public/customize-v2.js b/public/customize-v2.js index 89d4404c..2a8388ca 100644 --- a/public/customize-v2.js +++ b/public/customize-v2.js @@ -2430,6 +2430,16 @@ arr[parseInt(path[1])][path[2]] = inp.value; setOverride('home', path[0], arr); } + // #1648 M6 (M5 CDP carry-forward): live re-render of the glyph + // preview when an operator types a new `ph:` token or + // legacy emoji into a steps.N.emoji input. Without this, the + // .cust-emoji-preview swatch only updated on Save+reload. + if (inp.classList.contains('cust-emoji-input')) { + var preview = inp.previousElementSibling; + if (preview && preview.classList && preview.classList.contains('cust-emoji-preview')) { + preview.innerHTML = renderConfigGlyph(inp.value); + } + } }); }); diff --git a/public/geofilter-builder.html b/public/geofilter-builder.html index 48556052..2a55ca84 100644 --- a/public/geofilter-builder.html +++ b/public/geofilter-builder.html @@ -73,7 +73,7 @@
0 points - +
diff --git a/public/index.html b/public/index.html index b42da335..4b1f79de 100644 --- a/public/index.html +++ b/public/index.html @@ -137,7 +137,7 @@