5.9 KiB
Feature 0: Foundation
Context
Before implementing any user-facing features, we need shared infrastructure: package structure, platform abstraction, config persistence, and logging. Every subsequent feature depends on this.
Dependencies
- None (this is the base layer)
- Depended on by: All other features (1-5)
New Dependencies to Add
gradle/libs.versions.toml
| Library | Coordinate | Version | Purpose |
|---|---|---|---|
| kotlinx-serialization-json | org.jetbrains.kotlinx:kotlinx-serialization-json | 1.10.0 | JSON config persistence |
| kotlin-logging-jvm | io.github.oshai:kotlin-logging-jvm | 7.0.3 | Kotlin-idiomatic logging |
| logback-classic | ch.qos.logback:logback-classic | 1.5.18 | Logging backend with file rotation |
Plugins
| Plugin | ID | Version |
|---|---|---|
| Kotlin Serialization | org.jetbrains.kotlin.plugin.serialization | (uses kotlin version) |
Package Structure
com.rukira.wowbackup/
├── main.kt # Entry point (existing, to be updated)
├── App.kt # Root composable (existing, to be gutted)
├── platform/
│ ├── Platform.kt # OS enum + detection
│ ├── AppDirectories.kt # Platform-specific app data paths
│ └── WoWLocations.kt # Default WoW install detection
├── config/
│ ├── AppConfig.kt # @Serializable data class
│ └── ConfigManager.kt # Read/write JSON config file
└── logging/
└── LoggingSetup.kt # Logback initialization
Implementation Steps
Step 1: Add dependencies
Files to modify:
gradle/libs.versions.toml— add versions, libraries, and serialization pluginbuild.gradle.kts(root) — register serialization plugincomposeApp/build.gradle.kts— apply serialization plugin, add new deps to jvmMainsettings.gradle.kts— rename root project from "MyApplication" to "WoWBackup"
Step 2: Platform abstraction
Files to create:
composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/platform/Platform.kt
enum class OS { Windows, Mac, Linux, Other }
object Platform {
val current: OS = System.getProperty("os.name").lowercase().let { name ->
when {
"mac" in name -> OS.Mac
"win" in name -> OS.Windows
"nux" in name || "nix" in name -> OS.Linux
else -> OS.Other
}
}
}
Files to delete:
composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/Platform.kt(old template file)composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/Greeting.kt(template boilerplate)
Step 3: App directories
Files to create:
composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/platform/AppDirectories.kt
Resolves platform-specific app data directory:
- macOS:
~/Library/Application Support/WoWBackup/ - Windows:
%APPDATA%/WoWBackup/ - Linux:
$XDG_CONFIG_HOME/WoWBackup/(fallback~/.config/WoWBackup/)
Creates directory on first access if it doesn't exist.
Step 4: WoW install detection
Files to create:
composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/platform/WoWLocations.kt
Scans known default install paths:
- macOS:
/Applications/World of Warcraft/,~/Applications/World of Warcraft/ - Windows:
C:\Program Files (x86)\World of Warcraft\,C:\Program Files\World of Warcraft\
Validates by checking for WTF folder, WoW executable, or .app bundle.
Returns File? — null if not found.
Step 5: Config data model + persistence
Files to create:
composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/config/AppConfig.kt
@Serializable
data class AppConfig(
val wowInstallPath: String? = null,
val backupPath: String? = null,
val backupTimeOfDay: LocalTime = LocalTime(hour = 3, minute = 0), // daily at 3:00 AM
val backupHistoryCount: Int = 5,
val forceBackupWhileRunning: Boolean = false,
val backupWtf: Boolean = true,
val backupInterface: Boolean = true,
val compressionEnabled: Boolean = false,
val notificationsEnabled: Boolean = true,
val runAtStartup: Boolean = false,
)
composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/config/ConfigManager.kt
Responsibilities:
- Load config from
<appDataDir>/config.json(returns defaults if file missing) - Save config to
<appDataDir>/config.json(pretty-printed JSON) - Expose config as
StateFlow<AppConfig>for reactive UI updates isConfigured: Boolean— true when wowInstallPath and backupPath are set- Thread-safe reads/writes
Step 6: Logging setup
Files to create:
composeApp/src/jvmMain/resources/logback.xml
Configuration:
-
Console appender (for development)
-
Rolling file appender to
<appDataDir>/logs/wowbackup.log -
Daily rotation + 10MB max size per file
-
30 days retention / 100MB total cap
-
Pattern:
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n -
composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/logging/LoggingSetup.kt
Programmatically sets the log directory to the platform-specific app data path at startup (since logback.xml can't reference runtime-resolved paths directly).
Step 7: Update entry point
Files to modify:
composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/main.kt- Initialize logging on startup
- Load config via ConfigManager
- Update window title to "WoW Backup"
composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/App.kt- Strip template boilerplate
- Simple placeholder screen showing config status ("Configured" / "Not configured")
Verification
./gradlew composeApp:run— app launches, shows placeholder UI- Check
~/Library/Application Support/WoWBackup/(macOS) exists after launch - Check
config.jsonis created with defaults after first run - Check
logs/wowbackup.logexists and contains startup log entries - If WoW is installed in default location, verify auto-detection logs the path