Skip to content

Stores de estado

Pablo Martínez López edited this page Oct 29, 2026 · 10 revisions

Stores de estado

English · Español

Todo el estado del frontend vive en stores de Zustand. Siete stores globales en src/stores/ cubren el mazo abierto, el plan de páginas, el estado de pantalla y las preferencias, el trabajo largo en marcha, la máquina de estados de la actualización de la app, la carta que se diseña con cambios sin guardar y la vista de la mesa de juego; unos cuantos stores de área viven junto a su feature. Los stores nunca calculan maquetación ni hacen trabajo pesado: guardan lo que devolvió Rust, llaman a Rust mediante call() y exponen acciones a los componentes.

Visión general

Store (hook) Fichero Persistencia Papel en una línea
useDeck src/stores/deck-store.ts Rust (SQLite) mediante deck_save, autoguardado con debounce El mazo abierto, el historial de deshacer/rehacer, el estado de guardado.
usePlan src/stores/plan-store.ts ninguna (derivado) El PagePlan que calcula Rust para el mazo abierto.
useUi src/stores/ui-store.ts clave de localStorage proxybuilder.preferencias (solo el subconjunto de preferencias) Vista, zoom, selección, diálogo abierto, tutorial abierto, última acción, preferencias de la lista.
useTrabajo src/stores/trabajo-store.ts ninguna El único trabajo largo en curso (resolver una lista, exportar un PDF, completar un idioma de diseño o preparar la mesa de juego).
useActualizacion src/stores/actualizacion-store.ts ninguna Máquina de estados de la actualización dentro de la app.
useEdicion src/stores/edicion-store.ts ninguna La carta que se diseña, cuando tiene cambios sin guardar, y cómo guardarla.
useMesa src/stores/mesa-store.ts ninguna (la partida vive en Rust) La vista de la mesa de juego tal como la manda Rust, parcheada en el sitio, y lo que es solo de pantalla: selección, carta bajo el ratón, inspección y registro.
usePantalla src/features/mesa/pantalla.ts ninguna Estado de pantalla de la mesa: el diálogo de la mesa abierto, el modo de elegir objetivo, el arrastre en curso y la zona bajo el ratón.
useTemasInstalados src/lib/temas-instalados.ts clave de localStorage proxybuilder.temasInstalados (espejo) Temas de tweakcn instalados; véase Temas y skins.
useIconos src/stores/iconos-store.ts ninguna (Rust guarda los elegidos en disco) Datos de los iconos de Iconify que no van incluidos, pedidos por tandas con iconos_datos; véase Iconos.
useBiblioteca, usePrefsBiblioteca, useFiltrosBiblioteca src/features/library/store.ts, preferencias.ts Rust para los datos; proxybuilder.biblioteca para las preferencias de vista; los filtros no se persisten Datos y vista de la biblioteca; véase Biblioteca de mazos.
useMarcosOriginales, usePacksDescargables src/features/disenar/marcos-originales.ts, packs-descargables.ts ninguna Estado y progreso de descarga de los packs de marcos descargables; véase Packs de marcos.

El estado del tema y del idioma no es de Zustand: son contextos de React (ThemeProvider, LanguageProvider) respaldados por localStorage; véanse Temas y skins e Internacionalización.

Flujo de datos

flowchart LR
    UI["Componentes"] -->|"aplicar / setCartas / setAjustes"| DS["useDeck (deck-store)"]
    DS -->|"subscribe: cambió version"| AG["iniciarAutoguardado (800 ms)"]
    AG -->|"call deck_save"| R["Rust: SQLite + blobs"]
    DS -->|"subscribe: cambió cards / settings / defaultBack"| PS["iniciarPlan (100 ms)"]
    PS -->|"call compute_page_plan"| R
    R -->|"PagePlan"| PL["usePlan"]
    PL --> PV["PreviewPane / pie"]
    UI -->|"seleccionar, abrir, setZoom"| UIS["useUi"]
    UIS -->|"localStorage proxybuilder.preferencias"| LS[("localStorage")]
    UI -->|"empezar / avanzar / terminar"| TS["useTrabajo"]
    R -->|"eventos resolve:// export://"| LT["lanzarTrabajo (lib/trabajos.ts)"]
    LT --> TS
    UPD["useActualizacion"] -->|"call update_check / update_download / update_install"| R
    UPD -->|"guardarAhora antes de instalar"| DS
    DSG["DisenarScreen"] -->|"poner({ guardar }) mientras hay cambios sin guardar"| ED["useEdicion"]
    ED -->|"pendiente.guardar()"| DSG
    CC["ConfirmarCierre / AppShell"] -->|"pendientes() lee trabajo, paso, edicion"| TS
    CC --> UPD
    CC --> ED
    CC -->|"partida: useMesa.activa"| MS
    R -->|"Channel MensajeMesa: Vista / Parche"| MS["useMesa (mesa-store)"]
    UI -->|"hacer(accion): call mesa_accion"| R
    CC -->|"guardarAhora al cerrar"| DS
