Make app fully portable, remove installers

- Remove MSI and NSIS installer targets, only produce vesper.exe
- Remove tauri-plugin-window-state, replace with manual window state
  save/restore to data/window-state.json next to the exe
- Redirect WebView2 user data (including localStorage) to data/
  folder next to the exe via WEBVIEW2_USER_DATA_DIR
- Nothing written to AppData, registry, or any system location
- Update README with portable usage info
This commit is contained in:
Your Name
2026-02-14 12:01:26 +02:00
parent 2af6b7ebe7
commit 99abaac097
9 changed files with 105 additions and 44 deletions

16
src-tauri/Cargo.lock generated
View File

@@ -3703,21 +3703,6 @@ dependencies = [
"zbus",
]
[[package]]
name = "tauri-plugin-window-state"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73736611e14142408d15353e21e3cca2f12a3cfb523ad0ce85999b6d2ef1a704"
dependencies = [
"bitflags 2.10.0",
"log",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"thiserror 2.0.18",
]
[[package]]
name = "tauri-runtime"
version = "2.10.0"
@@ -4305,7 +4290,6 @@ dependencies = [
"tauri-plugin-dialog",
"tauri-plugin-fs",
"tauri-plugin-opener",
"tauri-plugin-window-state",
]
[[package]]

View File

@@ -19,7 +19,6 @@ tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
tauri-plugin-window-state = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

View File

@@ -13,8 +13,6 @@
"core:window:allow-start-dragging",
"fs:default",
"fs:allow-read-text-file",
"fs:read-all",
"window-state:allow-restore-state",
"window-state:allow-save-window-state"
"fs:read-all"
]
}

View File

@@ -1,10 +1,94 @@
use std::env;
use std::fs;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use tauri::Manager;
#[derive(Serialize, Deserialize, Default)]
struct WindowState {
x: Option<i32>,
y: Option<i32>,
width: Option<u32>,
height: Option<u32>,
maximized: Option<bool>,
}
fn get_data_dir() -> PathBuf {
env::current_exe()
.ok()
.and_then(|p| p.parent().map(|p| p.to_path_buf()))
.unwrap_or_else(|| PathBuf::from("."))
.join("data")
}
fn load_window_state() -> WindowState {
let path = get_data_dir().join("window-state.json");
fs::read_to_string(&path)
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default()
}
fn save_window_state(state: &WindowState) {
let dir = get_data_dir();
let _ = fs::create_dir_all(&dir);
let path = dir.join("window-state.json");
if let Ok(json) = serde_json::to_string_pretty(state) {
let _ = fs::write(path, json);
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let data_dir = get_data_dir();
let _ = fs::create_dir_all(&data_dir);
// Redirect WebView2 user data to portable location next to the exe
env::set_var(
"WEBVIEW2_USER_DATA_DIR",
data_dir.to_string_lossy().to_string(),
);
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_window_state::Builder::new().build())
.setup(|app| {
let state = load_window_state();
let window = app.get_webview_window("main").unwrap();
if let (Some(x), Some(y)) = (state.x, state.y) {
let _ = window.set_position(tauri::Position::Physical(
tauri::PhysicalPosition::new(x, y),
));
}
if let (Some(w), Some(h)) = (state.width, state.height) {
let _ = window.set_size(tauri::Size::Physical(
tauri::PhysicalSize::new(w, h),
));
}
if let Some(true) = state.maximized {
let _ = window.maximize();
}
Ok(())
})
.on_window_event(|window, event| {
if let tauri::WindowEvent::CloseRequested { .. } = event {
let maximized = window.is_maximized().unwrap_or(false);
if let (Ok(pos), Ok(size)) = (window.outer_position(), window.outer_size()) {
let state = WindowState {
x: Some(pos.x),
y: Some(pos.y),
width: Some(size.width),
height: Some(size.height),
maximized: Some(maximized),
};
save_window_state(&state);
}
}
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -30,7 +30,7 @@
},
"bundle": {
"active": true,
"targets": "all",
"targets": [],
"icon": [
"icons/32x32.png",
"icons/128x128.png",