Files
mtgc/README.md
Garrett Mills 683f13e6bc Make 'tauri android dev --open' find a Toolbox-installed Android Studio
cargo-mobile2 locates Studio by scanning XDG data dirs for a .desktop entry
whose Name is exactly "Android Studio", then runs its Exec line. A standalone
install writes that name; JetBrains Toolbox writes a versioned one ("Android
Studio Quail 2 2026.1.2 Patch 1"), so the lookup misses and it falls back to
running "Android Studio" as a literal command, failing with an ENOENT that
names every env var except the actual problem.

android-setup.sh now writes a hidden shim entry when no exact-name match
exists, reusing the Exec line Toolbox already generated rather than hardcoding
a path. Idempotent: a later run reports Studio as discoverable and rewrites
nothing.
2026-08-02 13:27:24 -05:00

209 lines
7.9 KiB
Markdown

# mtgc — Personal Magic: The Gathering Library Catalog
A desktop app (Tauri v2 + React) for cataloging your MTG collection: rapid
fuzzy card entry, rich criteria search (colors, commander color-identity
compatibility, mana value, type/subtype/keyword), and deck organization with
per-card deck membership. Built so the eventual mobile port is cheap — all logic
lives in a platform-agnostic Rust core.
## Architecture
```
src/ React + TypeScript UI (swappable presentation)
src-tauri/ Tauri v2 shell — exposes the core as typed commands
crates/mtgc-core/ Pure Rust: db, fuzzy search, filters, decks, ingester
```
Two SQLite databases, joined at runtime via `ATTACH`:
- **`reference.db`** — read-only card data built from Scryfall bulk. Bulky,
re-downloadable per device, **not synced**.
- **`library.db`** — your owned cards + decks. Small, portable, **the file you
sync** (drop it in Syncthing/Dropbox/Nextcloud). Point the app at it via
*Choose library file…*.
Both default to the OS app-data dir (e.g. `~/.local/share/com.garrettmills.mtgc/`).
## First-time setup
```bash
./scripts/devel-setup.sh # Tauri system libs (webkit2gtk/gtk3) + Tauri CLI
npm install # frontend deps
```
> **Why GTK3, not GTK4?** Tauri v2's Linux backend (`wry`/`tao`) hard-depends on
> GTK3 + `webkit2gtk-4.1`; there's no GTK4/webkitgtk-6.0 option yet. GTK4 support is
> tracked upstream but unmerged with no ETA:
> [tauri#7335](https://github.com/tauri-apps/tauri/issues/7335),
> [tao#1104](https://github.com/tauri-apps/tao/pull/1104),
> [wry#1228](https://github.com/tauri-apps/wry/issues/1228). Revisit once Tauri
> ships GTK4 support (likely Tauri v3).
## Run
```bash
cargo tauri dev # launches the desktop app (dev mode)
```
On first launch, click **Download card data** — it fetches the Scryfall
*Oracle Cards* + *Default Cards* bulk (~a few hundred MB) and builds
`reference.db`. After that the app is fully offline.
You can also build the card database from the command line:
```bash
cargo run -p mtgc-core --bin mtgc-ingest -- /path/to/reference.db
```
## Production build
Build an optimized app with the frontend embedded into the executable:
```bash
./scripts/devel-setup.sh # once, if not already done
cargo tauri build
```
Outputs (under the workspace `target/release/`):
- **`target/release/mtgc`** — the standalone executable. The web UI and all
app logic are baked into this one file. It dynamically links the system WebKit/GTK
libraries (`webkit2gtk4.1`, `gtk3`), so those must be present on the machine that
runs it — they are on any modern desktop Linux and are installed by
`devel-setup.sh`.
- **`target/release/bundle/rpm/*.rpm`** and **`.../deb/*.deb`** — native installers
(Fedora / Debian-based) that add a menu entry and icon.
Useful variants:
```bash
cargo tauri build --no-bundle # only the bare target/release/mtgc binary
```
Notes:
- The `mtgc` binary is a single file but relies on the system WebKit/GTK libs
being present (installed by `devel-setup.sh`).
- The binary does **not** embed card data — first run still downloads the Scryfall
bulk into the OS app-data dir, and your synced `library.db` lives there by default
(override with *Choose library file…*).
- The `[profile.release]` settings in `Cargo.toml` (strip + LTO) make the release
build take a few minutes but keep the binary small.
## Using it
- **Add Cards** — type a name (typos OK); ↑/↓ then Enter adds a copy.
- **Library** — filter owned cards by name, colors, commander identity (color
identity ⊆ chosen), mana value range, type/subtype/keyword, or deck. Hover a
row's thumbnail for a full-size preview that tracks the cursor. Rows show which
decks a card is in and a warning (⚠) when it's assigned to more decks than you
own. Toggle **List / Tiles** (top-right) to switch between the table and a grid
of full card images; the choice is remembered.
- Deck contents offer the same List / Tiles toggle and hover previews.
- **Decks** — create decks and assign owned cards; set per-deck quantities.
## Develop / test
```bash
cargo test -p mtgc-core # core unit tests (db, search, filters, decks, ingest)
npm run build # typecheck + build frontend
cargo build # build everything incl. the desktop app
```
## Android
The Rust core and frontend are platform-agnostic, so the Android build shares
all app logic with the desktop; only data provisioning differs.
### One-time setup
Install Android Studio and **launch it once** so its wizard downloads the SDK,
then:
```bash
./scripts/android-setup.sh
```
That adds what Android Studio doesn't install by default: the SDK
command-line tools, a pinned stable **NDK**, and the Rust Android targets. It
prints the three environment variables to export (`ANDROID_HOME`, `NDK_HOME`,
`JAVA_HOME`).
> **Why the NDK?** `mtgc-core` builds `rusqlite` with the `bundled` feature
> (compiles SQLite from C) and `rustls` pulls in `ring` (C/asm), so a C
> cross-toolchain is required. The bundled SQLite can't be swapped for
> Android's system libsqlite3 either — the card schema needs FTS5 with the
> `trigram` tokenizer, which platform SQLite often lacks.
> **Why `JAVA_HOME`?** Gradle/AGP reject very new JDKs. The script prefers
> Android Studio's bundled JetBrains Runtime (JDK 21), which needs no extra
> install.
### Build and run
Every Android command needs `ANDROID_HOME` / `NDK_HOME` / `JAVA_HOME`. Source
the helper once per shell — it auto-detects all three:
```bash
source scripts/android-env.sh # bash / zsh
```
```fish
source scripts/android-env.fish # fish
```
Only the env helper is shell-specific (it has to mutate the calling shell);
`android-setup.sh` is executed rather than sourced, so it works from any shell.
Then, from the project root:
```bash
cargo tauri android dev --open # dev server + open in Android Studio
cargo tauri android build --apk # standalone release APK
```
`src-tauri/gen/android` is committed (unlike the rest of `gen/`) because
`AndroidManifest.xml` is hand-edited — Android permissions have no
`tauri.conf.json` equivalent.
### Working in Android Studio
Use **`cargo tauri android dev --open`** rather than opening
`src-tauri/gen/android` directly. Studio's Run button delegates the Rust build
to `cargo tauri android android-studio-script`, which expects a running Tauri
dev session — it reads the dev-server address from
`/tmp/<identifier>-server-addr`. Open the Gradle project on its own and the
build fails with `failed to read missing addr file`.
With the dev session running, Studio's device dropdown, Device Manager
(emulator), and debugger all work normally.
> **JetBrains Toolbox users:** `--open` finds Studio by scanning XDG data dirs
> for a `.desktop` entry whose `Name` is *exactly* `Android Studio`. Toolbox
> writes a versioned name instead (e.g. `Android Studio Quail 2 2026.1.2 Patch
> 1`), so the lookup misses and the launch fails with a confusing
> `Cmd(["Android Studio"]) ... No such file or directory`. `android-setup.sh`
> drops in a hidden shim entry (`~/.local/share/applications/android-studio.desktop`)
> to fix this; delete that file to undo it.
To build without Studio and without a dev server, use `cargo tauri android
build` — that path needs no addr file.
### Card data on a phone
Downloading card data streams ~740MB of JSON to build a ~73MB database, which
is slow on mobile and may be killed if the app is backgrounded. Faster: copy
`reference.db` from a desktop install onto the device and use **Import card
data…**. The file is validated before it replaces anything.
## Branding
The logo is an original pirate-ship silhouette in the terracotta brand color
(`#C8553D`) — a nod to the MTG card "Pirate Ship", not a reproduction of its
art. Source: `src-tauri/icons/logo-source.svg`. To regenerate the full
platform icon set after editing it:
```bash
cargo tauri icon src-tauri/icons/logo-source.svg
```