Loading

Hay dos bucles derivados que se arrancan una sola vez, desde useDeckInicial() (src/features/shell/use-deck-inicial.ts): iniciarAutoguardado() e iniciarPlan(). Ambos se suscriben a useDeck fuera de React y devuelven una función de parada que se usa al desmontar.

useDeck: el mazo abierto (src/stores/deck-store.ts)

Forma: { deck: Deck | null, guardado: EstadoGuardado, errorGuardado, version: number, pasado: Instantanea[], futuro: Instantanea[] } más las acciones.

Deck es el tipo generado (src/bindings/Deck.ts): id, name, decklistText, cards: DeckCard[], settings: PrintSettings, defaultBack, schemaVersion (detalles del modelo en Modelo del mazo).

EstadoGuardado = "saved" | "saving" | "dirty" | "error" (lo pinta src/features/shell/save-indicator.tsx, con las etiquetas «Guardado / Guardando… / Sin guardar / Error al guardar»).

Acciones

Acción Comportamiento
cargar(deck) Sustituye el mazo y restablece guardado: "saved", pasado/futuro: [], version: 0.
aplicar(cambio) La única vía de mutación con historial. cambio(deck) devuelve un Partial<Deck>; la Instantanea anterior (cards, settings, defaultBack) se empuja a pasado (con tope MAX_HISTORIAL = 100), futuro se vacía, guardado = "dirty", version++.
setCartas(f), setAjustes(f) Envoltorios de conveniencia sobre aplicar para la lista de cartas y para PrintSettings.
setSinHistorial({ name?, decklistText? }) Cambios que se guardan pero no se pueden deshacer (el nombre del mazo, el texto de la lista). Incrementa version, marca como sucio, sin instantánea.
refrescarCartas(antes, nuevas) Para actualizaciones de cartas iniciadas por el sistema que no son ediciones del usuario (miniaturas de diseño vueltas a renderizar, rehacerMiniaturas en src/features/disenar/renumerar.ts). Sustituye solo las cartas cuyo objeto actual sigue siendo la misma referencia que en antes, de modo que una edición del usuario hecha mientras tanto gana. Sin historial; marca como sucio.
deshacer() / rehacer() Sacan de pasado / de futuro (por delante), intercambiando con la instantánea actual; marcan como sucio. No tocan name ni decklistText.
_marcarGuardado(estado, error?) Interna: la usan el autoguardado y guardarAhora.

Invariantes:

  • Nunca se llama a deck_save desde una pantalla. Toda la persistencia pasa por iniciarAutoguardado() (o guardarAhora() justo antes de abandonar el mazo).
  • Todo cambio debe pasar por aplicar/setSinHistorial/refrescarCartas para que version se incremente; los dos bucles derivados se guían por ella.
  • El historial guarda referencias a arrays cards completos (compartición estructural), no copias profundas, así que 100 entradas son baratas. Por tanto, las ediciones deben ser inmutables (usa los helpers puros de src/lib/deck-ops.ts: mover, moverGrupo, duplicar, quitar, insertar, actualizar, huecos).

