Commit Graph

29 Commits

Author SHA1 Message Date
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
17c927afa3 Add a fish version of the Android env helper
Only the env helper needs a per-shell variant, since it has to mutate the
calling shell; android-setup.sh is executed rather than sourced and already
works from fish.

The fish version finds NDK/JDK candidates via `find` rather than globs because
fish raises an error on a wildcard matching nothing, and "SDK with no ndk/
directory yet" is a normal state the helper has to report cleanly.

Also switch the bash helper's NDK selection to `find | sort -V` so the two
agree: glob order is lexicographic, which would rank 9.x above 10.x. Verified
both pick the same NDK and JDK on this machine and against a fixture with
9.0.111 / 10.0.222 / 28.2.333 / 30.0.999-beta2 installed.
2026-08-02 13:22:36 -05:00
d494f2543b Add a sourceable Android env helper and document the Studio workflow
scripts/android-env.sh exports ANDROID_HOME/NDK_HOME/JAVA_HOME by
auto-detection, so there are no machine-specific paths to keep in sync. It
skips prerelease NDKs when several are installed and prefers Android Studio's
bundled JDK 21, which Gradle accepts.

Also document that Android Studio must be launched via
`cargo tauri android dev --open` rather than by opening src-tauri/gen/android
directly: Studio's Run button delegates the Rust build to
`tauri android android-studio-script`, which reads the dev-server address from
/tmp/<identifier>-server-addr and fails with "missing addr file" if no Tauri
dev session is running. `cargo tauri android build` needs no addr file.
2026-08-02 13:19:24 -05:00
d3154738d2 Add Android support via Tauri
The Rust core was already portable (caller-provided db paths, rustls rather
than openssl) and the shell already had the mobile entry point, so this is
mostly toolchain, provisioning, and platform-gating.

Toolchain:
- scripts/android-setup.sh installs what Android Studio doesn't: SDK
  cmdline-tools, a pinned stable NDK, and the Rust Android targets. The NDK is
  pinned rather than "latest" because sdkmanager's newest is often a beta
  (30.0.15729638 is r30-beta2) -- not something to compile SQLite with. Prefers
  Android Studio's bundled JDK 21, since Gradle rejects newer JDKs.
- Track src-tauri/gen/android (excluding build output): AndroidManifest.xml is
  hand-edited and Android permissions have no tauri.conf.json equivalent.
- vite: serve on TAURI_DEV_HOST so a device/emulator can reach the dev server.

Card data: downloading streams ~740MB to build a ~73MB db, which is rough on a
phone. Add import_reference_db to adopt a desktop-built file instead, validated
by db::inspect_reference_db first so picking the wrong file is a clean error
rather than a broken install. Installs via copy-then-rename so an interrupted
copy can't leave a partial db.

Ingest hardening (helps desktop too):
- Give ureq connect/read timeouts. Cancellation is only polled per parsed
  record, so a stalled socket previously hung forever with no way to cancel.
- Drop the remove_file before rename; POSIX rename already replaces atomically,
  and the delete-first window could leave no reference.db at all if the process
  was killed -- much likelier on Android.

UI: hide Open/New Library on mobile (scoped storage has no arbitrary-path
equivalent, and SQLite can't open a content:// URI), reported via a cfg!(mobile)
flag on Status rather than user-agent sniffing. Use 100dvh so the soft keyboard
doesn't break the shell.

Verified: 70/70 core tests, desktop build clean, and the aarch64 APK builds
(bundled SQLite C and ring both cross-compile under NDK 28). Not yet run on a
device.
2026-08-02 13:16:34 -05:00
bde9cf8a98 Cohesive styling pass: design tokens, shared primitives, responsive groundwork
The UI had a good terracotta-on-dark system that wasn't applied
consistently — magic-number spacing, ad-hoc page headers, ~6 copied
panel surfaces, ~8 drifting label variants, and scattered accent use.

- Tokens: warm the neutral ramp, add spacing/radius/type/motion scales,
  --accent-quiet + panel highlight. Terracotta hue unchanged.
- Primitives: .panel, .section-label, .page-header/.toolbar — collapse
  the copied surfaces and label variants onto shared classes.
- Unify each filter-heavy page's display controls (group/sort/view,
  select-all, count) into a workspace toolbar inside the filter panel,
  so they read as one surface instead of floating above it.
- Labels: sentence-case, higher-contrast form labels; uppercase section
  markers bumped faint -> muted.
- Accent hierarchy: reserve full terracotta for primary actions/active
  state; demote decorative labels and the group-header underline.
- Responsive groundwork: reflow classes for the deck two-pane and nav
  rail (stacks / icon-collapse at breakpoints), touch-target sizing.
  Desktop unchanged in kind.
