diff --git a/public/bottom-nav.js b/public/bottom-nav.js index f46b84e6..1aaf7fac 100644 --- a/public/bottom-nav.js +++ b/public/bottom-nav.js @@ -41,35 +41,44 @@ if (typeof document === 'undefined') return; - // 5 primary tabs + the More toggle. Each entry: { route, hash, label, icon }. - // For More, hash is null (not a route). + // Phosphor sprite ref helper (#1648 M1). + // Returns an inline SVG that uses the bundled sprite at /icons/phosphor-sprite.svg. + // Inherits color via currentColor and size via the surrounding font-size. + function phIconHTML(name) { + return ''; + } + + // 5 primary tabs + the More toggle. Each entry: { route, hash, label, ph }. + // For More, hash is null (not a route). `ph` is the Phosphor icon id + // (no "ph-" prefix) β€” see public/icons/phosphor-sprite.svg. var TABS = [ - { route: 'home', hash: '#/home', label: 'Home', icon: '🏠' }, - { route: 'packets', hash: '#/packets', label: 'Packets', icon: 'πŸ“¦' }, - { route: 'live', hash: '#/live', label: 'Live', icon: 'πŸ”΄' }, - { route: 'map', hash: '#/map', label: 'Map', icon: 'πŸ—ΊοΈ' }, - { route: 'channels', hash: '#/channels', label: 'Channels', icon: 'πŸ’¬' }, - { route: 'more', hash: null, label: 'More', icon: '☰' }, + { route: 'home', hash: '#/home', label: 'Home', ph: 'house' }, + { route: 'packets', hash: '#/packets', label: 'Packets', ph: 'package' }, + { route: 'live', hash: '#/live', label: 'Live', ph: 'broadcast' }, + { route: 'map', hash: '#/map', label: 'Map', ph: 'map-trifold' }, + { route: 'channels', hash: '#/channels', label: 'Channels', ph: 'chat-circle' }, + { route: 'more', hash: null, label: 'More', ph: 'list' }, ]; // Long-tail routes surfaced in the More sheet. Mirrors data-route values // from the existing top-nav (public/index.html). Order matches what // operators expect from the desktop top-nav. // - // ⚠️ MANUAL SYNC REQUIRED ⚠️ + // !! MANUAL SYNC REQUIRED !! // This list is intentionally hardcoded (not generated from // `.top-nav .nav-link[data-route]`) because the top-nav HTML is in // mid-rewrite and not a reliable single-source-of-truth. If you add a // new top-nav route (e.g. a future "Lab" page), you MUST also append - // it here, or it will be unreachable on phones at ≀768px (the - // hamburger is hidden at that breakpoint β€” see bottom-nav.css). + // it here, or it will be unreachable on phones at <=768px (the + // hamburger is hidden at that breakpoint -- see bottom-nav.css). var MORE_ROUTES = [ - { route: 'nodes', hash: '#/nodes', label: 'Nodes', icon: 'πŸ–₯️' }, - { route: 'tools', hash: '#/tools', label: 'Tools', icon: 'πŸ› οΈ' }, - { route: 'observers', hash: '#/observers', label: 'Observers', icon: 'πŸ‘οΈ' }, - { route: 'analytics', hash: '#/analytics', label: 'Analytics', icon: 'πŸ“Š' }, - { route: 'perf', hash: '#/perf', label: 'Perf', icon: '⚑' }, - { route: 'audio-lab', hash: '#/audio-lab', label: 'Audio Lab', icon: '🎡' }, + { route: 'nodes', hash: '#/nodes', label: 'Nodes', ph: 'monitor' }, + { route: 'tools', hash: '#/tools', label: 'Tools', ph: 'wrench' }, + { route: 'observers', hash: '#/observers', label: 'Observers', ph: 'eye' }, + { route: 'analytics', hash: '#/analytics', label: 'Analytics', ph: 'chart-bar' }, + { route: 'perf', hash: '#/perf', label: 'Perf', ph: 'lightning' }, + { route: 'audio-lab', hash: '#/audio-lab', label: 'Audio Lab', ph: 'music-note' }, ]; var SHEET_ID = 'bottomNavMoreSheet'; @@ -115,7 +124,7 @@ var ic = document.createElement('span'); ic.className = 'bottom-nav-icon'; ic.setAttribute('aria-hidden', 'true'); - ic.textContent = t.icon; + ic.innerHTML = phIconHTML(t.ph); var lb = document.createElement('span'); lb.className = 'bottom-nav-label'; @@ -202,7 +211,7 @@ var ic = document.createElement('span'); ic.className = 'bottom-nav-sheet-icon'; ic.setAttribute('aria-hidden', 'true'); - ic.textContent = r.icon; + ic.innerHTML = phIconHTML(r.ph); var lb = document.createElement('span'); lb.className = 'bottom-nav-sheet-label'; @@ -262,7 +271,7 @@ var btn = document.querySelector('[data-bottom-nav-dark-toggle]'); if (!btn) return; var isDark = document.documentElement.getAttribute('data-theme') === 'dark'; - btn.querySelector('.bottom-nav-sheet-icon').textContent = isDark ? 'πŸŒ™' : 'β˜€οΈ'; + btn.querySelector('.bottom-nav-sheet-icon').innerHTML = phIconHTML(isDark ? 'moon' : 'sun'); btn.querySelector('.bottom-nav-sheet-label').textContent = isDark ? 'Light mode' : 'Dark mode'; } diff --git a/public/icons/README.md b/public/icons/README.md new file mode 100644 index 00000000..015f9c33 --- /dev/null +++ b/public/icons/README.md @@ -0,0 +1,51 @@ +# 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 `` per icon + (regular weight, `viewBox="0 0 256 256"` to match Phosphor's native grid). + +## Markup pattern + +```html + +``` + +CSS helper (defined in `public/style.css`): + +```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 + + (or `assets/fill/NAME.svg` for the rare filled-circle / star-fill cases). +2. Append a `…` to + `phosphor-sprite.svg`. Strip the outer `` wrapper and any `fill=` attrs + on the inner `` (we want `currentColor` from the parent). +3. Reference it with ``. + +## 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). diff --git a/public/icons/phosphor-sprite.svg b/public/icons/phosphor-sprite.svg new file mode 100644 index 00000000..67c5c3da --- /dev/null +++ b/public/icons/phosphor-sprite.svg @@ -0,0 +1,36 @@ + \ No newline at end of file diff --git a/public/index.html b/public/index.html index dd566f03..b42da335 100644 --- a/public/index.html +++ b/public/index.html @@ -120,14 +120,14 @@ Home Packets Map - πŸ”΄ Live + Live Channels Nodes Tools Observers Analytics - ⚑ Perf - 🎡 Lab + Perf + Lab - - + + - + diff --git a/public/mobile-page-actions.js b/public/mobile-page-actions.js index 30a51836..248c90a6 100644 --- a/public/mobile-page-actions.js +++ b/public/mobile-page-actions.js @@ -152,8 +152,8 @@ const mirrors = [ { id: 'favToggle', icon: '⭐', label: 'Favorites' }, - { id: 'searchToggle', icon: 'πŸ”', label: 'Search' }, - { id: 'customizeToggle', icon: '🎨', label: 'Customize' }, + { id: 'searchToggle', ph: 'magnifying-glass', label: 'Search' }, + { id: 'customizeToggle', ph: 'palette', label: 'Customize' }, ]; const sep = sheet.querySelector('.bottom-nav-sheet-sep'); @@ -169,7 +169,14 @@ const ic = document.createElement('span'); ic.className = 'bottom-nav-sheet-icon'; ic.setAttribute('aria-hidden', 'true'); - ic.textContent = m.icon; + if (m.ph) { + // #1648 M1 β€” Phosphor sprite ref. + ic.innerHTML = + ''; + } else { + ic.textContent = m.icon; + } const lb = document.createElement('span'); lb.className = 'bottom-nav-sheet-label'; diff --git a/public/nav-drawer.js b/public/nav-drawer.js index b26ae09c..37820d21 100644 --- a/public/nav-drawer.js +++ b/public/nav-drawer.js @@ -54,16 +54,22 @@ var prevFocus = null; // Long-tail routes mirror PR #1174 / bottom-nav.js MORE_ROUTES exactly. - // ⚠️ Keep in sync with public/bottom-nav.js MORE_ROUTES. + // !! Keep in sync with public/bottom-nav.js MORE_ROUTES. + // `ph` is the Phosphor icon id (no "ph-" prefix); see public/icons/phosphor-sprite.svg. var ROUTES = [ - { route: 'nodes', hash: '#/nodes', label: 'Nodes', icon: 'πŸ–₯️' }, - { route: 'tools', hash: '#/tools', label: 'Tools', icon: 'πŸ› οΈ' }, - { route: 'observers', hash: '#/observers', label: 'Observers', icon: 'πŸ‘οΈ' }, - { route: 'analytics', hash: '#/analytics', label: 'Analytics', icon: 'πŸ“Š' }, - { route: 'perf', hash: '#/perf', label: 'Perf', icon: '⚑' }, - { route: 'audio-lab', hash: '#/audio-lab', label: 'Audio Lab', icon: '🎡' }, + { route: 'nodes', hash: '#/nodes', label: 'Nodes', ph: 'monitor' }, + { route: 'tools', hash: '#/tools', label: 'Tools', ph: 'wrench' }, + { route: 'observers', hash: '#/observers', label: 'Observers', ph: 'eye' }, + { route: 'analytics', hash: '#/analytics', label: 'Analytics', ph: 'chart-bar' }, + { route: 'perf', hash: '#/perf', label: 'Perf', ph: 'lightning' }, + { route: 'audio-lab', hash: '#/audio-lab', label: 'Audio Lab', ph: 'music-note' }, ]; + function phIconHTML(name) { + return ''; + } + var EDGE_PX = 44; // pointerdown must start within left N px (drawer trigger zone) var EDGE_MIN_PX = 24; // first N px reserved for iOS Safari back-swipe (do not claim) var NARROW_MAX = 768; // Option A: disabled at ≀ this width @@ -130,7 +136,7 @@ var ic = document.createElement('span'); ic.className = 'nav-drawer-icon'; ic.setAttribute('aria-hidden', 'true'); - ic.textContent = r.icon; + ic.innerHTML = phIconHTML(r.ph); var lb = document.createElement('span'); lb.className = 'nav-drawer-label'; diff --git a/public/observer-detail.js b/public/observer-detail.js index 5b62b85d..79172fae 100644 --- a/public/observer-detail.js +++ b/public/observer-detail.js @@ -97,7 +97,7 @@ window.ObserverDetailNaiveBanner = {