Autoguardado: iniciarAutoguardado(retardoMs = 800)

  1. Se suscribe a useDeck; cuando version cambió y guardado === "dirty", (re)inicia un temporizador de 800 ms (debounce).
  2. Al dispararse, guardar() lee { deck, version }; si ya hay un guardado en vuelo, pone pendiente = true y retorna (solo hay un deck_save en vuelo).
  3. Pone "saving", hace await call("deck_save", { deck }) y pone "saved" solo si useDeck.getState().version === version (no llegó ningún cambio durante el guardado); si no, "dirty".
  4. Si falla: "error", con el error guardado en errorGuardado (el tooltip del indicador muestra mensaje(error)). El reintento ocurre con el siguiente cambio (el suscriptor necesita guardado === "dirty", que pone aplicar); no hay bucle de reintento automático.
  5. En finally: si pendiente o el estado es "dirty", programa otra ronda con la versión más reciente.

Helpers relacionados en el mismo fichero:

Función Uso
abrirDeckInicial(nombreNuevo) Arranque: lee el ajuste de la app last_deck_id (app_setting_get), intenta deck_open, recurre a deck_create, escribe last_deck_id y luego cargar. Un id obsoleto (mazo borrado) recurre en silencio a un mazo nuevo.
guardarAhora() Vuelca al instante los cambios pendientes (deck_save, y luego "saved" si la versión no se movió). Se usa antes de cambiar de mazo (abrirDeck), antes de cargar la biblioteca (useBiblioteca.cargar), antes de instalar una actualización y en cada petición de cierre de la ventana (ConfirmarCierre, para que no se pierda un cambio hecho dentro de los 800 ms de espera del autoguardado; si falla, pasa a ser una razón para preguntar, véase Ajustes y shell).
abrirDeck(id) guardarAhora() y luego deck_open, recuerda last_deck_id, cargar.

Los atajos de deshacer (Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z) se registran en useDeckInicial y se saltan los inputs/textareas/contenteditable con foco para que los campos de texto conserven su propio deshacer.

Lado de la persistencia: deck_save es el comando de Rust de src-tauri/src/commands/library.rs que devuelve SaveResult { updatedAt }; el almacenamiento se describe en Almacenamiento y blobs.

usePlan: el plan de páginas (src/stores/plan-store.ts)

Forma: { plan: PagePlan | null, error: unknown, deDeck: Deck | null }. deDeck es el objeto de mazo exacto a partir del cual se calculó el plan, para que los consumidores puedan asociar el índice de una celda con la carta que existía cuando Rust hizo el cálculo.

iniciarPlan(retardoMs = 100):

  • Se suscribe a useDeck. Si el objeto del mazo es el mismo, no hace nada. Si cambió el id del mazo o pasó a null/no nulo, pide el plan de inmediato (programar(true)). En otro caso lo pide tras 100 ms solo si cards, settings o defaultBack cambiaron por referencia (el nombre y el texto de la lista no afectan a la maquetación).
  • Un contador creciente peticion implementa «gana la última petición»: una respuesta tardía de una petición anterior se descarta. Si hay error, solo se fija error; el plan anterior sigue visible.
  • Comando de Rust: compute_page_plan({ deck }) (src-tauri/src/commands/layout.rs); la geometría está en Motor de maquetación. Nada en pantalla recalcula posiciones; PagePlan va en milímetros y la vista previa los multiplica por el zoom (véase Vista previa en vivo).

useUi: estado de pantalla y preferencias (src/stores/ui-store.ts)

Dos grupos de campos en un mismo store.

Solo de sesión (no se persisten):

Campo Significado
vista: "deck" | "biblioteca" | "disenar" | "mesa" / setVista Qué vista principal muestra AppShell.
disenando: string | null / disenar(id) Id de la carta que se está diseñando; disenar(id) también cambia vista a "disenar".
verReversos: boolean / setVerReversos La vista previa muestra los reversos de las cartas.
tutorial: Vista | null / abrirTutorial(v) La pantalla cuyo tutorial está abierto. abrirTutorial(v) además añade v a tutorialesVistos (una vez) y guarda; abrirTutorial(null) lo cierra. Véase Tutoriales.
seleccion: string[], ancla: string | null / seleccionar(ids, ancla?) Ids de las cartas seleccionadas y la última carta pulsada (ancla para los rangos con Shift).
dialogo: Dialogo | null / abrir(d) El diálogo global abierto, una unión discriminada por tipo: editar, variante, vincular (cada uno con id), vincularLote (ids?, soloSeleccion?), anadir (en? posición de inserción), moverA (ids), fichas, sets, estadisticas, colecciones, recursosDiseno, disenarDeck, exportarPng.
ultimaAccion: string | null / anunciar(texto) Mensaje del pie («PDF guardado ({n} páginas)»).