2026-07-22 23:44:21 -05:00
ecb946328a Add a card details info tile to the click-to-preview modal
Shows type line, mana value/colors, price, per-format legality pills,
and ownership context (owned copies, decks it's in) when the caller
has that data — LibraryCard/DeckEntry rows do, bare CardRefs don't, so
the section just omits itself when absent.

The info tile sits beside the card art via flex-wrap and drops to its
own line on narrow viewports, rather than shrinking either face to
make room. Also shrinks a double-faced Battle's rotated front face
when it has a sibling back face, so it no longer renders at
full single-card size next to a much smaller back face.
2026-07-22 23:11:26 -05:00
d3e8a8f25a Prefer Scryfall's representative printing for default card images
Card previews were picking whichever printing happened to load first
during ingest (ORDER BY rowid over every printing, promos and foreign
versions included), so a card's default image was effectively random.
Capture the representative printing's images straight from the Oracle
Cards bulk pass instead, and prefer that over the old printings-table
guess, falling back gracefully on an older reference.db that predates
these columns.

Also add docs/printing-tracking-design.md, a design (not yet
implemented) for optionally tracking which specific printing/finish a
library or deck card is.
2026-07-22 22:05:44 -05:00
aaf632a417 Fix Battle cards not detected as horizontal during ingest
Real Battle cards are layout "transform" (they flip into a creature),
not "battle" as previously assumed — no shipped card actually uses
that layout, so the horizontal flag never got set for them. Detect
Battle orientation from the front face's type line instead.
2026-07-22 21:30:08 -05:00
2b361c4a85 Add a Card Search tab and fix horizontal-card (Battle/Plane) preview orientation
Card Search browses the whole downloaded card universe (not just owned
cards), reusing the Library tab's filter/group/sort/list/tiles UI; results
show ownership and offer quick "own" and "add to deck" actions.

Battles, Planes, and Phenomena store their Scryfall image portrait-shaped
with the content itself rotated, so hover/click previews now detect this
(a new `horizontal` flag threaded through ingest and every card query, with
graceful fallback for a reference.db built before the column existed) and
rotate the image back into place. Single-faced and non-DFC cases are
verified fixed; a double-faced Battle's front face still needs another pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 20:55:17 -05:00
3c625a9830 Add deck synergy suggestions, a responsive deck layout, and undoable library bulk-add
Suggestion scoring is fully offline (tribal/mechanic/archetype/mana-base
signals derived at ingest, no external synergy database), with an "unusual
picks" mode that surfaces underappreciated cards instead of staples. The
deck page's top panels now use a CSS grid so wide windows don't waste
horizontal space. Library's bulk add-to-deck can clobber existing deck
quantities, so it now offers an undo (snackbar + command palette, the
latter with no timeout) that restores each card's prior state exactly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 21:04:36 -05:00
008ab33723 Add deck legality checking, deck/library import-export, and a command palette
Threads Scryfall's per-format legalities through the reference db and adds a
deck-page indicator (structural rules + banned/restricted lists) that lists
violation reasons inline. Decks and selected library cards can now be
exported to/imported from plain-text decklists, with best-effort name
resolution and a report of anything that didn't match. Also adds a shuffle-
and-draw sample hand modal, and an Alt+/ command palette for jumping to a
tab, deck, or card without leaving the keyboard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 00:21:22 -05:00
cf9c930b02 Add card USD pricing and a deck maybeboard
Prices come from Scryfall's prices.usd on printings, shown as a hover badge
on card tiles. The price is picked independently of whichever printing
supplies the display image (the cheapest known price across all printings),
since the display printing is often a promo/variant with no listed price.

The maybeboard lets a deck track cards under consideration without counting
them in deck analysis stats or library assignment totals. Cards move between
mainboard and maybeboard via a per-card toggle; maybeboarding the commander
clears commander status.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 21:32:25 -05:00
e3f8a299de Add deck analysis panel with mana production/consumption bars and oracle text search
Threads a new produced_mana/produces_colorless field through the whole card
read path (ingest, library, filters, decks, search) so the deck view can
show what colors a deck's manabase actually produces, alongside its existing
color identity. Also adds a mana consumption bar (colored casting costs of
nonland spells) and stacks the mana curve histogram by color. Adds an
oracle-text substring filter to the library search, mirroring the existing
location filter. Rearranges the deck sidebar list into two rows (name, then
format chip + actions) so long names and action buttons no longer collide,
and removes the drop shadows from the commander hero panel/image.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 23:54:47 -05:00
4ac536caa2 Add multi-select bulk-remove and a quantity stepper to the Deck tab
- Mirror the Library tab's multi-select: checkboxes on every row/tile,
  a "Select all" toggle, and a bulk-action bar with a single "Remove
  from deck" action (reuses CardTile's cornerLeft/selected support and
  the existing .bulk-bar/.tile-select styling — no new CSS needed).
