tauri-plugin-vicons 2.2.1

Icon API for Tauri plugins (Created for VasakOS)
Documentation

Tauri Plugin vicons

Obtiene íconos nativos del sistema Linux (GTK) como base64, listos para usar en cualquier elemento <img>. Soporta temas de iconos del sistema, cacheo automático, y detección de cambios de tema en vivo.

Requisitos

  • Rust 1.80.0+
  • Tauri v2
  • Linux con GTK 3 (entornos GNOME, KDE, Xfce, etc.)

Instalación

1. Agregar el crate Rust

[dependencies]
tauri-plugin-vicons = { git = "https://github.com/Vasak-OS/tauri-plugin-vicons", branch = "v2" }

2. Registrar el plugin en lib.rs

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_vicons::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

3. Agregar el paquete JS (opcional)

bun add @vasakgroup/plugin-vicons

4. Configurar permisos

En src-tauri/capabilities/default.json:

{
  "permissions": [
    "vicons:default"
  ]
}

API

Comandos Rust

Comando Descripción Retorno
get_icon Obtiene un icono regular por nombre o ruta String (base64)
get_symbol Obtiene un icono simbólico por nombre o ruta String (base64)
has_icon Si el tema tiene el icono regular, sin traerlo bool
has_symbol Si el tema tiene el icono simbólico bool

Los cuatro aceptan un solo argumento name: &str. Si es una ruta de archivo válida, leen el archivo directamente. Si es un nombre de icono GTK, lo buscan en el tema de iconos del sistema.

has_icon y has_symbol hacen la misma búsqueda que su get_ —mismo tamaño, mismas banderas—, así que un true garantiza que el get_ correspondiente devuelve ese icono.

Eventos emitidos

Evento Cuándo se emite Payload
vicons:theme-changed El tema de iconos del sistema cambió null

Escucharlo desde el frontend:

import { listen } from "@tauri-apps/api/event";

await listen("vicons:theme-changed", () => {
  // recargar íconos, refrescar UI, etc.
});

Funciones JS (guest-js)

Función Descripción Retorno
getIconSource Obtiene un data URI completo listo para <img src> string
getSymbolSource Igual que getIconSource pero con iconos simbólicos string
getIcon Obtiene solo el base64 de un icono regular (sin data URI) Promise<string>
getSymbol Obtiene solo el base64 de un icono simbólico (sin data URI) Promise<string>
hasIcon Si el tema tiene el icono, para elegir antes de dibujar Promise<boolean>
hasSymbol Igual que hasIcon pero con iconos simbólicos Promise<boolean>

getIconSource y getSymbolSource detectan automáticamente el tipo MIME del icono (PNG, JPEG, GIF, WebP, BMP, SVG) mediante magic bytes.

Por qué hacen falta hasIcon y hasSymbol

getIconSource y getSymbolSource no fallan cuando el icono no está: el tema devuelve image-missing —el cuadrito de imagen rota— como si fuera el icono pedido, y lo que llega es un data URI perfectamente válido. Comprobar el resultado no alcanza, porque nunca está vacío:

import { getSymbolSource, hasSymbol } from '@vasakgroup/plugin-vicons';

// Mal: `icono` nunca es "", así que el `if` no protege de nada.
const icono = await getSymbolSource('google-symbolic');
if (icono) mostrar(icono); // dibuja el cuadrito si el tema no lo tiene

// Bien: se pregunta primero y se elige otro nombre si falta.
const nombre = (await hasSymbol('google-symbolic'))
  ? 'google-symbolic'
  : 'goa-account-symbolic';
mostrar(await getSymbolSource(nombre));

Ante un error del plugin contestan false, no una excepción: quien pregunta esto lo hace para elegir un icono alternativo, y caer al alternativo es lo razonable. El error queda en la consola.

Uso

Básico

import { getIconSource } from '@vasakgroup/plugin-vicons';

const icon = await getIconSource('folder');
// → "data:image/svg+xml;base64,PHN2ZyB4bWxucz0..."

Con Vue

