import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:sqlite3/sqlite3.dart'; import 'package:xp_nix/src/monitors/productivity_monitor.dart'; import 'package:xp_nix/src/config/config_manager.dart'; import 'package:xp_nix/src/logging/logger.dart'; import 'package:xp_nix/src/web/dashboard_server.dart'; import 'package:xp_nix/src/database/database_manager.dart'; import 'package:xp_nix/src/providers/system_time_provider.dart'; import 'package:xp_nix/src/build/dashboard_builder.dart'; // Window manager detection and factories import 'package:xp_nix/src/detectors/window_manager_detector.dart'; import 'package:xp_nix/src/factories/idle_monitor_factory.dart'; import 'package:xp_nix/src/factories/activity_detector_factory.dart'; import 'package:xp_nix/src/factories/desktop_enhancer_factory.dart'; // Interfaces import 'package:xp_nix/src/interfaces/i_idle_monitor.dart'; import 'package:xp_nix/src/interfaces/i_activity_detector.dart'; import 'package:xp_nix/src/interfaces/i_desktop_enhancer.dart'; late final Database _db; late final IIdleMonitor _idleMonitor; late final SystemTimeProvider _timeProvider; late final IDesktopEnhancer _desktopEnhancer; late final IActivityDetector? _activityDetector; late final ProductivityMonitor _monitor; late final DashboardServer _dashboardServer; // Enhanced main function with interactive commands and one-shot mode void main(List args) async { // Parse command line arguments String dbPath = 'productivity_tracker.db'; int port = 8080; for (int i = 0; i < args.length; i++) { switch (args[i]) { case '--db': if (i + 1 < args.length) { dbPath = args[i + 1]; i++; // Skip next argument as it's the db path } else { print('Error: --db flag requires a database path'); exit(1); } break; case '--port': if (i + 1 < args.length) { port = int.tryParse(args[i + 1]) ?? 8080; i++; // Skip next argument as it's the port number } else { print('Error: --port flag requires a port number'); exit(1); } break; case '--help': case '-h': print(''' XP Nix Server - Productivity Tracking with Gamification A productivity tracking system that works with both Hyprland and Sway window managers. Automatically detects your environment and adapts functionality accordingly. Usage: dart xp_nix.dart [options] Options: --db PATH Database file path (default: productivity_tracker.db) --port PORT Server port (default: 8080) --help, -h Show this help message Supported Window Managers: • Hyprland: Full visual effects, idle monitoring, activity detection • Sway: Activity detection, idle monitoring (no visual effects) • Detection: Automatic via environment variables Examples: dart xp_nix.dart dart xp_nix.dart --db test_data.db --port 8081 Requirements: • Hyprland: hypridle, hyprctl • Sway: swayidle, swaymsg '''); exit(0); } } print('🗄️ Using database: $dbPath'); print('🌐 Starting server on port: $port'); // Initialize logging system await Logger.instance.initialize(level: LogLevel.info, logDirectory: 'logs', maxFileSizeMB: 10, maxFiles: 5); // Initialize configuration manager await ConfigManager.instance.initialize(); // Detect window manager and display information final windowManager = WindowManagerDetector.instance.detect(); final detectionInfo = WindowManagerDetector.instance.getDetectionInfo(); print('🖼️ Detected window manager: ${windowManager.toString()}'); print(' Detection method: ${detectionInfo['detection_method']}'); // Log additional environment info Logger.info('Window manager detection: $detectionInfo'); _db = sqlite3.open(dbPath); // Create window manager-specific dependencies using factories try { _idleMonitor = IdleMonitorFactory.create(windowManager); _desktopEnhancer = DesktopEnhancerFactory.create(windowManager); _activityDetector = ActivityDetectorFactory.create(windowManager); _timeProvider = SystemTimeProvider(); // Validate requirements for the detected window manager final idleSupported = await IdleMonitorFactory.validateRequirements(windowManager); final activitySupported = await ActivityDetectorFactory.validateRequirements(windowManager); final enhancerSupported = await DesktopEnhancerFactory.validateRequirements(windowManager); print('💡 Component availability:'); print(' Idle monitoring: ${idleSupported ? '✅' : '❌'}'); print(' Activity detection: ${activitySupported ? '✅' : '❌'}'); print(' Visual enhancements: ${enhancerSupported ? '✅' : '❌'}'); if (!idleSupported) { print(' ⚠️ Idle monitoring may not work properly'); } if (!activitySupported) { print(' ⚠️ Activity detection will fall back to legacy polling'); } if (!enhancerSupported) { print(' ⚠️ Visual enhancements may be limited'); } } catch (e) { print('❌ Error creating window manager components: $e'); print(' Falling back to legacy mode...'); // Fallback to original Hyprland implementations (import at top of file) // Note: This requires adding imports back if needed Logger.error('Component creation failed, falling back to legacy mode: $e'); exit(1); // For now, exit on component creation failure } // Create monitor with dependency injection _monitor = ProductivityMonitor( db: _db, idleMonitor: _idleMonitor, timeProvider: _timeProvider, desktopEnhancer: _desktopEnhancer, activityDetector: _activityDetector, // May be null for fallback to legacy polling ); _dashboardServer = DashboardServer.withDatabase(DatabaseManager(_db)); ProcessSignal.sigint.watch().listen((_) async { Logger.info('Shutting down XP Nix...'); print('\nShutting down...'); _quit(); }); // Start the dashboard server try { await _dashboardServer.start(port); Logger.info('Dashboard available at: ${_dashboardServer.dashboardUrl}'); print('🌐 Dashboard available at: ${_dashboardServer.dashboardUrl}'); } catch (e) { Logger.error('Failed to start dashboard server: $e'); print('⚠️ Dashboard server failed to start: $e'); _quit(); } _monitor.start(); _monitor.printDetailedStats(); // Add command listener for manual controls stdin.transform(utf8.decoder).transform(LineSplitter()).listen((line) { final parts = line.trim().split(' '); final command = parts[0].toLowerCase(); switch (command) { case 'stats': _monitor.printDetailedStats(); break; case 'test': if (parts.length > 1) { final levelOrCommand = parts[1].toLowerCase(); if (levelOrCommand == 'overlay') { _testOverlay(parts.length > 2 ? int.tryParse(parts[2]) : null); } else { final level = int.tryParse(levelOrCommand) ?? 1; _monitor.testTheme(level); } } else { print('Usage: test [level|overlay] - Specify a level number or "overlay" to test XP overlay'); } break; case 'restore': _monitor.restoreDesktop(); break; case 'refresh': _monitor.refreshConfig(); break; case 'build': _handleBuildCommand(); break; case 'help': print(''' Available commands: - stats: Show current productivity stats - test [level]: Test theme for specific level - test overlay [value]: Test XP overlay at cursor position - restore: Restore desktop backup - refresh: Refresh base config from current system config - build: Build Flutter dashboard and copy to static files - help: Show this help '''); break; } }); print('💡 Type "help" for available commands'); // Keep running and show stats periodically while (true) { await Future.delayed(Duration(seconds: 1)); if (DateTime.now().second == 0 && DateTime.now().minute % 10 == 0) { _monitor.printDetailedStats(); } } } /// Handles the build command to rebuild the Flutter dashboard void _handleBuildCommand() async { print('🚀 Starting dashboard build process...'); // Check if Flutter is available if (!await DashboardBuilder.checkFlutterAvailable()) { print('❌ Flutter is not available in the system PATH'); print(' Please ensure Flutter is installed and available in your PATH'); return; } // Build the dashboard final success = await DashboardBuilder.buildDashboard(); if (success) { print('🎉 Dashboard build completed successfully!'); print(' The server is now serving the updated Flutter dashboard'); } else { print('❌ Dashboard build failed. Check the logs for more details.'); } } /// Test the XP overlay with an optional XP value Future _testOverlay(int? xpValue) async { print('🧪 Testing XP overlay system...'); final windowManager = WindowManagerDetector.instance.detect(); if (windowManager == WindowManager.unknown) { print('❌ No supported window manager detected'); return; } final overlayManager = _monitor.cursorOverlayManager; if (overlayManager == null) { print('❌ Overlay manager not initialized'); return; } final available = await overlayManager.isAvailable(); if (!available) { print('❌ Overlay system is not available. Check logs for details.'); return; } print('✅ Overlay system is available, showing test overlay...'); await overlayManager.testOverlay(testXP: xpValue ?? 42); } Future _quit() async { _monitor.stop(); await _dashboardServer.stop(); await Logger.instance.dispose(); _db.dispose(); exit(0); }