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:
41
src-tauri/Cargo.lock
generated
41
src-tauri/Cargo.lock
generated
@@ -3577,6 +3577,22 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-store"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca1a8ff83c269b115e98726ffc13f9e548a10161544a92ad121d6d0a96e16ea"
|
||||
dependencies = [
|
||||
"dunce",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.9.2"
|
||||
@@ -3798,9 +3814,21 @@ dependencies = [
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.18"
|
||||
@@ -3962,9 +3990,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.36"
|
||||
@@ -4026,6 +4066,7 @@ dependencies = [
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-log",
|
||||
"tauri-plugin-store",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "typogenie"
|
||||
version = "1.0.0"
|
||||
description = "TypoGenie - Markdown to Word document converter"
|
||||
description = "TypoGenie - Portable Markdown to Word document converter"
|
||||
authors = ["TypoGenie Contributors"]
|
||||
license = "MIT"
|
||||
repository = "https://git.lashman.live/lashman/typogenie"
|
||||
@@ -23,3 +23,4 @@ tauri = { version = "2.9.5" }
|
||||
tauri-plugin-log = "2"
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-fs = "2"
|
||||
tauri-plugin-store = "2"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Default capabilities for TypoGenie",
|
||||
"description": "Default capabilities for TypoGenie portable app",
|
||||
"windows": [
|
||||
"main"
|
||||
],
|
||||
@@ -19,9 +19,22 @@
|
||||
"fs:allow-rename",
|
||||
"fs:allow-exists",
|
||||
"fs:allow-mkdir",
|
||||
"fs:allow-applocaldata-read",
|
||||
"fs:allow-applocaldata-write",
|
||||
"store:default",
|
||||
"store:allow-get",
|
||||
"store:allow-set",
|
||||
"store:allow-save",
|
||||
"store:allow-load",
|
||||
{
|
||||
"identifier": "fs:scope",
|
||||
"allow": [
|
||||
{
|
||||
"path": "$EXE/**"
|
||||
},
|
||||
{
|
||||
"path": "$EXE/../**"
|
||||
},
|
||||
{
|
||||
"path": "$HOME"
|
||||
},
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": ["msi", "nsis", "dmg", "app", "deb", "rpm", "appimage"],
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
@@ -41,15 +41,16 @@
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"category": "Productivity",
|
||||
"shortDescription": "Markdown to Word document converter",
|
||||
"longDescription": "TypoGenie is a free, open-source typesetting engine that transforms Markdown into beautifully formatted Microsoft Word documents with 40+ professional styles.",
|
||||
"shortDescription": "Portable Markdown to Word document converter",
|
||||
"longDescription": "TypoGenie is a free, open-source, portable typesetting engine. No installation required - just run the EXE. All data stays in the same folder.",
|
||||
"publisher": "TypoGenie Contributors",
|
||||
"copyright": "Copyright (c) 2026 TypoGenie Contributors",
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.lashman.live/lashman/typogenie",
|
||||
"windows": {
|
||||
"webviewInstallMode": {
|
||||
"type": "embedBootstrapper"
|
||||
"nsis": {
|
||||
"installMode": "currentUser",
|
||||
"installerIcon": "icons/icon.ico"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user