diff --git a/app/lib/main.dart b/app/lib/main.dart index ec37c2a..cdf5ba0 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -51,9 +51,11 @@ void main() async { await windowManager.ensureInitialized(); + bool acrylicInitialized = false; if (Platform.isWindows || Platform.isMacOS) { try { - await Window.initialize(); + await Window.initialize().timeout(const Duration(seconds: 3)); + acrylicInitialized = true; } catch (_) { // AppLogger not yet initialized here; app continues without acrylic effects } @@ -62,6 +64,11 @@ void main() async { final storage = await StorageConfig.create(); await storage.ensureDirectories(); AppLogger.initialize(storage.logsPath); + AppLogger.info( + 'Startup: platform=${Platform.operatingSystem}, ' + 'version=${Platform.operatingSystemVersion}, ' + 'acrylicInit=$acrylicInitialized', + ); FlutterError.onError = (details) { AppLogger.error( @@ -95,20 +102,22 @@ void main() async { try { if (Platform.isWindows) { + AppLogger.info('main: applying initial Mica effect'); await Window.setEffect( effect: WindowEffect.mica, color: const Color(0x00000000), dark: _isMicaDark(config.themeMode), - ); + ).timeout(const Duration(seconds: 2)); + AppLogger.info('main: Mica effect applied'); } else if (Platform.isMacOS) { await Window.setEffect( effect: WindowEffect.sidebar, color: const Color(0x00000000), dark: _isMicaDark(config.themeMode), - ); + ).timeout(const Duration(seconds: 2)); } } catch (e) { - AppLogger.error('Window.setEffect failed: $e'); + AppLogger.warn('main: Window.setEffect failed (non-fatal): $e'); } runApp( diff --git a/app/lib/shell/app_window.dart b/app/lib/shell/app_window.dart index 72b0b0f..71a7fbd 100644 --- a/app/lib/shell/app_window.dart +++ b/app/lib/shell/app_window.dart @@ -84,36 +84,66 @@ class AppWindow { } Future init({bool startVisible = false}) async { - await windowManager.waitUntilReadyToShow(null, () async { - await windowManager.setTitle('CopyPaste'); - await windowManager.setSize(Size(_popupWidth, _popupHeight)); - await windowManager.setMinimumSize(Size(_popupWidth, 400)); - await windowManager.setMaximumSize(Size(_popupWidth, 900)); - await windowManager.setTitleBarStyle( - TitleBarStyle.hidden, - windowButtonVisibility: !Platform.isMacOS, + AppLogger.info( + 'AppWindow.init: startVisible=$startVisible, ' + 'showInTaskbar=$showInTaskbar, ' + 'size=${_popupWidth}x$_popupHeight', + ); + try { + await windowManager + .waitUntilReadyToShow(null, () async { + await _configureWindow(startVisible); + }) + .timeout(const Duration(seconds: 5)); + AppLogger.info('AppWindow.init: waitUntilReadyToShow completed'); + } catch (e) { + AppLogger.warn( + 'AppWindow.init: waitUntilReadyToShow failed ($e), ' + 'attempting direct configuration', ); - await windowManager.setAlwaysOnTop(true); - await windowManager.setResizable(false); - await windowManager.setMaximizable(false); - await windowManager.setPreventClose(true); - final inTaskbar = showInTaskbar && Platform.isWindows; - await windowManager.setSkipTaskbar(!inTaskbar); - if (Platform.isWindows || Platform.isMacOS) { - await windowManager.setBackgroundColor(const Color(0x00000000)); - await applyEffect(); + try { + await _configureWindow(startVisible); + AppLogger.info('AppWindow.init: direct configuration succeeded'); + } catch (e2) { + AppLogger.error('Window configuration failed: $e2'); } - if (startVisible) { - await windowManager.center(); - await windowManager.focus(); - } else if (inTaskbar) { - await windowManager.minimize(); - } else { - await windowManager.hide(); - } - }); + } _visible = startVisible; _ready = true; + AppLogger.info('AppWindow.init: done, ready=$_ready, visible=$_visible'); + } + + Future _configureWindow(bool startVisible) async { + await windowManager.setTitle('CopyPaste'); + await windowManager.setSize(Size(_popupWidth, _popupHeight)); + await windowManager.setMinimumSize(Size(_popupWidth, 400)); + await windowManager.setMaximumSize(Size(_popupWidth, 900)); + await windowManager.setTitleBarStyle( + TitleBarStyle.hidden, + windowButtonVisibility: !Platform.isMacOS, + ); + await windowManager.setAlwaysOnTop(true); + await windowManager.setResizable(false); + await windowManager.setMaximizable(false); + await windowManager.setPreventClose(true); + final inTaskbar = showInTaskbar && Platform.isWindows; + await windowManager.setSkipTaskbar(!inTaskbar); + if (Platform.isWindows || Platform.isMacOS) { + await windowManager.setBackgroundColor(const Color(0x00000000)); + AppLogger.info('_configureWindow: applying initial effect'); + await applyEffect(); + } + if (startVisible) { + AppLogger.info('_configureWindow: centering and focusing'); + await windowManager.center(); + await windowManager.focus(); + } else if (inTaskbar) { + AppLogger.info('_configureWindow: minimizing to taskbar'); + await windowManager.minimize(); + } else { + AppLogger.info('_configureWindow: hiding window'); + await windowManager.hide(); + } } bool _isDark = false; @@ -126,13 +156,13 @@ class AppWindow { effect: WindowEffect.mica, color: const Color(0x00000000), dark: _isDark, - ); + ).timeout(const Duration(seconds: 2)); } else if (Platform.isMacOS) { await Window.setEffect( effect: WindowEffect.sidebar, color: const Color(0x00000000), dark: _isDark, - ); + ).timeout(const Duration(seconds: 2)); } } catch (e) { // Effect failure is non-fatal — app runs without the acrylic effect. @@ -282,6 +312,7 @@ class AppWindow { } Future show() async { + AppLogger.info('AppWindow.show: starting'); if (Platform.isLinux) { // On X11/GTK, show the window first (so it gets realized/mapped by the WM), // then set the position (avoids WM initial-placement overriding our offset), @@ -294,11 +325,14 @@ class AppWindow { } else { await _positionNearCursor(); if (Platform.isWindows) { - await applyEffect(); await windowManager.setSkipTaskbar(false); } await windowManager.show(); await windowManager.focus(); + AppLogger.info('AppWindow.show: window shown and focused'); + if (Platform.isWindows) { + await applyEffect(); + } } _visible = true; onVisibilityChanged?.call(true); @@ -375,16 +409,18 @@ class AppWindow { bool get isGateMode => _gateMode; Future enterGateMode() async { + AppLogger.info('AppWindow.enterGateMode: starting'); _gateMode = true; await windowManager.setResizable(false); await windowManager.setMinimumSize(const Size(_gateWidth, _gateHeight)); await windowManager.setMaximumSize(const Size(_gateWidth, _gateHeight)); await windowManager.setSize(const Size(_gateWidth, _gateHeight)); await windowManager.setAlwaysOnTop(false); - await windowManager.show(); await windowManager.center(); + await windowManager.show(); await windowManager.focus(); _visible = true; + AppLogger.info('AppWindow.enterGateMode: done'); } Future exitGateMode() async {