mirror of
https://github.com/smittix/intercept.git
synced 2026-07-05 16:18:12 -07:00
Improve cross-app UX: accessibility, mode consistency, and render performance
This commit is contained in:
+110
-116
@@ -52,27 +52,43 @@
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/responsive.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/global-nav.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/aprs.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/tscm.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/analytics.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/signal-cards.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/signal-timeline.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/activity-timeline.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/device-cards.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/proximity-viz.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/spy-stations.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/meshtastic.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/sstv.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/weather-satellite.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/sstv-general.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/gps.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/subghz.css') }}?v={{ version }}&r=subghz_layout9">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/bt_locate.css') }}?v={{ version }}&r=btlocate4">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/space-weather.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/function-strip.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/toast.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/ux-platform.css') }}">
|
||||
<script>
|
||||
window.INTERCEPT_MODE_STYLE_MAP = {
|
||||
aprs: "{{ url_for('static', filename='css/modes/aprs.css') }}",
|
||||
tscm: "{{ url_for('static', filename='css/modes/tscm.css') }}",
|
||||
analytics: "{{ url_for('static', filename='css/modes/analytics.css') }}",
|
||||
spystations: "{{ url_for('static', filename='css/modes/spy-stations.css') }}",
|
||||
meshtastic: "{{ url_for('static', filename='css/modes/meshtastic.css') }}",
|
||||
sstv: "{{ url_for('static', filename='css/modes/sstv.css') }}",
|
||||
weathersat: "{{ url_for('static', filename='css/modes/weather-satellite.css') }}",
|
||||
sstv_general: "{{ url_for('static', filename='css/modes/sstv-general.css') }}",
|
||||
gps: "{{ url_for('static', filename='css/modes/gps.css') }}",
|
||||
subghz: "{{ url_for('static', filename='css/modes/subghz.css') }}?v={{ version }}&r=subghz_layout9",
|
||||
bt_locate: "{{ url_for('static', filename='css/modes/bt_locate.css') }}?v={{ version }}&r=btlocate4",
|
||||
spaceweather: "{{ url_for('static', filename='css/modes/space-weather.css') }}"
|
||||
};
|
||||
window.INTERCEPT_MODE_STYLE_LOADED = {};
|
||||
window.ensureModeStyles = function(mode) {
|
||||
const href = window.INTERCEPT_MODE_STYLE_MAP ? window.INTERCEPT_MODE_STYLE_MAP[mode] : null;
|
||||
if (!href) return;
|
||||
if (window.INTERCEPT_MODE_STYLE_LOADED[href]) return;
|
||||
window.INTERCEPT_MODE_STYLE_LOADED[href] = true;
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = href;
|
||||
link.dataset.modeStyle = mode;
|
||||
document.head.appendChild(link);
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -921,30 +937,10 @@
|
||||
<div class="bt-main-area">
|
||||
<!-- Left side panels -->
|
||||
<div class="bt-side-panels">
|
||||
<div class="wifi-visual-panel bt-side-panel">
|
||||
<div class="wifi-visual-panel bt-side-panel bt-tracker-panel">
|
||||
<h5>Tracker Detection</h5>
|
||||
<div id="btTrackerList" style="font-size: 11px; max-height: 200px; overflow-y: auto;">
|
||||
<div style="color: var(--text-dim); padding: 10px; text-align: center;">Monitoring for AirTags, Tiles...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wifi-visual-panel bt-side-panel">
|
||||
<h5>Signal Distribution</h5>
|
||||
<div class="bt-signal-dist" id="btSignalDist">
|
||||
<div class="signal-range"><span>Strong (-50+)</span>
|
||||
<div class="signal-bar-bg">
|
||||
<div class="signal-bar strong" id="btSignalStrong" style="width: 0%;"></div>
|
||||
</div><span id="btSignalStrongCount">0</span>
|
||||
</div>
|
||||
<div class="signal-range"><span>Medium (-70)</span>
|
||||
<div class="signal-bar-bg">
|
||||
<div class="signal-bar medium" id="btSignalMedium" style="width: 0%;"></div>
|
||||
</div><span id="btSignalMediumCount">0</span>
|
||||
</div>
|
||||
<div class="signal-range"><span>Weak (-90)</span>
|
||||
<div class="signal-bar-bg">
|
||||
<div class="signal-bar weak" id="btSignalWeak" style="width: 0%;"></div>
|
||||
</div><span id="btSignalWeakCount">0</span>
|
||||
</div>
|
||||
<div id="btTrackerList" class="bt-tracker-list">
|
||||
<div class="app-collection-state is-empty">Monitoring for AirTags, Tiles...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -999,6 +995,26 @@
|
||||
<span class="bt-summary-value" id="btSummaryStrongest">--</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bt-list-signal-strip">
|
||||
<div class="bt-list-signal-title">Signal Distribution</div>
|
||||
<div class="bt-signal-dist bt-signal-dist-compact" id="btSignalDist">
|
||||
<div class="signal-range"><span>Strong</span>
|
||||
<div class="signal-bar-bg">
|
||||
<div class="signal-bar strong" id="btSignalStrong" style="width: 0%;"></div>
|
||||
</div><span id="btSignalStrongCount">0</span>
|
||||
</div>
|
||||
<div class="signal-range"><span>Medium</span>
|
||||
<div class="signal-bar-bg">
|
||||
<div class="signal-bar medium" id="btSignalMedium" style="width: 0%;"></div>
|
||||
</div><span id="btSignalMediumCount">0</span>
|
||||
</div>
|
||||
<div class="signal-range"><span>Weak</span>
|
||||
<div class="signal-bar-bg">
|
||||
<div class="signal-bar weak" id="btSignalWeak" style="width: 0%;"></div>
|
||||
</div><span id="btSignalWeakCount">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bt-device-toolbar">
|
||||
<input type="search" id="btDeviceSearch" class="bt-device-search" placeholder="Filter by name, MAC, manufacturer...">
|
||||
</div>
|
||||
@@ -1010,9 +1026,7 @@
|
||||
<button class="bt-filter-btn" data-filter="trackers">Trackers</button>
|
||||
</div>
|
||||
<div class="wifi-device-list-content" id="btDeviceListContent">
|
||||
<div style="color: var(--text-dim); text-align: center; padding: 30px;">
|
||||
Start scanning to discover Bluetooth devices
|
||||
</div>
|
||||
<div class="app-collection-state is-empty">Start scanning to discover Bluetooth devices</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3394,12 +3408,30 @@
|
||||
|
||||
// Mode from query string (e.g., /?mode=wifi)
|
||||
let pendingStartMode = null;
|
||||
const validModes = new Set([
|
||||
'pager', 'sensor', 'rtlamr', 'aprs', 'listening',
|
||||
'spystations', 'meshtastic', 'wifi', 'bluetooth', 'bt_locate',
|
||||
'tscm', 'satellite', 'sstv', 'weathersat', 'sstv_general', 'gps', 'websdr', 'subghz',
|
||||
'analytics', 'spaceweather'
|
||||
]);
|
||||
const modeCatalog = {
|
||||
pager: { label: 'Pager', indicator: 'PAGER', outputTitle: 'Pager Decoder', group: 'signals' },
|
||||
sensor: { label: '433MHz', indicator: '433MHZ', outputTitle: '433MHz Sensor Monitor', group: 'signals' },
|
||||
rtlamr: { label: 'Meters', indicator: 'METERS', outputTitle: 'Utility Meter Monitor', group: 'signals' },
|
||||
listening: { label: 'Listening Post', indicator: 'LISTENING POST', outputTitle: 'Listening Post', group: 'signals' },
|
||||
subghz: { label: 'SubGHz', indicator: 'SUBGHZ', outputTitle: 'SubGHz Transceiver', group: 'signals' },
|
||||
aprs: { label: 'APRS', indicator: 'APRS', outputTitle: 'APRS Tracker', group: 'tracking' },
|
||||
gps: { label: 'GPS', indicator: 'GPS', outputTitle: 'GPS Receiver', group: 'tracking' },
|
||||
satellite: { label: 'Satellite', indicator: 'SATELLITE', outputTitle: 'Satellite Monitor', group: 'space' },
|
||||
sstv: { label: 'ISS SSTV', indicator: 'ISS SSTV', outputTitle: 'ISS SSTV Decoder', group: 'space' },
|
||||
weathersat: { label: 'Weather Sat', indicator: 'WEATHER SAT', outputTitle: 'Weather Satellite Decoder', group: 'space' },
|
||||
sstv_general: { label: 'HF SSTV', indicator: 'HF SSTV', outputTitle: 'HF SSTV Decoder', group: 'space' },
|
||||
spaceweather: { label: 'Space Weather', indicator: 'SPACE WX', outputTitle: 'Space Weather Monitor', group: 'space' },
|
||||
wifi: { label: 'WiFi', indicator: 'WIFI', outputTitle: 'WiFi Scanner', group: 'wireless' },
|
||||
bluetooth: { label: 'Bluetooth', indicator: 'BLUETOOTH', outputTitle: 'Bluetooth Scanner', group: 'wireless' },
|
||||
bt_locate: { label: 'BT Locate', indicator: 'BT LOCATE', outputTitle: 'BT Locate — SAR Tracker', group: 'wireless' },
|
||||
meshtastic: { label: 'Meshtastic', indicator: 'MESHTASTIC', outputTitle: 'Meshtastic Mesh Monitor', group: 'wireless' },
|
||||
tscm: { label: 'TSCM', indicator: 'TSCM', outputTitle: 'TSCM Counter-Surveillance', group: 'intel' },
|
||||
analytics: { label: 'Analytics', indicator: 'ANALYTICS', outputTitle: 'Cross-Mode Analytics', group: 'intel' },
|
||||
spystations: { label: 'Spy Stations', indicator: 'SPY STATIONS', outputTitle: 'Spy Stations', group: 'intel' },
|
||||
websdr: { label: 'WebSDR', indicator: 'WEBSDR', outputTitle: 'HF/Shortwave WebSDR', group: 'intel' },
|
||||
};
|
||||
const validModes = new Set(Object.keys(modeCatalog));
|
||||
window.interceptModeCatalog = Object.assign({}, modeCatalog);
|
||||
|
||||
function getModeFromQuery() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
@@ -3513,9 +3545,31 @@
|
||||
indicator.appendChild(dot);
|
||||
indicator.appendChild(document.createTextNode(String(label || '')));
|
||||
}
|
||||
|
||||
function applyKeyboardAccessibility(root = document) {
|
||||
const interactive = root.querySelectorAll('[onclick]:not(button):not(a):not(input):not(select):not(textarea)');
|
||||
interactive.forEach((el) => {
|
||||
if (!el.hasAttribute('role')) el.setAttribute('role', 'button');
|
||||
if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0');
|
||||
el.setAttribute('data-keyboard-activate', 'true');
|
||||
});
|
||||
}
|
||||
|
||||
if (!window._keyboardActivationBound) {
|
||||
window._keyboardActivationBound = true;
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key !== 'Enter' && event.key !== ' ') return;
|
||||
const target = event.target && event.target.closest ? event.target.closest('[data-keyboard-activate="true"]') : null;
|
||||
if (!target) return;
|
||||
event.preventDefault();
|
||||
target.click();
|
||||
});
|
||||
}
|
||||
|
||||
// Update clock every second
|
||||
setInterval(updateHeaderClock, 1000);
|
||||
updateHeaderClock(); // Initial call
|
||||
applyKeyboardAccessibility();
|
||||
|
||||
// Pager message filter functions
|
||||
function loadPagerFilters() {
|
||||
@@ -3861,20 +3915,11 @@
|
||||
}
|
||||
|
||||
function updateDropdownActiveState() {
|
||||
// Map modes to their dropdown groups
|
||||
const modeGroups = {
|
||||
'pager': 'signals', 'sensor': 'signals', 'rtlamr': 'signals', 'listening': 'signals', 'subghz': 'signals',
|
||||
'adsb': 'tracking', 'ais': 'tracking', 'aprs': 'tracking', 'gps': 'tracking',
|
||||
'satellite': 'space', 'sstv': 'space', 'weathersat': 'space', 'sstv_general': 'space', 'spaceweather': 'space',
|
||||
'wifi': 'wireless', 'bluetooth': 'wireless', 'bt_locate': 'wireless', 'meshtastic': 'wireless',
|
||||
'tscm': 'intel', 'analytics': 'intel', 'spystations': 'intel', 'websdr': 'intel'
|
||||
};
|
||||
|
||||
// Remove has-active from all dropdowns
|
||||
document.querySelectorAll('.mode-nav-dropdown').forEach(d => d.classList.remove('has-active'));
|
||||
|
||||
// Add has-active to the dropdown containing the current mode
|
||||
const activeGroup = modeGroups[currentMode];
|
||||
const activeGroup = modeCatalog[currentMode] ? modeCatalog[currentMode].group : null;
|
||||
if (activeGroup) {
|
||||
const dropdown = document.querySelector(`.mode-nav-dropdown[data-group="${activeGroup}"]`);
|
||||
if (dropdown) dropdown.classList.add('has-active');
|
||||
@@ -3942,20 +3987,16 @@
|
||||
closeAllDropdowns();
|
||||
updateDropdownActiveState();
|
||||
|
||||
if (typeof window.ensureModeStyles === 'function') {
|
||||
window.ensureModeStyles(mode);
|
||||
}
|
||||
|
||||
// Remove active from all nav buttons, then add to the correct one
|
||||
document.querySelectorAll('.mode-nav-btn').forEach(btn => btn.classList.remove('active'));
|
||||
const modeMap = {
|
||||
'pager': 'pager', 'sensor': '433',
|
||||
'satellite': 'satellite', 'wifi': 'wifi', 'bluetooth': 'bluetooth', 'bt_locate': 'bt locate',
|
||||
'listening': 'listening', 'aprs': 'aprs', 'tscm': 'tscm', 'meshtastic': 'meshtastic',
|
||||
'dmr': 'dmr', 'websdr': 'websdr', 'sstv_general': 'hf sstv',
|
||||
'analytics': 'analytics'
|
||||
};
|
||||
document.querySelectorAll('.mode-nav-btn').forEach(btn => {
|
||||
const label = btn.querySelector('.nav-label');
|
||||
if (label && label.textContent.toLowerCase().includes(modeMap[mode])) {
|
||||
btn.classList.add('active');
|
||||
}
|
||||
btn.classList.toggle('active', btn.dataset.mode === mode);
|
||||
});
|
||||
document.querySelectorAll('.mobile-nav-btn').forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.mode === mode);
|
||||
});
|
||||
document.getElementById('pagerMode')?.classList.toggle('active', mode === 'pager');
|
||||
document.getElementById('sensorMode')?.classList.toggle('active', mode === 'sensor');
|
||||
@@ -4001,31 +4042,8 @@
|
||||
if (satelliteDashboardBtn) satelliteDashboardBtn.style.display = mode === 'satellite' ? 'inline-flex' : 'none';
|
||||
|
||||
// Update active mode indicator
|
||||
const modeNames = {
|
||||
'pager': 'PAGER',
|
||||
'sensor': '433MHZ',
|
||||
'rtlamr': 'METERS',
|
||||
'satellite': 'SATELLITE',
|
||||
'sstv': 'ISS SSTV',
|
||||
'weathersat': 'WEATHER SAT',
|
||||
'sstv_general': 'HF SSTV',
|
||||
'gps': 'GPS',
|
||||
'wifi': 'WIFI',
|
||||
'bluetooth': 'BLUETOOTH',
|
||||
'bt_locate': 'BT LOCATE',
|
||||
'listening': 'LISTENING POST',
|
||||
'aprs': 'APRS',
|
||||
'tscm': 'TSCM',
|
||||
'ais': 'AIS VESSELS',
|
||||
'spystations': 'SPY STATIONS',
|
||||
'meshtastic': 'MESHTASTIC',
|
||||
'dmr': 'DIGITAL VOICE',
|
||||
'websdr': 'WEBSDR',
|
||||
'subghz': 'SUBGHZ',
|
||||
'analytics': 'ANALYTICS',
|
||||
'spaceweather': 'SPACE WX'
|
||||
};
|
||||
setActiveModeIndicator(modeNames[mode] || mode.toUpperCase());
|
||||
const modeMeta = modeCatalog[mode] || {};
|
||||
setActiveModeIndicator(modeMeta.indicator || mode.toUpperCase());
|
||||
const wifiLayoutContainer = document.getElementById('wifiLayoutContainer');
|
||||
const btLayoutContainer = document.getElementById('btLayoutContainer');
|
||||
const satelliteVisuals = document.getElementById('satelliteVisuals');
|
||||
@@ -4080,32 +4098,8 @@
|
||||
if (sensorTimelineContainer) sensorTimelineContainer.style.display = mode === 'sensor' ? 'block' : 'none';
|
||||
|
||||
// Update output panel title based on mode
|
||||
const titles = {
|
||||
'pager': 'Pager Decoder',
|
||||
'sensor': '433MHz Sensor Monitor',
|
||||
'rtlamr': 'Utility Meter Monitor',
|
||||
'satellite': 'Satellite Monitor',
|
||||
'sstv': 'ISS SSTV Decoder',
|
||||
'weathersat': 'Weather Satellite Decoder',
|
||||
'sstv_general': 'HF SSTV Decoder',
|
||||
'gps': 'GPS Receiver',
|
||||
'wifi': 'WiFi Scanner',
|
||||
'bluetooth': 'Bluetooth Scanner',
|
||||
'bt_locate': 'BT Locate — SAR Tracker',
|
||||
'listening': 'Listening Post',
|
||||
'aprs': 'APRS Tracker',
|
||||
'tscm': 'TSCM Counter-Surveillance',
|
||||
'ais': 'AIS Vessel Tracker',
|
||||
'spystations': 'Spy Stations',
|
||||
'meshtastic': 'Meshtastic Mesh Monitor',
|
||||
'dmr': 'Digital Voice Decoder',
|
||||
'websdr': 'HF/Shortwave WebSDR',
|
||||
'subghz': 'SubGHz Transceiver',
|
||||
'analytics': 'Cross-Mode Analytics',
|
||||
'spaceweather': 'Space Weather Monitor'
|
||||
};
|
||||
const outputTitle = document.getElementById('outputTitle');
|
||||
if (outputTitle) outputTitle.textContent = titles[mode] || 'Signal Monitor';
|
||||
if (outputTitle) outputTitle.textContent = modeMeta.outputTitle || 'Signal Monitor';
|
||||
|
||||
// Initialize mode-specific timelines
|
||||
initializeModeTimeline(mode);
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
#}
|
||||
|
||||
<!-- Help Modal -->
|
||||
<div id="helpModal" class="help-modal" onclick="if(event.target === this) hideHelp()">
|
||||
<div class="help-content">
|
||||
<button class="help-close" onclick="hideHelp()">×</button>
|
||||
<h2>iNTERCEPT Help</h2>
|
||||
|
||||
<div class="help-tabs">
|
||||
<button class="help-tab active" data-tab="icons" onclick="switchHelpTab('icons')">Icons</button>
|
||||
<button class="help-tab" data-tab="modes" onclick="switchHelpTab('modes')">Modes</button>
|
||||
<button class="help-tab" data-tab="wifi" onclick="switchHelpTab('wifi')">WiFi</button>
|
||||
<button class="help-tab" data-tab="tips" onclick="switchHelpTab('tips')">Tips</button>
|
||||
</div>
|
||||
|
||||
<!-- Icons Section -->
|
||||
<div id="help-icons" class="help-section active">
|
||||
<div id="helpModal" class="help-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="helpModalTitle" onclick="if(event.target === this) hideHelp()">
|
||||
<div class="help-content" tabindex="-1">
|
||||
<button type="button" class="help-close" onclick="hideHelp()" aria-label="Close help">×</button>
|
||||
<h2 id="helpModalTitle">iNTERCEPT Help</h2>
|
||||
|
||||
<div class="help-tabs" role="tablist" aria-label="Help sections">
|
||||
<button type="button" class="help-tab active" data-tab="icons" onclick="switchHelpTab('icons')" role="tab" aria-controls="help-icons" aria-selected="true">Icons</button>
|
||||
<button type="button" class="help-tab" data-tab="modes" onclick="switchHelpTab('modes')" role="tab" aria-controls="help-modes" aria-selected="false">Modes</button>
|
||||
<button type="button" class="help-tab" data-tab="wifi" onclick="switchHelpTab('wifi')" role="tab" aria-controls="help-wifi" aria-selected="false">WiFi</button>
|
||||
<button type="button" class="help-tab" data-tab="tips" onclick="switchHelpTab('tips')" role="tab" aria-controls="help-tips" aria-selected="false">Tips</button>
|
||||
</div>
|
||||
|
||||
<!-- Icons Section -->
|
||||
<div id="help-icons" class="help-section active" role="tabpanel">
|
||||
<h3>Stats Bar Icons</h3>
|
||||
<div class="icon-grid">
|
||||
<div class="icon-item"><span class="icon icon--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="4" y="5" width="16" height="14" rx="2"/><line x1="8" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="12" y2="14"/></svg></span><span class="desc">POCSAG messages decoded</span></div>
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Modes Section -->
|
||||
<div id="help-modes" class="help-section">
|
||||
<div id="help-modes" class="help-section" role="tabpanel" hidden>
|
||||
<h3>Pager Mode</h3>
|
||||
<ul class="tip-list">
|
||||
<li>Decodes POCSAG and FLEX pager signals using RTL-SDR</li>
|
||||
@@ -254,7 +254,7 @@
|
||||
</div>
|
||||
|
||||
<!-- WiFi Section -->
|
||||
<div id="help-wifi" class="help-section">
|
||||
<div id="help-wifi" class="help-section" role="tabpanel" hidden>
|
||||
<h3>Monitor Mode</h3>
|
||||
<ul class="tip-list">
|
||||
<li><strong>Enable Monitor:</strong> Puts WiFi adapter in monitor mode for passive scanning</li>
|
||||
@@ -302,7 +302,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Tips Section -->
|
||||
<div id="help-tips" class="help-section">
|
||||
<div id="help-tips" class="help-section" role="tabpanel" hidden>
|
||||
<h3>General Tips</h3>
|
||||
<ul class="tip-list">
|
||||
<li><strong>Collapsible sections:</strong> Click any section header (∇) to collapse/expand</li>
|
||||
@@ -360,41 +360,62 @@
|
||||
|
||||
<script>
|
||||
// Help modal functions - defined here so all pages have them
|
||||
(function() {
|
||||
// Only define if not already defined (index.html defines its own)
|
||||
if (typeof window.showHelp === 'undefined') {
|
||||
window.showHelp = function() {
|
||||
document.getElementById('helpModal').classList.add('active');
|
||||
document.body.style.overflow = 'hidden';
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window.hideHelp === 'undefined') {
|
||||
window.hideHelp = function() {
|
||||
document.getElementById('helpModal').classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window.switchHelpTab === 'undefined') {
|
||||
window.switchHelpTab = function(tab) {
|
||||
document.querySelectorAll('.help-tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('.help-section').forEach(s => s.classList.remove('active'));
|
||||
document.querySelector('.help-tab[data-tab="' + tab + '"]').classList.add('active');
|
||||
document.getElementById('help-' + tab).classList.add('active');
|
||||
};
|
||||
}
|
||||
(function() {
|
||||
let lastHelpFocusEl = null;
|
||||
|
||||
// Only define if not already defined (index.html defines its own)
|
||||
if (typeof window.showHelp === 'undefined') {
|
||||
window.showHelp = function() {
|
||||
const modal = document.getElementById('helpModal');
|
||||
lastHelpFocusEl = document.activeElement;
|
||||
modal.classList.add('active');
|
||||
modal.setAttribute('aria-hidden', 'false');
|
||||
const content = modal.querySelector('.help-content');
|
||||
if (content) content.focus();
|
||||
document.body.style.overflow = 'hidden';
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window.hideHelp === 'undefined') {
|
||||
window.hideHelp = function() {
|
||||
const modal = document.getElementById('helpModal');
|
||||
modal.classList.remove('active');
|
||||
modal.setAttribute('aria-hidden', 'true');
|
||||
document.body.style.overflow = '';
|
||||
if (lastHelpFocusEl && typeof lastHelpFocusEl.focus === 'function') {
|
||||
lastHelpFocusEl.focus();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window.switchHelpTab === 'undefined') {
|
||||
window.switchHelpTab = function(tab) {
|
||||
document.querySelectorAll('.help-tab').forEach(t => {
|
||||
const isActive = t.dataset.tab === tab;
|
||||
t.classList.toggle('active', isActive);
|
||||
t.setAttribute('aria-selected', isActive ? 'true' : 'false');
|
||||
});
|
||||
document.querySelectorAll('.help-section').forEach(s => {
|
||||
const isActive = s.id === ('help-' + tab);
|
||||
s.classList.toggle('active', isActive);
|
||||
s.hidden = !isActive;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Keyboard shortcuts for help (only add once)
|
||||
if (!window._helpKeyboardSetup) {
|
||||
window._helpKeyboardSetup = true;
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') hideHelp();
|
||||
// Open help with F1 or ? key (when not typing in an input)
|
||||
var helpModal = document.getElementById('helpModal');
|
||||
if (helpModal && (e.key === 'F1' || (e.key === '?' && !e.target.matches('input, textarea, select'))) && !helpModal.classList.contains('active')) {
|
||||
e.preventDefault();
|
||||
showHelp();
|
||||
if (!window._helpKeyboardSetup) {
|
||||
window._helpKeyboardSetup = true;
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
const modal = document.getElementById('helpModal');
|
||||
if (modal && modal.classList.contains('active')) hideHelp();
|
||||
}
|
||||
// Open help with F1 or ? key (when not typing in an input)
|
||||
var helpModal = document.getElementById('helpModal');
|
||||
if (helpModal && (e.key === 'F1' || (e.key === '?' && !e.target.matches('input, textarea, select'))) && !helpModal.classList.contains('active')) {
|
||||
e.preventDefault();
|
||||
showHelp();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+24
-18
@@ -17,17 +17,17 @@
|
||||
{% macro mode_item(mode, label, icon_svg, href=None) -%}
|
||||
{%- set is_active = 'active' if active_mode == mode else '' -%}
|
||||
{%- if href %}
|
||||
<a href="{{ href }}" class="mode-nav-btn {{ is_active }}" style="text-decoration: none;">
|
||||
<a href="{{ href }}" class="mode-nav-btn {{ is_active }}" style="text-decoration: none;" data-mode="{{ mode }}" data-mode-label="{{ label }}" aria-label="{{ label }} mode">
|
||||
<span class="nav-icon icon">{{ icon_svg | safe }}</span>
|
||||
<span class="nav-label">{{ label }}</span>
|
||||
</a>
|
||||
{%- elif is_index_page %}
|
||||
<button class="mode-nav-btn {{ is_active }}" onclick="switchMode('{{ mode }}')">
|
||||
<button type="button" class="mode-nav-btn {{ is_active }}" data-mode="{{ mode }}" data-mode-label="{{ label }}" aria-label="{{ label }} mode" onclick="switchMode('{{ mode }}')">
|
||||
<span class="nav-icon icon">{{ icon_svg | safe }}</span>
|
||||
<span class="nav-label">{{ label }}</span>
|
||||
</button>
|
||||
{%- else %}
|
||||
<a href="/?mode={{ mode }}" class="mode-nav-btn {{ is_active }}" style="text-decoration: none;">
|
||||
<a href="/?mode={{ mode }}" class="mode-nav-btn {{ is_active }}" style="text-decoration: none;" data-mode="{{ mode }}" data-mode-label="{{ label }}" aria-label="{{ label }} mode">
|
||||
<span class="nav-icon icon">{{ icon_svg | safe }}</span>
|
||||
<span class="nav-label">{{ label }}</span>
|
||||
</a>
|
||||
@@ -37,15 +37,15 @@
|
||||
{% macro mobile_item(mode, label, icon_svg, href=None) -%}
|
||||
{%- set is_active = 'active' if active_mode == mode else '' -%}
|
||||
{%- if href %}
|
||||
<a href="{{ href }}" class="mobile-nav-btn {{ is_active }}" style="text-decoration: none;">
|
||||
<a href="{{ href }}" class="mobile-nav-btn {{ is_active }}" style="text-decoration: none;" data-mode="{{ mode }}" data-mode-label="{{ label }}" aria-label="{{ label }} mode">
|
||||
<span class="icon icon--sm">{{ icon_svg | safe }}</span> {{ label }}
|
||||
</a>
|
||||
{%- elif is_index_page %}
|
||||
<button class="mobile-nav-btn {{ is_active }}" data-mode="{{ mode }}" onclick="switchMode('{{ mode }}')">
|
||||
<button type="button" class="mobile-nav-btn {{ is_active }}" data-mode="{{ mode }}" data-mode-label="{{ label }}" aria-label="{{ label }} mode" onclick="switchMode('{{ mode }}')">
|
||||
<span class="icon icon--sm">{{ icon_svg | safe }}</span> {{ label }}
|
||||
</button>
|
||||
{%- else %}
|
||||
<a href="/?mode={{ mode }}" class="mobile-nav-btn {{ is_active }}" style="text-decoration: none;">
|
||||
<a href="/?mode={{ mode }}" class="mobile-nav-btn {{ is_active }}" style="text-decoration: none;" data-mode="{{ mode }}" data-mode-label="{{ label }}" aria-label="{{ label }} mode">
|
||||
<span class="icon icon--sm">{{ icon_svg | safe }}</span> {{ label }}
|
||||
</a>
|
||||
{%- endif %}
|
||||
@@ -55,7 +55,7 @@
|
||||
<nav class="mode-nav" id="mainNav">
|
||||
{# Signals Group #}
|
||||
<div class="mode-nav-dropdown" data-group="signals">
|
||||
<button class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('signals')"{% endif %}>
|
||||
<button type="button" class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('signals')"{% endif %}>
|
||||
<span class="nav-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h4l3-8 3 16 3-8h4"/><path d="M22 12h-1"/><path d="M1 12h1"/></svg></span>
|
||||
<span class="nav-label">Signals</span>
|
||||
<span class="dropdown-arrow icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
||||
@@ -72,7 +72,7 @@
|
||||
|
||||
{# Tracking Group #}
|
||||
<div class="mode-nav-dropdown" data-group="tracking">
|
||||
<button class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('tracking')"{% endif %}>
|
||||
<button type="button" class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('tracking')"{% endif %}>
|
||||
<span class="nav-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg></span>
|
||||
<span class="nav-label">Tracking</span>
|
||||
<span class="dropdown-arrow icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
||||
@@ -88,7 +88,7 @@
|
||||
|
||||
{# Space Group #}
|
||||
<div class="mode-nav-dropdown" data-group="space">
|
||||
<button class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('space')"{% endif %}>
|
||||
<button type="button" class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('space')"{% endif %}>
|
||||
<span class="nav-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/></svg></span>
|
||||
<span class="nav-label">Space</span>
|
||||
<span class="dropdown-arrow icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
{# Wireless Group #}
|
||||
<div class="mode-nav-dropdown" data-group="wireless">
|
||||
<button class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('wireless')"{% endif %}>
|
||||
<button type="button" class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('wireless')"{% endif %}>
|
||||
<span class="nav-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="nav-label">Wireless</span>
|
||||
<span class="dropdown-arrow icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
||||
@@ -125,7 +125,7 @@
|
||||
|
||||
{# Intel Group #}
|
||||
<div class="mode-nav-dropdown" data-group="intel">
|
||||
<button class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('intel')"{% endif %}>
|
||||
<button type="button" class="mode-nav-dropdown-btn"{% if is_index_page %} onclick="toggleNavDropdown('intel')"{% endif %}>
|
||||
<span class="nav-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></span>
|
||||
<span class="nav-label">Intel</span>
|
||||
<span class="dropdown-arrow icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></span>
|
||||
@@ -160,11 +160,11 @@
|
||||
</a>
|
||||
<div class="nav-divider"></div>
|
||||
<div class="nav-tools">
|
||||
<button class="nav-tool-btn" onclick="toggleAnimations()" title="Toggle Animations">
|
||||
<button type="button" class="nav-tool-btn" onclick="toggleAnimations()" title="Toggle Animations" aria-label="Toggle animations">
|
||||
<span class="icon-effects-on icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg></span>
|
||||
<span class="icon-effects-off icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/><line x1="2" y1="2" x2="22" y2="22"/></svg></span>
|
||||
</button>
|
||||
<button class="nav-tool-btn" onclick="toggleTheme()" title="Toggle Light/Dark Theme">
|
||||
<button type="button" class="nav-tool-btn" onclick="toggleTheme()" title="Toggle Light/Dark Theme" aria-label="Toggle theme">
|
||||
<span class="icon-moon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg></span>
|
||||
<span class="icon-sun icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></span>
|
||||
</button>
|
||||
@@ -174,11 +174,11 @@
|
||||
<a href="/controller/manage" class="nav-tool-btn" title="Manage Remote Agents" style="text-decoration: none;">
|
||||
<span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"/><rect x="2" y="14" width="20" height="8" rx="2" ry="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></svg></span>
|
||||
</a>
|
||||
<button class="nav-tool-btn" onclick="showSettings()" title="Settings">
|
||||
<button type="button" class="nav-tool-btn" onclick="showSettings()" title="Settings" aria-label="Open settings">
|
||||
<span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span>
|
||||
</button>
|
||||
<button class="nav-tool-btn" onclick="showHelp()" title="Help & Documentation">?</button>
|
||||
<button class="nav-tool-btn" onclick="logout(event)" title="Logout">
|
||||
<button type="button" class="nav-tool-btn" onclick="showHelp()" title="Help & Documentation" aria-label="Open help">?</button>
|
||||
<button type="button" class="nav-tool-btn" onclick="logout(event)" title="Logout" aria-label="Logout">
|
||||
<span class="power-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg></span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -257,7 +257,8 @@
|
||||
const current = html.getAttribute('data-animations') || 'on';
|
||||
const next = current === 'on' ? 'off' : 'on';
|
||||
html.setAttribute('data-animations', next);
|
||||
localStorage.setItem('animations', next);
|
||||
localStorage.setItem('intercept-animations', next);
|
||||
localStorage.removeItem('animations');
|
||||
};
|
||||
}
|
||||
|
||||
@@ -316,7 +317,12 @@
|
||||
document.documentElement.setAttribute('data-theme', savedTheme);
|
||||
}
|
||||
|
||||
const savedAnimations = localStorage.getItem('intercept-animations');
|
||||
const legacyAnimations = localStorage.getItem('animations');
|
||||
const savedAnimations = localStorage.getItem('intercept-animations') || legacyAnimations;
|
||||
if (legacyAnimations && !localStorage.getItem('intercept-animations')) {
|
||||
localStorage.setItem('intercept-animations', legacyAnimations);
|
||||
localStorage.removeItem('animations');
|
||||
}
|
||||
if (savedAnimations) {
|
||||
document.documentElement.setAttribute('data-animations', savedAnimations);
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
<!-- Settings Modal -->
|
||||
<div id="settingsModal" class="settings-modal" onclick="if(event.target === this) hideSettings()">
|
||||
<div class="settings-content">
|
||||
<div id="settingsModal" class="settings-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="settingsModalTitle" onclick="if(event.target === this) hideSettings()">
|
||||
<div class="settings-content" tabindex="-1">
|
||||
<div class="settings-header">
|
||||
<h2>
|
||||
<h2 id="settingsModalTitle">
|
||||
<span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span>
|
||||
Settings
|
||||
</h2>
|
||||
<button class="settings-close" onclick="hideSettings()">×</button>
|
||||
<button type="button" class="settings-close" onclick="hideSettings()" aria-label="Close settings">×</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-tabs">
|
||||
<button class="settings-tab active" data-tab="offline" onclick="switchSettingsTab('offline')">Offline</button>
|
||||
<button class="settings-tab" data-tab="location" onclick="switchSettingsTab('location')">Location</button>
|
||||
<button class="settings-tab" data-tab="display" onclick="switchSettingsTab('display')">Display</button>
|
||||
<button class="settings-tab" data-tab="updates" onclick="switchSettingsTab('updates')">Updates</button>
|
||||
<button class="settings-tab" data-tab="tools" onclick="switchSettingsTab('tools')">Tools</button>
|
||||
<button class="settings-tab" data-tab="alerts" onclick="switchSettingsTab('alerts')">Alerts</button>
|
||||
<button class="settings-tab" data-tab="recording" onclick="switchSettingsTab('recording')">Recording</button>
|
||||
<button class="settings-tab" data-tab="about" onclick="switchSettingsTab('about')">About</button>
|
||||
<div class="settings-tabs" role="tablist" aria-label="Settings sections">
|
||||
<button type="button" class="settings-tab active" data-tab="offline" onclick="switchSettingsTab('offline')" role="tab" aria-controls="settings-offline" aria-selected="true">Offline</button>
|
||||
<button type="button" class="settings-tab" data-tab="location" onclick="switchSettingsTab('location')" role="tab" aria-controls="settings-location" aria-selected="false">Location</button>
|
||||
<button type="button" class="settings-tab" data-tab="display" onclick="switchSettingsTab('display')" role="tab" aria-controls="settings-display" aria-selected="false">Display</button>
|
||||
<button type="button" class="settings-tab" data-tab="updates" onclick="switchSettingsTab('updates')" role="tab" aria-controls="settings-updates" aria-selected="false">Updates</button>
|
||||
<button type="button" class="settings-tab" data-tab="tools" onclick="switchSettingsTab('tools')" role="tab" aria-controls="settings-tools" aria-selected="false">Tools</button>
|
||||
<button type="button" class="settings-tab" data-tab="alerts" onclick="switchSettingsTab('alerts')" role="tab" aria-controls="settings-alerts" aria-selected="false">Alerts</button>
|
||||
<button type="button" class="settings-tab" data-tab="recording" onclick="switchSettingsTab('recording')" role="tab" aria-controls="settings-recording" aria-selected="false">Recording</button>
|
||||
<button type="button" class="settings-tab" data-tab="about" onclick="switchSettingsTab('about')" role="tab" aria-controls="settings-about" aria-selected="false">About</button>
|
||||
</div>
|
||||
|
||||
<!-- Offline Section -->
|
||||
<div id="settings-offline" class="settings-section active">
|
||||
<div id="settings-offline" class="settings-section active" role="tabpanel">
|
||||
<div class="settings-group">
|
||||
<div class="settings-group-title">Offline Mode</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user