- Replace the deck table's plain number input (which only committed on
  blur) and the tile view's static "in deck ×N" badge with the same
  QtyStepper used in the Library tab, so bumping a card's count no
  longer means removing it and re-adding it. Decrementing to 0 now
  gets the same grace-period undo as the Library tab (dims the row for
  5s, + brings it back) instead of deleting it from the deck instantly.
- QtyStepper's `warn` state now flags deck cards you don't own enough
  copies of (scoped to cards you own at least one of, so every row in
  the already-labeled "Not owned" section doesn't light up too).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 08:20:20 -05:00
2581b1fb24 Add multi-select and bulk actions to the Library tab
- Checkboxes on every row/tile plus a "Select all" (filtered-set-aware,
  indeterminate-aware) toggle in the toolbar.
- A bulk-action bar appears once anything's selected: add to a deck
  ("Add 1" sets each selected card to 1 copy, "Add All" sets it to
  however many you own — the latter is the one-click fix for bulk-adding
  a stack of basic lands), set location, and adjust owned quantity by a
  configurable delta. All reuse the same per-card commands the
  single-card UI already calls, so behavior stays consistent (including
  the existing grace-period undo on quantity_owned hitting 0).
- CardTile gains a `cornerLeft` slot and `selected` prop (ring highlight,
  keeps the checkbox visible without hovering) to support this in tile
  view without disturbing the existing top-right qty-stepper corner.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 08:20:09 -05:00
d8a9787ecd Rebrand to mtgc: pirate-ship logo, lowercase app name, mtgc binary
- New logomark: an original pirate-ship silhouette (hull, twin billowing
  sails, swallow-tail pennant) in the terracotta brand gradient on a dark
  square — a nod to the MTG card "Pirate Ship", not a reproduction of its
  art. Source at src-tauri/icons/logo-source.svg; regenerate the full
  platform icon set (desktop/Windows Store/iOS/Android) with
  `cargo tauri icon src-tauri/icons/logo-source.svg`.
- Apply it in-app via a new inline ShipLogo component (App.tsx), replacing
  the old "M" sidebar mark and "✦" empty-state mark with dark rounded
  squares containing the ship glyph.
- Rename the app to lowercase "mtgc" everywhere: window title and
  productName (tauri.conf.json), <title> (index.html), sidebar brand text,
  empty-state heading.
- Rename the Tauri package/binary mtgc-app -> mtgc (and its lib crate
  mtgc_app_lib -> mtgc_lib), so `cargo tauri build` now produces
  target/release/mtgc; updated main.rs and every README reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:53:36 -05:00
67c97bbd7a Show reference-data update time, location filter UI, and tile fixes
- Status gains reference_updated_at (sourced from reference.db's file
  mtime, so it needs no separate persisted state) and the sidebar's
  "Your library" panel shows it as a relative "Updated Xh ago" stamp
  that self-refreshes every minute.
- Add a Location field to the Library tab's filters panel, wired to the
  new LibraryFilter.location.
- Bump every font-size in the stylesheet up one step for a slightly
  larger, easier-to-read UI overall.
- Fix the library/deck tile view's +/- quantity overlay permanently
  covering the card art's own printed mana cost (top-right corner) —
  it now only fades in on hover/focus, like the other row actions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:35:09 -05:00
436975f64f Add location filter to library search
LibraryFilter.location does a case-insensitive substring match against
the stored location (e.g. "Box 1"), mirroring the existing text filter.
Tested for substring/case-insensitivity and the empty-string no-op case.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:34:58 -05:00
e57c3dec51 Deck actions UI, consistent icon buttons, native select fix, sidebar stats
- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
  a ConfirmDialog for delete and PromptDialog (with friendly name-
  collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
  every ad hoc close/remove button in the app with it: deck actions,
  remove-card-from-deck (list and tile), clear-commander, the card-
  preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
  overridden, leaving native OS chrome (height, arrow, popup) in place.
  Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
  on wide viewports instead of requiring Flip, and size the preview
  images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
  panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
  polling get_library_stats while card data is loaded.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:47 -05:00
3e41c9d055 Add library_stats aggregate query for the sidebar summary
New LibraryStats (unique cards owned, total copies, deck count, and
per-color copy counts including colorless) via Catalog::library_stats,
tested against the fixture catalog.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:33 -05:00
e734ef650b Add deck rename and duplicate operations
New Catalog::rename_deck / duplicate_deck (duplicating format, commander,
notes, and every deck_cards row under a new name), with tests covering
the UNIQUE(name) collision cases the UI needs to surface as friendly
errors rather than raw SQL messages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 21:21:26 -05:00
eefe1dc954 Card preview modal, library quantity steppers, and UI polish
- Fix keyboard-nav selection highlight (search results, deck list): the
  Discord-styling rewrite dropped the --panel-2 CSS variable it relied
  on, so ↑/↓ moved selection but rendered no visible highlight.
- Add click-to-preview: clicking a card thumbnail/tile now opens a
  larger centered modal (list and tile views), with a flip button for
  double-faced cards' back face. Hover preview is unchanged and clears
  when a click-preview opens.
- Add a QtyStepper component (rounded [− N +] pill, with an `overlay`
  variant for tile corners) and use it consistently for owned-copy
  counts on Add Cards, and the Library list and tile views.
- Add matching +/- steppers to the Library views; decrementing to 0
  dims the row for a 5s grace period (still fully interactive) before
  it drops out, so an accidental removal can be undone with +.
- Topbar: fill remaining width with the library path (no more premature
  truncation), add icons to Open/New Library and Download/Update card
  data, and drop the trailing "…" from the library buttons.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:18 -05:00
e18664ea8c Persist last-opened library file across restarts; fix path-switch bug
- Remember which library.db was last chosen (via a small config.json in
  the app-data dir) so relaunching the app reopens the same file instead
  of always defaulting to library.db.
- Fix set_library_path returning a hard error (and thus never updating
  the topbar) whenever the reference catalog fails to reopen against the
  new path — e.g. a stale reference.db schema. Picking a library file
  and successfully reattaching the reference db are separate concerns;
  the chosen path now always persists and is reported back, with the
  catalog reopened best-effort.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:55:05 -05:00
2ea3c56511 Capture back-face printing images for double-faced cards
Add a back_image_uri column to printings, populated during ingest from
a card's second card_faces image (only present for transform/MDFC/
reversible cards — split/adventure cards share one combined image and
correctly get ""). Plumb it through CardRef across every query path
(search, library, filters, deck contents) via the same printing row,
using a shared deterministic ORDER BY so front/back always come from
one printing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:54:55 -05:00
342b8da797 Overhaul card-data ingest: filter art cards, segmented cancellable progress
- Exclude Scryfall Art Series cards (layout "art_series") from ingest —
  they aren't playable cards and were polluting search results.
- Replace the coarse per-stage progress text with a byte-accurate,
  weighted multi-segment progress bar (Index / Card data / Printings /
  Indexing) that fills as one continuous bar instead of swapping per
  stage; segment weights are computed from the real download sizes
  reported by Scryfall's bulk-data index (the download responses
  themselves are chunked with no Content-Length to rely on).
