This commit is contained in:
Rukira 2026-08-24 15:22:22 +01:00
parent b992d78ef6
commit 57b5ede338
13 changed files with 198 additions and 21 deletions

View file

@ -1,26 +1,83 @@
# WoW Backup
## Tech
This is a Kotlin [Compose Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform.html) project targeting Desktop (JVM).
A lightweight, native-feeling desktop application built with Kotlin and Compose Multiplatform to automatically backup and restore World of Warcraft configuration data (WTF and Interface directories) on macOS and Windows.
* [/composeApp](./composeApp/src) is for code that will be shared across your Compose Multiplatform applications.
It contains several subfolders:
- [commonMain](./composeApp/src/commonMain/kotlin) is for code that's common for all targets.
- Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name.
If you want to edit the Desktop (JVM) specific part, the [jvmMain](./composeApp/src/jvmMain/kotlin)
folder is the appropriate location.
## Overview
### Build and Run Application
World of Warcraft stores all UI configurations, macros, keybindings, and addon saved variables in the `WTF/` and `Interface/` folders. Losing this data due to game corruption, accidental deletion, or addon errors can mean losing years of customized setups. **WoW Backup** runs discreetly in the system tray, automatically creates scheduled, timestamped backups (optionally compressed as ZIP archives), and will provide seamless one-click restoration.
To build and run the development version of the desktop app, use the run configuration from the run widget
in your IDE's toolbar or run it directly from the terminal:
- on macOS/Linux
## Key Features
- 🛡️ **System Tray Native Life Cycle**: Lives in the menu bar / system tray with custom template icons matching light/dark OS appearances. Closing the window hides to tray; quitting is explicit.
- ⚙️ **Smart Auto-Configuration**: Automatically detects standard WoW install locations (supporting retail and classic directories) and validates folder permissions.
- ⏰ **Automated & Scheduled Backups**: Background scheduler powered by Cardiologist ensures dependable daily backups at a user-defined time.
- 📦 **Compression & History Management**: Supports both folder replication and ZIP compression, with automatic pruning of backups beyond the configured retention limit (130 backups).
- 🎮 **Game State Detection**: Checks for active WoW processes before running backups to prevent file-locking conflicts, with optional "force backup" override and safety confirmation.
- 🔔 **Native OS Notifications**: Notifies the user when backups start, succeed, fail, or are skipped due to game execution.
- 🚀 **Launch at Startup**: Integrated with macOS LaunchAgents (`~/Library/LaunchAgents/`) and Windows Registry (`HKCU\...\Run`).
- 🎨 **Modern Theming & Customization**: Dynamic Material 3 theming supporting System/Light/Dark modes and configurable accent colors (via MaterialKolor).
- 🔄 **Restore System (In Progress)**: Interactive restore interface to safely roll back WTF and Interface configurations.
---
## Tech Stack & Architecture
- **Language & Runtime**: Kotlin Multiplatform targeting Desktop (JVM, Java 17+)
- **UI Framework**: JetBrains Compose Multiplatform with Material 3 Design
- **Architecture**: MVI / MVVM with Kotlin Coroutines and StateFlow
- **Scheduling**: `io.github.kevincianfarini.cardiologist` for drift-free daily execution
- **Logging**: `logback-classic` + `kotlin-logging-jvm` with daily rotation and size archiving in app data directories
- **Serialization**: `kotlinx-serialization-json` for typed JSON configuration persistence
- **Theming**: `com.materialkolor:material-kolor` for dynamic palette generation
---
## Build & Run
### Prerequisites
- JDK 17 or higher installed
### Development Run
- **macOS / Linux**:
```shell
./gradlew :composeApp:run
```
- on Windows
- **Windows**:
```shell
.\gradlew.bat :composeApp:run
```
### Package Application Distributions
- **macOS DMG**:
```shell
./gradlew :composeApp:packageDmg
```
- **Windows MSI**:
```shell
.\gradlew.bat :composeApp:packageMsi
```
- **Linux DEB**:
```shell
./gradlew :composeApp:packageDeb
```
---
## Feature Implementation Status
| Feature | Plan Document | Status | Description |
|---|---|---|---|
| **0. Foundation** | `docs/plans/feature-0-foundation.md` | ✅ **Completed** | Architecture, config persistence, logging, platform abstractions |
| **1. System Tray** | `docs/plans/feature-1-system-tray.md` | ✅ **Completed** | Tray icon, menu, tray-first lifecycle, window alignment |
| **2. Configuration** | `docs/plans/feature-2-configuration.md` | ✅ **Completed** | Full settings UI, path validation, native file dialogs, startup manager |
| **3. Backup Engine** | `docs/plans/feature-3-backup.md` | ✅ **Completed** | Parallel copy, ZIP compression, scheduler, history pruning, notifications |
| **4. Status Screen** | `docs/plans/feature-4-status.md` | ✅ **Completed** | Live status dashboard, progress indicator, manual backup trigger, shortcuts |
| **5. Restore Screen** | `docs/plans/feature-5-restore.md` | 🚧 **Pending** | Backup list browser, ZIP extraction, confirmation flow, safety checks |
---
## Application Data Locations
- **macOS**: `~/Library/Application Support/WoWBackup/` (Logs: `.../logs/wowbackup.log`)
- **Windows**: `%APPDATA%\WoWBackup\` (Logs: `...\logs\wowbackup.log`)
- **Linux**: `~/.config/WoWBackup/` (Logs: `.../logs/wowbackup.log`)

View file

@ -2,6 +2,19 @@
- Provide a simple and configurable way to automatically backup WoW's config data
- Provide a way to restore backed up data
## Current Implementation Status
| Milestone / Feature | Status | Notes |
|---|---|---|
| **Foundation (Feature 0)** | ✅ Complete | Package structure, JSON persistence (`ConfigManager`), platform abstractions (`AppDirectories`, `Platform`), daily rolling logback logging. |
| **System Tray (Feature 1)** | ✅ Complete | Dynamic Java2D tray icon with template mode for dark/light themes, tray context menu, tray-aligned positioning for macOS & Windows. |
| **Configuration (Feature 2)** | ✅ Complete | Full M3 Settings UI, path auto-detection & validation, native file pickers, startup registration (macOS LaunchAgent / Windows Registry), theme/accent customization. |
| **Backup Engine (Feature 3)** | ✅ Complete | Background parallel copy and ZIP archive creation, process detection (`WoWProcessDetector`), drift-free scheduling (`Cardiologist`), automatic retention pruning, native notifications. |
| **Status Screen (Feature 4)** | ✅ Complete | Real-time status cards, live backup progress tracking, "Backup Now" trigger, missing configuration banner, quick folder shortcuts. |
| **Restore Screen (Feature 5)** | 🚧 In Progress / Next Priority | Restore engine, backup browser UI, extraction/overwrite mechanics, safety validation, cancellation flow. |
---
## Scope
- This is a desktop application, supporting both MacOS and Windows
- This uses Kotlin Compose Multiplatform to share as much code as possible
@ -15,19 +28,23 @@
- All app operations should be logged to a file
- The log should be human-readable and timestamped
- Log files should be rotated and archived to keep sizes manageable
- **Status**: Implemented (`logback-classic` + `LoggingSetup.kt`, rotated daily and capped at 100MB).
#### Native look and feel
- The app should use native components for the UI, whenever possible
- The app should leverage native APIs, like the MacOS system tray, whenever possible
- **If** needed, prioritise MacOS over Windows
- **Status**: Implemented (Tray integration, AWT `FileDialog` for macOS directories, `JFileChooser` fallback, tray-aligned window positioning).
#### Permissions
- The app should request the minimum permissions it needs to run, as proactively as possible
- The app should not request any permissions it doesn't need
- The app should gracefully handle missing permissions, showing a warning to the user explaining the reasoning for needing those permissions
- We must pay special attention to file and directory access permissions, particularly on MacOS
- **Status**: Implemented (`WoWLocations.validateWoWInstall` explicitly tests directory readability and returns user-facing permission warnings).
### Features
#### 1. System Tray Icon
- Show a system tray icon when the app is active
- **Status**: Complete (`TrayIcon.kt`, `main.kt`).
#### 2. Configuration screen
- A configuration screen needs to be accessible
- Should be accessible through the system tray icon
@ -36,28 +53,36 @@
- Run at startup
- User should be able to enable/disable this
- If enabled, the app should run automatically when the user logs in
- **Status**: Complete (`StartupManager.kt`).
- WoW install location
- User should be able to pick the location within their file system
- The app should try to scan default locations for an existing install, potentially saving the user some clicks
- **Status**: Complete (`WoWLocations.findWoWInstall()`, `ConfigScreen.kt`).
- Backup location
- User should be able to pick the destination location within their file system
- Destination should be a folder
- **Status**: Complete (`NativeFolderPicker.kt`).
- Backup frequency
- User should be able to define a schedule for their backups
- A small message warning that the backup will run only if WoW isn't running
- User should be able to define how much backup history to keep
- **Status**: Complete (`TimePicker.kt`, `BackupScheduler.kt`).
- Force backup
- A checkbox to force the backup to run even when WoW is running
- A confirmation dialog should be shown if the user enables this
- **Status**: Complete (`ConfirmationDialog.kt`).
- Folder selection
- The user should be able to toggle the WTF and Interface folders for backup
- Each folder should have a small description of why it can be important to backup
- **Status**: Complete (`ConfigScreen.kt`).
- Notifications
- User should be able to toggle notifications for backup progress
- Notifications should be native to the platform
- **Status**: Complete (`BackupNotifier.kt`).
- Compression
- User should be able to toggle compression for backup files
- A description should inform the user what the consequences of compression are
- **Status**: Complete (`BackupEngine.kt`, `ConfigScreen.kt`).
#### 3. Backup
- The actual backup process should happen in the background
- Backup only happens when the app is configured
@ -69,6 +94,7 @@
- When backup starts
- When backup finishes
- When backup fails
- **Status**: Complete (`BackupEngine.kt`, `BackupScheduler.kt`, `BackupHistory.kt`).
#### 4. Status screen
- Accessible when clicking the system tray icon
- Should show the status of the backup process, including:
@ -81,12 +107,15 @@
- Should show a button to open the configuration screen
- Should show a button to start the restore process
- Should show a message if configuration is missing or incomplete
- **Status**: Complete (`StatusScreen.kt`, `StatusViewModel.kt`).
#### 5. Restore screen
- Should show a button to open the backup location
- Should display a list of all existing backups, and their date in a human-readable format
- Should allow the user to select a backup to restore
- Confirmation dialog should be shown before restoring, informing all current WoW data will be replaced
- **Status**: Pending (Feature 5 implementation).
##### 5.1 Restore process
- The restore process should happen in the foreground, with a proper dialog displaying the progress and ETA to finish
- The restore should be done in a way that is as transparent as possible to the user
- The restore process should be cancellable by the user
- The restore process should be cancellable by the user
- **Status**: Pending (Feature 5 implementation).

View file

@ -1,5 +1,7 @@
# Feature 0: Foundation
**Status:** ✅ **Completed**
## 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.

View file

@ -1,5 +1,7 @@
# Feature 1: System Tray Icon
**Status:** ✅ **Completed**
## 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.

View file

@ -1,5 +1,7 @@
# Feature 2: Configuration Screen
**Status:** ✅ **Completed**
## Context
The configuration screen is the primary setup interface. It appears automatically on first run (when config is incomplete) and is accessible anytime via the tray menu. All backup behavior depends on the values set here.

View file

@ -1,5 +1,7 @@
# Feature 3: Backup
**Status:** ✅ **Completed**
## Context
This is the core functionality — automatically backing up WoW's WTF and Interface folders on a daily schedule. The backup runs in the background, respects configuration, and notifies the user of results.

View file

@ -1,5 +1,7 @@
# Feature 4: Status Screen
**Status:** ✅ **Completed**
## Context
The status screen is the default view when the user clicks the tray icon. It gives a quick overview of backup health and provides shortcuts to key actions.

View file

@ -1,5 +1,7 @@
# Feature 5: Restore
**Status:** 🚧 **Pending / Next Priority**
## Context
Allows users to restore a previous backup, replacing their current WoW configuration. This is a destructive operation (overwrites current WTF/Interface folders), so it requires clear confirmation and progress feedback.

83
docs/roadmap.md Normal file
View file

@ -0,0 +1,83 @@
# WoW Backup — Project Roadmap & Next Steps
This document outlines the current state of the project, remaining core features from the initial masterplan, and future enhancements.
---
## 1. Immediate Priority: Feature 5 (Restore System)
The core backup and scheduling pipeline is complete, but the restoration flow is currently a stub placeholder. Implementing Feature 5 is the final step to complete the original functional scope of `docs/masterplan.md`.
### Tasks:
1. **Restore Engine (`RestoreEngine.kt`)**
- Implement `restoreBackup(entry: BackupEntry, targetDir: File): RestoreResult`.
- Support restoring from both timestamped directory structures and `.zip` archives (extracting using `ZipInputStream`).
- Clean/delete existing target folders (`WTF` and/or `Interface`) safely before copy/extraction.
- Implement live progress reporting (`RestoreProgress` with total files, completed count, current file name).
- Ensure full cancellation support via Kotlin Coroutines.
2. **Safety & Validation Mechanics**
- Warn or guard if World of Warcraft is currently running during restoration.
- Verify backup integrity and destination disk space before starting destructive deletion.
- Create an automatic temporary safety snapshot of current `WTF`/`Interface` folders prior to overwrite.
3. **Restore ViewModel (`RestoreViewModel.kt`)**
- Expose `RestoreUiState` combining available backups list, selected entry, progress, and confirmation states.
- Manage restore lifecycle (start, cancel, dismiss, error handling).
4. **Restore Screen UI (`RestoreScreen.kt`)**
- Replace placeholder with scrollable list of existing backups displaying date/time, archive size, and format badge.
- Material 3 confirmation dialog detailing destructive overwrite.
- Progress overlay with `LinearProgressIndicator`, active file, and cancellation button.
- Empty state when no backups are present.
5. **App Wiring**
- Connect `Screen.RESTORE` in `App.kt` to `RestoreScreen`.
- Enable the "Restore" button on `StatusScreen.kt`.
---
## 2. Testing Infrastructure & Automated Test Suite
Currently, the project contains no automated test suite (`src/jvmTest` is empty). Adding tests will prevent regressions during future maintenance.
### Test Targets:
1. **`BackupHistoryTest`**:
- Timestamp parsing across formats and edge cases.
- Backup listing and correct descending sort order.
- Retention policy pruning (ensuring only oldest backups exceeding count are deleted).
- Metrics computation for directory trees and ZIP files.
2. **`BackupEngineTest` & `RestoreEngineTest`**:
- File copy accuracy, recursive folder preservation, and attribute retention.
- ZIP compression and decompression fidelity.
- Cancellation responsiveness and cleanup of partial writes.
3. **`ConfigManagerTest`**:
- JSON serialization / deserialization defaults and edge cases.
- Thread-safe updates and reactive `StateFlow` emissions.
4. **`WoWLocationsTest`**:
- Install directory heuristics and resolution from root `World of Warcraft` to `_retail_` or `_classic_`.
- Missing folder detection and permission warnings.
---
## 3. Build Automation & Release Pipeline
The project contains packaging configurations in `composeApp/build.gradle.kts` and a `release.sh` script, but lacks automated CI/CD workflows.
### Tasks:
1. **GitHub Actions CI Workflow**
- Automated compile, lint, and test execution on pull requests and pushes to `main`.
- Matrix builds testing on macOS (`macos-latest`) and Windows (`windows-latest`).
2. **Automated Distribution Packaging**
- Automated artifact generation for `.dmg` (macOS), `.msi` / `.zip` (Windows), and `.deb` (Linux) on GitHub releases.
- Code signing and notarization configuration for macOS distributions.
---
## 4. Future Enhancements (Post-v1.0)
1. **Multi-Flavour & Account Profile Support**
- Support backing up multiple game versions simultaneously (`_retail_`, `_classic_`, `_classic_era_`, `_ptr_`).
- Account-level filtering (allowing selective backup of specific WTF accounts/characters).
2. **Cloud Storage & Offsite Sync**
- Optional sync integration to Google Drive, Dropbox, OneDrive, or custom S3-compatible buckets.
3. **Diff & Changelog Inspector**
- Inspect changes between backups (e.g., addon list changes, modified WTF saved variables).
4. **Menu Bar Quick Actions**
- Add a direct "Backup Now" action item to the system tray context menu.

Binary file not shown.

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

5
gradlew vendored
View file

@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"

3
gradlew.bat vendored
View file

@ -70,11 +70,10 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell