feat(studio): un solo set de iconos en vez de emojis mezclados con símbolos

En la misma pantalla convivían dos lenguajes: emojis a color (🌙 📊 🧠 ✍️ 📄
🌐 🔁) y símbolos tipográficos monocromos (✎ ✕ ← ↻ ⚠ ⬇ ✓). Eso se lee como
descuido, y encima los emojis los dibuja el sistema operativo — el mismo panel
se ve distinto en Mac, en Windows y en Android, sin que podamos hacer nada.

Ahora hay un set propio de iconos SVG de trazo: mismo grosor de línea, misma
caja, y heredan currentColor, así que toman el color del texto que los rodea y
funcionan igual en tema claro y oscuro. Al lado uno de otro tienen el mismo
peso visual, que es lo que hacía falta.

El emoji suelto de la pantalla de inicio pasa a ser Umi, que ya es el personaje
del producto.

Un detalle de implementación que costó dos intentos: los iconos NO pueden
declararse como cadenas de HTML para v-html. Dentro de un <svg>, v-html parsea
como HTML y los <path> quedan sin el namespace de SVG: aparecen en el DOM pero
no dibujan nada, así que la primera versión se veía perfecta en el código y en
blanco en pantalla. Van declarados como pares [etiqueta, atributos] para que
Vue los cree con el namespace correcto.

Y AgenteDetail usaba cinco iconos sin importar el componente. Verificado ahora
sobre todos los archivos: cada componente usado está importado donde se usa.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-22 10:45:43 -05:00
co-authored by Claude Opus 5
parent 002b75e71b
commit ee9bb2cfb7
14 changed files with 173 additions and 54 deletions
+8 -7
View File
@@ -3,6 +3,7 @@ import { computed, ref, watch } from 'vue'
import { api } from '../lib/api.js'
import { apiUmind } from '../lib/contexto.js'
import UiEmptyState from '../components/ui/UiEmptyState.vue'
import UiIcono from '../components/ui/UiIcono.vue'
const props = defineProps({ id: { type: String, required: true } })
@@ -16,11 +17,11 @@ const desde = ref(new Date(hoy.getFullYear(), hoy.getMonth(), 1).toISOString().s
const hasta = ref(hoy.toISOString().slice(0, 10))
const ETIQUETAS = {
ia: { nombre: 'Inteligencia artificial', icono: '🧠', color: '#8eb02f' },
ocr: { nombre: 'Lectura de imágenes', icono: '🖼️', color: '#2f7fb0' },
whisper: { nombre: 'Transcripción de audio', icono: '🎙️', color: '#b0752f' },
ia: { nombre: 'Inteligencia artificial', icono: 'cerebro', color: '#8eb02f' },
ocr: { nombre: 'Lectura de imágenes', icono: 'imagen', color: '#2f7fb0' },
whisper: { nombre: 'Transcripción de audio', icono: 'microfono', color: '#b0752f' },
}
const etiqueta = (t) => ETIQUETAS[t] || { nombre: t, icono: '', color: '#94a3b8' }
const etiqueta = (t) => ETIQUETAS[t] || { nombre: t, icono: 'grafico', color: '#94a3b8' }
async function cargar() {
cargando.value = true
@@ -88,7 +89,7 @@ watch(() => props.id, cargar, { immediate: true })
<h1 class="text-xl font-semibold text-texto">Consumo</h1>
<p class="text-sm text-tenue mt-0.5">Lo que usaron tus agentes y cuánto cuesta.</p>
</div>
<router-link :to="`/tenants/${id}`" class="btn-ghost"> Agentes</router-link>
<router-link :to="`/tenants/${id}`" class="btn-ghost inline-flex items-center gap-1.5"><UiIcono nombre="atras" :tam="15" /> Agentes</router-link>
</div>
<!-- Filtros -->
@@ -162,12 +163,12 @@ watch(() => props.id, cargar, { immediate: true })
</div>
<UiEmptyState
v-if="resumen.length === 0"
icono="📊"
estado="durmiendo"
titulo="Sin consumo en este período"
detalle="Cuando tus agentes respondan mensajes, lean imágenes o transcriban audios, vas a verlo acá."
/>
<div v-for="r in resumen" :key="r.tipo" class="px-4 py-3 flex items-center gap-3">
<span class="text-lg">{{ etiqueta(r.tipo).icono }}</span>
<UiIcono :nombre="etiqueta(r.tipo).icono" :tam="18" class="text-tenue" />
<div class="min-w-0 flex-1">
<p class="text-sm text-texto">{{ etiqueta(r.tipo).nombre }}</p>
<p class="text-xs text-tenue tabular-nums">