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
|
|
|
// Typed wrappers over the Tauri command bridge into mtgc-core.
|
|
|
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
|
|
|
|
|
|
|
|
// --- Types (mirror the serde-serialized core structs) ---------------------
|
|
|
|
|
|
|
|
|
|
export interface CardRef {
|
|
|
|
|
oracle_id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
mana_value: number;
|
|
|
|
|
colors: number; // WUBRG bitmask
|
|
|
|
|
color_identity: number; // WUBRG bitmask
|
|
|
|
|
type_line: string;
|
|
|
|
|
image_uri: string; // Scryfall "normal" image URL, or "" if unavailable
|
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
|
|
|
back_image_uri: string; // back face for transform/MDFC/reversible cards, else ""
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LibraryCard extends CardRef {
|
|
|
|
|
quantity_owned: number;
|
|
|
|
|
decks: string[];
|
|
|
|
|
quantity_assigned: number;
|
|
|
|
|
location: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface DeckEntry extends CardRef {
|
|
|
|
|
quantity: number;
|
|
|
|
|
is_commander: boolean;
|
|
|
|
|
quantity_owned: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface Deck {
|
|
|
|
|
id: number;
|
|
|
|
|
name: string;
|
|
|
|
|
format: string;
|
|
|
|
|
commander_oracle_id: string | null;
|
|
|
|
|
notes: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LibraryFilter {
|
|
|
|
|
text?: string | null;
|
|
|
|
|
colors_exact?: number | null;
|
|
|
|
|
colors_include?: number | null;
|
|
|
|
|
commander_identity?: number | null;
|
|
|
|
|
cmc_min?: number | null;
|
|
|
|
|
cmc_max?: number | null;
|
|
|
|
|
types?: string[];
|
|
|
|
|
subtypes?: string[];
|
|
|
|
|
keywords?: string[];
|
|
|
|
|
in_deck?: number | null;
|
2026-07-07 21:35:09 -05:00
|
|
|
location?: string | null;
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface Status {
|
|
|
|
|
library_path: string;
|
|
|
|
|
reference_path: string;
|
|
|
|
|
reference_ready: boolean;
|
|
|
|
|
card_count: number;
|
2026-07-07 21:35:09 -05:00
|
|
|
reference_updated_at: number | null; // unix seconds
|
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
|
|
|
}
|
|
|
|
|
|
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
|
|
|
export interface LibraryStats {
|
|
|
|
|
unique_cards: number;
|
|
|
|
|
total_copies: number;
|
|
|
|
|
deck_count: number;
|
|
|
|
|
color_counts: [number, number, number, number, number]; // W, U, B, R, G
|
|
|
|
|
colorless_count: number;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-07 20:22:31 -05:00
|
|
|
export interface IngestStageInfo {
|
|
|
|
|
id: string;
|
|
|
|
|
label: string;
|
|
|
|
|
weight: number; // fraction of total work, 0..1, sums to 1 across all stages
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface IngestProgress {
|
|
|
|
|
stage: "index" | "oracle" | "printings" | "optimize" | string;
|
|
|
|
|
message: string;
|
|
|
|
|
overall_percent: number; // 0..100 cumulative across all stages
|
|
|
|
|
stages: IngestStageInfo[];
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// --- WUBRG bitmask helpers ------------------------------------------------
|
|
|
|
|
|
|
|
|
|
export const COLORS: { bit: number; code: string; label: string }[] = [
|
|
|
|
|
{ bit: 1, code: "W", label: "White" },
|
|
|
|
|
{ bit: 2, code: "U", label: "Blue" },
|
|
|
|
|
{ bit: 4, code: "B", label: "Black" },
|
|
|
|
|
{ bit: 8, code: "R", label: "Red" },
|
|
|
|
|
{ bit: 16, code: "G", label: "Green" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export function maskToLetters(mask: number): string {
|
|
|
|
|
return COLORS.filter((c) => mask & c.bit)
|
|
|
|
|
.map((c) => c.code)
|
|
|
|
|
.join("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Commands -------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
export const api = {
|
|
|
|
|
getStatus: () => invoke<Status>("get_status"),
|
|
|
|
|
setLibraryPath: (path: string) => invoke<Status>("set_library_path", { path }),
|
|
|
|
|
updateCardData: () => invoke<Status>("update_card_data"),
|
2026-07-07 20:22:31 -05:00
|
|
|
cancelUpdateCardData: () => invoke<void>("cancel_update_card_data"),
|
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
|
|
|
|
|
|
|
|
searchCards: (query: string, limit = 20) =>
|
|
|
|
|
invoke<CardRef[]>("search_cards", { query, limit }),
|
|
|
|
|
|
|
|
|
|
addOwned: (oracleId: string, delta: number) =>
|
|
|
|
|
invoke<number>("add_owned", { oracleId, delta }),
|
|
|
|
|
setOwned: (oracleId: string, quantity: number) =>
|
|
|
|
|
invoke<number>("set_owned", { oracleId, quantity }),
|
|
|
|
|
setLocation: (oracleId: string, location: string) =>
|
|
|
|
|
invoke<void>("set_location", { oracleId, location }),
|
|
|
|
|
|
|
|
|
|
searchLibrary: (filter: LibraryFilter) =>
|
|
|
|
|
invoke<LibraryCard[]>("search_library", { filter }),
|
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
|
|
|
getLibraryStats: () => invoke<LibraryStats>("get_library_stats"),
|
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
|
|
|
|
|
|
|
|
listDecks: () => invoke<Deck[]>("list_decks"),
|
|
|
|
|
createDeck: (name: string, commanderOracleId: string | null) =>
|
|
|
|
|
invoke<Deck>("create_deck", { name, commanderOracleId }),
|
|
|
|
|
deleteDeck: (deckId: number) => invoke<void>("delete_deck", { deckId }),
|
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
|
|
|
renameDeck: (deckId: number, name: string) => invoke<void>("rename_deck", { deckId, name }),
|
|
|
|
|
duplicateDeck: (deckId: number, newName: string) =>
|
|
|
|
|
invoke<Deck>("duplicate_deck", { deckId, newName }),
|
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
|
|
|
setDeckFormat: (deckId: number, format: string) =>
|
|
|
|
|
invoke<void>("set_deck_format", { deckId, format }),
|
|
|
|
|
setDeckCommander: (deckId: number, oracleId: string | null) =>
|
|
|
|
|
invoke<void>("set_deck_commander", { deckId, oracleId }),
|
|
|
|
|
setDeckCard: (
|
|
|
|
|
deckId: number,
|
|
|
|
|
oracleId: string,
|
|
|
|
|
quantity: number,
|
|
|
|
|
isCommander = false,
|
|
|
|
|
) => invoke<void>("set_deck_card", { deckId, oracleId, quantity, isCommander }),
|
|
|
|
|
deckContents: (deckId: number) => invoke<DeckEntry[]>("deck_contents", { deckId }),
|
|
|
|
|
cardDecks: (oracleId: string) => invoke<Deck[]>("card_decks", { oracleId }),
|
|
|
|
|
};
|