Skip to content

implement: Portable Mode #4171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/theseus-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,4 @@ jobs:
target/universal-apple-darwin/release/bundle/dmg/Modrinth App_*.dmg*
target/release/bundle/nsis/Modrinth App_*-setup.exe*
target/release/bundle/nsis/Modrinth App_*-setup.nsis.zip*
target/release/Modrinth App.exe
187 changes: 187 additions & 0 deletions apps/docs/src/content/docs/guide/portable-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
---
title: Portable Mode
description: Learn how to use Modrinth App in portable mode for maximum flexibility and convenience.
---

The Modrinth App supports **portable mode**, which allows you to run the application from any location without requiring installation to system directories. In portable mode, all application data is stored alongside the executable, making it perfect for USB drives, shared computers, or when you want a completely self-contained setup.

## What is Portable Mode?

Portable mode changes how the Modrinth App stores its data. Instead of using system directories like:

- `%APPDATA%\ModrinthApp` (Windows)
- `~/Library/Application Support/ModrinthApp` (macOS)
- `~/.local/share/ModrinthApp` (Linux)

All data is stored in a `ModrinthAppData` folder right next to the executable, making the entire installation truly portable.

## Benefits of Portable Mode

- **No Installation Required**: Run directly from any location
- **System Independence**: No registry entries or system directory modifications
- **Easy Backup**: Simply copy the entire folder to backup everything
- **Multi-System**: Use the same setup across different computers
- **Clean Removal**: Delete the folder to completely remove all traces
- **Isolation**: Perfect for testing without affecting main installations

## Enabling Portable Mode

There are two methods to enable portable mode:

### Method 1: portable.txt File (Recommended)

1. Create an empty file named `portable.txt` in the same directory as your Modrinth App executable
2. Launch the app normally
3. The app will automatically detect the file and enable portable mode

```
YourFolder/
├── Modrinth App.exe (or modrinth-app on Linux/macOS)
├── portable.txt ← Create this file
└── ModrinthAppData/ ← Created automatically on first run
```

### Method 2: Environment Variable

Set the `MODRINTH_PORTABLE` environment variable to any value before launching the app:

```bash
# Windows (PowerShell)
$env:MODRINTH_PORTABLE="true"
.\ModrinthApp.exe

# Windows (Command Prompt)
set MODRINTH_PORTABLE=true
ModrinthApp.exe

# Linux/macOS
export MODRINTH_PORTABLE=true
./modrinth-app
```

## Directory Structure

When portable mode is enabled, your directory structure will look like this:

```
YourPortableFolder/
├── Modrinth App.exe # The application executable
├── portable.txt # Enables portable mode (Method 1)
└── ModrinthAppData/ # All app data stored here
├── profiles/ # Minecraft instances and profiles
│ ├── vanilla/
│ ├── modded/
│ └── ...
├── caches/ # Downloaded files cache
│ ├── mods/
│ ├── resource_packs/
│ └── ...
├── launcher_logs/ # Application logs
├── meta/ # Metadata files
└── app.db # Application database
```

## Creating a Portable Installation

### From Source (Developers)

If you're building from source:

1. Build the application:

```bash
pnpm app:build
```

2. Copy the executable from `target/release/` to your portable folder

3. Create the portable indicator:
```bash
# Create portable.txt file
echo "Portable mode enabled" > portable.txt
```

### From Release Binary