Preferencias (type Preferencias, persistidas):

Campo Valor por defecto Significado
lang "en" Idioma con el que se piden las impresiones de las cartas (código de idioma de Scryfall: en es fr de it pt ja ko ru zhs zht, la lista IDIOMAS_SCRYFALL de src/lib/idiomas.ts). No es el idioma de la interfaz (ese es proxybuilder.lang, véase Internacionalización). Se envía como ResolveOptions.lang y lo usa el diálogo de añadir carta.
imagePolicy "withImage" ImagePolicy = bestAvailable / withImage / highresOnly: qué impresión se elige cuando la lista no indica set.
addTokens false Añade las fichas relacionadas al resolver una lista.
calidadVista 488 Ancho de la miniatura de la vista previa (AnchoMiniatura; el selector ofrece 244 / 488 / 672 / 745). Solo pantalla; el PDF usa siempre la resolución de exportación.
pedirConfirmacionAlCargar true Pregunta antes de sustituir las cartas existentes al cargar una lista.
zoom 0.8 Zoom de la vista previa, limitado a [ZOOM_MIN 0.4, ZOOM_MAX 2.5] y redondeado a 0.01 en setZoom.
infoCarta false Muestra insignias de información por carta en la vista previa.
actualizarSola true Comprueba/descarga actualizaciones automáticamente (véase Ajustes y shell).
tutorialesVistos [] Pantallas (Vista) cuyo tutorial ya ha salido; useTutorialInicial solo abre solo el recorrido de una pantalla que no esté en la lista. Véase Tutoriales.
mesa MESA_DE_FABRICA PreferenciasMesa, los ajustes de la mesa de juego (tamaño de las cartas, calidad, vista grande, panel de estado, animaciones, sombras, contador de fotogramas, sonidos de las cartas, del turno y del chat, mantener pulsado para el menú, ofrecer el bono, fantasma, contadores de jugador, movimiento en vivo en red, seguir el turno en una partida local y la disposición de los tableros por número de jugadores). Se cambian con setMesa(parcial); al leerse se funden sobre MESA_DE_FABRICA, así un campo nuevo sale con su valor de fábrica. Véase Mesa de juego.

Persistencia: leer() mezcla el JSON guardado sobre PREDETERMINADAS, así que añadir una preferencia nueva nunca rompe objetos guardados antiguos; guardar() escribe solo las claves de Preferencias (nunca vista, la selección ni el diálogo) y lo llaman setZoom, setInfoCarta, setPreferencia(k, v), setMesa y abrirTutorial. Ambas están envueltas en try/catch (modo privado, almacenamiento bloqueado): la app simplemente funciona con los valores por defecto durante la sesión. setVerReversos deliberadamente no se persiste.

Protección: src/stores/__tests__/ui-store.test.ts (valores por defecto, mezcla sobre lo guardado, JSON roto, almacenamiento bloqueado, límites del zoom, el conjunto exacto de claves guardadas, un tutorial que se apunta como visto una vez y se recuerda mientras que el abierto no, y los ajustes de la mesa fundidos con los de fábrica).

useTrabajo: el trabajo largo en marcha (src/stores/trabajo-store.ts)

