Repository navigation
Icons
English · Español
Every icon of the app is an Iconify icon drawn by one component, <Icono icon="prefix:name">. There are two kinds: the ones the code writes literally (bundled into the app at build time, painted at once, no network) and the ones the user picks with the icon picker (for now, the icon of a library tag), which Rust downloads once from the Iconify API and keeps on disk so they show offline.
The only exception is src/components/ui/: the shadcn/judlex primitives are read-only and keep importing lucide-react (chevrons of selects and menus, the close cross of dialogs, the check of checkboxes). Everything outside that folder uses <Icono>.
| File | Responsibility |
|---|---|
src/components/icono.tsx |
Icono({ icon, size, ... }): an <svg> with the icon's viewBox and body. Bundled icons come from INCLUIDOS; any other id is asked for through useIconos().pedir(id) and its box (24 px by default, or the size-* class) is kept empty until it arrives, so nothing jumps. An empty id paints nothing. |
src/lib/iconos/incluidos.ts |
Generated by scripts/iconos.mjs (never edited by hand, excluded from Biome): INCLUIDOS: Record<id, { body, vb }>, the data of every icon id written literally in src/. |
scripts/iconos.mjs |
Walks src/**/*.{ts,tsx} (skipping node_modules, bindings, assets, __tests__ and test files), finds every quoted prefix:name, looks it up in the @iconify/json collections (aliases resolved with getIconData/iconToSVG from @iconify/utils) and writes incluidos.ts. Quoted strings that are not icons (16:9, pbimg:…) drop out because their prefix is not a collection; an id whose collection exists but whose name does not is an error. --check fails if the file is not up to date. |
src/components/selector-icono.tsx |
SelectorIcono({ valor, onChange, quitable }): the icon picker. |
src/stores/iconos-store.ts |
useIconos: datos (the icons Rust already returned in this session), pedir(id) (batches the ids asked for during one paint, 16 ms, into a single iconos_datos; on failure the ids can be asked for again) and recibir. |
src-tauri/src/iconos.rs |
The Iconify client: catalogue, search, icon data, chosen icons, SVG sanitising. |
src-tauri/src/commands/iconos.rs |
The five commands below. |
Dependencies: @iconify/utils (runtime dependency, used by the script) and @iconify/json (dev dependency with every collection; listed in knip.json ignoreDependencies because only the script reads it from node_modules).
flowchart LR
SRC["src/**/*.tsx<br/>"game-icons:crown""] -->|pnpm iconos| GEN["src/lib/iconos/incluidos.ts"]
JSON["@iconify/json"] --> GEN
GEN --> ICONO["<Icono>"]
ICONO -->|id not bundled| STORE["useIconos.pedir"] -->|iconos_datos| RUST["iconos.rs"]
After adding or changing an icon id in the code, run pnpm iconos. pnpm check runs pnpm check:iconos (node scripts/iconos.mjs --check), so a forgotten regeneration fails locally and in CI. Why generate instead of importing per icon: one component for both kinds of icon (written and chosen), no network for the app's own icons, and only the icons actually used end up in the bundle.
An id built with a template (`lucide:${x}`) is invisible to the script: write the full ids somewhere in the code (a table) so they get bundled. Not bundled means fetched from the network at run time, which the app's own icons must never need.
Which collection: lucide:* for general interface icons (the same family the app used before), game-icons:* for game concepts on the «Jugar» screens and the game log: poison (bottle), monarch (crown), initiative (dungeon gate), the city's blessing (castle), graveyard (tombstone), the day/night sun and moon, «doesn't untap» (padlock), phased out (portal), temporary notes (hourglass), and so on. Inferred: other collections are fine when they fit, as long as the id is written literally.
SelectorIcono is a button showing the current icon (or a placeholder) that opens a popover, modelled on the IconPicker of the project's website, but every request goes through Rust because the webview never goes online:
-
Collection chooser: every visible Iconify collection (
iconos_colecciones, sorted by category and name, with how many icons each has) plus «Todas las colecciones», which searches by name across all of them (iconos_buscar). It opens on the collection of the current icon, or ongame-iconswhen there is none. -
Filter: inside a collection, a local filter over its names (
iconos_nombres); in «all collections», the search text («Buscar en todas (2 letras o más)…»: at least two letters). -
Paging: 240 icons at a time (
PAGINA) and a «Mostrar más» button for the next batch; the visible ones are fetched together throughuseIconos. -
Picking calls
iconos_elegir(id), which stores the icon's data for offline use, and returns the full id (game-icons:crown) toonChange. Withquitable, «Sin icono» clears it.
The collections, names and searches already loaded are also kept in module variables for the rest of the session.
| Command | Does |
|---|---|
iconos_colecciones() |
GET api.iconify.design/collections → Vec<ColeccionIconos { prefijo, nombre, total, categoria }>, hidden collections and odd prefixes left out. |
iconos_nombres(prefijo) |
GET /collection?prefix=… → every name (categorised and uncategorised), sorted. |
iconos_buscar(texto) |
GET /search?query=…&limit=999 (the API maximum); the text is lower-cased, cut to 40 characters, letters, digits, spaces and hyphens only. Results cached in memory per query. |
iconos_datos(ids) |
BTreeMap<id, DatosIcono { body, vb }>, from memory, from the chosen icons on disk or from GET /<prefix>.json?icons=a,b,… (120 per request, at most 2,000 ids per call). |
iconos_elegir(id) |
Like iconos_datos for one id, then writes it to the chosen-icons file. NotFound if the icon does not exist. |
-
Network: through
fuentes::Externowith the new allow-list entry("api.iconify.design", "/"), so redirects are re-checked and responses size-capped (8 MiB of JSON,MAX_JSON). See External Sources. -
Ids:
partiraccepts onlyprefix:namewith lower-case ASCII letters, digits,-and_, up to 64 characters each; nothing else ever reaches a URL (los_ids_raros_no_llegan_a_la_url).id_validois used by the library to refuse a bad tag icon. -
Aliases:
resolverfollows up to 6 alias hops, inheriting size and combining rotations and flips;a_svgdoes whaticonToSVGof@iconify/utilsdoes (transforms in a<g>, width and height swapped for 90°/270°), so bundled and downloaded icons look the same. -
Sanitising (
seguro): the body is inserted into the page as-is (dangerouslySetInnerHTML), so any body with<script,foreignObject,javascript:,<image,<iframe,<!entity, anon…=handler, or anhref/url(that is not a local#idis dropped (el_svg_peligroso_se_rechaza). Bundled icons come from@iconify/jsonat build time and are trusted. -
Storage: the catalogue (the collection list and the names of each collection) is cached in
app_cache_dir/iconos/(colecciones.json,<prefix>.json) and renewed after 7 days (RENOVAR); it can be deleted. Icon data seen in the picker lives in memory only. The chosen icons, which something stored refers to, go toapp_data_dir/iconos/elegidos.json(written atomically), so a tag icon still shows with no network or after clearing the cache. See Storage and Blobs.
-
Library tags:
tags.icon(migration0007_icono_etiqueta.sql),Tag.icon,tag_icon_set, carried in the.pbdeck. See Deck Library and Deck Package Format.
- Rust:
iconos::tests(ids that could break a URL are refused, dangerous SVG is refused whileurl(#a)and names containing «on» are not, an alias inherits from its parent and is flipped). -
pnpm check:iconoskeepsincluidos.tsin step with the code.
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
- Icons · Iconos
- 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