- 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>
124 lines
3.8 KiB
TypeScript
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 }),
|
|
};
|