Files
mtgc/src/api.ts
garrettmills 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

124 lines
3.8 KiB
TypeScript

// 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
}
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;
}
export interface Status {
library_path: string;
reference_path: string;
reference_ready: boolean;
card_count: number;
}
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[];
}
// --- 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"),
cancelUpdateCardData: () => invoke<void>("cancel_update_card_data"),
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 }),
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 }),
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 }),
};