Repository navigation
Diálogo Editar carta
English · Español
«Editar carta» es el modal para retocar la imagen ya existente de una carta: rotación, encuadre con zoom/desplazamiento y once ajustes de color y detalle. Aplica el resultado a esa carta, a sus copias o a todas las cartas, y muestra una vista previa en vivo renderizada por el mismo código de Rust que el PDF. No es el diseñador de cartas; véase Relación con la vista del diseñador más abajo.

| Fichero | Función |
|---|---|
src/features/card-editor/card-editor-dialog.tsx |
CardEditorDialog (montado una vez en src/App.tsx) y su Contenido interno |
src/features/card-editor/propagar.ts |
Alcance, copiasDe, aplicarEdicion: funciones puras que deciden qué cartas reciben la edición |
src/features/card-editor/__tests__/propagar.test.ts |
Tests de las reglas de alcance |
src/lib/imagen-viva.ts |
useAlRitmo, useRafaga, useImagenViva (actualización de imágenes en tiempo real, compartida con la Interfaz del diseñador) |
src/lib/image-url.ts |
parametros, urlMiniatura, EDICION_NEUTRA
|
- Doble clic en una casilla de carta de la vista previa (
src/features/preview/slot-view.tsx, solo si la carta tiene imagen:carta?.front). - Opción «Editar carta» del menú contextual (
src/features/preview/slot-menu.tsx; desactivada con varias cartas seleccionadas o cuando la carta no tiene imagen).
Ambas llaman a useUi.getState().abrir({ tipo: "editar", id }); CardEditorDialog lee useUi.dialogo, busca la carta con cartaPorId(id) (src/features/preview/acciones-carta.ts) y solo se abre si existe carta.front.
CardEdits (src-tauri/crates/proxy-core/src/model.rs, binding en src/bindings/CardEdits) se guarda en cada DeckCard.edits:
| Campo | Significado | Interfaz |
|---|---|---|
rotation |
r0, r90, r180, r270 (sin pérdida) |
Dos botones: girar a la izquierda/derecha, muestra 0/90/180/270 |
crop |
zoom (>= 1), offsetX, offsetY en [-1, 1] dentro de la holgura que deja el zoom (0 = centrado) |
Deslizador de zoom 1x-2x, deslizadores de encuadre horizontal/vertical, arrastre sobre la imagen cuando el zoom es > 1 |
adjust |
Adjustments: los once retoques |
Deslizadores |
Los once retoques, en el orden de la lista RETOQUES (el orden de la herramienta web original que esta app clona): brillo, exposición, contraste, saturación, calidez, intensidad (todos de -100 a 100); nitidez, claridad, reducción de ruido, viñeta, desvanecido (todos de 0 a 100). 0 = sin efecto, y EDICION_NEUTRA es el valor con todo a cero. Rust los aplica en un orden fijo (reducción de ruido, exposición, brillo, contraste, calidez, saturación, intensidad, claridad, nitidez, desvanecido, viñeta) con radios en milímetros, de modo que una miniatura de 488 px y un PDF a 600 ppp se vean igual (src-tauri/crates/proxy-core/src/imaging/adjust.rs).
Encuadre en Rust (imaging/frame.rs): rotar, luego caja de recorte a partir del zoom y el desplazamiento, luego remuestreo Lanczos3 al tamaño exacto de la casilla. La imagen se estira a la forma de la carta; solo si su proporción se aleja más de un 15 % (MAX_ESTIRADO) se recorta para cubrir. Véase Imagen y PDF.
flowchart LR
S["deslizadores / botones / arrastre"] --> B["borrador: CardEdits (useState)"]
B -->|"useAlRitmo 60 ms"| P["pintado"]
P -->|"useRafaga"| W{"¿ráfaga?"}
W -->|sí| U1["urlMiniatura 488 px"]
W -->|no| U2["urlMiniatura 745 px"]
U1 --> V["useImagenViva"]
U2 --> V
V --> I["img (la última cargada)"]
B -->|"filtroAproximado"| F["el filtro CSS salva el intervalo"]
F --> I
-
borradorguarda las ediciones que se están editando.pintado = useAlRitmo(borrador, 60)deja pasar el valor como máximo cada 60 ms (el último siempre llega), de modo que a Rust se le pide una imagen nueva a la velocidad del deslizador pero nunca se le inunda. - La URL de la imagen es
urlMiniatura(front.blob, parametros(ajustes, pintado, false, 0), ancho)→pbimg://…/r/<blob>/<base64url params>/<width>(src-tauri/src/protocol.rs).parametrosempaqueta enPreviewParamseledgeDarkeningdel mazo, el modo de sangrado, el relleno de esquinas, el tamaño de carta y las ediciones. Rust la renderiza conAppState::miniatura(src-tauri/src/render.rs), el mismoimaging::pipeline::renderque usa el PDF, y cachea el JPEG enapp_cache_dir/renders/<RenderSpec key>.jpg. -
useRafaga(pintado)es verdadero mientras las ediciones cambian más a menudo que cada 250 ms; entonces la petición es de 488 px de ancho, y si no, de 745 px. -
useImagenViva(url)mantiene en pantalla la imagen anterior hasta que ha cargado la nueva (sin parpadeos ni huecos en blanco). - Entre el movimiento de un deslizador y la llegada de la imagen nueva,
filtroAproximado(borrador.adjust, enPantalla.adjust)aplica un filtro CSSbrightness/contrast/saturatecalculado a partir de la diferencia entre el borrador actual y los ajustes con los que se renderizó la imagen mostrada. Un mapaporUrl(se vacía al pasar de 64 entradas) recuerda qué ediciones usó cada URL pedida, de manera queenPantallaes exacto aunque aún se muestre una imagen más antigua. Solo se aproximan el brillo, la exposición, el contraste, la saturación y la intensidad; la rotación, el encuadre, la nitidez y el resto simplemente esperan a Rust. - Mientras una imagen carga aparece un indicador de progreso (
cargando).
alBajar solo inicia un arrastre cuando borrador.crop.zoom > 1. alMover convierte el desplazamiento del puntero (como fracción del marco) en incrementos de desplazamiento escalados por la holgura 1 - 1/zoom, limita ambos desplazamientos a [-1, 1] y los escribe en el borrador. El cursor muestra grab solo cuando el zoom es > 1.
Para las cartas de Scryfall y las fichas (source.kind === "scryfall" | "token"), un zoom superior a 1.05 muestra un aviso ámbar: «Con este zoom se recorta el pie de la carta (artista y copyright).». Es solo un aviso.
| Botón | Alcance |
Efecto (aplicarEdicion) |
|---|---|---|
| «Aplicar» | carta |
Solo esta carta recibe la edición completa. Desactivado cuando nada ha cambiado (JSON.stringify(borrador) === JSON.stringify(carta.edits)). |
«Aplicar también a sus copias (n)» (solo si n > 0) |
copias |
Esta carta más cada carta con el mismo hash de imagen frontal (front.blob) recibe la edición completa: rotación, encuadre y retoques, porque la imagen es idéntica. |
| «Aplicar a todas las cartas» | todas |
Esta carta recibe la edición completa; cualquier otra carta con imagen recibe solo los ajustes de color (adjust) fusionados en sus propios edits (se conservan su rotación y su encuadre: dependen de cada imagen). |
-
copiasDe(cartas, carta)= las otras cartas cuyofront.blobes igual al de esta carta; una carta sin imagen no tiene copias aunque haya otras casillas en blanco. - Las cartas que no se tocan conservan la misma identidad de objeto (los tests lo comprueban), lo que mantiene barato a React y al historial.
- Las tres pasan por una sola llamada a
useDeck.getState().setCartas(...), así que un solo paso de deshacer revierte toda la operación (véase Stores de estado). Después se muestra un anuncio («Carta retocada», «Carta y sus N copias retocadas» o «Retoques aplicados a todas las cartas») y el diálogo se cierra. - «Restablecer todo» devuelve el borrador a
EDICION_NEUTRA(desactivado cuando ya es neutro); no cierra ni aplica. - Cerrar el diálogo con la X descarta el borrador. El diálogo no pasa
cambiosSinGuardar, así que se aplica el comportamiento por defecto desrc/components/ui/dialog.tsx(que sigue los controles tocados). Deducido: el comportamiento exacto de confirmar al cerrar viene de ese componente; léelo antes de fiarte de él.
Dos mecanismos distintos editan el aspecto de una carta:
| Editar carta (esta página) | Diseñador («Diseñar») | |
|---|---|---|
| Qué cambia | Los píxeles de una imagen existente: DeckCard.edits
|
Un CardDesign estructurado que renderiza el motor |
| Datos |
CardEdits (rotación, recorte, ajustes) |
DeckCard.design (Interfaz del diseñador, Motor del diseñador) |
Lo aplica imaging::pipeline::render a partir del blob original |
Se vuelve a pintar desde el diseño al tamaño de impresión (render_desde_imagen tras diseno::pintar) |
|
| Copias | Copias con la misma imagen mediante propagar.ts
|
Grupo de la misma carta de Scryfall mediante grupos.ts
|
Puntos de interacción, verificados en el código:
- Una carta diseñada es una carta del mazo cuyo
frontes una miniatura JPEG conImageOrigin::Designed. «Editar carta» se puede abrir para ella (tienefront). - Guardar un diseño restablece
editsa su valor por defecto (diseno::vista::aplicarponenueva.edits = CardEdits::default()), y el camino de cartas diseñadas del exportador ignoraeditsy el oscurecimiento de bordes (encargoensrc-tauri/src/commands/export.rsponespec.edits = CardEdits::default(),spec.edge = None;render_desde_imagense salta las esquinas, el encuadre y los retoques;edge_fordevuelveNoneparaDesigned). - La vista previa en pantalla de una carta diseñada sigue pasando por
/r/…con loseditsde la carta, así que un retoque hecho con «Editar carta» (o con «Aplicar a todas las cartas», que toca todas las cartas con imagen, incluidas las diseñadas) cambia la miniatura en pantalla pero no el PDF impreso. Deducido de esos caminos de código; la interfaz no avisa de ello. Para cambiar el aspecto impreso de una carta diseñada, usa el diseñador. - «Recortar de una carta…» del diseñador (
src/features/disenar/recortar-dialog.tsx) lista solo las cartas con imagen que no están diseñadas y usa su blob original, no sus ediciones. - Ediciones de la imagen original frente a la instantánea «before»: cuando se crea un diseño,
design.before.editsconserva las ediciones de la carta para que «Quitar diseño» las restaure.
- La carta no tiene imagen: la opción del menú está desactivada y el diálogo no se abre (
open={!!carta?.front}). - La carta desaparece mientras el diálogo está abierto (borrada, deshacer):
cartaPorIddevuelveundefinedy el diálogo se desmonta. - Una imagen de vista previa que no carga no se muestra y la última imagen buena sigue visible (
CargaUltimase salta las cargas fallidas); el diálogo no muestra ningún aviso de error. - El rango de zoom aquí es 1x-2x, más estrecho que el zoom de ilustración del diseñador (1x-3x); el propio modelo admite cualquier zoom >= 1.
src/features/card-editor/__tests__/propagar.test.ts: las copias son cartas con la misma imagen; una carta sin imagen no tiene copias; «solo esta carta» deja idénticos los demás objetos; «copias» copia todo solo a las cartas con la misma imagen; «todas» da la edición completa a esta carta y solo el color al resto; se conservan el orden y el número. No hay tests de componente para el diálogo (el comportamiento en vivo lo cubren src/lib/__tests__/imagen-viva.test.ts para las utilidades de carga y la ejecución de la app). Rust: los tests de renderizado y de ajustes de la tubería están en src-tauri/crates/proxy-core/src/imaging/ (Tests y CI).
- Retoque nuevo: añade un campo a
Adjustmentsenmodel.rs, impleméntalo enimaging/adjust.rs(ten en cuenta el orden fijo y los radios en mm), regenera los bindings conpnpm bindings, añádelo aRETOQUESencard-editor-dialog.tsxcon el texto fuente en español y su entrada en inglés ensrc/i18n/en/(lo compruebapnpm idiomas), y decide sifiltroAproximadodebe aproximarlo. - Para cambiar a qué cartas llega una edición: edita solo
propagar.tsy su test.
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