actual: EstadoTrabajo | null, donde EstadoTrabajo = { tipo: TipoTrabajo, jobId: number | null, fase: string, hechas, total }. Acciones: empezar(tipo, fase), setId(jobId), avanzar(fase, hechas, total), terminar(). Solo un trabajo a la vez: resolver() en src/features/decklist/acciones.ts lanza { code: "Busy" } si actual está fijado; fase ya es texto traducido. El pie del panel izquierdo y el botón de exportar lo leen; TipoTrabajo es "resolver" | "exportar" | "idioma" | "mesa"; "mesa" es preparar una partida (mesa_empezar, mesa_retomar, véase Mesa de juego); "idioma" es el trabajo design_complete_lang que lanza completarIdioma (src/features/disenar/renumerar.ts) cuando el deck pasa a un idioma de diseño que les falta a sus diseños, o antes de exportar PNG en ese idioma (véase Interfaz del diseñador). cancelarTrabajo() (src/features/decklist/acciones.ts) llama a export_cancel con "exportar" y a job_cancel con los demás, con jobId. El jobId es null hasta que lanzarTrabajo retorna (véase Arquitectura del frontend). En el lado de Rust, la guarda equivalente es AppState::ocupado(), que además hace que update_install devuelva Busy, de modo que una actualización nunca mate una exportación de PDF; también es verdadera mientras hay una partida abierta en la mesa.

Otras operaciones largas (identificar en bloque, exportar PNG de diseños, descarga de marcos originales) mantienen su propio estado de progreso local en vez de useTrabajo.

useActualizacion: la máquina de actualización dentro de la app (src/stores/actualizacion-store.ts)

Estado: soportada: boolean | null, actual (versión en ejecución), paso, info: ActualizacionInfo | null, progreso: number | null (0 a 100), error.

stateDiagram-v2
    [*] --> inactiva
    inactiva --> buscando: buscar()
    buscando --> alDia: sin versión más nueva
    buscando --> disponible: update_check encontró una
    buscando --> inactiva: error (sin info)
    disponible --> bajando: bajar()
    bajando --> lista: update_download ok
    bajando --> disponible: error de descarga
    lista --> instalando: instalar()
    instalando --> lista: error de instalación (se puede reintentar)
    instalando --> [*]: la app se reinicia
Loading

(alDia es el valor "al-dia" en el código.)

Acción Comportamiento
iniciar() Pregunta a Rust una vez (update_status) si esta compilación puede actualizarse (soportada es false en desarrollo o cuando el binario no vino de un instalador); los errores significan no soportada.
buscar() Se ignora mientras está en buscando/bajando/instalando. Si ya está en lista, devuelve la info descargada (sin volver a descargar hasta el reinicio). Llama a update_check.
bajar() Solo desde disponible. Se suscribe a update://progress ({ hecho, total }, total 0 = tamaño desconocido) para calcular progreso, llama a update_download({ version }) y siempre cancela la suscripción.
instalar() Solo desde lista. con la mesa abierta (useMesa.activa) solo pone alSalirDeLaMesa (la instalación se hace al cerrarse la mesa, desde useAvisosActualizacion); si no, primero guardarAhora() (vuelca el mazo), luego update_install (un Busy motivo: partida también pone alSalirDeLaMesa); si tiene éxito, Rust reinicia la app y la promesa nunca se resuelve; si falla, vuelve a lista y relanza el error para que quien llama muestre un toast.

El lado de Rust (src-tauri/src/actualizar.rs, src-tauri/src/commands/actualizar.rs) y el pipeline de releases están documentados en Versiones y actualizaciones automáticas; los consumidores de la UI (toasts, botón de la barra, puntito, sección de Ajustes) están en Ajustes y shell.

useEdicion: la carta con cambios sin guardar (src/stores/edicion-store.ts)

Estado: pendiente: { guardar: () => Promise<boolean> } | null y la acción poner(p). Es una ranura de registro diminuta, no una copia del borrador: DisenarScreen (la vista Diseñar) pone en ella un callback guardar mientras su borrador tiene cambios sin guardar y lo quita cuando se guardan, se descartan o la pantalla se desmonta (véase Interfaz del diseñador). guardar devuelve false cuando guardar falló; el fallo ya se mostró al usuario, así que quien llama simplemente no continúa.

Por qué un store: lo que puede interrumpir Diseñar (cambiar de vista en AppShell, cerrar la ventana en ConfirmarCierre) vive en otro sitio y debe poder decir «Guardar y continuar» sin saber cómo se guarda una carta (design_save, aplicarlo a las otras copias, anunciar «Diseño guardado»). pendiente !== null es además la señal de que hay algo que perder: lib/cierre.ts lo lee como la razón edicion. Sin persistencia ni historial.

useMesa: la mesa de juego (src/stores/mesa-store.ts)

