fix: Resolve TSCM function strip visibility and clipping issues

- Fix function strip content being clipped by changing overflow to visible
- Add min-height and increased padding to function strip
- Add explicit colors for TSCM strip stat values and labels
- Fix output-panel overflow for TSCM mode using :has() selector
- Add CSS variables --bg-dark and --bg-panel aliases
- Clean up sidebar section margins for consistent spacing
- Add unique IDs to WiFi/Bluetooth export sections for restore function

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-01-30 15:40:10 +00:00
parent 68d831dbe3
commit 978e6cdaea
17 changed files with 704 additions and 95 deletions
+30 -8
View File
@@ -6,9 +6,10 @@
background: linear-gradient(180deg, var(--bg-panel) 0%, var(--bg-dark) 100%);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 6px 12px;
padding: 8px 12px;
margin-bottom: 10px;
overflow-x: auto;
overflow: visible;
min-height: 44px;
}
.function-strip-inner {
@@ -23,7 +24,7 @@
display: flex;
flex-direction: column;
align-items: center;
padding: 4px 10px;
padding: 6px 10px;
background: rgba(74, 158, 255, 0.05);
border: 1px solid rgba(74, 158, 255, 0.15);
border-radius: 4px;
@@ -307,16 +308,37 @@
color: var(--accent-orange);
}
.function-strip.tscm-strip {
margin-top: 4px; /* Extra clearance to prevent top clipping */
}
.function-strip.tscm-strip .strip-stat {
background: rgba(255, 59, 48, 0.05);
border-color: rgba(255, 59, 48, 0.15);
background: rgba(255, 59, 48, 0.15);
border: 1px solid rgba(255, 59, 48, 0.4);
}
.function-strip.tscm-strip .strip-stat:hover {
background: rgba(255, 59, 48, 0.1);
border-color: rgba(255, 59, 48, 0.3);
background: rgba(255, 59, 48, 0.25);
border-color: rgba(255, 59, 48, 0.6);
}
.function-strip.tscm-strip .strip-value {
color: var(--accent-red);
color: #ef4444; /* Explicit red color */
}
.function-strip.tscm-strip .strip-label {
color: #9ca3af; /* Explicit light gray */
}
.function-strip.tscm-strip .strip-select {
color: #e8eaed; /* Explicit white for selects */
background: rgba(0, 0, 0, 0.4);
}
.function-strip.tscm-strip .strip-btn {
color: #e8eaed; /* Explicit white for buttons */
}
.function-strip.tscm-strip .strip-tool {
color: #e8eaed; /* Explicit white for tool indicators */
}
.function-strip.tscm-strip .strip-time,
.function-strip.tscm-strip .strip-status span {
color: #9ca3af; /* Explicit gray for status/time */
}
.function-strip.rtlamr-strip .strip-stat {
+1 -1
View File
@@ -593,7 +593,7 @@
border-bottom: 1px solid var(--border-color);
padding: 0 20px;
position: relative;
z-index: 10;
z-index: 100;
}
@media (min-width: 1024px) {
+8
View File
@@ -17,6 +17,10 @@
--bg-elevated: #1a202c;
--bg-overlay: rgba(0, 0, 0, 0.7);
/* Background aliases for components */
--bg-dark: var(--bg-primary);
--bg-panel: var(--bg-secondary);
/* Accent colors */
--accent-cyan: #4a9eff;
--accent-cyan-dim: rgba(74, 158, 255, 0.15);
@@ -150,6 +154,10 @@
--bg-elevated: #f8fafc;
--bg-overlay: rgba(255, 255, 255, 0.9);
/* Background aliases for components */
--bg-dark: var(--bg-primary);
--bg-panel: var(--bg-secondary);
--accent-cyan: #2563eb;
--accent-cyan-dim: rgba(37, 99, 235, 0.1);
--accent-cyan-hover: #1d4ed8;
+96
View File
@@ -1414,6 +1414,7 @@ header h1 .tagline {
overflow: visible;
padding: 12px;
position: relative;
margin: 0; /* Reset any inherited margins - spacing handled by parent gap */
}
.section h3 {
@@ -1590,6 +1591,101 @@ header h1 .tagline {
border-color: var(--accent-cyan);
}
/* WiFi Mode Tab Buttons */
.wifi-mode-tab {
flex: 1;
padding: 8px;
font-size: 11px;
font-family: 'JetBrains Mono', monospace;
text-transform: uppercase;
letter-spacing: 0.05em;
background: var(--bg-tertiary);
color: var(--text-secondary);
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
}
.wifi-mode-tab:hover {
background: var(--bg-secondary);
color: var(--text-primary);
border-color: var(--accent-cyan);
}
.wifi-mode-tab.active {
background: var(--accent-green);
color: #000;
border-color: var(--accent-green);
}
.wifi-mode-tab.active:hover {
background: #1db954;
border-color: #1db954;
}
/* WiFi Start/Stop Buttons */
.wifi-start-btn {
background: var(--accent-green) !important;
color: #000 !important;
border-color: var(--accent-green) !important;
}
.wifi-start-btn:hover {
background: #1db954 !important;
border-color: #1db954 !important;
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.3);
}
.wifi-stop-btn {
background: var(--accent-red) !important;
color: #fff !important;
border-color: var(--accent-red) !important;
}
.wifi-stop-btn:hover {
background: #e62e50 !important;
border-color: #e62e50 !important;
box-shadow: 0 2px 8px rgba(255, 51, 102, 0.3);
}
/* WiFi Monitor Mode Buttons */
.wifi-monitor-btn {
background: var(--accent-green) !important;
color: #000 !important;
border-color: var(--accent-green) !important;
}
.wifi-monitor-btn:hover {
background: #1db954 !important;
border-color: #1db954 !important;
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.3);
}
.wifi-monitor-stop-btn {
background: var(--accent-orange) !important;
color: #000 !important;
border-color: var(--accent-orange) !important;
}
.wifi-monitor-stop-btn:hover {
background: #e68a00 !important;
border-color: #e68a00 !important;
box-shadow: 0 2px 8px rgba(255, 159, 28, 0.3);
}
/* WiFi Danger/Attack Buttons */
.wifi-danger-btn {
border-color: var(--accent-red) !important;
color: var(--accent-red) !important;
}
.wifi-danger-btn:hover {
background: var(--accent-red) !important;
color: #fff !important;
box-shadow: 0 2px 8px rgba(255, 51, 102, 0.3);
}
.run-btn {
width: 100%;
padding: 12px;
+7 -2
View File
@@ -32,12 +32,17 @@
.threat-card.low.active { background: rgba(0,255,136,0.2); }
/* TSCM Dashboard */
/* Ensure output-panel doesn't clip TSCM content */
.output-panel:has(.tscm-dashboard) {
overflow: visible;
}
.tscm-dashboard {
display: flex;
flex-direction: column;
gap: 16px;
overflow-y: auto;
padding-bottom: 80px; /* Space for status bar */
overflow: visible;
padding: 20px 16px 80px 16px; /* Extra top padding for function strip visibility */
}
.tscm-threat-banner {
display: flex;
+92
View File
@@ -227,6 +227,9 @@ function startScanner() {
isScannerPaused = false;
scannerSignalActive = false;
// Update listening strip
updateListeningStripRunning(true);
// Update controls (with null checks)
const startBtn = document.getElementById('scannerStartBtn');
if (startBtn) {
@@ -289,6 +292,9 @@ function stopScanner() {
scannerSignalActive = false;
currentSignalLevel = 0;
// Update listening strip
updateListeningStripRunning(false);
// Re-enable listen button (will be in local mode after stop)
updateListenButtonState(false);
@@ -572,6 +578,10 @@ function handleFrequencyUpdate(data) {
const mainFreq = document.getElementById('mainScannerFreq');
if (mainFreq) mainFreq.textContent = freqStr;
// Update function strip frequency
const stripFreq = document.getElementById('listeningStripFreq');
if (stripFreq) stripFreq.textContent = freqStr;
// Update progress bar
const progress = ((data.frequency - scannerStartFreq) / (scannerEndFreq - scannerStartFreq)) * 100;
const progressBar = document.getElementById('scannerProgressBar');
@@ -622,6 +632,10 @@ function handleSignalFound(data) {
const mainSignalCount = document.getElementById('mainSignalCount');
if (mainSignalCount) mainSignalCount.textContent = scannerSignalCount;
// Update function strip signal count
const stripSignals = document.getElementById('listeningStripSignals');
if (stripSignals) stripSignals.textContent = scannerSignalCount;
// Update sidebar
updateScannerDisplay('SIGNAL FOUND', 'var(--accent-green)');
const signalPanel = document.getElementById('scannerSignalPanel');
@@ -2597,3 +2611,81 @@ window.tuneToFrequency = tuneToFrequency;
window.clearScannerLog = clearScannerLog;
window.exportScannerLog = exportScannerLog;
// ============== FUNCTION STRIP SUPPORT ==============
/**
* Update the listening post function strip running state
*/
function updateListeningStripRunning(running) {
const listeningStripDot = document.getElementById('listeningStripDot');
const listeningStripStatus = document.getElementById('listeningStripStatus');
const listeningStripStartBtn = document.getElementById('listeningStripStartBtn');
const listeningStripStopBtn = document.getElementById('listeningStripStopBtn');
const listeningStripFreqInput = document.getElementById('listeningStripFreqInput');
const listeningStripMode = document.getElementById('listeningStripMode');
const listeningStripGain = document.getElementById('listeningStripGain');
if (listeningStripDot) listeningStripDot.className = 'status-dot ' + (running ? 'scanning' : 'inactive');
if (listeningStripStatus) {
listeningStripStatus.textContent = running ? 'SCANNING' : 'STANDBY';
listeningStripStatus.style.color = running ? 'var(--accent-cyan)' : '';
}
if (listeningStripStartBtn) listeningStripStartBtn.style.display = running ? 'none' : 'inline-block';
if (listeningStripStopBtn) listeningStripStopBtn.style.display = running ? 'inline-block' : 'none';
if (listeningStripFreqInput) listeningStripFreqInput.disabled = running;
if (listeningStripMode) listeningStripMode.disabled = running;
if (listeningStripGain) listeningStripGain.disabled = running;
}
/**
* Update listening strip stats
*/
function updateListeningStrip(freq, bandwidth, signalCount) {
const freqEl = document.getElementById('listeningStripFreq');
const bwEl = document.getElementById('listeningStripBW');
const signalsEl = document.getElementById('listeningStripSignals');
if (freqEl && freq !== undefined) freqEl.textContent = freq;
if (bwEl && bandwidth !== undefined) bwEl.textContent = bandwidth;
if (signalsEl && signalCount !== undefined) signalsEl.textContent = signalCount;
}
/**
* Start listening from the function strip
*/
function startListeningFromStrip() {
// Get values from strip
const freq = document.getElementById('listeningStripFreqInput')?.value;
const mode = document.getElementById('listeningStripMode')?.value;
const gain = document.getElementById('listeningStripGain')?.value;
// Update the main controls if they exist
if (freq) {
const mainFreqInput = document.getElementById('radioScanStart');
if (mainFreqInput) mainFreqInput.value = freq;
}
if (mode) {
currentModulation = mode.toLowerCase();
}
if (gain) {
const gainValueEl = document.getElementById('radioGainValue');
if (gainValueEl) gainValueEl.textContent = gain;
}
// Start the scanner
startScanner();
}
/**
* Stop listening from the function strip
*/
function stopListening() {
stopScanner();
}
// Export strip functions
window.updateListeningStripRunning = updateListeningStripRunning;
window.updateListeningStrip = updateListeningStrip;
window.startListeningFromStrip = startListeningFromStrip;
window.stopListening = stopListening;
+435 -11
View File
@@ -336,6 +336,9 @@ const WiFiMode = (function() {
if (elements.scanModeDeep) {
elements.scanModeDeep.addEventListener('click', () => setScanMode('deep'));
}
// Initialize button visibility (default to quick mode)
setScanMode('quick');
}
function setScanMode(mode) {
@@ -349,6 +352,21 @@ const WiFiMode = (function() {
elements.scanModeDeep.classList.toggle('active', mode === 'deep');
}
// Update button visibility based on mode
if (!isScanning) {
if (elements.quickScanBtn) {
elements.quickScanBtn.style.display = mode === 'quick' ? 'inline-block' : 'none';
elements.quickScanBtn.textContent = 'Start Quick Scan';
}
if (elements.deepScanBtn) {
elements.deepScanBtn.style.display = mode === 'deep' ? 'inline-block' : 'none';
elements.deepScanBtn.textContent = 'Start Deep Scan';
}
if (elements.stopScanBtn) {
elements.stopScanBtn.style.display = 'none';
}
}
console.log('[WiFiMode] Scan mode set to:', mode);
}
@@ -357,7 +375,10 @@ const WiFiMode = (function() {
// ==========================================================================
async function startQuickScan() {
if (isScanning) return;
if (isScanning) {
showInfo('Scan already in progress');
return;
}
// Check for agent mode conflicts
if (!checkAgentConflicts()) {
@@ -365,6 +386,7 @@ const WiFiMode = (function() {
}
console.log('[WiFiMode] Starting quick scan...');
showInfo('Starting quick scan...');
setScanning(true, 'quick');
try {
@@ -436,6 +458,9 @@ const WiFiMode = (function() {
// Process results
processQuickScanResult({ ...scanResult, access_points: accessPoints });
// Show success notification
showInfo(`Found ${accessPoints.length} network${accessPoints.length !== 1 ? 's' : ''}`);
// For quick scan, we're done after one scan
// But keep polling if user wants continuous updates
if (scanMode === 'quick') {
@@ -449,7 +474,10 @@ const WiFiMode = (function() {
}
async function startDeepScan() {
if (isScanning) return;
if (isScanning) {
showInfo('Scan already in progress');
return;
}
// Check for agent mode conflicts
if (!checkAgentConflicts()) {
@@ -457,6 +485,7 @@ const WiFiMode = (function() {
}
console.log('[WiFiMode] Starting deep scan...');
showInfo('Starting deep scan (requires monitor mode)...');
setScanning(true, 'deep');
try {
@@ -516,6 +545,7 @@ const WiFiMode = (function() {
async function stopScan() {
console.log('[WiFiMode] Stopping scan...');
showInfo('Stopping scan...');
// Stop polling
if (pollTimer) {
@@ -538,6 +568,7 @@ const WiFiMode = (function() {
} else if (scanMode === 'deep') {
await fetch(`${CONFIG.apiBase}/scan/stop`, { method: 'POST' });
}
showInfo('Scan stopped');
} catch (error) {
console.warn('[WiFiMode] Error stopping scan:', error);
}
@@ -549,15 +580,21 @@ const WiFiMode = (function() {
isScanning = scanning;
if (mode) scanMode = mode;
// Update buttons
if (elements.quickScanBtn) {
elements.quickScanBtn.style.display = scanning ? 'none' : 'inline-block';
}
if (elements.deepScanBtn) {
elements.deepScanBtn.style.display = scanning ? 'none' : 'inline-block';
}
if (elements.stopScanBtn) {
elements.stopScanBtn.style.display = scanning ? 'inline-block' : 'none';
// Update buttons based on scanning state and current mode
if (scanning) {
// Scanning: hide start buttons, show stop button
if (elements.quickScanBtn) elements.quickScanBtn.style.display = 'none';
if (elements.deepScanBtn) elements.deepScanBtn.style.display = 'none';
if (elements.stopScanBtn) elements.stopScanBtn.style.display = 'inline-block';
} else {
// Not scanning: show appropriate start button based on mode, hide stop
if (elements.quickScanBtn) {
elements.quickScanBtn.style.display = scanMode === 'quick' ? 'inline-block' : 'none';
}
if (elements.deepScanBtn) {
elements.deepScanBtn.style.display = scanMode === 'deep' ? 'inline-block' : 'none';
}
if (elements.stopScanBtn) elements.stopScanBtn.style.display = 'none';
}
// Update status
@@ -1426,3 +1463,390 @@ document.addEventListener('DOMContentLoaded', () => {
WiFiMode.init();
}
});
// =============================================================================
// WiFi Helper Functions
// Implements UI helper functions for Monitor Mode, Deauth, Watch List, etc.
// =============================================================================
const WiFiHelpers = (function() {
'use strict';
// Note: Monitor mode and attack endpoints are in /wifi/ (v1), not /wifi/v2/
const CONFIG = {
apiBase: '/wifi/v2',
apiV1Base: '/wifi',
};
// Watch list state
let watchList = [];
// ==========================================================================
// Monitor Mode
// ==========================================================================
async function enableMonitorMode() {
const iface = document.getElementById('wifiInterfaceSelect')?.value;
const killProcesses = document.getElementById('killProcesses')?.checked || false;
const startBtn = document.getElementById('monitorStartBtn');
const stopBtn = document.getElementById('monitorStopBtn');
const statusEl = document.getElementById('monitorStatus');
// Provide immediate feedback
console.log('[WiFiHelpers] Enable monitor mode clicked');
showNotification('Monitor Mode', 'Enabling monitor mode...', 'info');
if (!iface) {
showNotification('Monitor Mode', 'No interface selected. Please wait for interface detection.', 'error');
return;
}
// Update UI to show processing
if (startBtn) {
startBtn.disabled = true;
startBtn.textContent = 'Enabling...';
}
try {
// Use v1 API which has monitor mode support
const response = await fetch(`${CONFIG.apiV1Base}/monitor`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
interface: iface,
action: 'enable',
kill_processes: killProcesses
}),
});
const result = await response.json();
if (!response.ok || result.error) {
throw new Error(result.error || result.message || 'Failed to enable monitor mode');
}
// Success - update UI
if (startBtn) {
startBtn.style.display = 'none';
startBtn.disabled = false;
startBtn.textContent = 'Enable Monitor';
}
if (stopBtn) stopBtn.style.display = 'inline-block';
if (statusEl) {
statusEl.innerHTML = `Monitor mode: <span style="color: var(--accent-green);">Active</span> (${result.monitor_interface || iface})`;
}
showNotification('Monitor Mode', `Enabled on ${result.monitor_interface || iface}`, 'success');
} catch (error) {
console.error('[WiFiHelpers] Enable monitor mode error:', error);
showNotification('Monitor Mode', error.message, 'error');
// Reset button
if (startBtn) {
startBtn.disabled = false;
startBtn.textContent = 'Enable Monitor';
}
}
}
async function disableMonitorMode() {
const iface = document.getElementById('wifiInterfaceSelect')?.value;
const startBtn = document.getElementById('monitorStartBtn');
const stopBtn = document.getElementById('monitorStopBtn');
const statusEl = document.getElementById('monitorStatus');
console.log('[WiFiHelpers] Disable monitor mode clicked');
showNotification('Monitor Mode', 'Disabling monitor mode...', 'info');
if (stopBtn) {
stopBtn.disabled = true;
stopBtn.textContent = 'Disabling...';
}
try {
// Use v1 API which has monitor mode support
const response = await fetch(`${CONFIG.apiV1Base}/monitor`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
interface: iface,
action: 'disable'
}),
});
const result = await response.json();
if (!response.ok || result.error) {
throw new Error(result.error || result.message || 'Failed to disable monitor mode');
}
// Success - update UI
if (stopBtn) {
stopBtn.style.display = 'none';
stopBtn.disabled = false;
stopBtn.textContent = 'Disable Monitor';
}
if (startBtn) startBtn.style.display = 'inline-block';
if (statusEl) {
statusEl.innerHTML = `Monitor mode: <span style="color: var(--accent-red);">Inactive</span>`;
}
showNotification('Monitor Mode', 'Disabled successfully', 'info');
} catch (error) {
console.error('[WiFiHelpers] Disable monitor mode error:', error);
showNotification('Monitor Mode', error.message, 'error');
// Reset button
if (stopBtn) {
stopBtn.disabled = false;
stopBtn.textContent = 'Disable Monitor';
}
}
}
// ==========================================================================
// Deauth Attack
// ==========================================================================
async function sendDeauth() {
const bssid = document.getElementById('targetBssid')?.value?.trim();
const client = document.getElementById('targetClient')?.value?.trim() || 'FF:FF:FF:FF:FF:FF';
const count = parseInt(document.getElementById('deauthCount')?.value) || 5;
const iface = document.getElementById('wifiInterfaceSelect')?.value;
console.log('[WiFiHelpers] Send deauth clicked');
if (!bssid || !/^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/.test(bssid)) {
showNotification('Deauth Attack', 'Please enter a valid target BSSID (e.g., AA:BB:CC:DD:EE:FF)', 'error');
return;
}
// Confirm action
if (!confirm(`Send ${count} deauth frames to ${bssid}?\n\nWARNING: Only use on networks you are authorized to test.`)) {
return;
}
showNotification('Deauth Attack', `Sending ${count} deauth frames...`, 'info');
try {
// Use v1 API which has deauth support
const response = await fetch(`${CONFIG.apiV1Base}/deauth`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
bssid: bssid,
client: client,
count: count,
interface: iface
}),
});
const result = await response.json();
if (!response.ok || result.error) {
throw new Error(result.error || result.message || 'Deauth attack failed');
}
showNotification('Deauth Attack', `Sent ${count} deauth frames to ${bssid}`, 'success');
} catch (error) {
console.error('[WiFiHelpers] Deauth error:', error);
showNotification('Deauth Attack', error.message, 'error');
}
}
// ==========================================================================
// Watch List (Proximity Alerts)
// ==========================================================================
function addWatchMac() {
const input = document.getElementById('watchMacInput');
const mac = input?.value?.trim().toUpperCase();
console.log('[WiFiHelpers] Add watch MAC clicked, value:', mac);
if (!mac) {
showNotification('Watch List', 'Please enter a MAC address', 'error');
return;
}
if (!/^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/.test(mac)) {
showNotification('Watch List', 'Invalid format. Use XX:XX:XX:XX:XX:XX', 'error');
return;
}
if (watchList.includes(mac)) {
showNotification('Watch List', 'MAC address already in watch list', 'info');
return;
}
watchList.push(mac);
input.value = '';
updateWatchListDisplay();
showNotification('Watch List', `Added ${mac} to watch list`, 'success');
// Save to localStorage
try {
localStorage.setItem('wifiWatchList', JSON.stringify(watchList));
} catch (e) {
console.warn('[WiFiHelpers] Could not save watch list to localStorage');
}
}
function removeWatchMac(mac) {
watchList = watchList.filter(m => m !== mac);
updateWatchListDisplay();
// Save to localStorage
try {
localStorage.setItem('wifiWatchList', JSON.stringify(watchList));
} catch (e) {
console.warn('[WiFiHelpers] Could not save watch list to localStorage');
}
}
function updateWatchListDisplay() {
const container = document.getElementById('watchList');
if (!container) return;
if (watchList.length === 0) {
container.innerHTML = '<span style="color: var(--text-dim);">No MAC addresses in watch list</span>';
return;
}
container.innerHTML = watchList.map(mac => `
<div style="display: flex; justify-content: space-between; align-items: center; padding: 4px 0; border-bottom: 1px solid var(--border-color);">
<code style="font-size: 10px;">${mac}</code>
<button onclick="WiFiHelpers.removeWatchMac('${mac}')" style="background: none; border: none; color: var(--accent-red); cursor: pointer; font-size: 12px; padding: 2px 6px;">&times;</button>
</div>
`).join('');
}
function loadWatchList() {
try {
const saved = localStorage.getItem('wifiWatchList');
if (saved) {
watchList = JSON.parse(saved);
updateWatchListDisplay();
}
} catch (e) {
console.warn('[WiFiHelpers] Could not load watch list from localStorage');
}
}
// ==========================================================================
// Handshake Capture
// ==========================================================================
async function checkCaptureStatus() {
const statusEl = document.getElementById('captureStatus');
const bssid = document.getElementById('captureTargetBssid')?.textContent;
console.log('[WiFiHelpers] Check capture status clicked');
showNotification('Handshake Capture', 'Checking capture status...', 'info');
try {
// Use v1 API which has handshake capture support
const response = await fetch(`${CONFIG.apiV1Base}/handshake/status`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ bssid: bssid }),
});
const result = await response.json();
if (statusEl) {
if (result.capturing || result.running) {
statusEl.textContent = result.handshake_found ? 'Handshake captured!' : 'Capturing...';
statusEl.style.color = result.handshake_found ? 'var(--accent-green)' : 'var(--accent-orange)';
} else {
statusEl.textContent = 'Not capturing';
statusEl.style.color = 'var(--text-secondary)';
}
}
if (result.handshake_found) {
showNotification('Handshake Capture', `Handshake captured! File: ${result.capture_file || 'check captures folder'}`, 'success');
} else if (result.capturing || result.running) {
showNotification('Handshake Capture', 'Still capturing, no handshake yet...', 'info');
} else {
showNotification('Handshake Capture', 'No active capture', 'info');
}
} catch (error) {
console.error('[WiFiHelpers] Check capture status error:', error);
showNotification('Handshake Capture', error.message, 'error');
if (statusEl) {
statusEl.textContent = 'Error checking status';
statusEl.style.color = 'var(--accent-red)';
}
}
}
async function stopHandshakeCapture() {
const panel = document.getElementById('captureStatusPanel');
const statusEl = document.getElementById('captureStatus');
console.log('[WiFiHelpers] Stop handshake capture clicked');
showNotification('Handshake Capture', 'Stopping capture...', 'info');
try {
// Use v1 API - handshake stop endpoint
const response = await fetch(`${CONFIG.apiV1Base}/scan/stop`, {
method: 'POST',
});
const result = await response.json();
if (!response.ok || result.error) {
throw new Error(result.error || result.message || 'Failed to stop capture');
}
if (statusEl) {
statusEl.textContent = 'Stopped';
statusEl.style.color = 'var(--text-secondary)';
}
// Hide panel after delay
setTimeout(() => {
if (panel) panel.style.display = 'none';
}, 2000);
showNotification('Handshake Capture', 'Capture stopped', 'success');
} catch (error) {
console.error('[WiFiHelpers] Stop capture error:', error);
showNotification('Handshake Capture', error.message, 'error');
}
}
// ==========================================================================
// Utility
// ==========================================================================
function showNotification(title, message, type) {
if (typeof window.showNotification === 'function') {
window.showNotification(title, message, type);
} else {
console.log(`[${type.toUpperCase()}] ${title}: ${message}`);
}
}
// ==========================================================================
// Initialize
// ==========================================================================
// Load watch list on page load
document.addEventListener('DOMContentLoaded', loadWatchList);
// ==========================================================================
// Public API
// ==========================================================================
return {
enableMonitorMode,
disableMonitorMode,
sendDeauth,
addWatchMac,
removeWatchMac,
checkCaptureStatus,
stopHandshakeCapture,
getWatchList: () => [...watchList],
};
})();
-2
View File
@@ -47,8 +47,6 @@
<input type="checkbox" id="showAllAgents" onchange="toggleShowAllAgents()"> All
</label>
</div>
<a href="#" onclick="history.back(); return false;" class="back-link">Back</a>
<a href="/" class="back-link">Main Dashboard</a>
</div>
</header>
-35
View File
@@ -223,26 +223,6 @@
opacity: 0.5;
}
/* Navigation links */
.nav-links {
display: flex;
gap: 16px;
margin-bottom: 20px;
}
.back-link {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--accent-cyan);
text-decoration: none;
font-size: 14px;
}
.back-link:hover {
text-decoration: underline;
}
/* Toast notifications */
.toast {
position: fixed;
@@ -301,21 +281,6 @@
</header>
<div class="agents-container">
<div class="nav-links">
<a href="#" onclick="history.back(); return false;" class="back-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 12H5M12 19l-7-7 7-7"/>
</svg>
Back
</a>
<a href="/" class="back-link">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
Dashboard
</a>
</div>
<div class="agents-header">
<h1>Remote Agents</h1>
-2
View File
@@ -48,8 +48,6 @@
<input type="checkbox" id="showAllAgents" onchange="toggleShowAllAgents()"> All
</label>
</div>
<a href="#" onclick="history.back(); return false;" class="back-link">Back</a>
<a href="/" class="back-link">Main Dashboard</a>
</div>
</header>
+3 -3
View File
@@ -1500,7 +1500,7 @@
</div>
<!-- TSCM Dashboard -->
<div id="tscmVisuals" class="tscm-dashboard" style="display: none; padding: 16px;">
<div id="tscmVisuals" class="tscm-dashboard" style="display: none;">
<!-- TSCM Function Bar -->
<div class="function-strip tscm-strip" id="tscmStrip" style="margin-bottom: 12px;">
<div class="function-strip-inner">
@@ -3017,8 +3017,8 @@
restoreSectionsToMode('rtlamrMode', ['#rtlamrPpm', '#rtlamrUnique', '#rtlamrLogging'], 'Utility Meter');
restoreSectionsToMode('aprsMode', [], 'APRS Tracking');
restoreSectionsToMode('spystationsMode', ['#filterTypeNumber', '#countryFilters', '#modeFilters', '#spyStatsNumber'], 'Spy Stations');
restoreSectionsToMode('wifiMode', ['#wifiScanModeQuick', '#monitorStartBtn', '#wifiChannel', '#watchMacInput', '#targetBssid'], 'WiFi Scanning');
restoreSectionsToMode('bluetoothMode', ['#btAdapterSelect', '#btTransport', '#btScanDuration'], 'Bluetooth Scanning');
restoreSectionsToMode('wifiMode', ['#wifiScanModeQuick', '#monitorStartBtn', '#wifiChannel', '#watchMacInput', '#targetBssid', '#wifiExportCsvBtn'], 'WiFi Scanning');
restoreSectionsToMode('bluetoothMode', ['#btAdapterSelect', '#btTransport', '#btScanDuration', '#btExportCsvBtn', '#btCapabilityMarker'], 'Bluetooth Scanning');
restoreSectionsToMode('tscmMode', ['#tscmWifiEnabled', '#tscmVerboseResults', '#tscmBaselineName'], 'TSCM Sweep');
restoreSectionsToMode('satelliteMode', ['#satTrackingList', '#obsLat', '#obsLon'], 'Satellite Command');
restoreSectionsToMode('sstvMode', ['#sstvFrequency'], 'ISS SSTV Decoder');
-2
View File
@@ -159,8 +159,6 @@
{% block dashboard_header_center %}{% endblock %}
<div class="header-utilities" style="gap: var(--space-2);">
{% block agent_selector %}{% endblock %}
<a href="#" onclick="history.back(); return false;" class="back-link" style="font-size: var(--text-sm);">Back</a>
<a href="/" class="back-link" style="font-size: var(--text-sm);">Main Dashboard</a>
</div>
</div>
</header>
-2
View File
@@ -514,8 +514,6 @@
<span>// MULTI-AGENT VIEW</span>
</div>
<nav class="header-nav">
<a href="#" onclick="history.back(); return false;">Back</a>
<a href="/">Dashboard</a>
<a href="/controller/manage">Manage Agents</a>
</nav>
</header>
+3 -2
View File
@@ -2,6 +2,7 @@
<div id="bluetoothMode" class="mode-content">
<!-- Capability Status -->
<div id="btCapabilityStatus" class="section" style="display: none;">
<span id="btCapabilityMarker" style="display: none;"></span>
<!-- Populated by JavaScript with capability warnings -->
</div>
@@ -50,10 +51,10 @@
<!-- Message Container for status cards -->
<div id="btMessageContainer"></div>
<div class="section" style="margin-top: 10px;">
<div class="section" id="btExportSection" style="margin-top: 10px;">
<h3>Export</h3>
<div style="display: flex; gap: 8px;">
<button class="preset-btn" onclick="btExport('csv')" style="flex: 1;">
<button class="preset-btn" id="btExportCsvBtn" onclick="btExport('csv')" style="flex: 1;">
Export CSV
</button>
<button class="preset-btn" onclick="btExport('json')" style="flex: 1;">
+6 -6
View File
@@ -97,8 +97,8 @@
</div>
<!-- Advanced -->
<div class="section" style="margin-top: 12px;">
<h3 style="margin-bottom: 12px;">Baseline Recording</h3>
<div class="section">
<h3>Baseline Recording</h3>
<div class="form-group">
<input type="text" id="tscmBaselineName" placeholder="Baseline name...">
</div>
@@ -111,8 +111,8 @@
<div id="tscmBaselineStatus" style="margin-top: 8px; font-size: 11px; color: var(--text-muted);"></div>
</div>
<div class="section" style="margin-top: 12px;">
<h3 style="margin-bottom: 12px;">Meeting Window</h3>
<div class="section">
<h3>Meeting Window</h3>
<div id="tscmMeetingStatus" style="font-size: 11px; color: var(--text-muted); margin-bottom: 8px;">
No active meeting
</div>
@@ -131,8 +131,8 @@
</div>
<!-- Tools -->
<div class="section" style="margin-top: 12px;">
<h3 style="margin-bottom: 10px;">Tools</h3>
<div class="section">
<h3>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
+23 -14
View File
@@ -5,19 +5,28 @@
<p style="color: var(--text-secondary); font-size: 11px; line-height: 1.5; margin-bottom: 15px;">
Scan for nearby WiFi networks, analyze channels, and monitor wireless activity.
</p>
<div style="background: rgba(74, 158, 255, 0.1); border: 1px solid rgba(74, 158, 255, 0.3); border-radius: 4px; padding: 8px; font-size: 10px;">
<span style="color: var(--accent-cyan);">Controls in function bar above</span>
</div>
<!-- Scan Mode Tabs -->
<div style="margin-top: 12px;">
<div class="wifi-scan-mode-tabs" style="display: flex; gap: 4px;">
<button id="wifiScanModeQuick" class="wifi-mode-tab active" style="flex: 1; padding: 8px; font-size: 11px; background: var(--accent-green); color: #000; border: none; border-radius: 4px; cursor: pointer;">
<div class="wifi-scan-mode-tabs" style="display: flex; gap: 4px; margin-bottom: 10px;">
<button id="wifiScanModeQuick" class="wifi-mode-tab active">
Quick Scan
</button>
<button id="wifiScanModeDeep" class="wifi-mode-tab" style="flex: 1; padding: 8px; font-size: 11px; background: var(--bg-tertiary); color: #888; border: 1px solid var(--border-color); border-radius: 4px; cursor: pointer;">
<button id="wifiScanModeDeep" class="wifi-mode-tab">
Deep Scan
</button>
</div>
<!-- Scan Control Buttons -->
<div id="wifiScanControls" style="display: flex; gap: 8px;">
<button id="wifiQuickScanBtn" class="preset-btn wifi-start-btn" onclick="if(typeof WiFiMode !== 'undefined') WiFiMode.startQuickScan()" style="flex: 1;">
Start Quick Scan
</button>
<button id="wifiDeepScanBtn" class="preset-btn wifi-start-btn" onclick="if(typeof WiFiMode !== 'undefined') WiFiMode.startDeepScan()" style="flex: 1; display: none;">
Start Deep Scan
</button>
<button id="wifiStopScanBtn" class="preset-btn wifi-stop-btn" onclick="if(typeof WiFiMode !== 'undefined') WiFiMode.stopScan()" style="flex: 1; display: none;">
Stop Scan
</button>
</div>
<div id="wifiCapabilityStatus" class="info-text" style="margin-top: 8px; font-size: 10px;"></div>
<!-- Show All Agents option (visible when agents are available) -->
<div id="wifiShowAllAgentsContainer" style="margin-top: 8px; display: none;">
@@ -32,10 +41,10 @@
<div class="section">
<h3>Monitor Mode</h3>
<div style="display: flex; gap: 8px;">
<button class="preset-btn" id="monitorStartBtn" onclick="enableMonitorMode()" style="flex: 1; background: var(--accent-green); color: #000;">
<button class="preset-btn wifi-monitor-btn" id="monitorStartBtn" onclick="WiFiHelpers.enableMonitorMode()" style="flex: 1;">
Enable Monitor
</button>
<button class="preset-btn" id="monitorStopBtn" onclick="disableMonitorMode()" style="flex: 1; display: none;">
<button class="preset-btn wifi-monitor-stop-btn" id="monitorStopBtn" onclick="WiFiHelpers.disableMonitorMode()" style="flex: 1; display: none;">
Disable Monitor
</button>
</div>
@@ -75,7 +84,7 @@
<label>Deauth Count</label>
<input type="text" id="deauthCount" value="5" placeholder="5">
</div>
<button class="preset-btn" onclick="sendDeauth()" style="width: 100%; border-color: var(--accent-red); color: var(--accent-red);">
<button class="preset-btn wifi-danger-btn" onclick="WiFiHelpers.sendDeauth()" style="width: 100%;">
Send Deauth
</button>
</div>
@@ -88,7 +97,7 @@
<div class="form-group">
<input type="text" id="watchMacInput" placeholder="AA:BB:CC:DD:EE:FF">
</div>
<button class="preset-btn" onclick="addWatchMac()" style="width: 100%; margin-bottom: 8px;">
<button class="preset-btn" onclick="WiFiHelpers.addWatchMac()" style="width: 100%; margin-bottom: 8px;">
Add to Watch List
</button>
<div id="watchList" style="max-height: 80px; overflow-y: auto; font-size: 10px; color: var(--text-dim);"></div>
@@ -115,10 +124,10 @@
<span id="captureStatus" style="font-weight: bold;">--</span>
</div>
<div style="display: flex; gap: 8px;">
<button class="preset-btn" onclick="checkCaptureStatus()" style="flex: 1; font-size: 10px; padding: 4px;">
<button class="preset-btn" onclick="WiFiHelpers.checkCaptureStatus()" style="flex: 1; font-size: 10px; padding: 4px;">
Check Status
</button>
<button class="preset-btn" onclick="stopHandshakeCapture()" style="flex: 1; font-size: 10px; padding: 4px; background: var(--accent-red); border: none; color: #fff;">
<button class="preset-btn wifi-stop-btn" onclick="WiFiHelpers.stopHandshakeCapture()" style="flex: 1; font-size: 10px; padding: 4px;">
Stop Capture
</button>
</div>
@@ -137,10 +146,10 @@
</div>
</div>
<div class="section">
<div class="section" id="wifiExportSection">
<h3>Export</h3>
<div style="display: flex; gap: 8px;">
<button class="preset-btn" onclick="WiFiMode.exportData('csv')" style="flex: 1;">
<button class="preset-btn" id="wifiExportCsvBtn" onclick="WiFiMode.exportData('csv')" style="flex: 1;">
Export CSV
</button>
<button class="preset-btn" onclick="WiFiMode.exportData('json')" style="flex: 1;">
-5
View File
@@ -68,7 +68,6 @@
<span id="trackingStatus">TRACKING</span>
</div>
<div class="datetime" id="utcTime">--:--:-- UTC</div>
<a href="/" class="back-link">Main Dashboard</a>
</div>
</header>
@@ -304,10 +303,6 @@
function setupEmbeddedMode() {
if (isEmbedded) {
// Hide back link when embedded
const backLink = document.querySelector('.back-link');
if (backLink) backLink.style.display = 'none';
// Add embedded class to body for CSS adjustments
document.body.classList.add('embedded');