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
+10 -8
View File
@@ -5,6 +5,7 @@ import { api } from '../lib/api.js'
import { apiUmind, contexto } from '../lib/contexto.js'
import UiEmptyState from '../components/ui/UiEmptyState.vue'
import UiMascota from '../components/ui/UiMascota.vue'
import UiIcono from '../components/ui/UiIcono.vue'
const props = defineProps({
tenantId: { type: String, required: true },
@@ -414,7 +415,7 @@ async function enviarChatPrueba() {
const r = await api.post(apiUmind('/umind/chat'), { agente_id: agenteIdNum.value, session_id: chatSessionId, mensaje: texto })
chatMensajes.value.push({ role: 'assistant', content: r.respuesta })
} catch (e) {
chatMensajes.value.push({ role: 'assistant', content: ` ${e.message}` })
chatMensajes.value.push({ role: 'assistant', content: `${e.message}` })
} finally {
chatEnviando.value = false
}
@@ -507,7 +508,7 @@ watch(
<!-- Un archivo que se reenvía sirve para justificar el gasto puertas
adentro; un panel al que hay que entrar, no. -->
<a :href="urlReporte" class="btn-ghost shrink-0" title="Conversaciones y consumo del mes en Excel">
Reporte del mes
<UiIcono nombre="descargar" :tam="15" /> Reporte del mes
</a>
</div>
@@ -532,12 +533,12 @@ watch(
<div class="card p-4 mb-4">
<div class="flex gap-1 mb-3">
<button
v-for="[k, l] in [['texto', '✍️ Escribir'], ['archivo', '📄 Subir archivo'], ['url', '🌐 Sitio web']]"
v-for="[k, ico, l] in [['texto', 'escribir', 'Escribir'], ['archivo', 'archivo', 'Subir archivo'], ['url', 'sitio', 'Sitio web']]"
:key="k"
class="px-3 py-1.5 rounded-lg text-sm"
class="px-3 py-1.5 rounded-lg text-sm inline-flex items-center gap-1.5"
:class="fuenteNueva === k ? 'bg-brand text-white font-medium' : 'text-tenue hover:text-texto'"
@click="fuenteNueva = k"
>{{ l }}</button>
><UiIcono :nombre="ico" :tam="15" /> {{ l }}</button>
</div>
<!-- Lo que el dueño sabe y no está escrito en ningún lado. -->
@@ -616,7 +617,7 @@ watch(
class="text-xs text-tenue hover:text-texto"
:title="d.auto_actualizar ? 'Dejar de releer sola' : 'Releer el sitio cada semana'"
@click="alternarAuto(d)"
>{{ d.auto_actualizar ? '🔁 auto' : 'manual' }}</button>
><UiIcono :nombre="d.auto_actualizar ? 'auto' : 'refrescar'" :tam="13" class="mr-1" />{{ d.auto_actualizar ? 'auto' : 'manual' }}</button>
<!-- Solo lo que tiene texto propio guardado: una URL se rehace
crawleando, editarla a mano se perdería en la próxima pasada. -->
<button
@@ -740,7 +741,7 @@ watch(
<label class="label flex items-center gap-1">
<input v-model="p.requerido" type="checkbox" /> req.
</label>
<button type="button" class="text-red-400 text-xs" @click="quitarParametro(i)"></button>
<button type="button" class="text-red-400 text-xs" @click="quitarParametro(i)"><UiIcono nombre="cerrar" :tam="13" /></button>
</div>
<p v-if="toolForm.parametros.length === 0" class="text-xs text-gray-400">Sin parámetros.</p>
</div>
@@ -788,7 +789,8 @@ watch(
:class="widgetCopiado ? 'bg-green-600 text-white' : 'bg-brand hover:bg-brand-dark text-white'"
@click="copiarWidget"
>
{{ widgetCopiado ? '✓ Copiado' : 'Copiar código' }}
<UiIcono v-if="widgetCopiado" nombre="ok" :tam="14" />
{{ widgetCopiado ? 'Copiado' : 'Copiar código' }}
</button>
</div>
<p class="label mb-2">