Repository navigation
Ajustes y shell
English · Español
El shell es el marco de la app: la barra superior (selector de vista, nombre del mazo, indicador de guardado, botón de actualización, botón de ayuda, botón de Ajustes), los tres paneles redimensionables de la vista de mazo y los hooks de arranque que abren el último mazo y arrancan el autoguardado. El diálogo de Ajustes contiene el idioma de la interfaz, las actualizaciones dentro de la app, el índice opcional de nombres multilingües y todo el sistema de apariencia. Esta página documenta ambos, qué hace cada ajuste y dónde se persiste.
Código: src/features/shell/ y src/features/settings/.
| Fichero | Responsabilidad |
|---|---|
app-shell.tsx |
AppShell: cabecera + área principal; NombreDeck (input en línea del nombre del mazo); monta <Tutorial /> y llama a useTutorialInicial() (Tutoriales); monta <ConfirmarCierre />; cambiarVista pregunta antes de salir de Diseñar con la carta sin guardar (más abajo); useEnlacesEntrantes (src/features/salas/enlaces-entrantes.ts) pasa a Jugar cuando llega y se acepta un enlace de invitación proxybuilder://unirse/<código> (con una partida en marcha se rechaza con un toast; Juego en línea). |
scroll-por-vista.ts |
useScrollPorVista(vista, raiz): recuerda el scroll de cada vista y lo devuelve al volver a ella (más abajo). |
confirmar-cierre.tsx |
ConfirmarCierre: la guarda que pregunta antes de cerrar la ventana cuando se perdería algo (más abajo). |
use-deck-inicial.ts |
useDeckInicial(): arranque del mazo, el autoguardado, el plan y las teclas de deshacer/rehacer. |
save-indicator.tsx |
SaveIndicator: «Guardado / Guardando… / Sin guardar / Error al guardar». |
actualizacion-automatica.tsx |
Bucle de actualización automática, toasts de actualización, BotonActualizar, PuntoActualizacion, useAvisoAjustes. |
De izquierda a derecha:
- Logo y título («Proxy Builder», subtítulo «Scryfall · listo para imprimir»).
- Un
ToggleGroupde selección única ligado auseUi().vista: Deck, Diseñar, Biblioteca, Jugar (iconoSwords, la mesa de juego; lleva una pequeña etiqueta «Beta»,t("Beta")enshell.json, mientras la mesa siga en beta).onValueChangesolo acepta estos cuatro valores (pulsar el elemento activo devuelve un valor vacío que se ignora, así que siempre hay uno seleccionado). -
NombreDeck(oculto en las vistas Biblioteca y Jugar): unInputque editadeck.name. Confirma al perder el foco o con Enter, Escape revierte; un valor vacío revierte; los cambios pasan poruseDeck.setSinHistorial({ name })(se guarda, no se puede deshacer). - A la derecha:
SaveIndicator,BotonActualizar, el botón de ayuda (botón de iconoCircleHelp, con tooltip yaria-label«Tutorial de esta pantalla»; llama auseUi().abrirTutorial(vista)con la pantalla actual, véase Tutoriales) y el botón de Ajustes.
Varios de estos elementos llevan un atributo data-tutorial (pestanas, nombre-deck, ayuda, ajustes): son las anclas que rodea el recorrido del deck.
SaveIndicator lee useDeck().guardado / errorGuardado; no renderiza nada hasta que hay un mazo abierto y, si hay error, envuelve la etiqueta en un tooltip con mensaje(error).
vista === "deck": ResizablePanelGroup horizontal con izquierda (lista, 22 % por defecto, mín. 260 px), centro (vista previa, mín. 360 px), derecha (exportar, 22 % por defecto, mín. 260 px) y dos asas con agarraderas. Los tamaños se persisten en localStorage["proxybuilder.paneles"] mediante useDefaultLayout. Las demás vistas son un simple cambio según vista: disenar, mesa y, para cualquier otro valor, biblioteca pintan la pantalla que les pasa App. mesa es MesaScreen (src/features/mesa/mesa-screen.tsx, Mesa de juego): pregunta una vez a Rust con mesa_estado y, hasta saberlo, no pinta nada; sin partida abierta enseña la entrada EntradaMesa y, si la hay, la mesa. Salir de la pestaña deja la partida abierta en Rust y la guarda (guardar() al desmontarse); al volver se retoma tal cual.
La entrada (src/features/mesa/entrada.tsx) tiene la lista de decks de la biblioteca (buscador, «Historial» de las partidas terminadas, el deck abierto más recientemente ya elegido), la tarjeta «Partida a medias» cuando hay una partida guardada («Seguir la partida» / «Descartar»), el nombre del jugador, el formato, la lista para jugar (obligatoria en Commander si el deck pasa de 100 cartas), las vidas, el mulligan, las ayudas del turno, la intensidad del sparring y «Tapete de este deck…», y cuatro formas de empezar: «Solitario» (pregunta antes si sustituiría la partida guardada), «Con amigos…» (servidor de salas), «Partida local…» y «En red…» («Con amigos…» y «En red…» pasan a «Volver a la sala» mientras haya una sala suya abierta). La ventana tiene un mínimo de 960 x 640 y un tamaño por defecto de 1440 x 900 (src-tauri/tauri.conf.json).
Cambiar de pestaña desmonta la vista anterior, así que antes se perdía su posición de scroll. AppShell llama a useScrollPorVista(vista, principal) con una referencia a <main>:
-
Apuntar. Un único listener de
scrollen fase de captura sobre<main>(scrollno sube por el DOM) guarda elscrollTopde cada viewport deScrollArea([data-slot="scroll-area-viewport"]) en unMap<Vista, Map<índice, top>>dentro de una referencia. La clave de cada zona de scroll es su orden entre los viewports de<main>; los diálogos van por portal fuera de<main>, así que no cuentan. -
Reponer. Al activarse una vista, un bucle de
requestAnimationFramepone cadascrollTopguardado en cuanto ese viewport es lo bastante alto (scrollHeight - clientHeight >= top), durante un máximo de 2 s (ESPERA_MS). Las listas y la vista previa se llenan después del primer pintado, así que ponerlo una sola vez al montar se quedaría en 0. Unawheel, unpointerdowno unkeydowndentro de<main>detiene el bucle, para no pelearse nunca con el usuario. -
Por qué no se mantienen montadas y ocultas.
display: nonetambién reinicia el scroll, y la vista previa y el diseñador seguirían trabajando por debajo (Vista previa en vivo, Interfaz del diseñador). - Límites. Solo en memoria (se pierde al cerrar la app). Como la clave es el orden, una zona de scroll que aparece o desaparece entre visitas (por ejemplo el formulario de Diseñar según haya o no carta elegida) puede cambiar qué posición recibe cada una.
AppShell pasa el cambio de pestaña por cambiarVista(v). Si la vista actual es disenar y useEdicion.getState().pendiente está puesto (la carta que se diseña tiene cambios sin guardar), el movimiento se aparca en salirDeDisenar y un segundo Confirmar («¿Descartar los cambios?» / «Hay cambios sin guardar en esta carta.») pregunta con tres salidas: Cancelar, Descartar (destructiva, ejecuta el movimiento aparcado) y Guardar y continuar (guarda con pendiente.guardar() y solo se mueve si devolvió true). Por qué existe: DisenarScreen se desmonta al mostrar otra vista, así que antes de esta guarda el borrador se perdía en silencio. Por qué el shell no sabe cómo se guarda una carta: DisenarScreen registra un callback guardar en useEdicion mientras tiene cambios (Stores de estado, Interfaz del diseñador) y el shell solo lo llama.
ConfirmarCierre registra una sola vez getCurrentWindow().onCloseRequested(...). Flujo de una petición de cierre:
flowchart TD
A["Petición de cierre"] --> B{"¿Diálogo ya abierto?"}
B -->|"sí"| C["preventDefault, nada más"]
B -->|"no"| D["guardarAhora(): vuelca el mazo"]
D --> E["pendientes(deckSinGuardar) en lib/cierre.ts"]
E --> F{"¿hayPendientes?"}
F -->|"no"| G["return sin preventDefault: el envoltorio de Tauri llama a destroy(), la ventana se cierra"]
F -->|"sí"| H["preventDefault + Confirmar: ¿Cerrar Proxy Builder?"]
H --> I["Cancelar: se queda, vuelve a preguntar en el siguiente intento"]
H --> J["Cerrar igualmente: cancelarTrabajo si hay uno, guarda el mazo, destroy()"]
H --> K["Guardar y cerrar (solo con la carta sin guardar): useEdicion.pendiente.guardar() y cierra; si guardar falla, el diálogo y la ventana se quedan"]
Decisiones e invariantes:
-
Primero se vuelca el mazo. El autoguardado espera 800 ms tras el último cambio (Stores de estado), así que un cambio hecho justo antes de cerrar se perdía.
guardarAhora()corre en cada petición de cierre; si lanza, «No se ha podido guardar el deck: se perderán sus últimos cambios.» pasa a ser una razón más para preguntar (pendientes(true)). -
Razones (
src/lib/cierre.ts,pendientes()/hayPendientes(); cada una es un campo dePendientesy el diálogo lista solo las que se cumplen): un trabajo largo (useTrabajo.actual, con el texto de su fase: resolver, exportar, idioma), una actualización bajando o instalando (useActualizacion.pasoesbajandooinstalando; una actualización solo disponible o ya lista no cuenta), un diálogo abierto con un proceso en marcha o texto sin guardar (lib/dialogos-abiertos.ts, más abajo), la carta que se diseña con cambios sin guardar (useEdicion.pendiente), el mazo que no se pudo guardar y una partida abierta en la mesa (useMesa.activa, motivo «Hay una partida en la mesa: se guarda y podrás seguirla al volver.»). -
La partida se guarda en cada petición de cierre. Justo después de guardar el mazo, el manejador llama a
guardar()desrc/features/mesa/conexion.ts(mesa_guardar), y «Cerrar igualmente» lo vuelve a llamar antes dedestroy(). Una partida abierta sigue contando como razón para que quien juega sepa que la tendrá esperando en «Jugar» (Mesa de juego). -
Sin ninguna razón no sale diálogo. El manejador vuelve sin
preventDefault()y la ventana se cierra como siempre. -
«Cerrar igualmente» es destructiva: cancela el trabajo en marcha con
cancelarTrabajo()(export_cancelojob_cancel), intenta guardar el mazo una vez más y llama adestroy(). Las exportaciones escriben de forma atómica, así que cerrar a mitad de una exportación nunca deja un PDF a medias con su nombre final. Sidestroy()falla, un toast dice «No se ha podido cerrar la ventana.». -
Un segundo intento de cierre con el diálogo abierto se ignora (ref
abierto): se le hacepreventDefaulty no se apila otro diálogo. -
Fuera de Tauri (la vista abierta en un navegador durante el desarrollo)
getCurrentWindow()lanza y se captura: no hay ventana que guardar. - 🛑
destroy()necesita un permiso. Con un escuchante de JavaScript enonCloseRequested, Tauri ya no cierra la ventana por su cuenta; lo hacedestroy()(el envoltorio o el manejador). Ese comando escore:window:allow-destroy, que no forma parte decore:defaultni decore:window:default, así quesrc-tauri/capabilities/default.jsonlo lista expresamente (véase Capa de aplicación Rust). Sin él la ventana no se podría cerrar; la compilación valida el identificador. -
Esta guarda no bloquea al actualizador. «Reiniciar y actualizar» guarda el mazo por su cuenta (
guardarAhora()) y reinicia desde Rust (request_restart), que no pasa por la petición de cierre de la ventana. Deducido: del comportamiento de Tauri; no se ha probado de extremo a extremo. - No cubierto. Deducido: en macOS, Cmd+Q o «Salir» desde el Dock puede que no emitan una petición de cierre de ventana, y entonces la guarda no correría.
Algunos diálogos tienen un proceso largo o un estado sin guardar propio. Se lo dicen a la guarda de cierre con useEstadoParaCerrar({ enCurso, sinGuardar }), llamado con los mismos valores que el componente ya pasa a DialogContent como ocupado / cambiosSinGuardar. El hook guarda los últimos valores en un ref y registra un lector en un Set del módulo (registrarDialogo, que devuelve la función que lo quita); estadoDeLosDialogos() junta con OR todos los lectores. Como quien registra es el Contenido del diálogo, que solo existe mientras está abierto, el registro vive justo lo que el diálogo.
Por qué un hook por diálogo y no dentro de DialogContent: src/components/ui/ es de solo lectura (heredado del sistema de diseño en que se basa la app), así que dialog.tsx no se toca.
| Diálogo | Fichero | enCurso |
sinGuardar |
|---|---|---|---|
| Diseñar el deck | src/features/disenar/lote-dialog.tsx |
en marcha | no |
| Exportar imágenes | src/features/disenar/png-dialog.tsx |
en marcha | no |
| Vincular en bloque | src/features/variants/vincular-lote.tsx |
trabajando | revisando filas (fase === "revisar" con filas) |
| Colecciones | src/features/disenar/colecciones-dialog.tsx |
no | cambiada |
A los diálogos sin el hook no se les pregunta. Deducido: solo tienen la regla del texto escrito de DialogContent para cerrarse ellos, no para cerrar la app.
Tests: src/lib/__tests__/cierre.test.ts (Tests y CI).
-
iniciarAutoguardado()einiciarPlan()(véase Stores de estado). - Un
useDeck.subscribeque, cada vez que cambia el id del mazo abierto, llama arehacerMiniaturas()(src/features/disenar/renumerar.ts) para volver a renderizar las miniaturas de diseño que han quedado obsoletas por un cambio de colección, de letra o de motor. -
abrirDeckInicial(t("Deck sin título")): reabrelast_deck_ido crea un mazo nuevo. El error devuelto (si lo hay) se guarda en el estado del hook. - Por separado, un listener de
keydownparaCtrl/Cmd+Z(deshacer) y su variante conShift(rehacer), desactivado mientras se escribe en un input, textarea ocontenteditable.
Decisión: la ventana se crea oculta con un color de fondo oscuro y se muestra lo antes posible, para evitar un destello blanco.
-
tauri.conf.json:"visible": false,"backgroundColor": "#1d1b19". -
src-tauri/src/lib.rs: el plugin window-state guarda tamaño/posición/estado maximizado de todos los flags salvoVISIBLE(si no, el estado guardado podría mostrar la ventana antes de que se haya pintado).on_page_loadmuestra la ventana enPageLoadEvent::Finished; un hilo en segundo plano la muestra tras 3 s si sigue oculta (la página nunca cargó). -
src/main.tsxtambién llama agetCurrentWindow().show()justo después derender(no espera a un fotograma: WebKitGTK no entregarequestAnimationFramea las ventanas ocultas), yponerTemaAntesDeMontar()ya había puesto los colores correctos, así que no hay destello claro. Véanse Arquitectura del frontend y Temas y skins.
Lo abre el botón de Ajustes (AppShell guarda open en estado local). DialogContent es sm:max-w-3xl con cambiosSinGuardar={false} (no hay nada que perder al cerrar). La cabecera muestra la versión leída una vez con getVersion() de @tauri-apps/api/app («Proxy Builder v2.5.3») y, cuando hay una actualización descargada, el botón ReiniciarParaActualizar.
Cuerpo, de arriba abajo:
| Sección | Componente | Efecto | Persistencia |
|---|---|---|---|
| Idioma |
SelectorIdioma (src/components/selector-idioma.tsx): un Select con la bandera y el nombre propio de cada idioma, sobre los 15 idiomas de IDIOMAS (src/lib/idiomas.ts) |
Idioma de la interfaz, sin recarga. Primero se carga el diccionario nuevo y el cambio llega cuando está, así que la pantalla nunca enseña textos a medio traducir. |
localStorage["proxybuilder.lang"]; véase Internacionalización. |
| Actualizaciones |
Actualizaciones (actualizaciones.tsx) |
Buscar ahora, descargar, reiniciar; el interruptor de actualización automática. | Interruptor: useUi().actualizarSola en proxybuilder.preferencias. La máquina de estados, en memoria. |
| Nombres en otros idiomas |
NombresIdiomas (nombres-idiomas.tsx) |
Descarga opcional del índice de nombres de MTGJSON. | En disco, por Rust (véase abajo). |
| Apariencia |
Appearance (appearance.tsx) |
Modo, escala, scroll suave, skin, filigrana, fondo, material, efectos, tema de color, «Más temas…». | Claves de localStorage listadas en Temas y skins. |
| Mesa de juego (cerrada al principio) |
AjustesMesa (mesa.tsx) |
Seis grupos, más abajo. |
useUi().mesa en proxybuilder.preferencias, salvo el aspecto de los dados (Rust) y la identidad en red (Rust); véase Mesa de juego. |
- Llama a
useActualizacion.iniciar()al montar. Sisoportada === falsemuestra «Solo en la app instalada: esta copia no viene de un instalador (por ejemplo, en desarrollo).» y nada más. - «Buscar ahora» (
buscar) está desactivado mientras está ocupado; si se encuentra una versión y la actualización automática está activada, la descarga al instante (como al arrancar). - Cuando hay una versión disponible y la automática está desactivada: un botón «Descargar la {version}». Cuando está descargada (
lista/instalando): botón «Reiniciar y actualizar» (descarta el toast"actualizacion", llama ainstalar()y muestra el error en un toast si falla). - Aparece una barra
Progressdurante la descarga; el último error se imprime conmensaje(error); las notas de la release (texto Markdown, en inglés) aparecen en unScrollAreacuando existen; «Tienes la última versión ({version}).» cuando está al día. - La línea de estado dice que, en las instalaciones
.deb/.rpm, el sistema pedirá la contraseña al reiniciar (ActualizacionInfo.pidePermiso).
Los detalles de la máquina de estados están en Stores de estado; el actualizador de Rust, las comprobaciones de firma y el formato de las releases, en Versiones y actualizaciones automáticas. La instalación se rechaza con Busy mientras corre un trabajo largo (el mazo se guarda antes y el paso vuelve a lista). Con una partida abierta en la mesa la pantalla ni lo pide: la instalación queda en cola (alSalirDeLaMesa, toast «Se instalará al salir de la mesa.») y se hace al salir de la mesa; Rust la sigue rechazando con Busy motivo: partida como red de seguridad. Buscar y bajar no se bloquean.
El mismo SelectorIdioma elige el idioma de diseño en el panel de exportar y en la vista previa de Diseñar (src/features/export/export-panel.tsx, src/features/disenar/vista-previa.tsx): los idiomas de la interfaz, del diseño y del deck comparten los códigos de Scryfall de src/lib/idiomas.ts, así que un solo componente sirve para los tres. Las banderas son imágenes de Noto Color Emoji (Bandera en src/components/emoji.tsx), no la letra de emoji del sistema, porque la Segoe UI Emoji de Windows no tiene banderas; véase Arquitectura del frontend.
Descarga opcional (aproximadamente del tamaño que muestra el botón, «Descargar (unos {mb} MB)») del índice de nombres por idioma de MTGJSON, guardado como SQLite por Rust, de modo que las cartas escritas por su nombre impreso en español (u otro idioma) que Scryfall no tiene se puedan encontrar en local, tanto en la lista como al vincular. Sin él, la app pregunta en línea a magicthegathering.io, que es más lento. Comandos: names_index_info, names_index_download (evento de progreso names://progress con fases; durante la fase de «lectura» la barra está llena y el texto dice «Preparando el índice…»), names_index_remove. Muestra el número de nombres descargados. Véanse Fuentes externas e Identificación de cartas y OCR.
Documentada control por control en Temas y skins (modo, tamaño de 70 a 150 %, scroll suave, Plano/Zeus/Hera, filigrana, fondo, material, desenfoque/grano/sombra, «Incluye los paneles», cuadrícula de temas, «Más temas…»).
Scroll suave (proxybuilder.smoothScroll, activado por defecto) monta SmoothScroll (src/lib/SmoothScroll.ts) mediante SmoothScrollClient: la rueda anima el scrollTop nativo del ancestro con scroll más cercano. Al desactivarlo se destruye la instancia.
Cada control escribe un campo de useUi().mesa (PreferenciasMesa en src/stores/ui-store.ts, valores de fábrica en MESA_DE_FABRICA) con setMesa, salvo que se diga otra cosa. Al leerlas, las preferencias guardadas se funden sobre MESA_DE_FABRICA, así que un campo añadido después toma su valor de fábrica.
| Grupo | Controles (campo) |
|---|---|
| «Jugar» | Ofrecer el bono de auras y equipos al adjuntarlos (ofrecerBono, sí); ver la vida del fantasma en solitario (fantasma, sí); en red, ver cómo mueven los demás y mostrarles mi cursor (movimientoEnVivo, sí); en partida local, pasar a mirar al jugador del turno (seguirTurnoLocal, no); intensidad del sparring al empezar en solitario (sparring, apagado; la entrada tiene el mismo selector); mantener pulsado abre el menú de la carta (clicSostenido, no). |
| «Tablero» | Tamaño de las cartas del campo (tamanoCartas, de 25 a 200 %, 100); tamaño de la vista grande (vistaGrande, de 200 a 500 px, 320); panel de estado junto a la vista grande (panelEstado, sí). |
«Dados y monedas» (aspecto-dados.tsx, AjustesDados) |
El aspecto de tus dados y tu moneda, con una muestra en CSS 3D que se tira con «Probar» (con sonido): color del dado (el del jugador o uno propio), color de los números, acabado (liso, nacarado, marmol, purpurina, translucido), una imagen propia en cada cara (sustituye al color y al acabado) y el metal de la moneda (oro, plata, cobre). No está en useUi: useAspectoDados (src/features/mesa/dados/aspecto.ts) lo lee y lo escribe con mesa_dados_get, mesa_dados_set y mesa_dados_imagen; Rust lo guarda en app_settings, clave mesa.dados, y la imagen en el almacén de blobs (src-tauri/src/mesa/dados.rs). Los cambios se mandan 250 ms después del último. |
| «Sonidos» | Un interruptor por grupo de sonidos: dados y monedas (sonidoDados), cartas: barajar, robar, girar, jugar (sonidoCartas), pasar el turno y cuando es el tuyo (sonidoTurno), mensajes y fantasma del sparring (sonidoChat), todos encendidos; un volumen para todos (volumen, de 0 a 100 %, 60). |
«Identidad en red» (identidad-red.tsx) |
Exportar la identidad en red de la instalación a un fichero JSON o importar otra (pregunta antes; la de ahora se queda como copia en la carpeta de datos de la app). Lo hace Rust (red_identidad_exportar, red_identidad_importar); importar se rechaza con Busy mientras haya una partida o una sala abiertas. No es una preferencia. |
| «Rendimiento» | Calidad de las imágenes del campo (calidad: 146, 244 o 488 px de ancho, 244); dados y monedas (dados: auto, 3d con física, css 3D ligero, 2d; auto); reducir animaciones (reducirAnimaciones, no); sombras (sombras, sí); contador de fotogramas (fps, no); «Modo rendimiento (todo al mínimo)» aplica MODO_RENDIMIENTO (src/features/mesa/fps.tsx: calidad 146, sin sombras, animaciones reducidas, sin panel de estado). |
PreferenciasMesa guarda además campos sin control aquí: sparringVisto (ya salió la tarjeta de «Cómo funciona» del sparring), trackers (los contadores de jugador que se ven junto a la vida) y disposicion (el reparto de los tableros, recordado por número de jugadores), que se cambian desde la propia mesa.
| Pieza | Comportamiento |
|---|---|
useActualizacionAutomatica() |
Solo cuando useUi().actualizarSola es verdadero (por defecto). Tras 8 s (PRIMERA_MS) y después cada 6 h (CADA_MS): iniciar(); si no está soportada, se detiene; buscar(); si existe una versión, bajar(). Los fallos de red o la ausencia de release son silenciosos. Los temporizadores se limpian al desactivar el interruptor. |
useAvisosActualizacion() |
Un único useActualizacion.subscribe, montado una vez en App: un toast de carga «Descargando Proxy Builder {version}…» con el porcentaje como descripción mientras bajando; un toast de éxito persistente «Proxy Builder {version} está lista» con una acción Reiniciar cuando termina la descarga (una vez por versión); un toast de error «No se ha podido descargar la actualización.» con el error traducido si falla la descarga. Todos usan el id de toast "actualizacion" para sustituirse entre sí. |
BotonActualizar |
Botón outline de la barra superior «Actualizar a {version}», visible solo en lista/instalando; spinner mientras se instala; el tooltip dice si el sistema pedirá una contraseña. |
PuntoActualizacion |
Puntito sobre el botón de Ajustes: ámbar y pulsante mientras bajando; del color primario del tema en lista/instalando. |
useAvisoAjustes() |
Texto de tooltip/aria para el botón de Ajustes mientras hay una actualización en curso («Descargando la {version}…» / «La {version} está lista: reinicia desde Ajustes.»). |
Los textos de los toasts leen el idioma actual a través de una ref (textos.current = lang), así que un cambio de idioma durante la descarga se respeta sin volver a suscribirse.
| Ajuste | Ubicación en la UI | Guardado en |
|---|---|---|
| Idioma de la interfaz | Ajustes |
localStorage proxybuilder.lang
|
| Tema, modo, skin, fondo, material, desenfoque, grano, sombra, escala, flags, scroll suave | Ajustes > Apariencia |
localStorage proxybuilder.theme / .mode / .skin / .bg / .material / .blur / .noise / .shadow / .scale / .fxSidebar / .heraFiligree / .smoothScroll
|
| Temas de tweakcn instalados | Ajustes > Más temas | Ficheros de Rust en la carpeta de datos de la app temas/, con espejo en proxybuilder.temasInstalados
|
| Actualización automática | Ajustes > Actualizaciones |
useUi().actualizarSola (proxybuilder.preferencias) |
| Ajustes de la mesa de juego | Ajustes > Mesa de juego |
useUi().mesa (proxybuilder.preferencias) |
| Aspecto de los dados y la moneda | Ajustes > Mesa de juego > «Dados y monedas» |
app_settings de Rust, clave mesa.dados (la imagen propia, en el almacén de blobs) |
| Nombre del jugador en la mesa | Entrada de la pestaña Jugar |
localStorage proxybuilder.mesa.nombre
|
| Tapete de la mesa (por deck y general) | Pestaña Jugar: «Tapete de este deck…» y la barra de la mesa |
app_settings de Rust, claves mesa.tapete.<id del deck> y mesa.tapete (blob protegido en blob_refs) |
| Pantallas cuyo tutorial ya salió | automático (primera visita) |
useUi().tutorialesVistos (proxybuilder.preferencias) |
| Índice de nombres de MTGJSON | Ajustes | Rust (fichero descargado, índice SQLite) |
| Idioma de las cartas, política de imagen, añadir fichas, calidad de la vista previa, preguntar antes de sustituir | Panel de la lista > Opciones |
useUi() (proxybuilder.preferencias) |
| Zoom de la vista previa, superposición de info de carta | Barra de herramientas de la vista previa |
useUi() (proxybuilder.preferencias); «Ver reversos» no se persiste |
| Anchos de los paneles | arrastrar las asas |
localStorage proxybuilder.paneles, .panel-lista, .paneles.disenar
|
| Sección abierta/cerrada | cabeceras de sección |
localStorage proxybuilder.seccion.<id>
|
| Vista, orden, agrupación y tamaño de la biblioteca | Barra de herramientas de la biblioteca |
localStorage proxybuilder.biblioteca (los filtros no se guardan) |
| Último mazo abierto | automático | Rust, clave de app_settings last_deck_id
|
| Última carpeta de importar / última carpeta de exportar | automático | Rust, claves de app_settings carpeta_importar, carpeta_exportar
|
| Tamaño, posición y maximizado de la ventana | automático |
tauri-plugin-window-state (la visibilidad, excluida) |
| Ajustes de impresión (tamaño de página, tamaño de carta, márgenes, sangrado, guías de corte, etc.) y presets de exportación | Panel de exportar | Dentro del mazo (Deck.settings, se guarda con el mazo); presets en Rust (comandos preset_*). Véase Exportación. |
Valores de fábrica que conviene conocer (son decisiones de producto, no accidentes): idioma de la interfaz inglés; tema Amber Minimal en modo oscuro con material mate; zoom de la vista previa 80 %; calidad de la vista previa 488 px; «preguntar antes de sustituir cartas» activado; actualización automática activada. Los valores por defecto de impresión (2 mm de separación, sangrado 0, guías de corte desactivadas, «Rellenar las esquinas» desactivado) viven en el código de layout/exportación, véase Motor de maquetación.
Todos los diálogos de ficheros pasan por src/lib/dialogos.ts; nunca llames directamente a @tauri-apps/plugin-dialog.
| Función | Uso |
|---|---|
abrirParaImportar(opciones) |
Diálogo de abrir que empieza en la última carpeta de importar; devuelve las rutas (array) o null; recuerda la carpeta de la primera ruta. |
guardarParaExportar(opciones, nombre) |
Diálogo de guardar que empieza en la última carpeta de exportar con nombre como nombre de fichero propuesto. |
elegirCarpetaParaExportar() |
Selector de directorio (exportación de imágenes sueltas); también recuerda la carpeta de exportar. |
recordar(tipo, ruta) |
Recordar sin esperar al resultado; se usa cuando se arrastran ficheros sobre la app. |
Mecánica: carpeta_inicial({ tipo, nombre }) (Rust, src-tauri/src/commands/carpetas.rs) devuelve la carpeta guardada si todavía existe (unida con nombre si se da); si no, nombre o null. carpeta_recordar({ tipo, ruta }) guarda el directorio de la ruta (la propia ruta si es un directorio; si no, su carpeta padre) bajo carpeta_importar o carpeta_exportar en app_settings. TipoCarpeta es "importar" | "exportar" (tipo generado). Las carpetas de importar y de exportar son separadas a propósito, para que exportar un PDF al Escritorio no redirija la siguiente importación de imágenes. Los fallos de cualquiera de los dos comandos se tragan (el diálogo simplemente se abre en su ubicación por defecto). Lo usan features/decklist/acciones.ts, features/export/acciones.ts, features/export/menu-exportar.tsx, features/library/library-screen.tsx y los diálogos del diseñador. Tests: src/lib/__tests__/dialogos.test.ts y el test unitario la_carpeta_de_una_ruta de commands/carpetas.rs.
No hay ninguna sección de «almacenamiento» en el diálogo de Ajustes. Dos acciones de mantenimiento viven donde son relevantes:
-
Limpiar la caché de miniaturas («Limpiar caché de miniaturas»): en el panel de la lista (
decklist-panel.tsx), un diálogo de confirmación muestra el tamaño y el número de ficheros decache_statsy luegocache_clearvacía solo la caché de renderizados derivados; las imágenes de los mazos nunca se tocan. -
Liberar las imágenes sin usar («Liberar espacio»): en la pantalla de la biblioteca,
blob_gcelimina los blobs que ningún mazo referencia (Rust conserva los recientes durante 24 h); el toast informa del número y de los megabytes.
Motivo: las imágenes de los mazos viven en el almacén de blobs (son datos), la caché solo guarda derivados y siempre se puede borrar. Véase Almacenamiento y blobs.
| Situación | Comportamiento |
|---|---|
localStorage bloqueado o corrupto |
Toda lectura/escritura va en try/catch; se aplican los valores por defecto durante la sesión. |
Falla getVersion()
|
Simplemente no se muestra la etiqueta de versión. |
| Falla la comprobación de actualización (sin conexión, sin release, sin paquete para este instalador) | Silencioso en segundo plano; el «Buscar ahora» manual muestra el error traducido (Network, NotFound...). |
| Falla la instalación (p. ej. se cancela la petición de contraseña) | El estado vuelve a lista, toast de error; se puede reintentar sin volver a descargar. |
| Actualizar mientras se exporta un PDF o hay una partida abierta en la mesa |
update_install devuelve Busy (con una partida, motivo: partida); la pantalla no lo pide mientras la mesa está abierta: la instalación queda en cola y se hace al salir de la mesa. |
| Cerrar la ventana con un trabajo, una descarga, una carta sin guardar, un diálogo ocupado, el mazo sin poder guardarse o una partida abierta |
ConfirmarCierre pregunta; «Cerrar igualmente» cancela el trabajo, guarda la partida y cierra. |
| Cerrar la ventana sin nada pendiente | Se vuelca el mazo y la ventana se cierra sin diálogo. |
destroy() falla |
Toast «No se ha podido cerrar la ventana.». |
| El tema instalado seleccionado ya no existe |
SincronizarTemas restablece Amber Minimal. |
- Ajuste nuevo: si afecta al aspecto, añádelo solo en código adyacente a
ThemeProvidermediante CSS nuevo/tema-inicial.ts(el propio proveedor es de solo lectura); si es una preferencia de lista/vista previa, añádela aPreferenciasdeuseUi(Stores de estado); si el backend debe conocerla entre sesiones, usaapp_setting_get/seto un comando dedicado. - Sección nueva de Ajustes: añade un componente de tarjeta en
src/features/settings/y renderízalo enSettingsDialog; el texto, port(); el scroll ya lo aportaDialogCuerpo.
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