Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions ui/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"env": {
"browser": true,
"es2022": true
},
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module"
},
"rules": {
"no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
"no-undef": "error",
"no-var": "error",
"prefer-const": "warn",
"eqeqeq": ["error", "always"],
"no-eval": "error",
"no-implied-eval": "error",
"no-new-func": "error",
"no-script-url": "error",
"no-alert": "warn",
"no-console": ["warn", { "allow": ["warn", "error", "info"] }],
"curly": ["warn", "multi-line"],
"no-throw-literal": "error",
"prefer-template": "warn",
"no-duplicate-imports": "error"
},
"ignorePatterns": [
"node_modules/",
"mobile/",
"vendor/",
"*.min.js"
]
}
123 changes: 85 additions & 38 deletions ui/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ import { wsService } from './services/websocket.service.js';
import { healthService } from './services/health.service.js';
import { sensingService } from './services/sensing.service.js';
import { backendDetector } from './utils/backend-detector.js';
import { KeyboardShortcuts } from './utils/keyboard-shortcuts.js';
import { PerfMonitor } from './utils/perf-monitor.js';
import { toastManager } from './utils/toast.js';
import { ThemeToggle } from './utils/theme-toggle.js';
import { CommandPalette } from './utils/command-palette.js';
import { ActivityLog } from './utils/activity-log.js';
import { DataExport } from './utils/data-export.js';
import { FullscreenManager } from './utils/fullscreen.js';
import { ConnectionStatus } from './utils/connection-status.js';

class WiFiDensePoseApp {
constructor() {
Expand All @@ -30,10 +39,13 @@ class WiFiDensePoseApp {

// Initialize UI components
this.initializeComponents();


// Initialize enhancements
this.initializeEnhancements();

// Set up global event listeners
this.setupEventListeners();

this.isInitialized = true;
console.log('WiFi DensePose UI initialized successfully');

Expand Down Expand Up @@ -167,6 +179,58 @@ class WiFiDensePoseApp {
}
}

// Initialize enhancement modules
initializeEnhancements() {
// Toast notifications
toastManager.init();

// Connection status widget in header
this.connectionStatus = new ConnectionStatus();
this.connectionStatus.init();

// Theme toggle
this.themeToggle = new ThemeToggle();
this.themeToggle.init();

// Performance monitor
this.perfMonitor = new PerfMonitor();
this.perfMonitor.init();

// Activity log
this.activityLog = new ActivityLog();
this.activityLog.init();

// Data export
this.dataExport = new DataExport();
this.dataExport.init();

// Fullscreen manager
this.fullscreenManager = new FullscreenManager();
this.fullscreenManager.init();

// Command palette (Ctrl+K)
this.commandPalette = new CommandPalette(this);
this.commandPalette.init();

// Keyboard shortcuts (pass app reference for tab switching)
this.keyboardShortcuts = new KeyboardShortcuts(this);
this.keyboardShortcuts.register('l', 'Toggle activity log', () => {
document.dispatchEvent(new CustomEvent('toggle-activity-log'));
});
this.keyboardShortcuts.register('e', 'Export sensor data', () => {
document.dispatchEvent(new CustomEvent('export-data'));
});
this.keyboardShortcuts.register('f', 'Toggle fullscreen', () => {
document.dispatchEvent(new CustomEvent('toggle-fullscreen'));
});
this.keyboardShortcuts.init();

// Listen for show-shortcuts from command palette
document.addEventListener('show-shortcuts', () => {
this.keyboardShortcuts.showHelp();
});
}

// Handle tab changes
handleTabChange(newTab, oldTab) {
console.log(`Tab changed from ${oldTab} to ${newTab}`);
Expand Down Expand Up @@ -272,45 +336,17 @@ class WiFiDensePoseApp {
});
}

// Show backend status notification
// Show backend status notification (uses enhanced toast system)
showBackendStatus(message, type) {
// Create status notification if it doesn't exist
let statusToast = document.getElementById('backendStatusToast');
if (!statusToast) {
statusToast = document.createElement('div');
statusToast.id = 'backendStatusToast';
statusToast.className = 'backend-status-toast';
document.body.appendChild(statusToast);
}

statusToast.textContent = message;
statusToast.className = `backend-status-toast ${type}`;
statusToast.classList.add('show');

// Auto-hide success messages, keep warnings and errors longer
const timeout = type === 'success' ? 3000 : 8000;
setTimeout(() => {
statusToast.classList.remove('show');
}, timeout);
const toastType = type === 'success' ? 'success' : 'warning';
toastManager[toastType](message, {
duration: type === 'success' ? 3000 : 8000
});
}

// Show global error message
// Show global error message (uses enhanced toast system)
showGlobalError(message) {
// Create error toast if it doesn't exist
let errorToast = document.getElementById('globalErrorToast');
if (!errorToast) {
errorToast = document.createElement('div');
errorToast.id = 'globalErrorToast';
errorToast.className = 'error-toast';
document.body.appendChild(errorToast);
}

errorToast.textContent = message;
errorToast.classList.add('show');

setTimeout(() => {
errorToast.classList.remove('show');
}, 5000);
toastManager.error(message, { duration: 6000 });
}

// Clean up resources
Expand All @@ -326,9 +362,20 @@ class WiFiDensePoseApp {

// Disconnect all WebSocket connections
wsService.disconnectAll();

// Stop health monitoring
healthService.dispose();

// Dispose enhancements
if (this.keyboardShortcuts) this.keyboardShortcuts.dispose();
if (this.perfMonitor) this.perfMonitor.dispose();
if (this.themeToggle) this.themeToggle.dispose();
if (this.commandPalette) this.commandPalette.dispose();
if (this.activityLog) this.activityLog.dispose();
if (this.dataExport) this.dataExport.dispose();
if (this.fullscreenManager) this.fullscreenManager.dispose();
if (this.connectionStatus) this.connectionStatus.dispose();
toastManager.dispose();
}

// Public API
Expand Down
43 changes: 39 additions & 4 deletions ui/components/TabManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,33 @@ export class TabManager {
tab.addEventListener('click', () => this.switchTab(tab));
});

