Repository navigation
Modelo del mazo
English · Español
El modelo de datos del mazo es el contrato entre Rust, la base de datos SQLite y la pantalla de React. Vive en src-tauri/crates/proxy-core/src/model.rs, es puro (sin Tauri, sin red, sin base de datos) y cada tipo se exporta a TypeScript con ts-rs a src/bindings/ (lo genera pnpm bindings, nunca se edita a mano).
Una regla lo condiciona todo: un DeckCard por copia física, no uno por carta con una cantidad. Así, cada copia puede tener su propia variante, ediciones, reverso, diseño, marca y posición.
classDiagram
class Deck {
id: String
name: String
decklistText: String
cards: Vec~DeckCard~
settings: PrintSettings
defaultBack: Option~FaceImage~
schemaVersion: u32
}
class DeckCard {
id: String (uuid v4)
source: CardSource
front: Option~FaceImage~
back: BackSpec
edits: CardEdits
locked: bool
meta: CardMeta
design: Option~CardDesign~
marca: Option~MarcaCarta~
}
class CardSource {
<<enum kind>>
Scryfall scryfallId
Token scryfallId
Custom scryfallId? externa?
Blank
}
class RefExterna {
fuente: FuenteCarta
id: String
}
class FaceImage {
blob: String (SHA-256)
width: u32
height: u32
origin: ImageOrigin
}
class BackSpec {
<<enum kind>>
DeckDefault
Dfc image
Custom image
}
class CardEdits {
rotation: Rotation
crop: Crop
adjust: Adjustments
}
class CardMeta {
name, set, collectorNumber, lang
langFallback, layout, isDfc
imageStatus, highres, typeLine
imageLang?
}
class PrintSettings
class CardDesign
Deck "1" *-- "many" DeckCard
Deck "1" *-- "1" PrintSettings
Deck "1" o-- "0..1" FaceImage : defaultBack
DeckCard *-- CardSource
DeckCard o-- "0..1" FaceImage : front
DeckCard *-- BackSpec
BackSpec o-- FaceImage
DeckCard *-- CardEdits
DeckCard *-- CardMeta
DeckCard o-- "0..1" CardDesign : design
CardSource o-- RefExterna : Custom.externa
CardDesign pertenece al diseñador de cartas (src-tauri/crates/proxy-core/src/diseno/modelo.rs) y se documenta en Motor del diseñador.
| Campo | Significado |
|---|---|
id |
uuid v4, creado en store::decks::create. |
name |
Nombre visible (recortado al crear o renombrar). |
decklist_text |
La lista del mazo tal como la escribió el usuario. Es una entrada; el mazo son las cards. Se conserva para que el panel pueda volver a mostrarla y para que Exportación pueda recuperar las secciones de la lista (véase más abajo). |
cards |
Vec<DeckCard> ordenado; el orden es el de impresión y se guarda en deck_cards.position. |
settings |
PrintSettings (véase más abajo). |
default_back |
Reverso que usa toda carta cuyo BackSpec es DeckDefault. Un mazo nuevo recibe el reverso genérico (BlobStore::generic_back). |
schema_version |
DECK_SCHEMA_VERSION (actualmente 2). |
#[serde(tag = "kind", rename_all = "camelCase")], de modo que el JSON queda como {"kind":"scryfall","scryfallId":"..."}.
| Variante | Significado |
|---|---|
Scryfall { scryfall_id } |
Una impresión concreta de Scryfall. |
Token { scryfall_id } |
Una ficha añadida desde los all_parts de otra carta. |
Custom { scryfall_id?, externa? } |
Una imagen aportada por el usuario (o descargada de una fuente externa). scryfall_id la vincula opcionalmente con la carta real de Scryfall que representa (se usa para la información de la carta y para las Estadísticas); la imagen sigue siendo la del usuario. externa (RefExterna { fuente: FuenteCarta, id }) la vincula con una carta de una fuente que no es Scryfall; hoy el único FuenteCarta es Cockatrice (fichas que Scryfall no tiene, véase Fuentes externas). |
Blank |
Una casilla vacía (lo que deja «Añadir página»). |
Decisión: externa es un campo opcional de Custom, no una variante nueva del enum. Motivo: una app antigua que no conozca el campo sigue deserializando la carta y simplemente ve una carta propia normal (serde ignora los campos desconocidos), de modo que las versiones antiguas siguen abriendo esos mazos.
FaceImage { blob, width, height, origin }: blob es el SHA-256 en hexadecimal minúscula (64 caracteres) del fichero guardado; los bytes viven en el almacén de blobs, véase Almacenamiento y blobs. ImageOrigin decide, entre otras cosas, si se aplica el oscurecimiento de bordes (EdgeDarkening.apply_scryfall / apply_uploads / apply_backs):
| Valor | Origen |
|---|---|
ScryfallPng |
PNG de Scryfall (745x1040, esquinas transparentes). |
ScryfallLarge |
JPG large de Scryfall (672x936), cuando no hay PNG. |
Upload |
Imagen subida por el usuario (también el valor de reserva al interpretar una cadena de origen desconocida). |
Generated |
Generada por la app (el reverso genérico). |
Designed |
Miniatura de un diseño del diseñador de cartas; el PDF no la usa, vuelve a pintar el diseño a la resolución de exportación. |
front: None significa una casilla en blanco o una imagen aún sin descargar.
DeckDefault (usa Deck.default_back), Dfc { image } (la cara trasera de una carta de doble cara), Custom { image } (un reverso elegido para esta carta).
rotation (R0/R90/R180/R270), crop (zoom >= 1, offset_x/offset_y en [-1, 1] dentro del margen que deja el zoom; 0 = centrado) y adjust (Adjustments, los once retoques de fabricard, 0 = sin efecto: brightness, exposure, contrast, saturation, warmth, vibrance, sharpness, clarity, noise_reduction, vignette, fade). Las fórmulas viven en imaging::adjust, véase Imagen y PDF. is_identity() permite a la canalización saltarse trabajo.
Datos de visualización y de decisión: name, set, collector_number, lang, lang_fallback (se pidió un idioma que no existía, así que se quedó en inglés), layout (el layout de Scryfall), is_dfc, image_status, highres, type_line e image_lang. image_lang solo tiene sentido en las cartas propias: es el idioma de la imagen, que no tiene por qué coincidir con el lang de la carta vinculada (una imagen propia en español vinculada a la impresión en inglés). Lo fija quien vincula (el pie leído por el OCR, véase Identificación de cartas y OCR) o se elige en el editor de cartas.
Una carta fijada: «Auto ordenar» y «Reaplicar idioma» la saltan.
CardDesign opcional del diseñador de cartas (2.0). skip_serializing_if = "Option::is_none" mantiene el JSON de la v1 idéntico byte a byte para las cartas que no lo tienen.
marca: Option<MarcaCarta> es una etiqueta fija para organizar el mazo. No cambia lo que se imprime (una carta marcada sigue yendo al PDF). Cambia lo que cuenta.
MarcaCarta |
Nombre en la interfaz | ¿Cuenta para el tamaño del mazo, las copias y las estadísticas? |
|---|---|---|
| (ninguna) | sin marca | Sí. |
Variante |
variante de diseño | Solo cuenta una copia por nombre de carta, y solo si no existe una copia sin marca con ese nombre. |
Opcional |
opcional | No. |
Banquillo |
banquillo | No (pero se exporta a la sección del banquillo). |
Copia |
copia (hace de ficha) | No. Una carta de verdad que hace de ficha de un Clone o un cambiaformas: en la mesa va a las fichas («Las de tu deck»), no a la biblioteca, y no es candidata de una lista para jugar (mesa::preparar::secciones_de_mesa, candidatas_de_lista). La detección automática (es_ficha) solo conoce las fichas de Scryfall, así que esta marca es como se le dice que una carta real es una. Las versiones anteriores de la app no pueden abrir un deck que lleve esta marca. |
proxy_core::model::cuentan(cards) -> (Vec<&DeckCard>, Apartadas) es el único sitio donde se implementa esa regla. Los nombres se comparan recortados y en minúsculas. Apartadas { variantes, opcionales, banquillo, copias } informa de cuántas copias se dejaron fuera (total()), y se expone a la pantalla como DeckStats.setAside. Quién la usa:
-
src-tauri/src/commands/stats.rs(calcular): las estadísticas solo miran las cartas que cuentan. -
src-tauri/crates/proxy-core/src/decklist/exportar.rs(juntar): la lista exportada omite las cartas que no cuentan, salvoBanquillo, que va al bloque del banquillo. Las fichas y los huecos nunca se escriben (va_en_la_lista); una cartaCustomvinculada a una fuenteexternacuenta como ficha.
Test: las_marcadas_no_cuentan en model.rs.
Deck no tiene campo de sección. Las secciones vienen del parser de la lista (proxy_core::decklist::Section: Main, Commander, Sideboard, Companion, Maybeboard, Tokens, véase Parseo de la lista del mazo). Al resolver la lista (src-tauri/src/scryfall/resolve.rs), solo dos secciones se convierten en marcas: Sideboard pasa a Banquillo y Maybeboard (si se incluye) pasa a Opcional; el resto no recibe marca. Al exportar, las secciones se vuelven a deducir por nombre a partir de decklist_text (secciones_de / seccion_de en exportar.rs), así que renombrar una carta del mazo sin editar la lista puede hacerle perder su sección: Deducido de la búsqueda por nombre; lee exportar.rs antes de fiarte de ello.
Resolver una línea de la lista con cantidad N crea N DeckCard con uuids nuevos (resolve.rs: for _ in 0..l.qty). Por tanto, «copias de una carta» es un recuento sobre cards filtrado por cuentan, no un número guardado.
PrintSettings (todo lo que decide cómo sale el papel) se guarda dentro del mazo como Deck.settings, serializado en decks.settings_json. Es el mismo tipo que consumen Motor de maquetación e Imagen y PDF, y el que edita el panel de exportación mediante useDeck.setAjustes (véase Stores de estado). Las copias con nombre y reutilizables de los ajustes de impresión o de los desplazamientos de fila viven aparte como presets (store::presets, tabla presets, tipos frontOffsets, backOffsets, print; interfaz en src/features/export/presets.tsx).
| Campo | Tipo / valor por defecto | Notas |
|---|---|---|
paper |
PaperFormat, A4
|
A3 A4 A5 Letter Legal Tabloid Photo4x6 Photo5x7 Photo8x10 Photo8x12 (paper.rs). |
landscape |
false |
|
card_size |
CardSize::MTG 63x88 mm |
Alternativas CardSize::POKER 63,5x88,9 mm (2,5x3,5 in) y CardSize::FUNDA 65x90 mm, que llena justa una funda (véase Exportación). |
min_margin_mm |
5.0 | |
gap_mm |
2.0 | Separación en blanco entre cartas, como en fabricard; cada carta conserva su tamaño exacto de 63x88 y el sangrado no cubre la separación. |
bleed_mm |
0.0 | El sangrado está disponible pero apagado de fábrica. |
bleed_on_backs |
true |
|
bleed_mode |
Clamp |
Clamp repite el píxel del borde, Mirror lo refleja. |
fill_corners |
false (#[serde(default)]) |
Pinta las esquinas redondeadas transparentes o claras con el color del borde; apagado significa que las esquinas transparentes salen del color del papel. |
cut_guides |
CutGuides, desactivadas |
enabled, color (#00c853), width_px 0.5, offset_mm 0, length_mm 5. |
edge_darkening |
EdgeDarkening, desactivado |
mode (ContrastEdges/SolidBlack/Threshold), amount 80, width_pct 8, auto_detect, apply_scryfall/uploads/backs. |
duplex |
Off |
Off, LongEdge (el reverso se refleja de izquierda a derecha y nunca se gira; un shortEdge guardado se lee como LongEdge). |
interleave_dfc |
false |
|
dfc_filter |
All |
HideDfc quita las cartas de doble cara del plan y del PDF. |
quality |
Dpi600 |
Dpi300/600/900; dpi() da el número. |
encoding |
JPEG, calidad 95 |
ImageFormat::{Jpeg,Png}, jpeg_quality 1..=100 (solo JPEG). |
row_offsets |
vacío |
RowOffsets { front, back }, milímetros por fila. |
split_every |
None |
Divide el PDF cada N hojas. |
design_lang |
En en mazos nuevos; Es si falta en un mazo guardado (#[serde(default)]) |
Idioma en que se imprimen las cartas diseñadas (DesignLang::{Es,En}). |
big_cards_back |
false (#[serde(default)]) |
Las cartas grandes (planos, Vanguard) imprimen el reverso del mazo en dúplex; si no, su reverso queda en blanco. |
Invariantes que conviene conocer:
- Decisión:
RowOffsets.backse expresa en el marco de la cara delantera y se indexa por la fila delantera (lo que ves sosteniendo la hoja a contraluz desde delante).layout::planlo aplica antes de reflejar. Las filas que faltan valen 0 (front_row,back_row). - Decisión: los valores por defecto (2 mm de separación, 0 de sangrado, guías apagadas, rellenar esquinas apagado, 600 dpi, JPEG 95) los comprueba
los_ajustes_por_defecto_son_los_de_fabricard. Cámbialos ahí y en el test a la vez. - Decisión: la carta se estira hasta la casilla exacta y solo se recorta cuando la proporción difiere en más de un 15 por ciento (
MAX_ESTIRADO, enimaging::frame). Es una regla de imagen, no un campo del modelo.
- Todas las structs y enums usan
#[serde(rename_all = "camelCase")]; los enums con etiqueta interna usantag = "kind"(CardSource,BackSpec). Los enums de unidad se serializan como cadenas en camelCase ("dpi600","off"). Testslos_campos_van_en_camel_caseyel_origen_de_una_carta_viaja_etiquetado. -
ts-rslo refleja: donde serde renombra un campo de forma explícita (scryfallId), el código añade#[ts(rename = ...)]y#[ts(optional)]para los camposOptionque se omiten cuando sonNone. - La base de datos guarda cada carta como una sola cadena
card_jsony los ajustes comosettings_json(véase Almacenamiento y blobs). El paquete.pbdeckincrusta elDeckentero como JSON enmanifest.json(véase Formato del paquete de mazo).
-
DECK_SCHEMA_VERSION = 2. La versión 2 añadióDeckCard.design. Decisión: se subió la constante porque una app 1.x que abriera y volviera a guardar un mazo v2 perdería los diseños sin avisar (serde ignora el campo desconocidodesigny el nuevo guardado lo omite). -
store::decks::savesiempre escribe elDECK_SCHEMA_VERSIONactual endecks.schema_version;store::decks::opendevuelve el valor guardado. No he encontrado código que rechace un mazo por suschema_versional abrirlo desde la base de datos; solo la importación de paquetes comprueba una versión (package::VERSION, véase Formato del paquete de mazo). - Estrategia para campos nuevos (léela antes de añadir un campo): los datos antiguos deben seguir deserializándose, así que un campo nuevo debe ser
Option<T>con#[serde(default, skip_serializing_if = "Option::is_none")](comodesign,marca,image_lang,Custom.externa) o llevar#[serde(default)](comofill_corners,design_lang,big_cards_back).skip_serializing_ifademás mantiene idénticas byte a byte las cartas sin cambios al volver a guardar. La gran mayoría de los campos dePrintSettingsno tienen valor serde por defecto, así que añadir un campo obligatorio ahí haría que todos los mazos guardados fallaran al abrirse (ErrorCode::Db,de_jsonenstore/decks.rs). - Una app antigua que abre un mazo más nuevo: los campos desconocidos se ignoran (no hay
deny_unknown_fieldsen ningún sitio desrc-tauri), así que los datos opcionales nuevos (marcas, vínculos, diseños) los pierde la app antigua en el siguiente guardado. En cambio, una variante o valor de enum nuevo que la app antigua no conoce (por ejemplo unImageOriginnuevo) es un error duro de deserialización. Por esoexternano se hizo variante. -
CardDesigntiene su propiaversion(VERSION_DISENO = 1,diseno/modelo.rs).
| Dónde | Qué |
|---|---|
src-tauri/src/store/decks.rs |
create, open, save, duplicate (ids nuevos, imágenes compartidas). |
src-tauri/src/scryfall/resolve.rs |
Construye los DeckCard a partir de una lista interpretada (hacer, copias en el orden de la lista, fichas al final). |
src/stores/deck-store.ts |
El mazo abierto en la pantalla, el historial y el autoguardado (véase Almacenamiento y blobs). |
src/bindings/Deck.ts, DeckCard.ts, PrintSettings.ts... |
Tipos generados. |
- Añade el campo en
model.rscon los atributos serde de arriba, añade#[ts(optional)]si procede y ejecutapnpm bindings. - Si referencia imágenes, haz que
store::decks::refs_del_deck/package::blobs_del_decklo tengan en cuenta (si no, la recolección de basura puede borrar la imagen, véase Almacenamiento y blobs). - Si cambia el recuento, cambia
cuentany su test, y revisacommands/stats.rsydecklist/exportar.rs. - Actualiza cada literal
DeckCard { ... }que marque el compilador (tests enstore/,package.rs,exportar.rs,resolve.rs).
Use and build · Uso y desarrollo
- User-Guide · Guía de usuario
- Development-Setup · Preparación del entorno de desarrollo
- Testing-and-CI · Tests y CI
- Releases-and-Auto-Updates · Versiones y actualizaciones automáticas
- Conventions · Convenciones
Architecture · Arquitectura
- Architecture · Arquitectura
- IPC-and-Error-Handling · IPC y manejo de errores
- Rust-App-Layer · Capa de aplicación Rust
Data · Datos
- Deck-Model · Modelo del mazo
- Storage-and-Blobs · Almacenamiento y blobs
- Deck-Package-Format · Formato del paquete de mazo
- Deck-Library · Biblioteca de mazos
Getting cards · Conseguir cartas
- Decklist-Parsing · Parseo de la lista del mazo
- Scryfall-Integration · Integración con Scryfall
- External-Sources · Fuentes externas
- Card-Identification-and-OCR · Identificación de cartas y OCR
Layout, images, PDF · Maquetación, imagen y PDF
- Layout-Engine · Motor de maquetación
- Imaging-and-PDF · Imagen y PDF
- Export · Exportación
- Live-Preview · Vista previa en vivo
- Statistics · Estadísticas
Card designer · Diseñador de cartas
- Designer-Overview · Visión general del diseñador
- Designer-Engine · Motor del diseñador
- Designer-Composition · Composición del diseñador
- Frame-Packs · Packs de marcos
- Designer-App-Layer · Capa de aplicación del diseñador
- Designer-UI · Interfaz del diseñador
- Card-Editor-Dialog · Diálogo Editar carta
Game table · Mesa de juego
Frontend · Frontend
- Frontend-Architecture · Arquitectura del frontend
- State-Stores · Stores de estado
- Internationalization · Internacionalización
- Themes-and-Skins · Temas y skins
- Settings-and-Shell · Ajustes y shell
- Tutorials · Tutoriales
- Decklist-Panel · Panel de la lista del mazo
- Add-Card-and-Variants · Añadir carta y variantes
- Set-Browser · Explorador de sets
Reference · Referencia