import { TabManager } from './components/TabManager.js';
import { DashboardTab } from './components/DashboardTab.js';
import { HardwareTab } from './components/HardwareTab.js';
import { LiveDemoTab } from './components/LiveDemoTab.js';
import { SensingTab } from './components/SensingTab.js';
import { apiService } from './services/api.service.js';
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';
class WiFiDensePoseApp {
constructor() {
this.components = {};
this.isInitialized = false;
}
async init() {
try {
console.log('Initializing WiFi DensePose UI...');
this.setupErrorHandling();
await this.initializeServices();
this.initializeComponents();
this.setupEventListeners();
this.isInitialized = true;
console.log('WiFi DensePose UI initialized successfully');
} catch (error) {
console.error('Failed to initialize application:', error);
this.showGlobalError('Failed to initialize application. Please refresh the page.');
}
}
async initializeServices() {
apiService.addResponseInterceptor(async (response, url) => {
if (!response.ok && response.status === 401) {
console.warn('Authentication required for:', url);
}
return response;
});
const useMock = await backendDetector.shouldUseMockServer();
if (useMock) {
console.log('🧪 Initializing with mock server for testing');
const { mockServer } = await import('./utils/mock-server.js');
mockServer.start();
this.showBackendStatus('Mock server active - testing mode', 'warning');
} else {
console.log('🔌 Connecting to backend...');
try {
const health = await healthService.checkLiveness();
console.log('✅ Backend responding:', health);
this.showBackendStatus('Connected to Rust sensing server', 'success');
} catch (error) {
console.warn('⚠️ Backend not available:', error.message);
this.showBackendStatus('Backend unavailable — start sensing-server', 'warning');
}
sensingService.start();
}
}
initializeComponents() {
const container = document.querySelector('.container');
if (!container) {
throw new Error('Main container not found');
}
this.components.tabManager = new TabManager(container);
this.components.tabManager.init();
this.initializeTabComponents();
this.components.tabManager.onTabChange((newTab, oldTab) => {
this.handleTabChange(newTab, oldTab);
});
}
initializeTabComponents() {
const dashboardContainer = document.getElementById('dashboard');
if (dashboardContainer) {
this.components.dashboard = new DashboardTab(dashboardContainer);
this.components.dashboard.init().catch(error => {
console.error('Failed to initialize dashboard:', error);
});
}
const hardwareContainer = document.getElementById('hardware');
if (hardwareContainer) {
this.components.hardware = new HardwareTab(hardwareContainer);
this.components.hardware.init();
}
const demoContainer = document.getElementById('demo');
if (demoContainer) {
this.components.demo = new LiveDemoTab(demoContainer);
this.components.demo.init();
}
const sensingContainer = document.getElementById('sensing');
if (sensingContainer) {
this.components.sensing = new SensingTab(sensingContainer);
}
this.initTrainingTab();
}
async initTrainingTab() {
try {
const [{ default: TrainingPanel }, { default: ModelPanel }] = await Promise.all([
import('./components/TrainingPanel.js'),
import('./components/ModelPanel.js')
]);
const trainingContainer = document.getElementById('training-panel-container');
if (trainingContainer) {
this.components.trainingPanel = new TrainingPanel(trainingContainer);
}
const modelContainer = document.getElementById('model-panel-container');
if (modelContainer) {
this.components.modelPanel = new ModelPanel(modelContainer);
}
} catch (error) {
console.error('Failed to load Training tab components:', error);
}
}
handleTabChange(newTab, oldTab) {
console.log(`Tab changed from ${oldTab} to ${newTab}`);
if (oldTab === 'demo' && this.components.demo) {
this.components.demo.stopDemo();
}
switch (newTab) {
case 'dashboard':
break;
case 'hardware':
break;
case 'demo':
break;
case 'sensing':
if (this.components.sensing && !this.components.sensing.splatRenderer) {
this.components.sensing.init().catch(error => {
console.error('Failed to initialize sensing tab:', error);
});
}
break;
case 'training':
if (this.components.trainingPanel && typeof this.components.trainingPanel.refresh === 'function') {
this.components.trainingPanel.refresh();
}
if (this.components.modelPanel && typeof this.components.modelPanel.refresh === 'function') {
this.components.modelPanel.refresh();
}
break;
}
}
setupEventListeners() {
window.addEventListener('resize', () => {
this.handleResize();
});
document.addEventListener('visibilitychange', () => {
this.handleVisibilityChange();
});
window.addEventListener('beforeunload', () => {
this.cleanup();
});
}
handleResize() {
const canvases = document.querySelectorAll('canvas');
canvases.forEach(canvas => {
const rect = canvas.parentElement.getBoundingClientRect();
if (canvas.width !== rect.width || canvas.height !== rect.height) {
canvas.width = rect.width;
canvas.height = rect.height;
}
});
}
handleVisibilityChange() {
if (document.hidden) {
console.log('Page hidden, pausing updates');
healthService.stopHealthMonitoring();
} else {
console.log('Page visible, resuming updates');
healthService.startHealthMonitoring();
}
}
setupErrorHandling() {
window.addEventListener('error', (event) => {
if (event.error) {
console.error('Global error:', event.error);
this.showGlobalError('An unexpected error occurred');
}
});
window.addEventListener('unhandledrejection', (event) => {
if (event.reason) {
console.error('Unhandled promise rejection:', event.reason);
this.showGlobalError('An unexpected error occurred');
}
});
}
showBackendStatus(message, type) {
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');
const timeout = type === 'success' ? 3000 : 8000;
setTimeout(() => {
statusToast.classList.remove('show');
}, timeout);
}
showGlobalError(message) {
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);
}
cleanup() {
console.log('Cleaning up application resources...');
Object.values(this.components).forEach(component => {
if (component && typeof component.dispose === 'function') {
component.dispose();
}
});
wsService.disconnectAll();
healthService.dispose();
}
getComponent(name) {
return this.components[name];
}
isReady() {
return this.isInitialized;
}
}
document.addEventListener('DOMContentLoaded', () => {
window.wifiDensePoseApp = new WiFiDensePoseApp();
window.wifiDensePoseApp.init();
});
export { WiFiDensePoseApp };