mirror of
https://github.com/tiennm99/claude-code-usage-bubble.git
synced 2026-10-25 02:14:44 +00:00
feat: keep settings.json next to the portable exe and AppImage
The Windows exe and the AppImage now read and write settings.json in their own folder, so a copied or moved folder keeps its settings. A .deb install still uses the user's config folder, because it lives in a read-only system folder.
This commit is contained in:
1 parent
6afd603d5e
commit
7e446b6bda
4 files changed
+77
-12
No files matched your search
@@ -89,8 +89,10 @@ and start the app again.
|
||||
|
||||
Grab `usage-monitor.exe` from the
|
||||
[Releases page](https://github.com/tiennm99/usage-monitor/releases/latest).
|
||||
Put it anywhere on disk (e.g. `%LOCALAPPDATA%\UsageMonitor\`) and
|
||||
run it. The app self-updates from the same Releases feed.
|
||||
Put it in a folder of its own that you can write to (e.g.
|
||||
`%LOCALAPPDATA%\UsageMonitor\`) and run it. The app is portable: it keeps
|
||||
`settings.json` next to the exe, so moving the folder keeps your settings. It
|
||||
self-updates from the same Releases feed.
|
||||
|
||||
**First-run note:** the binary is unsigned, so SmartScreen will show
|
||||
"Windows protected your PC". Click **More info** → **Run anyway**. Code
|
||||
@@ -103,7 +105,9 @@ From the same
|
||||
pick one:
|
||||
|
||||
- `usage-monitor_X.Y.Z_amd64.AppImage`: make it executable
|
||||
(`chmod +x`) and run it. The AppImage self-updates in place.
|
||||
(`chmod +x`) and run it. Like the Windows exe it is portable and keeps
|
||||
`settings.json` next to the AppImage file, so give it a folder you can
|
||||
write to. The AppImage self-updates in place.
|
||||
- `usage-monitor_X.Y.Z_amd64.deb`: install it with
|
||||
`sudo apt install ./usage-monitor_X.Y.Z_amd64.deb`. The app
|
||||
still checks for updates, but installing one is left to you: download the
|
||||
@@ -209,8 +213,9 @@ Settings are saved to:
|
||||
|
||||
| OS | Settings file |
|
||||
|----|---------------|
|
||||
| Windows | `%APPDATA%\UsageMonitor\settings.json` |
|
||||
| Linux | `$XDG_CONFIG_HOME/UsageMonitor/settings.json`, usually `~/.config/UsageMonitor/settings.json` |
|
||||
| Windows | `settings.json` next to `usage-monitor.exe` |
|
||||
| Linux AppImage | `settings.json` next to the `.AppImage` file |
|
||||
| Linux `.deb` | `$XDG_CONFIG_HOME/UsageMonitor/settings.json`, usually `~/.config/UsageMonitor/settings.json` |
|
||||
|
||||
Other files the app writes:
|
||||
|
||||
|
||||
@@ -119,8 +119,9 @@ updater code at v0.4.0, v0.4.1 and v0.5.0, they need:
|
||||
`updates/updater-helper-*.exe` files they leave behind.
|
||||
|
||||
Since the rename, an upgraded install starts with default settings and
|
||||
keeps any autostart entry the legacy app registered: the settings folder
|
||||
and autostart entry names changed, and nothing migrates them. See
|
||||
keeps any autostart entry the legacy app registered: settings now live in
|
||||
`settings.json` next to the exe, the autostart entry name changed, and
|
||||
nothing migrates either. See
|
||||
[the rename plan](../plans/261021-1226-rename-to-usage-monitor/plan.md).
|
||||
|
||||
`scripts/make-latest-json.js` enforces the asset rules before
|
||||
|
||||
@@ -41,8 +41,9 @@ merges into `main`.
|
||||
|
||||
## Accepted costs
|
||||
|
||||
- Installs that upgrade start with default settings, because the settings
|
||||
folder name changed and nothing migrates it.
|
||||
- Installs that upgrade start with default settings: the portable Windows
|
||||
exe and the AppImage now keep `settings.json` next to the file, and nothing
|
||||
migrates the old one.
|
||||
- On Windows, an install that had autostart on keeps its old Run entry, which
|
||||
points at the same exe file. Turning autostart on in the new app adds a
|
||||
second entry.
|
||||
|
||||
+61
-3
@@ -1,6 +1,7 @@
|
||||
// Persisted settings. The path, schema, key names and physical-px positions
|
||||
// stay byte-compatible with the Win32 builds, so no migration step exists.
|
||||
// Persisted settings. The schema, key names and physical-px positions stay
|
||||
// byte-compatible with the Win32 builds.
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -8,6 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::core::geom::{
|
||||
position_on_any_monitor, Rect, DEFAULT_BUBBLE_SIZE, MAX_BUBBLE_SIZE, MIN_BUBBLE_SIZE,
|
||||
};
|
||||
use crate::update::channel::Channel;
|
||||
use crate::usage::ProviderId;
|
||||
|
||||
pub const APP_DIR_NAME: &str = "UsageMonitor";
|
||||
@@ -187,8 +189,33 @@ impl Settings {
|
||||
}
|
||||
}
|
||||
|
||||
/// The folder holding `settings.json`. The Windows exe and the AppImage are
|
||||
/// portable, so their settings sit next to the file and move with it. A
|
||||
/// `.deb` install lives in a read-only system folder and keeps them in the
|
||||
/// user's config folder.
|
||||
pub fn settings_dir() -> Option<PathBuf> {
|
||||
dirs::config_dir().map(|d| d.join(APP_DIR_NAME))
|
||||
portable_dir(
|
||||
crate::update::channel::current(),
|
||||
std::env::var_os("APPIMAGE"),
|
||||
std::env::current_exe().ok(),
|
||||
)
|
||||
.or_else(|| dirs::config_dir().map(|d| d.join(APP_DIR_NAME)))
|
||||
}
|
||||
|
||||
/// The folder of the portable file for `channel`: the exe on Windows, the
|
||||
/// AppImage file (not its temporary mount) on Linux. `None` for installs
|
||||
/// that are not portable.
|
||||
fn portable_dir(
|
||||
channel: Channel,
|
||||
appimage: Option<OsString>,
|
||||
exe: Option<PathBuf>,
|
||||
) -> Option<PathBuf> {
|
||||
let file = match channel {
|
||||
Channel::Portable => exe?,
|
||||
Channel::AppImage => PathBuf::from(appimage?),
|
||||
Channel::PackageManager | Channel::Unsupported => return None,
|
||||
};
|
||||
file.parent().map(Path::to_path_buf)
|
||||
}
|
||||
|
||||
pub fn settings_path() -> PathBuf {
|
||||
@@ -275,6 +302,37 @@ pub fn save_to(path: &Path, settings: &Settings) -> std::io::Result<()> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn portable_installs_keep_settings_next_to_the_file() {
|
||||
let folder = Path::new("Tools").join("Usage");
|
||||
assert_eq!(
|
||||
portable_dir(
|
||||
Channel::Portable,
|
||||
None,
|
||||
Some(folder.join("usage-monitor.exe"))
|
||||
),
|
||||
Some(folder)
|
||||
);
|
||||
// The AppImage file's folder, not the mount the exe runs from.
|
||||
assert_eq!(
|
||||
portable_dir(
|
||||
Channel::AppImage,
|
||||
Some(OsString::from("/home/u/Apps/usage-monitor.AppImage")),
|
||||
Some(PathBuf::from("/tmp/.mount_usage/usr/bin/usage-monitor")),
|
||||
),
|
||||
Some(PathBuf::from("/home/u/Apps"))
|
||||
);
|
||||
assert_eq!(
|
||||
portable_dir(
|
||||
Channel::PackageManager,
|
||||
None,
|
||||
Some(PathBuf::from("/usr/bin/usage-monitor"))
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(portable_dir(Channel::Unsupported, None, None), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn old_two_provider_settings_json_still_loads() {
|
||||
let json = r#"{
|
||||
|
||||
Reference in new issue
Block a user