# Feature 1: System Tray Icon ## Context The app lives primarily in the system tray. The window is secondary — it opens for configuration/status and closes back to tray. This is the shell that all UI features plug into. ## Dependencies - **Depends on**: Feature 0 (Foundation) — logging, platform detection - **Depended on by**: Features 2-5 (all UI features are accessed through the tray) ## Approach Use Compose Desktop's built-in `Tray` composable (`androidx.compose.ui.window.Tray`). No extra libraries needed — it supports icons, tooltips, context menus, and notifications natively. ## App Lifecycle Model The app always runs with a tray icon visible. The main window is shown/hidden based on user interaction. Closing the window hides it to tray — it does not exit the app. Exiting is only via the tray menu "Quit" item. ## Implementation Steps ### Step 1: Create a tray icon asset **Files to create:** - `composeApp/src/jvmMain/resources/tray-icon.png` (32x32, simple recognizable icon) For macOS dark mode support, add JVM arg: `-Dapple.awt.enableTemplateImages=true` ### Step 2: Restructure main.kt for tray-first lifecycle **Files to modify:** - `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/main.kt` ```kotlin fun main() = application { // Initialize logging (from Feature 0) var isWindowVisible by remember { mutableStateOf(true) } // Which screen to show: STATUS (default) or CONFIG var currentScreen by remember { mutableStateOf(Screen.STATUS) } Tray( icon = painterResource("tray-icon.png"), tooltip = "WoW Backup", onAction = { isWindowVisible = true }, // click tray -> show window menu = { Item("Status", onClick = { currentScreen = Screen.STATUS isWindowVisible = true }) Item("Settings", onClick = { currentScreen = Screen.CONFIG isWindowVisible = true }) Separator() Item("Quit", onClick = ::exitApplication) } ) if (isWindowVisible) { Window( onCloseRequest = { isWindowVisible = false }, // close -> hide to tray title = "WoW Backup", ) { App(currentScreen) } } } ``` ### Step 3: Define screen navigation enum **Files to create:** - `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/ui/Screen.kt` ```kotlin enum class Screen { STATUS, CONFIG, RESTORE } ``` ### Step 4: Update App.kt for screen routing **Files to modify:** - `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/App.kt` Strip all template code. Route to placeholder screens based on `currentScreen`: - `Screen.STATUS` -> "Status screen placeholder" - `Screen.CONFIG` -> "Config screen placeholder" - `Screen.RESTORE` -> "Restore screen placeholder" ### Step 5: Auto-show config if not configured In `main.kt`, on startup check `ConfigManager.isConfigured`. If false, set `currentScreen = Screen.CONFIG` and `isWindowVisible = true`. ### Step 6: macOS JVM args **Files to modify:** - `composeApp/build.gradle.kts` — add to desktop application config: ```kotlin compose.desktop { application { jvmArgs("-Dapple.awt.enableTemplateImages=true") } } ``` ## Verification 1. `./gradlew composeApp:run` — app launches with tray icon visible 2. Tray icon shows context menu with Status, Settings, Quit 3. Clicking "Status" or "Settings" opens the window to the correct placeholder 4. Closing the window hides it (app stays running in tray) 5. Clicking tray icon again re-shows the window 6. "Quit" exits the app fully 7. On first run (no config), window auto-opens to config screen