1. Download the Modrinth App executable from [modrinth.com/app](https://modrinth.com/app)
2. Place it in your desired portable folder
3. Create an empty `portable.txt` file next to the executable
4. Launch the app

## Use Cases

### USB Drive Setup

Perfect for carrying your complete Minecraft setup on a USB drive:

```
USB_Drive/
├── ModrinthApp/
│ ├── Modrinth App.exe
│ ├── portable.txt
│ └── ModrinthAppData/
└── other_files/
```

### Shared Computer

Use your personal setup on shared computers without affecting other users or requiring installation permissions.

### Testing Environment

Test different modrinth configurations or app versions without affecting your main installation.

### System Migration

Easily move your entire Minecraft setup to a new computer by copying the portable folder.

## Switching Between Modes

### Converting Regular Installation to Portable

1. Create a portable folder with the executable and `portable.txt`
2. Copy your existing data from the system directory to `ModrinthAppData/`
3. Launch the portable version

### Converting Portable to Regular Installation

1. Install the app normally
2. Copy data from `ModrinthAppData/` to the system directory
3. Remove the `portable.txt` file or unset the environment variable

## Technical Notes

- **Detection Priority**: Environment variable takes precedence over `portable.txt` file
- **Performance**: Portable mode has identical performance to regular installations
- **Platform Support**: Available on Windows, macOS, and Linux
- **First Launch**: The `ModrinthAppData` directory is created automatically on first run
- **File Permissions**: Ensure the portable folder has write permissions

## Troubleshooting

### App Not Detecting Portable Mode

- Verify `portable.txt` is in the same directory as the executable
- Check file permissions on the portable folder
- Try using the environment variable method instead

### Data Not Saving

- Ensure the portable folder has write permissions
- Check that antivirus software isn't blocking file creation
- Verify there's sufficient disk space

### Performance Issues

- Portable mode shouldn't affect performance
- If using a USB drive, ensure it has adequate read/write speeds
- Consider using USB 3.0+ for better performance

## Security Considerations

- **Portable Storage**: Be mindful of where you store portable installations
- **Data Encryption**: Consider encrypting USB drives with sensitive data
- **Access Control**: Set appropriate file permissions on shared systems
- **Backup**: Regularly backup your portable installation

Portable mode makes the Modrinth App incredibly flexible and convenient for various use cases while maintaining all the functionality of a standard installation.
1 change: 1 addition & 0 deletions packages/app-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ pub use event::{
};
pub use logger::start_logger;
pub use state::State;
pub use state::dirs::DirectoryInfo;
48 changes: 47 additions & 1 deletion packages/app-lib/src/state/dirs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::state::{JavaVersion, Profile, Settings};
use crate::util::fetch::IoSemaphore;
use dashmap::DashSet;
use std::path::{Path, PathBuf};
use std::env;
use std::sync::Arc;
use tokio::fs;

Expand All @@ -20,9 +21,54 @@ pub struct DirectoryInfo {
}

impl DirectoryInfo {
/// Returns the path to the directory containing the running executable, if possible.
fn exe_dir() -> Option<PathBuf> {
std::env::current_exe().ok().and_then(|p| p.parent().map(|p| p.to_path_buf()))
}

/// Checks if portable mode is enabled (env or portable.txt) and returns the portable data dir if so.
fn portable_data_dir() -> Option<PathBuf> {
// 1. Environment variable takes precedence
if env::var_os("MODRINTH_PORTABLE").is_some() {
if let Some(exe_dir) = Self::exe_dir() {
return Some(exe_dir.join("ModrinthAppData"));
}
}
// 2. portable.txt file next to executable
if let Some(exe_dir) = Self::exe_dir() {
let portable_txt = exe_dir.join("portable.txt");
if portable_txt.exists() {
return Some(exe_dir.join("ModrinthAppData"));
}
}
None
}

/// Sets the process environment variable for app data (e.g., APPDATA) to the portable dir if in portable mode.
fn set_portable_env(portable_dir: &Path) {
#[cfg(target_os = "windows")]
unsafe {
env::set_var("APPDATA", portable_dir);
}
#[cfg(target_os = "macos")]
unsafe {
// macOS typically uses HOME/Library/Application Support, but we can override XDG_DATA_HOME for some libs
env::set_var("XDG_DATA_HOME", portable_dir);
}
#[cfg(target_os = "linux")]
unsafe {
env::set_var("XDG_DATA_HOME", portable_dir);
}
}

// Get the settings directory
// init() is not needed for this function
pub fn get_initial_settings_dir() -> Option<PathBuf> {
if let Some(portable_dir) = Self::portable_data_dir() {
// Set env var for the process so all code uses portable dir
Self::set_portable_env(&portable_dir);
return Some(portable_dir);
}
Self::env_path("THESEUS_CONFIG_DIR")
.or_else(|| Some(dirs::data_dir()?.join("ModrinthApp")))
}
Expand Down Expand Up @@ -168,7 +214,7 @@ impl DirectoryInfo {
/// Get path from environment variable
#[inline]
fn env_path(name: &str) -> Option<PathBuf> {
std::env::var_os(name).map(PathBuf::from)
env::var_os(name).map(PathBuf::from)
}

#[tracing::instrument(skip(settings, exec, io_semaphore))]
Expand Down
2 changes: 1 addition & 1 deletion packages/app-lib/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::state::fs_watcher::FileWatcher;
use sqlx::SqlitePool;

// Submodules
mod dirs;
pub mod dirs;
pub use self::dirs::*;

mod profiles;
Expand Down