- Add a Cancel button, wired through a new CancelToken checked once per
  parsed record so cancellation lands almost instantly.
- Fix a related correctness bug this surfaced: ingest used to delete the
  existing reference.db before writing its replacement, so a cancelled
  or crashed update left the app with no card database at all. It now
  builds into a temp file and only atomically replaces reference.db on
  full success.
- Split the topbar's single "Choose library…" action into separate
  "Open Library…" (existing file) and "New Library…" (save-dialog for a
  new file) buttons.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:22:31 -05:00
0eaa41ec41 Add +/- quantity buttons to the Add Cards "Just added" list
Lets you quickly correct duplicate-copy counts right after adding a
card, instead of navigating to the Library tab.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 20:21:46 -05:00
dca03fe5b7 Remove AppImage from bundle targets
Restrict tauri.conf.json bundle targets to deb/rpm so `cargo tauri
build` no longer produces an AppImage (the README's AppImage mentions
were already dropped in the prior commit).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 19:50:04 -05:00
131554532a Document GTK4 blocker in README and devel-setup.sh
Tauri v2's Linux backend hard-depends on GTK3 + webkit2gtk-4.1; GTK4
support is tracked upstream (tauri#7335, tao#1104, wry#1228) but
unmerged with no ETA. Note this so it isn't re-investigated from
scratch later.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 19:49:50 -05:00
399c628461 Initial commit: MTG personal library catalog (Tauri v2 + React/TS)
Full working app with:
- Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB
  architecture (reference.db + library.db via ATTACH), criteria filters, deck ops
- Tauri v2 shell exposing the core as typed IPC commands
- React/TS frontend: card entry, library browser, deck editor
- Features: fuzzy card search, color/CMC/type/commander-identity filters,
  tile/list view toggle, hover card previews, deck format + commander support,
  per-card location field, grouping by type/subtype/CMC/color, localStorage
  persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 19:37:56 -05:00