<script setup lang="ts">
import { getIconSource } from '@vasakgroup/plugin-vicons';
import { ref, onMounted } from 'vue';

const icon = ref('');
onMounted(async () => {
  icon.value = await getIconSource('folder');
});
</script>

<template>
  <img :src="icon" alt="folder" />
</template>

Con React

import { getIconSource } from '@vasakgroup/plugin-vicons';
import { useEffect, useState } from 'react';

function FolderIcon() {
  const [src, setSrc] = useState('');
  useEffect(() => {
    getIconSource('folder').then(setSrc);
  }, []);
  return <img src={src} alt="folder" />;
}

Ruta de archivo directa

Si pasás una ruta de archivo existente, se lee directamente sin pasar por el tema GTK:

const icon = await getIconSource('/usr/share/icons/hicolor/48x48/apps/firefox.png');

Escuchar cambios de tema

El plugin detecta automáticamente cambios en el tema de iconos del sistema (a través de la señal changed de GTK) y emite un evento:

import { listen } from "@tauri-apps/api/event";
import { getIconSource } from '@vasakgroup/plugin-vicons';

listen("vicons:theme-changed", async () => {
  console.log("Theme changed, refreshing icons...");
  const icon = await getIconSource('folder');
  // actualizar UI con el nuevo icono
});

Arquitectura

flowchart TB
    subgraph Frontend
        A[getIconSource / getSymbolSource] --> B[getIconType<br/>magic bytes PNG/JPEG/GIF/WebP/BMP/SVG]
        O[hasIcon / hasSymbol]
        C[listen 'vicons:theme-changed']
    end

    subgraph Backend
        D[get_icon_impl / get_symbol_impl] --> E{¿ruta válida?}
        E -->|sí| F[leer archivo]
        E -->|no| G[buscar en GTK IconTheme]
        G --> H{¿cache hit?}
        H -->|sí + vigente| I[devolver cache]
        H -->|no o expirado| J[GTK lookup → base64 → guardar cache]
        J -.->|sin resultado| J2[image-missing<br/>el cuadrito, indistinguible del icono real]

        P[has_icon_impl / has_symbol_impl] --> E2{¿ruta válida?}
        E2 -->|sí| P1[true]
        E2 -->|no| P2[mismo GTK lookup, sin traer el archivo]
        P2 --> P3{¿hubo resultado?}
    end

    subgraph ThemeMonitor
        K[init_theme_monitor] --> L[connect_changed GTK signal]
        L --> M[clear_cache_internal]
        M --> N[emit 'vicons:theme-changed']
    end

    A --> D
    O[hasIcon / hasSymbol] --> P
    N -.->|Tauri event| C

Cache

  • Dos cachés separadas: ICON_CACHE y SYMBOL_CACHE (HashMap<String, CacheEntry>).
  • Cada entrada expira después de 30 minutos.
  • Al cambiar el tema de iconos del sistema, ambos cachés se limpian por completo.
  • La caché usa std::sync::LazyLock y std::sync::Mutex (sin dependencias externas).

Detección de cambios de tema

GTK monitorea internamente los cambios de tema mediante:

  • GSettings (org.gnome.desktop.interface icon-theme)
  • inotify sobre ~/.config/gtk-3.0/settings.ini
  • XDG Desktop Portal

Cuando detecta un cambio, dispara la señal changed del IconTheme. El plugin la captura, limpia la caché, y emite vicons:theme-changed al frontend.

Logging

El plugin escribe logs en {app_data_dir}/logs/icons.log con niveles INFO, WARN y ERROR. Sin dependencias externas de logging — usa std::io::LineWriter directo a archivo.

Errores

Error Causa
IconNotFound El nombre solicitado no existe en el tema
ThemeMonitorError No se pudo inicializar el monitor de tema
Io Error de lectura de archivo

Dependencias

Solo 5 crates además de Tauri:

Crate Propósito
serde Serialización
thiserror Errores tipados
gtk Acceso al theme de iconos GTK
glib Bindings de GLib (con GTK)
base64 Codificación base64

Licencia

GPLv3 — Vasak Group