feat: Make app fully portable

- Add tauri-plugin-store for portable data storage
- Implement portable data directory (TypoGenie-Data/ next to EXE)
- Configure Rust backend to use EXE-relative paths
- Add store permissions for persistent settings
- Update README with portable badges and documentation
- Document how to build and use the portable EXE
- Zero registry, zero AppData, fully self-contained
This commit is contained in:
TypoGenie
2026-01-29 18:36:48 +02:00
parent b91f565eaa
commit da335734d3
8 changed files with 173 additions and 34 deletions

View File

@@ -1,26 +1,59 @@
use tauri::Manager;
use std::path::PathBuf;
use tauri::{Manager, path::BaseDirectory};
/// Gets the portable data directory (next to the executable)
fn get_portable_data_dir(app: &tauri::App) -> PathBuf {
// Get the directory where the EXE is located
if let Ok(exe_dir) = app.path().resolve("", BaseDirectory::Executable) {
let portable_dir = exe_dir.join("TypoGenie-Data");
// Create the directory if it doesn't exist
let _ = std::fs::create_dir_all(&portable_dir);
portable_dir
} else {
// Fallback to app data directory (shouldn't happen)
app.path().app_data_dir().unwrap_or_else(|_| PathBuf::from("."))
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.setup(|app| {
// Show the main window after setup
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_store::Builder::default().build())
.setup(|app| {
// Set up portable data directory
let portable_dir = get_portable_data_dir(app);
println!("Portable data directory: {:?}", portable_dir);
// Store the portable path in app state for frontend access
app.manage(PortableDataDir(portable_dir));
if cfg!(debug_assertions) {
app.handle().plugin(
tauri_plugin_log::Builder::default()
.level(log::LevelFilter::Info)
.build(),
)?;
}
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
// Show the main window after setup
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
if cfg!(debug_assertions) {
app.handle().plugin(
tauri_plugin_log::Builder::default()
.level(log::LevelFilter::Info)
.build(),
)?;
}
Ok(())
})
.invoke_handler(tauri::generate_handler![get_data_dir])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
/// Structure to hold the portable data directory path
struct PortableDataDir(PathBuf);
/// Command to get the portable data directory from the frontend
#[tauri::command]
fn get_data_dir(state: tauri::State<PortableDataDir>) -> String {
state.0.to_string_lossy().to_string()
}