diff --git a/README.md b/README.md
index 12b2831..e7787b9 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,11 @@
+
+
+
@@ -146,12 +151,29 @@ Every style includes meticulously configured:
## 🚀 Quick Start
-### Option 1: Download Desktop App (Recommended)
+### Option 1: Portable Download (Recommended) 🎒
-Pre-built binaries coming soon for:
-- 🪟 **Windows** (.msi, .exe)
-- 🍎 **macOS** (.dmg, .app)
-- 🐧 **Linux** (.deb, .rpm, .AppImage)
+**TypoGenie is fully portable** — no installation, no registry entries, no files scattered across your system.
+
+Just download and run:
+- 🪟 **Windows**: `TypoGenie.exe` — Single executable, runs immediately
+- 🍎 **macOS**: `TypoGenie.app` — Drag and run
+- 🐧 **Linux**: `TypoGenie.AppImage` — Make executable and run
+
+**How it works:**
+```
+📁 Your Folder/
+├── 🚀 TypoGenie.exe ← Run this
+└── 📂 TypoGenie-Data/ ← Auto-created on first run
+ ├── config.json ← Your settings
+ └── cache/ ← Temporary files
+```
+
+✅ **No installer** — Just double-click the EXE
+✅ **No registry** — Windows registry untouched
+✅ **No AppData** — Everything stays in the same folder
+✅ **USB-friendly** — Run from a thumb drive anywhere
+✅ **Easy backup** — Copy the whole folder, done
### Option 2: Build from Source
@@ -189,7 +211,24 @@ npm run desktop
npm run desktop:build
```
-The built apps will be in `src-tauri/target/release/bundle/`
+**Build outputs:**
+
+| Platform | Output Location | Result |
+|----------|----------------|--------|
+| **Windows** | `src-tauri/target/release/typogenie.exe` | ⚡ **Portable EXE** — Run immediately, no install |
+| **Windows** | `src-tauri/target/release/bundle/nsis/*.exe` | 📦 NSIS Installer (optional) |
+| **macOS** | `src-tauri/target/release/bundle/dmg/*.dmg` | 🍎 DMG with App bundle |
+| **Linux** | `src-tauri/target/release/bundle/appimage/*.AppImage` | 🐧 Portable AppImage |
+
+**For the truly portable Windows experience:**
+```bash
+# After building, grab the raw EXE:
+copy src-tauri\target\release\typogenie.exe "C:\Path\To\Your\Portable\Folder\"
+
+# Run it:
+"C:\Path\To\Your\Portable\Folder\typogenie.exe"
+# Creates TypoGenie-Data/ folder next to EXE automatically
+```
### Usage
diff --git a/package-lock.json b/package-lock.json
index ae9f53c..8fedf7a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"@tauri-apps/api": "^2.0.0",
"@tauri-apps/plugin-dialog": "^2.0.0",
"@tauri-apps/plugin-fs": "^2.0.0",
+ "@tauri-apps/plugin-store": "^2.4.2",
"docx": "^8.5.0",
"lucide-react": "^0.563.0",
"marked": "12.0.0",
@@ -1401,6 +1402,15 @@
"@tauri-apps/api": "^2.8.0"
}
},
+ "node_modules/@tauri-apps/plugin-store": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-store/-/plugin-store-2.4.2.tgz",
+ "integrity": "sha512-0ClHS50Oq9HEvLPhNzTNFxbWVOqoAp3dRvtewQBeqfIQ0z5m3JRnOISIn2ZVPCrQC0MyGyhTS9DWhHjpigQE7A==",
+ "license": "MIT OR Apache-2.0",
+ "dependencies": {
+ "@tauri-apps/api": "^2.8.0"
+ }
+ },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
diff --git a/package.json b/package.json
index 6694e70..9d2e175 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"@tauri-apps/api": "^2.0.0",
"@tauri-apps/plugin-dialog": "^2.0.0",
"@tauri-apps/plugin-fs": "^2.0.0",
+ "@tauri-apps/plugin-store": "^2.4.2",
"docx": "^8.5.0",
"lucide-react": "^0.563.0",
"marked": "12.0.0",
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 76a4136..c9b9716 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -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]]
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 2d9f5db..c4fc40d 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -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"
diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json
index 090208e..ece6eb2 100644
--- a/src-tauri/capabilities/default.json
+++ b/src-tauri/capabilities/default.json
@@ -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"
},
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 05525f2..b57ad54 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -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