La vista que manda Rust (mesa::vista::VistaMesa), normalizada por clave: cartas (Record<IdCarta, VistaCarta>), zonas (por claveZona(RefZona): "<jugador>:<zona>" o "c:<zona>"), jugadores, asientos (el orden de los asientos), general, y además version, yo, formato, variantes y opciones. Campos solo de pantalla que nunca viajan a Rust: activa (hay una partida abierta y la pantalla está suscrita), registro (los últimos MAX_REGISTRO = 500 eventos, para el registro), seleccion, sobre (la carta bajo el ratón, para la vista grande y los atajos) e inspeccion.

Acción Comportamiento
recibirVista(v, registro?) Lo sustituye todo por desdeVista(v); conserva solo la selección y la carta bajo el ratón que sigan existiendo. Con registro (una vista entera por el canal: al suscribirse, deshacer o reiniciar) también sustituye el registro por los eventos que siguen contando; sin él (mesa_vista tras un salto) el registro se queda.
recibirParche(p, evento) conParche(m, p): solo si p.version === version + 1; copia solo los mapas tocados y deja las cartas no tocadas como los mismos objetos (sus componentes memorizados no se repintan). Devuelve false si hay un salto, y entonces conexion.ts pide mesa_vista. Añade el evento a registro y quita las cartas que desaparecen de la selección y de debajo del ratón.
cerrar() Vuelve a MESA_VACIA, activa: false.
seleccionar, alternar, setSobre, inspeccionar Estado de pantalla.

Reglas: nunca recalcula lo que decide Rust (quién ve qué, posiciones); los selectores que pueden no devolver nada devuelven el array compartido NINGUNA (un [] nuevo parecería un cambio en cada lectura), y los que construyen una lista usan useShallow. Los helpers cartasDe(m, zona) y cuantasEn(m, zona) leen una zona. La suscripción vive en src/features/mesa/conexion.ts. Protección: src/stores/__tests__/mesa-store.test.ts, con unos datos que escribe el motor (src/stores/__tests__/fixtures/mesa-parches.json). Véase Mesa de juego.

Stores de área que conviene conocer

  • useBiblioteca (src/features/library/store.ts): decks, detalles (detalles calculados por mazo), etiquetas; cada acción llama a Rust (deck_tags_set, deck_pin, deck_lang_set, deck_notes_set, deck_cover_set, CRUD de etiquetas) y después vuelve a leer (refrescar). cargar() empieza con guardarAhora() para que la biblioteca nunca muestre datos obsoletos del mazo abierto. Detalles en Biblioteca de mazos.
  • usePrefsBiblioteca: vista (cuadricula/lista), orden, dirección, agrupación, tamaño de carta, barra lateral, favoritos primero; se persiste bajo proxybuilder.biblioteca. useFiltrosBiblioteca: filtros, a propósito no recordados.
  • usePantalla (src/features/mesa/pantalla.ts): el diálogo de la mesa abierto (DialogoMesa, uno a la vez), el modo de elegir objetivo (adjuntar, flecha o nota: atar una nota a la siguiente carta del campo que se pulse), el arrastre en curso (Arrastre, al que sigue la copia arrastrada sin esperar a Rust; mientras lo hay, la vista grande se oculta), la zona bajo el ratón (para que funcionen los atajos de la biblioteca) y tapeteCambiado, un contador que sube avisarTapete() cuando el diálogo del tapete guarda, para que el tablero vuelva a leer el tapete, y redimensionando, verdadero mientras el tablero cambia de tamaño (se apaga 200 ms después del último cambio) para que las cartas salten a su sitio nuevo en vez de deslizarse. pedirNumero abre el diálogo de pedir un número.
  • useChat (src/stores/chat-store.ts): el chat de la mesa solo en esta pantalla: silenciados (jugadores silenciados para mí; el evento sigue viajando), bocadillos (el último mensaje o aviso de cada jugador, durante BOCADILLO_MS), sinLeer (contador de la pestaña) y limpiar() al cerrar la mesa. useRepeticion (src/stores/repeticion-store.ts): la repetición en curso (activa: VistaRepeticion o null) y su reproducción (reproduciendo, velocidad); mientras está puesta la mesa es de solo lectura. usePantalla guarda además el panel lateral (registro, chat o ninguno) y ANCHO_PANEL.
  • useMarcosOriginales / usePacksDescargables: info/lista, progreso y un contador version que se incrementa tras una descarga correcta para que las listas de packs se vuelvan a pedir.

