From 256c30e7cd9261ced2dd445a2ea352ec5306c849 Mon Sep 17 00:00:00 2001 From: Smittix Date: Tue, 20 Jan 2026 22:05:58 +0000 Subject: [PATCH] Add minimal SVG icon system for signal types Replace emoji icons with inline SVG for WiFi, Bluetooth, and RF/SDR indicators. Icons are standard symbols (arc, rune, wave) designed for screenshot legibility in reports. - Add Icons utility object in utils.js with SVG generators - Add icon CSS system with sizing variants and state animations - Update TSCM scanner indicators and capabilities bar - Remove decorative sensor type emojis (text labels suffice) - Keep signal strength SVG bars (already implemented) Co-Authored-By: Claude Opus 4.5 --- static/css/modes/tscm.css | 184 +++++++++++++++++++++++++++ static/js/components/signal-cards.js | 12 +- static/js/core/utils.js | 122 ++++++++++++++++++ templates/index.html | 23 ++-- templates/partials/modes/tscm.html | 28 ++-- 5 files changed, 334 insertions(+), 35 deletions(-) diff --git a/static/css/modes/tscm.css b/static/css/modes/tscm.css index 7b67623..a5d167f 100644 --- a/static/css/modes/tscm.css +++ b/static/css/modes/tscm.css @@ -631,6 +631,18 @@ color: var(--text-muted); font-size: 12px; } +.tscm-empty-primary { + font-weight: 500; + color: var(--text-secondary); + margin-bottom: 6px; +} +.tscm-empty-secondary { + font-size: 10px; + color: var(--text-muted); + max-width: 280px; + margin: 0 auto; + line-height: 1.4; +} /* Futuristic Scanner Progress */ .tscm-scanner-progress { @@ -795,6 +807,17 @@ } .cap-icon { font-size: 14px; + width: 16px; + height: 16px; + display: inline-flex; + align-items: center; + justify-content: center; +} +.cap-icon svg { + width: 100%; + height: 100%; + stroke: currentColor; + fill: none; } .cap-status { color: var(--text-muted); @@ -1164,6 +1187,14 @@ font-size: 24px; display: block; margin-bottom: 8px; + width: 24px; + height: 24px; +} +.cap-detail-item .cap-icon svg { + width: 100%; + height: 100%; + stroke: currentColor; + fill: none; } .cap-detail-item .cap-name { font-weight: 600; @@ -1256,3 +1287,156 @@ border-radius: 3px; margin-left: 8px; } + +/* ========================================================================== + Icon System + Minimal, functional icons that replace words. No decoration. + Designed for screenshot legibility in reports. + ========================================================================== */ + +.icon { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + flex-shrink: 0; +} + +.icon svg { + width: 100%; + height: 100%; + fill: currentColor; +} + +.icon--sm { + width: 12px; + height: 12px; +} + +.icon--lg { + width: 20px; + height: 20px; +} + +/* Signal Type Icons */ +.icon-wifi svg, +.icon-bluetooth svg, +.icon-cellular svg, +.icon-signal-unknown svg { + fill: var(--text-secondary); +} + +/* Recording State */ +.icon-recording { + color: #ff3366; +} + +.icon-recording.active svg { + animation: recording-pulse 1.5s ease-in-out infinite; +} + +@keyframes recording-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.4; } +} + +/* Anomaly Indicator */ +.icon-anomaly { + color: #ff9933; +} + +.icon-anomaly.critical { + color: #ff3366; +} + +/* Export Icon */ +.icon-export { + color: var(--text-secondary); +} + +/* Device Indicators with Icons */ +.device-indicator-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + opacity: 0.3; + transition: opacity 0.3s, transform 0.3s; +} + +.device-indicator-icon.active { + opacity: 1; + animation: device-pulse 1.5s ease-in-out infinite; +} + +.device-indicator-icon.inactive { + opacity: 0.2; +} + +.device-indicator-icon .icon { + width: 18px; + height: 18px; +} + +/* Protocol badge with icon */ +.protocol-icon-badge { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 10px; + padding: 2px 6px; + background: rgba(255, 255, 255, 0.1); + border-radius: 3px; + text-transform: uppercase; +} + +.protocol-icon-badge .icon { + width: 12px; + height: 12px; +} + +/* Recording status indicator */ +.recording-status { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 11px; +} + +.recording-status .icon-recording { + width: 10px; + height: 10px; +} + +.recording-status.active { + color: #ff3366; + font-weight: 600; +} + +/* Anomaly flag in device items */ +.anomaly-flag { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 6px; + border-radius: 3px; + font-size: 9px; + font-weight: 600; + text-transform: uppercase; +} + +.anomaly-flag.needs-review { + background: rgba(255, 153, 51, 0.2); + color: #ff9933; +} + +.anomaly-flag.high-interest { + background: rgba(255, 51, 51, 0.2); + color: #ff3333; +} + +.anomaly-flag .icon { + width: 10px; + height: 10px; +} diff --git a/static/js/components/signal-cards.js b/static/js/components/signal-cards.js index de7f18c..2f3187e 100644 --- a/static/js/components/signal-cards.js +++ b/static/js/components/signal-cards.js @@ -833,20 +833,10 @@ const SignalCards = (function() { ? createSignalIndicator(rssi, { compact: true }) : '--'; - // Determine sensor type icon - let sensorIcon = '📡'; - const model = (msg.model || '').toLowerCase(); - if (model.includes('weather') || msg.temperature !== undefined) sensorIcon = '🌡️'; - else if (model.includes('door') || model.includes('window')) sensorIcon = '🚪'; - else if (model.includes('motion') || model.includes('pir')) sensorIcon = '🚶'; - else if (model.includes('smoke') || model.includes('fire')) sensorIcon = '🔥'; - else if (model.includes('water') || model.includes('leak')) sensorIcon = '💧'; - else if (model.includes('car') || model.includes('tire') || model.includes('tpms')) sensorIcon = '🚗'; - card.innerHTML = `
- ${sensorIcon} ${escapeHtml(msg.model || 'Unknown')} + ${escapeHtml(msg.model || 'Unknown')} ID: ${escapeHtml(msg.id || 'N/A')} ${signalIndicator}
diff --git a/static/js/core/utils.js b/static/js/core/utils.js index e741de3..6f9676a 100644 --- a/static/js/core/utils.js +++ b/static/js/core/utils.js @@ -271,3 +271,125 @@ function clamp(num, min, max) { function mapRange(value, inMin, inMax, outMin, outMax) { return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; } + +// ============== ICON SYSTEM ============== +// Minimal SVG icons. Each returns HTML string. +// Designed for screenshot legibility - standard symbols only. + +const Icons = { + /** + * WiFi icon - standard arc/fan shape + */ + wifi: function(className) { + return ` + + + + + + + `; + }, + + /** + * Bluetooth icon - standard rune + */ + bluetooth: function(className) { + return ` + + + + `; + }, + + /** + * Cellular icon - ascending bars + */ + cellular: function(className) { + return ` + + + + + + + `; + }, + + /** + * Unknown/RF signal - generic wave + */ + signalUnknown: function(className) { + return ` + + + + `; + }, + + /** + * Recording indicator - filled circle + */ + recording: function(className) { + return ` + + + + `; + }, + + /** + * Anomaly indicator - filled circle (amber by default via CSS) + */ + anomaly: function(className) { + return ` + + + + `; + }, + + /** + * Export icon - arrow out of box + */ + export: function(className) { + return ` + + + + + + `; + }, + + /** + * Refresh icon - circular arrows + */ + refresh: function(className) { + return ` + + + + + + `; + }, + + /** + * Get icon by signal type + * Maps protocol names to appropriate icons + */ + forSignalType: function(type, className) { + const t = (type || '').toLowerCase(); + if (t.includes('wifi') || t.includes('802.11')) { + return this.wifi(className); + } + if (t.includes('bluetooth') || t.includes('bt') || t.includes('ble')) { + return this.bluetooth(className); + } + if (t.includes('cellular') || t.includes('lte') || t.includes('gsm') || t.includes('5g')) { + return this.cellular(className); + } + return this.signalUnknown(className); + } +}; diff --git a/templates/index.html b/templates/index.html index f92900d..cbf6ad3 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1398,15 +1398,15 @@
@@ -8293,7 +8296,7 @@ assessment = `MODERATE CONCERN: ${needsReview.length} item(s) requiring further review`; assessmentClass = 'needs-review'; } else { - assessment = 'LOW CONCERN: No significant threats detected. Environment appears normal.'; + assessment = 'LOW CONCERN: No anomalies flagged by automated scan. Manual inspection recommended for comprehensive assessment.'; } // Helper function to render device row @@ -8986,7 +8989,7 @@ function updateHighInterestPanel() { const panel = document.getElementById('tscmThreatList'); if (tscmHighInterestDevices.length === 0) { - panel.innerHTML = '
No flagged findings yet
'; + panel.innerHTML = '
Monitoring active — nothing flagged
Signals are being analyzed against baseline thresholds. This does not rule out passive or dormant devices.
'; } else { // Sort by score (highest first) const sorted = [...tscmHighInterestDevices].sort((a, b) => b.score - a.score); @@ -9475,7 +9478,7 @@ // Update threats list const threatList = document.getElementById('tscmThreatList'); if (tscmThreats.length === 0) { - threatList.innerHTML = '
No threats detected
'; + threatList.innerHTML = '
Monitoring active — nothing flagged
Signals are being analyzed against baseline thresholds. This does not rule out passive or dormant devices.
'; } else { threatList.innerHTML = '
' + tscmThreats.map(t => `
@@ -9797,19 +9800,19 @@

Available Detection Methods

- 📶 + WiFi Scanning ${wifiAvailable ? caps.wifi.mode : 'Not Available'} ${caps.wifi?.interface ? `${escapeHtml(caps.wifi.interface)}` : ''}
- 🔵 + Bluetooth Scanning ${btAvailable ? caps.bluetooth.mode : 'Not Available'} ${caps.bluetooth?.adapter ? `${escapeHtml(caps.bluetooth.adapter)}` : ''}
- 📡 + RF/SDR Scanning ${rfAvailable ? 'Available' : 'Not Available'} ${caps.rf?.device_type ? `${escapeHtml(caps.rf.device_type)}` : ''} diff --git a/templates/partials/modes/tscm.html b/templates/partials/modes/tscm.html index e59c5cf..9a985e7 100644 --- a/templates/partials/modes/tscm.html +++ b/templates/partials/modes/tscm.html @@ -62,7 +62,7 @@
@@ -90,28 +90,28 @@
INITIALIZING
- 📶 - 🔵 - 📡 + + +
@@ -143,10 +143,10 @@
Devices detected during meetings get flagged @@ -159,16 +159,16 @@

Tools