|
| 1 | +import 'dart:io' show Platform; |
1 | 2 | import 'package:flutter_skill/flutter_skill.dart'; |
2 | 3 | import 'package:flutter/foundation.dart'; // For kDebugMode |
3 | 4 | import 'package:flutter/material.dart'; |
| 5 | +import 'package:window_manager/window_manager.dart'; |
| 6 | +import 'package:tray_manager/tray_manager.dart'; |
4 | 7 | import 'services/daemon_service.dart'; |
| 8 | +import 'services/tray_service.dart'; |
5 | 9 | import 'widgets/daemon_status_card.dart'; |
6 | 10 | import 'pages/chat_page.dart'; |
7 | 11 |
|
8 | | -void main() { |
| 12 | +Future<void> main() async { |
| 13 | + WidgetsFlutterBinding.ensureInitialized(); |
| 14 | + |
9 | 15 | if (kDebugMode) { |
10 | 16 | FlutterSkillBinding.ensureInitialized(); |
11 | 17 | } |
12 | 18 |
|
| 19 | + // Initialize desktop features on desktop platforms |
| 20 | + if (!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux)) { |
| 21 | + await _initDesktopFeatures(); |
| 22 | + } |
| 23 | + |
13 | 24 | runApp(const OpenCLIApp()); |
14 | 25 | } |
15 | 26 |
|
| 27 | +/// Initialize desktop-specific features (window management, system tray) |
| 28 | +Future<void> _initDesktopFeatures() async { |
| 29 | + // Window management setup |
| 30 | + await windowManager.ensureInitialized(); |
| 31 | + |
| 32 | + const windowOptions = WindowOptions( |
| 33 | + size: Size(800, 600), |
| 34 | + minimumSize: Size(600, 400), |
| 35 | + center: true, |
| 36 | + backgroundColor: Colors.transparent, |
| 37 | + skipTaskbar: false, |
| 38 | + titleBarStyle: TitleBarStyle.normal, |
| 39 | + title: 'OpenCLI', |
| 40 | + ); |
| 41 | + |
| 42 | + windowManager.waitUntilReadyToShow(windowOptions, () async { |
| 43 | + await windowManager.show(); |
| 44 | + await windowManager.focus(); |
| 45 | + }); |
| 46 | + |
| 47 | + // System tray setup |
| 48 | + await _initSystemTray(); |
| 49 | +} |
| 50 | + |
| 51 | +/// Initialize system tray icon and menu |
| 52 | +Future<void> _initSystemTray() async { |
| 53 | + // Set tray icon based on platform |
| 54 | + String iconPath; |
| 55 | + if (Platform.isMacOS) { |
| 56 | + iconPath = 'assets/tray_icon_macos.png'; |
| 57 | + } else if (Platform.isWindows) { |
| 58 | + iconPath = 'assets/tray_icon_windows.ico'; |
| 59 | + } else { |
| 60 | + iconPath = 'assets/tray_icon_linux.png'; |
| 61 | + } |
| 62 | + |
| 63 | + // Note: Icon files need to be added to assets |
| 64 | + // For now, we'll use a placeholder approach |
| 65 | + try { |
| 66 | + await trayManager.setIcon(iconPath); |
| 67 | + } catch (e) { |
| 68 | + debugPrint('Failed to set tray icon: $e'); |
| 69 | + } |
| 70 | + |
| 71 | + // Set tray menu |
| 72 | + Menu menu = Menu( |
| 73 | + items: [ |
| 74 | + MenuItem( |
| 75 | + key: 'show', |
| 76 | + label: 'Show OpenCLI', |
| 77 | + ), |
| 78 | + MenuItem.separator(), |
| 79 | + MenuItem( |
| 80 | + key: 'exit', |
| 81 | + label: 'Exit', |
| 82 | + ), |
| 83 | + ], |
| 84 | + ); |
| 85 | + |
| 86 | + await trayManager.setContextMenu(menu); |
| 87 | + |
| 88 | + // Set tooltip |
| 89 | + await trayManager.setToolTip('OpenCLI - AI Task Orchestration'); |
| 90 | +} |
| 91 | + |
16 | 92 | class OpenCLIApp extends StatelessWidget { |
17 | 93 | const OpenCLIApp({super.key}); |
18 | 94 |
|
@@ -50,11 +126,15 @@ class HomePage extends StatefulWidget { |
50 | 126 | class _HomePageState extends State<HomePage> { |
51 | 127 | int _selectedIndex = 0; |
52 | 128 | final DaemonService _daemonService = DaemonService(); |
| 129 | + final TrayService? _trayService = (!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux)) |
| 130 | + ? TrayService() |
| 131 | + : null; |
53 | 132 | bool _isConnecting = false; |
54 | 133 |
|
55 | 134 | @override |
56 | 135 | void initState() { |
57 | 136 | super.initState(); |
| 137 | + _trayService?.init(); |
58 | 138 | _connectToDaemon(); |
59 | 139 | } |
60 | 140 |
|
@@ -86,6 +166,7 @@ class _HomePageState extends State<HomePage> { |
86 | 166 |
|
87 | 167 | @override |
88 | 168 | void dispose() { |
| 169 | + _trayService?.dispose(); |
89 | 170 | _daemonService.dispose(); |
90 | 171 | super.dispose(); |
91 | 172 | } |
|
0 commit comments