Claves de persistencia de un vistazo

Dónde Clave Propietario
app_settings de Rust last_deck_id deck-store.ts (abrirDeckInicial, abrirDeck)
app_settings de Rust carpeta_importar, carpeta_exportar commands/carpetas.rs mediante lib/dialogos.ts
localStorage proxybuilder.preferencias useUi
localStorage proxybuilder.lang LanguageProvider
localStorage proxybuilder.theme, .mode, .scale, .skin, .bg, .material, .blur, .noise, .shadow, .fxSidebar, .heraFiligree, .smoothScroll ThemeProvider
localStorage proxybuilder.temasInstalados lib/temas-instalados.ts
localStorage proxybuilder.biblioteca usePrefsBiblioteca
localStorage proxybuilder.paneles, proxybuilder.panel-lista, proxybuilder.paneles.disenar disposiciones de react-resizable-panels
localStorage proxybuilder.seccion.<id> estado plegado de Seccion
localStorage proxybuilder.disenar.coleccion última colección usada para los borradores de diseño nuevos (features/disenar/use-borrador.ts)
localStorage proxybuilder.mesa.nombre el nombre del jugador en la mesa de juego (features/mesa/entrada.tsx)
app_settings de Rust mesa.tapete.<id del deck>, mesa.tapete los tapetes de la mesa de juego (src-tauri/src/mesa/tapete.rs)

Regla práctica: lo que dependa de un mazo va en el mazo (Rust); lo que sea una comodidad por máquina va en localStorage envuelto en try/catch; lo que Rust necesite recordar entre sesiones (último mazo, últimas carpetas) va en app_settings.

Tests que protegen los stores

src/stores/__tests__/: deck-store.test.ts (cinco cambios rápidos provocan un solo guardado; deshacer tres veces y rehacer restauran el estado exacto; un guardado fallido deja error y reintenta con el siguiente cambio; los cambios sin historial se guardan pero no se pueden deshacer), deck-store-abrir.test.ts, plan-store.test.ts (primera petición inmediata, debounce, gana la última petición con call simulado), ui-store.test.ts, trabajo-store.test.ts, actualizacion-store.test.ts, mesa-store.test.ts (los parches sobre la primera vista dan la vista final, un salto de versión pide la vista, las cartas no tocadas conservan su identidad, la selección pierde las cartas que desaparecen, una vista con registro lo sustituye y una sin él lo conserva). La guarda de cierre, que lee useTrabajo, useActualizacion, useEdicion y useMesa, se prueba en src/lib/__tests__/cierre.test.ts (véase Tests y CI). Usan temporizadores falsos y simulan @/lib/ipc; localStorage se simula porque Vitest corre en el entorno node.

Por dónde empezar para ampliarlo

  • Cambio nuevo del mazo que se pueda deshacer: llama a useDeck.getState().setCartas(...)/setAjustes(...) con un helper puro; no mutes. Si no es una edición del usuario, usa refrescarCartas.
  • Preferencia nueva: añade la clave a Preferencias y a PREDETERMINADAS, y al objeto que se construye en guardar() de ui-store.ts; léela con useUi((s) => s.key). Un ajuste de la mesa de juego va en PreferenciasMesa y MESA_DE_FABRICA.
  • Diálogo global nuevo: añade una variante de Dialogo y monta el componente en src/App.tsx.
  • Trabajo largo nuevo: usa lanzarTrabajo y refleja el progreso en useTrabajo si el pie o el botón de exportar deben mostrarlo.

Véase también

Documentation · Documentación

Language: English · Español

Use and build · Uso y desarrollo

Architecture · Arquitectura

Data · Datos

Getting cards · Conseguir cartas

Layout, images, PDF · Maquetación, imagen y PDF

Card designer · Diseñador de cartas

Game table · Mesa de juego

Frontend · Frontend

Reference · Referencia

Clone this wiki locally