Partner commanders: Partner, Partner—<variant>, Partner with, Background, Doctor

A Commander/Brawl deck can now have two commanders when their abilities
allow it. Partner abilities are parsed from oracle text (Scryfall's keyword
list files every "Partner—Survivors"-style variant under plain "Partner"),
and a second search box offers only cards that can pair with the commander.
The legality check accepts a valid pair and checks color identity against
both; suggestions and decklist import/export handle both commanders.

Closes #4
This commit is contained in:
2026-10-23 22:29:56 -05:00
parent 5f19ddfa4a
commit bfad4c1f27
11 changed files with 526 additions and 54 deletions

View File

@@ -29,6 +29,13 @@ export interface LibraryCard extends CardRef {
location: string;
}
/** A partner-style ability, letting a card share the command zone with a
* second commander. Mirrors `mtgc_core::partner::Partner`. */
export type Partner =
| { kind: "partner" | "chooses_background" | "background" | "doctors_companion" | "doctor" }
/** "Partner—Survivors" and the like (`variant`); "Partner with <name>" (`with`). */
| { kind: "variant" | "with"; name: string };
export interface DeckEntry extends CardRef {
quantity: number;
is_commander: boolean;
@@ -38,6 +45,7 @@ export interface DeckEntry extends CardRef {
quantity_owned: number;
/** The printing whose art this slot shows; `image_uri` already reflects it. */
preferred_scryfall_id: string | null;
partner: Partner | null;
}
/** One printing of a card, for the printing picker and copy labels. */
@@ -487,6 +495,12 @@ export const api = {
invoke<void>("set_deck_format", { deckId, format }),
setDeckCommander: (deckId: number, oracleId: string | null) =>
invoke<void>("set_deck_commander", { deckId, oracleId }),
/** One commander, or a partner pair; the first is the deck's primary. */
setDeckCommanders: (deckId: number, oracleIds: string[]) =>
invoke<void>("set_deck_commanders", { deckId, oracleIds }),
/** Name search narrowed to cards that could partner with the commander. */
searchPartners: (commanderOracleId: string, query: string, limit: number) =>
invoke<CardRef[]>("search_partners", { commanderOracleId, query, limit }),
setDeckCard: (
deckId: number,
oracleId: string,