// Arrow key navigation within tab bar (WCAG)
const nav = this.container.querySelector('.nav-tabs');
if (nav) {
nav.addEventListener('keydown', (e) => {
const buttonTabs = this.tabs.filter(t => t.tagName === 'BUTTON' && !t.disabled);
const currentIndex = buttonTabs.indexOf(document.activeElement);
if (currentIndex === -1) return;

let nextIndex = -1;
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
nextIndex = (currentIndex + 1) % buttonTabs.length;
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
nextIndex = (currentIndex - 1 + buttonTabs.length) % buttonTabs.length;
} else if (e.key === 'Home') {
nextIndex = 0;
} else if (e.key === 'End') {
nextIndex = buttonTabs.length - 1;
}

if (nextIndex >= 0) {
e.preventDefault();
buttonTabs[nextIndex].focus();
this.switchTab(buttonTabs[nextIndex]);
}
});
}

// Activate first tab if none active
const activeTab = this.tabs.find(tab => tab.classList.contains('active'));
if (activeTab) {
Expand All @@ -36,14 +63,22 @@ export class TabManager {
return;
}

// Update tab states
// Update tab states and ARIA attributes
this.tabs.forEach(tab => {
tab.classList.toggle('active', tab === tabElement);
const isActive = tab === tabElement;
tab.classList.toggle('active', isActive);
if (tab.hasAttribute('aria-selected')) {
tab.setAttribute('aria-selected', String(isActive));
}
});

// Update content visibility
// Update content visibility and ARIA
this.tabContents.forEach(content => {
content.classList.toggle('active', content.id === tabId);
const isActive = content.id === tabId;
content.classList.toggle('active', isActive);
if (content.hasAttribute('role')) {
content.setAttribute('aria-hidden', String(!isActive));
}
});

// Update active tab
Expand Down
45 changes: 24 additions & 21 deletions ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,37 @@
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Skip to main content link for keyboard/screen reader users -->
<a href="#dashboard" class="skip-to-content">Skip to main content</a>

<div class="container">
<!-- Header -->
<header class="header">
<header class="header" role="banner">
<h1>WiFi DensePose</h1>
<p class="subtitle">Human Tracking Through Walls Using WiFi Signals</p>
<div class="header-info">
<span class="api-version"></span>
<span class="api-environment"></span>
<span class="overall-health"></span>
<span class="api-version" aria-label="API version"></span>
<span class="api-environment" aria-label="Environment"></span>
<span class="overall-health" role="status" aria-live="polite" aria-label="System health"></span>
</div>
</header>

<!-- Navigation -->
<nav class="nav-tabs">
<button class="nav-tab active" data-tab="dashboard">Dashboard</button>
<button class="nav-tab" data-tab="hardware">Hardware</button>
<button class="nav-tab" data-tab="demo">Live Demo</button>
<button class="nav-tab" data-tab="architecture">Architecture</button>
<button class="nav-tab" data-tab="performance">Performance</button>
<button class="nav-tab" data-tab="applications">Applications</button>
<button class="nav-tab" data-tab="sensing">Sensing</button>
<button class="nav-tab" data-tab="training">Training</button>
<nav class="nav-tabs" role="tablist" aria-label="Main navigation">
<button class="nav-tab active" data-tab="dashboard" role="tab" aria-selected="true" aria-controls="dashboard">Dashboard</button>
<button class="nav-tab" data-tab="hardware" role="tab" aria-selected="false" aria-controls="hardware">Hardware</button>
<button class="nav-tab" data-tab="demo" role="tab" aria-selected="false" aria-controls="demo">Live Demo</button>
<button class="nav-tab" data-tab="architecture" role="tab" aria-selected="false" aria-controls="architecture">Architecture</button>
<button class="nav-tab" data-tab="performance" role="tab" aria-selected="false" aria-controls="performance">Performance</button>
<button class="nav-tab" data-tab="applications" role="tab" aria-selected="false" aria-controls="applications">Applications</button>
<button class="nav-tab" data-tab="sensing" role="tab" aria-selected="false" aria-controls="sensing">Sensing</button>
<button class="nav-tab" data-tab="training" role="tab" aria-selected="false" aria-controls="training">Training</button>
<a href="pose-fusion.html" class="nav-tab" style="text-decoration:none">Pose Fusion</a>
<a href="observatory.html" class="nav-tab" style="text-decoration:none">Observatory</a>
</nav>

<!-- Dashboard Tab -->
<section id="dashboard" class="tab-content active">
<section id="dashboard" class="tab-content active" role="tabpanel" aria-labelledby="dashboard">
<div class="hero-section">
<h2>Revolutionary WiFi-Based Human Pose Detection</h2>
<p class="hero-description">
Expand Down Expand Up @@ -181,7 +184,7 @@ <h3>Low Cost</h3>
</section>

<!-- Hardware Tab -->
<section id="hardware" class="tab-content">
<section id="hardware" class="tab-content" role="tabpanel" aria-labelledby="hardware" aria-hidden="true">
<h2>Hardware Configuration</h2>

<div class="hardware-grid">
Expand Down Expand Up @@ -259,7 +262,7 @@ <h4>Real-time CSI Data</h4>
</section>

<!-- Demo Tab -->
<section id="demo" class="tab-content">
<section id="demo" class="tab-content" role="tabpanel" aria-labelledby="demo" aria-hidden="true">
<h2>Live Demonstration</h2>

<div class="demo-controls">
Expand Down Expand Up @@ -312,7 +315,7 @@ <h3>Human Pose Detection</h3>
</section>

<!-- Architecture Tab -->
<section id="architecture" class="tab-content">
<section id="architecture" class="tab-content" role="tabpanel" aria-labelledby="architecture" aria-hidden="true">
<h2>System Architecture</h2>

<div class="architecture-flow">
Expand Down Expand Up @@ -350,7 +353,7 @@ <h3>Wireframe Output</h3>
</section>

<!-- Performance Tab -->
<section id="performance" class="tab-content">
<section id="performance" class="tab-content" role="tabpanel" aria-labelledby="performance" aria-hidden="true">
<h2>Performance Analysis</h2>

<div class="performance-chart">
Expand Down Expand Up @@ -422,7 +425,7 @@ <h4>Limitations</h4>
</section>

<!-- Applications Tab -->
<section id="applications" class="tab-content">
<section id="applications" class="tab-content" role="tabpanel" aria-labelledby="applications" aria-hidden="true">
<h2>Real-World Applications</h2>

<div class="applications-grid">
Expand Down Expand Up @@ -489,10 +492,10 @@ <h3>Implementation Considerations</h3>
</section>

<!-- Sensing Tab -->
<section id="sensing" class="tab-content"></section>
<section id="sensing" class="tab-content" role="tabpanel" aria-labelledby="sensing" aria-hidden="true"></section>

<!-- Training Tab -->
<section id="training" class="tab-content">
<section id="training" class="tab-content" role="tabpanel" aria-labelledby="training" aria-hidden="true">
<div class="tab-header">
<h2>Model Training</h2>
<p>Record CSI data, train pose estimation models, and manage .rvf files</p>
Expand Down
Loading