mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -833,20 +833,10 @@ const SignalCards = (function() {
|
||||
? createSignalIndicator(rssi, { compact: true })
|
||||
: '<span class="signal-strength-indicator compact no-data" title="No signal data available">--</span>';
|
||||
|
||||
// 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 = `
|
||||
<div class="signal-card-header">
|
||||
<div class="signal-card-badges">
|
||||
<span class="signal-proto-badge sensor">${sensorIcon} ${escapeHtml(msg.model || 'Unknown')}</span>
|
||||
<span class="signal-proto-badge sensor">${escapeHtml(msg.model || 'Unknown')}</span>
|
||||
<span class="signal-freq-badge">ID: ${escapeHtml(msg.id || 'N/A')}</span>
|
||||
${signalIndicator}
|
||||
</div>
|
||||
|
||||
@@ -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 `<span class="icon icon-wifi ${className || ''}" aria-label="WiFi">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
|
||||
<path d="M1.42 9a16 16 0 0 1 21.16 0"/>
|
||||
<path d="M8.53 16.11a6 6 0 0 1 6.95 0"/>
|
||||
<circle cx="12" cy="20" r="1" fill="currentColor" stroke="none"/>
|
||||
</svg>
|
||||
</span>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Bluetooth icon - standard rune
|
||||
*/
|
||||
bluetooth: function(className) {
|
||||
return `<span class="icon icon-bluetooth ${className || ''}" aria-label="Bluetooth">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6.5 6.5 17.5 17.5 12 22 12 2 17.5 6.5 6.5 17.5"/>
|
||||
</svg>
|
||||
</span>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cellular icon - ascending bars
|
||||
*/
|
||||
cellular: function(className) {
|
||||
return `<span class="icon icon-cellular ${className || ''}" aria-label="Cellular">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<rect x="2" y="16" width="4" height="6" rx="1"/>
|
||||
<rect x="8" y="12" width="4" height="10" rx="1"/>
|
||||
<rect x="14" y="8" width="4" height="14" rx="1"/>
|
||||
<rect x="20" y="4" width="4" height="18" rx="1" opacity="0.3"/>
|
||||
</svg>
|
||||
</span>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unknown/RF signal - generic wave
|
||||
*/
|
||||
signalUnknown: function(className) {
|
||||
return `<span class="icon icon-signal-unknown ${className || ''}" aria-label="Unknown signal">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<path d="M2 12c0-3 2-6 5-6s4 3 5 6c1 3 2 6 5 6s5-3 5-6"/>
|
||||
</svg>
|
||||
</span>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Recording indicator - filled circle
|
||||
*/
|
||||
recording: function(className) {
|
||||
return `<span class="icon icon-recording ${className || ''}" aria-label="Recording">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="12" cy="12" r="8"/>
|
||||
</svg>
|
||||
</span>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Anomaly indicator - filled circle (amber by default via CSS)
|
||||
*/
|
||||
anomaly: function(className) {
|
||||
return `<span class="icon icon-anomaly ${className || ''}" aria-label="Anomaly">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<circle cx="12" cy="12" r="6"/>
|
||||
</svg>
|
||||
</span>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Export icon - arrow out of box
|
||||
*/
|
||||
export: function(className) {
|
||||
return `<span class="icon icon-export ${className || ''}" aria-label="Export">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="17 8 12 3 7 8"/>
|
||||
<line x1="12" y1="3" x2="12" y2="15"/>
|
||||
</svg>
|
||||
</span>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh icon - circular arrows
|
||||
*/
|
||||
refresh: function(className) {
|
||||
return `<span class="icon icon-refresh ${className || ''}" aria-label="Refresh">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<polyline points="1 20 1 14 7 14"/>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||
</svg>
|
||||
</span>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1398,15 +1398,15 @@
|
||||
<!-- Capabilities Summary Bar -->
|
||||
<div id="tscmCapabilitiesBar" class="tscm-capabilities-bar" style="display: none;">
|
||||
<div class="cap-item" id="capWifi" title="WiFi Capability">
|
||||
<span class="cap-icon">📶</span>
|
||||
<span class="cap-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><circle cx="12" cy="20" r="1" fill="currentColor" stroke="none"/></svg></span>
|
||||
<span class="cap-status" id="capWifiStatus">--</span>
|
||||
</div>
|
||||
<div class="cap-item" id="capBt" title="Bluetooth Capability">
|
||||
<span class="cap-icon">🔵</span>
|
||||
<span class="cap-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6.5 6.5 17.5 17.5 12 22 12 2 17.5 6.5 6.5 17.5"/></svg></span>
|
||||
<span class="cap-status" id="capBtStatus">--</span>
|
||||
</div>
|
||||
<div class="cap-item" id="capRf" title="RF/SDR Capability">
|
||||
<span class="cap-icon">📡</span>
|
||||
<span class="cap-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M2 12c0-3 2-6 5-6s4 3 5 6c1 3 2 6 5 6s5-3 5-6"/></svg></span>
|
||||
<span class="cap-status" id="capRfStatus">--</span>
|
||||
</div>
|
||||
<div class="cap-item" id="capRoot" title="Privilege Level">
|
||||
@@ -1505,7 +1505,10 @@
|
||||
<span class="badge" id="tscmThreatCount">0</span>
|
||||
</div>
|
||||
<div class="tscm-panel-content" id="tscmThreatList">
|
||||
<div class="tscm-empty">No threats detected</div>
|
||||
<div class="tscm-empty">
|
||||
<div class="tscm-empty-primary">No anomalies detected</div>
|
||||
<div class="tscm-empty-secondary">Start a sweep to scan for signals of interest</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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 = '<div class="tscm-empty">No flagged findings yet</div>';
|
||||
panel.innerHTML = '<div class="tscm-empty"><div class="tscm-empty-primary">Monitoring active — nothing flagged</div><div class="tscm-empty-secondary">Signals are being analyzed against baseline thresholds. This does not rule out passive or dormant devices.</div></div>';
|
||||
} 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 = '<div class="tscm-empty">No threats detected</div>';
|
||||
threatList.innerHTML = '<div class="tscm-empty"><div class="tscm-empty-primary">Monitoring active — nothing flagged</div><div class="tscm-empty-secondary">Signals are being analyzed against baseline thresholds. This does not rule out passive or dormant devices.</div></div>';
|
||||
} else {
|
||||
threatList.innerHTML = '<div class="tscm-threat-list">' + tscmThreats.map(t => `
|
||||
<div class="tscm-threat-item ${t.severity}">
|
||||
@@ -9797,19 +9800,19 @@
|
||||
<h4>Available Detection Methods</h4>
|
||||
<div class="capabilities-grid">
|
||||
<div class="cap-detail-item ${wifiAvailable ? 'available' : 'unavailable'}">
|
||||
<span class="cap-icon">📶</span>
|
||||
<span class="cap-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><circle cx="12" cy="20" r="1" fill="currentColor" stroke="none"/></svg></span>
|
||||
<span class="cap-name">WiFi Scanning</span>
|
||||
<span class="cap-status">${wifiAvailable ? caps.wifi.mode : 'Not Available'}</span>
|
||||
${caps.wifi?.interface ? `<span class="cap-detail">${escapeHtml(caps.wifi.interface)}</span>` : ''}
|
||||
</div>
|
||||
<div class="cap-detail-item ${btAvailable ? 'available' : 'unavailable'}">
|
||||
<span class="cap-icon">🔵</span>
|
||||
<span class="cap-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6.5 6.5 17.5 17.5 12 22 12 2 17.5 6.5 6.5 17.5"/></svg></span>
|
||||
<span class="cap-name">Bluetooth Scanning</span>
|
||||
<span class="cap-status">${btAvailable ? caps.bluetooth.mode : 'Not Available'}</span>
|
||||
${caps.bluetooth?.adapter ? `<span class="cap-detail">${escapeHtml(caps.bluetooth.adapter)}</span>` : ''}
|
||||
</div>
|
||||
<div class="cap-detail-item ${rfAvailable ? 'available' : 'unavailable'}">
|
||||
<span class="cap-icon">📡</span>
|
||||
<span class="cap-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M2 12c0-3 2-6 5-6s4 3 5 6c1 3 2 6 5 6s5-3 5-6"/></svg></span>
|
||||
<span class="cap-name">RF/SDR Scanning</span>
|
||||
<span class="cap-status">${rfAvailable ? 'Available' : 'Not Available'}</span>
|
||||
${caps.rf?.device_type ? `<span class="cap-detail">${escapeHtml(caps.rf.device_type)}</span>` : ''}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<button class="preset-btn" onclick="refreshTscmDevices()" style="width: 100%; margin-top: 8px; font-size: 10px;">
|
||||
🔄 Refresh Devices
|
||||
<span class="icon icon-refresh icon--sm" style="margin-right: 4px;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg></span>Refresh Devices
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,28 +90,28 @@
|
||||
<div class="scanner-info">
|
||||
<div class="scanner-status" id="tscmProgressLabel">INITIALIZING</div>
|
||||
<div class="scanner-devices">
|
||||
<span class="device-indicator" id="tscmWifiIndicator" title="WiFi">📶</span>
|
||||
<span class="device-indicator" id="tscmBtIndicator" title="Bluetooth">🔵</span>
|
||||
<span class="device-indicator" id="tscmRfIndicator" title="RF/SDR">📡</span>
|
||||
<span class="device-indicator-icon" id="tscmWifiIndicator" title="WiFi"><span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><circle cx="12" cy="20" r="1" fill="currentColor" stroke="none"/></svg></span></span>
|
||||
<span class="device-indicator-icon" id="tscmBtIndicator" title="Bluetooth"><span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6.5 6.5 17.5 17.5 12 22 12 2 17.5 6.5 6.5 17.5"/></svg></span></span>
|
||||
<span class="device-indicator-icon" id="tscmRfIndicator" title="RF/SDR"><span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M2 12c0-3 2-6 5-6s4 3 5 6c1 3 2 6 5 6s5-3 5-6"/></svg></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="preset-btn" id="tscmReportBtn" onclick="generateTscmReport()" style="display: none; width: 100%; margin-top: 8px; background: var(--accent-cyan); color: #000; font-weight: 600;">
|
||||
📄 Generate Report
|
||||
Generate Report
|
||||
</button>
|
||||
|
||||
<!-- Export Options -->
|
||||
<div id="tscmExportSection" style="display: none; margin-top: 8px;">
|
||||
<div style="display: flex; gap: 6px;">
|
||||
<button class="preset-btn" onclick="tscmDownloadPdf()" style="flex: 1; font-size: 10px;">
|
||||
📄 PDF
|
||||
<span class="icon icon-export icon--sm" style="margin-right: 2px;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg></span>PDF
|
||||
</button>
|
||||
<button class="preset-btn" onclick="tscmDownloadAnnex('json')" style="flex: 1; font-size: 10px;">
|
||||
📊 JSON
|
||||
<span class="icon icon-export icon--sm" style="margin-right: 2px;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg></span>JSON
|
||||
</button>
|
||||
<button class="preset-btn" onclick="tscmDownloadAnnex('csv')" style="flex: 1; font-size: 10px;">
|
||||
📈 CSV
|
||||
<span class="icon icon-export icon--sm" style="margin-right: 2px;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg></span>CSV
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -143,10 +143,10 @@
|
||||
<input type="text" id="tscmMeetingName" placeholder="Meeting name (optional)">
|
||||
</div>
|
||||
<button class="run-btn" id="tscmStartMeetingBtn" onclick="tscmStartMeeting()" style="width: 100%; padding: 8px;">
|
||||
🎯 Start Meeting Window
|
||||
Start Meeting Window
|
||||
</button>
|
||||
<button class="stop-btn" id="tscmEndMeetingBtn" onclick="tscmEndMeeting()" style="width: 100%; padding: 8px; display: none;">
|
||||
⏹ End Meeting Window
|
||||
End Meeting Window
|
||||
</button>
|
||||
<div style="font-size: 9px; color: var(--text-muted); margin-top: 4px;">
|
||||
Devices detected during meetings get flagged
|
||||
@@ -159,16 +159,16 @@
|
||||
<h3 style="margin-bottom: 10px;">Tools</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 6px;">
|
||||
<button class="preset-btn" onclick="tscmShowCapabilities()" style="font-size: 10px; padding: 8px;">
|
||||
⚙️ Capabilities
|
||||
Capabilities
|
||||
</button>
|
||||
<button class="preset-btn" onclick="tscmShowKnownDevices()" style="font-size: 10px; padding: 8px;">
|
||||
✅ Known Devices
|
||||
Known Devices
|
||||
</button>
|
||||
<button class="preset-btn" onclick="tscmShowCases()" style="font-size: 10px; padding: 8px;">
|
||||
📁 Cases
|
||||
Cases
|
||||
</button>
|
||||
<button class="preset-btn" onclick="tscmShowPlaybooks()" style="font-size: 10px; padding: 8px;">
|
||||
📋 Playbooks
|
||||
Playbooks
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user