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:
co-authored by
Claude Opus 5
parent
002b75e71b
commit
ee9bb2cfb7
@@ -3,6 +3,7 @@ import Sidebar from './components/Sidebar.vue'
|
||||
import { tema, alternarTema } from './lib/tema.js'
|
||||
import { menuAbierto } from './lib/ui.js'
|
||||
import DespertarUmind from './components/DespertarUmind.vue'
|
||||
import UiIcono from './components/ui/UiIcono.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -27,7 +28,7 @@ import DespertarUmind from './components/DespertarUmind.vue'
|
||||
:title="tema === 'oscuro' ? 'Cambiar a claro' : 'Cambiar a oscuro'"
|
||||
@click="alternarTema"
|
||||
>
|
||||
<span class="text-base leading-none">{{ tema === 'oscuro' ? '☀️' : '🌙' }}</span>
|
||||
<UiIcono :nombre="tema === 'oscuro' ? 'sol' : 'luna'" :tam="17" />
|
||||
</button>
|
||||
</header>
|
||||
<div class="flex-1 max-w-5xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-8 animate-aparecer">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import UiIcono from './ui/UiIcono.vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { api } from '../lib/api.js'
|
||||
@@ -191,14 +192,14 @@ onMounted(() => {
|
||||
title="Editar"
|
||||
@click="editarTenant(t)"
|
||||
>
|
||||
✎
|
||||
<UiIcono nombre="lapiz" :tam="14" />
|
||||
</button>
|
||||
<button
|
||||
class="p-1 text-tenue hover:text-red-600"
|
||||
title="Eliminar"
|
||||
@click="eliminar(t)"
|
||||
>
|
||||
✕
|
||||
<UiIcono nombre="cerrar" :tam="14" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<script setup>
|
||||
// Un solo set de iconos para todo uMind.
|
||||
//
|
||||
// Antes convivían emojis a color (🌙 📊 🧠 ✍️) con símbolos tipográficos
|
||||
// monocromos (✎ ✕ ← ↻). Dos lenguajes distintos en la misma pantalla se leen
|
||||
// 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.
|
||||
//
|
||||
// Estos son SVG de trazo que heredan currentColor, así que toman el color del
|
||||
// texto que los rodea y funcionan igual en tema claro y oscuro. Mismo grosor de
|
||||
// línea y misma caja en todos: al lado uno de otro tienen el mismo peso visual.
|
||||
defineProps({
|
||||
nombre: { type: String, required: true },
|
||||
tam: { type: [Number, String], default: 16 },
|
||||
})
|
||||
|
||||
// Cada icono es una lista de [etiqueta, atributos]. No es una cadena de HTML a
|
||||
// propósito: v-html dentro de un <svg> parsea como HTML y los <path> quedan sin
|
||||
// el namespace de SVG — se insertan en el DOM pero no dibujan nada. Declarados
|
||||
// así, Vue los crea con el namespace correcto porque ve el <svg> padre.
|
||||
const ICONOS = {
|
||||
luna: [['path', { d: 'M20 14.5A8.5 8.5 0 0 1 9.5 4a8.5 8.5 0 1 0 10.5 10.5Z' }]],
|
||||
sol: [
|
||||
['circle', { cx: 12, cy: 12, r: 4 }],
|
||||
['path', { d: 'M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4' }],
|
||||
],
|
||||
lapiz: [
|
||||
['path', { d: 'M12 20h9' }],
|
||||
['path', { d: 'M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z' }],
|
||||
],
|
||||
cerrar: [['path', { d: 'M18 6 6 18M6 6l12 12' }]],
|
||||
atras: [['path', { d: 'M19 12H5M12 19l-7-7 7-7' }]],
|
||||
grafico: [
|
||||
['path', { d: 'M3 3v18h18' }],
|
||||
['path', { d: 'M7 15v3M12 10v8M17 6v12' }],
|
||||
],
|
||||
cerebro: [
|
||||
['path', { d: 'M9.5 3A2.5 2.5 0 0 0 7 5.5v.2A2.7 2.7 0 0 0 5 8.3c0 .8.3 1.5.9 2A2.8 2.8 0 0 0 5 12.5c0 1 .5 1.9 1.3 2.4-.2.4-.3.8-.3 1.2A2.9 2.9 0 0 0 9 19a2.5 2.5 0 0 0 3 2V3.8A2.5 2.5 0 0 0 9.5 3Z' }],
|
||||
['path', { d: 'M14.5 3A2.5 2.5 0 0 1 17 5.5v.2a2.7 2.7 0 0 1 2 2.6c0 .8-.3 1.5-.9 2A2.8 2.8 0 0 1 19 12.5c0 1-.5 1.9-1.3 2.4.2.4.3.8.3 1.2A2.9 2.9 0 0 1 15 19a2.5 2.5 0 0 1-3 2' }],
|
||||
],
|
||||
imagen: [
|
||||
['rect', { x: 3, y: 3, width: 18, height: 18, rx: 2 }],
|
||||
['circle', { cx: 8.5, cy: 8.5, r: 1.5 }],
|
||||
['path', { d: 'm21 15-5-5L5 21' }],
|
||||
],
|
||||
microfono: [
|
||||
['rect', { x: 9, y: 2, width: 6, height: 12, rx: 3 }],
|
||||
['path', { d: 'M5 10a7 7 0 0 0 14 0M12 17v5' }],
|
||||
],
|
||||
escribir: [
|
||||
['path', { d: 'M3 21h18' }],
|
||||
['path', { d: 'M5 17 17.5 4.5a2.1 2.1 0 0 1 3 3L8 20l-4 1Z' }],
|
||||
],
|
||||
archivo: [
|
||||
['path', { d: 'M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7Z' }],
|
||||
['path', { d: 'M14 2v5h5' }],
|
||||
],
|
||||
sitio: [
|
||||
['circle', { cx: 12, cy: 12, r: 9 }],
|
||||
['path', { d: 'M3 12h18M12 3a15 15 0 0 1 0 18 15 15 0 0 1 0-18Z' }],
|
||||
],
|
||||
refrescar: [
|
||||
['path', { d: 'M21 12a9 9 0 1 1-3-6.7' }],
|
||||
['path', { d: 'M21 4v5h-5' }],
|
||||
],
|
||||
descargar: [
|
||||
['path', { d: 'M12 3v12' }],
|
||||
['path', { d: 'm7 11 5 5 5-5' }],
|
||||
['path', { d: 'M4 21h16' }],
|
||||
],
|
||||
alerta: [
|
||||
['path', { d: 'M12 9v4M12 17h.01' }],
|
||||
['path', { d: 'M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z' }],
|
||||
],
|
||||
ok: [['path', { d: 'M20 6 9 17l-5-5' }]],
|
||||
copiar: [
|
||||
['rect', { x: 9, y: 9, width: 12, height: 12, rx: 2 }],
|
||||
['path', { d: 'M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1' }],
|
||||
],
|
||||
mensaje: [['path', { d: 'M21 11.5a8.4 8.4 0 0 1-9 8.4 8.5 8.5 0 0 1-3.9-.9L3 21l1.9-5.1A8.4 8.4 0 0 1 4 11.5a8.5 8.5 0 0 1 8.5-8.5A8.4 8.4 0 0 1 21 11.5Z' }]],
|
||||
auto: [
|
||||
['path', { d: 'M17 2.1 21 6l-4 3.9' }],
|
||||
['path', { d: 'M3 11V9a4 4 0 0 1 4-4h14' }],
|
||||
['path', { d: 'M7 21.9 3 18l4-3.9' }],
|
||||
['path', { d: 'M21 13v2a4 4 0 0 1-4 4H3' }],
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
:width="tam"
|
||||
:height="tam"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.75"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="inline-block shrink-0 align-[-0.15em]"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<component
|
||||
v-for="(el, i) in ICONOS[nombre] || []"
|
||||
:key="i"
|
||||
:is="el[0]"
|
||||
v-bind="el[1]"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import UiIcono from './UiIcono.vue'
|
||||
defineProps({ titulo: { type: String, default: '' }, ancho: { type: String, default: 'max-w-lg' } })
|
||||
const emit = defineEmits(['cerrar'])
|
||||
</script>
|
||||
@@ -12,7 +13,7 @@ const emit = defineEmits(['cerrar'])
|
||||
<div :class="['card w-full p-6 max-h-[90vh] overflow-y-auto animate-escalar shadow-2xl', ancho]">
|
||||
<div v-if="titulo" class="flex items-center justify-between mb-4">
|
||||
<h2 class="font-semibold text-texto">{{ titulo }}</h2>
|
||||
<button class="btn-ghost !px-2 !py-1" type="button" @click="emit('cerrar')">✕</button>
|
||||
<button class="btn-ghost !px-2 !py-1" type="button" @click="emit('cerrar')"><UiIcono nombre="cerrar" :tam="15" /></button>
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -3,6 +3,7 @@ import { 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 } })
|
||||
|
||||
@@ -122,7 +123,7 @@ async function borrar(c) {
|
||||
<div>
|
||||
<div class="flex items-start justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<router-link :to="`/tenants/${id}`" class="text-sm text-tenue hover:text-texto">← Agentes</router-link>
|
||||
<router-link :to="`/tenants/${id}`" class="text-sm text-tenue hover:text-texto inline-flex items-center gap-1.5"><UiIcono nombre="atras" :tam="14" /> Agentes</router-link>
|
||||
<h1 class="text-xl font-semibold text-texto mt-1">Tu cuenta de IA</h1>
|
||||
<p class="text-sm text-tenue mt-1 max-w-xl">
|
||||
Podés usar tu propia cuenta de OpenAI, Claude o Gemini en vez de la nuestra.
|
||||
|
||||
@@ -68,7 +68,7 @@ onMounted(irAlAgenteSiEsUnoSolo)
|
||||
</div>
|
||||
|
||||
<div v-else class="flex flex-col items-center justify-center text-center py-24">
|
||||
<div class="text-4xl mb-4">💬</div>
|
||||
<UiMascota estado="normal" :tam="64" class="mx-auto mb-4 text-brand" />
|
||||
<h1 class="text-lg font-medium text-texto">
|
||||
{{ contexto.esPortal ? 'Elegí tu espacio de la izquierda' : 'Elegí un espacio de la izquierda' }}
|
||||
</h1>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useRouter } from 'vue-router'
|
||||
import { api } from '../lib/api.js'
|
||||
import { apiUmind, contexto } 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 } })
|
||||
const tenantId = computed(() => Number(props.id))
|
||||
@@ -168,7 +169,7 @@ watch(() => props.id, cargar, { immediate: true })
|
||||
<p class="text-xs text-tenue mt-1">{{ tenant.dominios_permitidos || 'sin dominios configurados' }}</p>
|
||||
</div>
|
||||
<router-link :to="`/tenants/${tenantId ?? id}/ia`" class="btn-ghost">Tu IA</router-link>
|
||||
<router-link :to="`/tenants/${tenantId ?? id}/uso`" class="btn-ghost">📊 Consumo</router-link>
|
||||
<router-link :to="`/tenants/${tenantId ?? id}/uso`" class="btn-ghost inline-flex items-center gap-1.5"><UiIcono nombre="grafico" /> Consumo</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -243,13 +244,13 @@ watch(() => props.id, cargar, { immediate: true })
|
||||
</div>
|
||||
|
||||
<div class="flex gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity shrink-0">
|
||||
<button class="p-1 text-tenue hover:text-texto" title="Editar" @click.prevent.stop="editarAgente(a)">✎</button>
|
||||
<button class="p-1 text-tenue hover:text-texto" title="Editar" @click.prevent.stop="editarAgente(a)"><UiIcono nombre="lapiz" :tam="14" /></button>
|
||||
<button
|
||||
class="p-1 text-tenue hover:text-texto"
|
||||
title="Duplicar: copia el conocimiento y las herramientas a un agente nuevo"
|
||||
@click.prevent.stop="duplicarAgente(a)"
|
||||
>⧉</button>
|
||||
<button class="p-1 text-tenue hover:text-red-600" title="Eliminar" @click.prevent.stop="eliminarAgente(a)">✕</button>
|
||||
><UiIcono nombre="copiar" :tam="14" /></button>
|
||||
<button class="p-1 text-tenue hover:text-red-600" title="Eliminar" @click.prevent.stop="eliminarAgente(a)"><UiIcono nombre="cerrar" :tam="14" /></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,8 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 96 96'%3E%3Crect width='96' height='96' rx='22' fill='%238eb02f'/%3E%3Cpath d='M32,42 V58 A14,14 0 0 0 60,58 V42' fill='none' stroke='%23fff' stroke-width='10' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M60,58 V64' fill='none' stroke='%23fff' stroke-width='10' stroke-linecap='round'/%3E%3Ccircle cx='60' cy='28' r='7' fill='%23fff'/%3E%3C/svg%3E" />
|
||||
<title>uMind Studio</title>
|
||||
<script type="module" crossorigin src="/orchestrator/assets/index-DwOw7la-.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/orchestrator/assets/index-0jLV4t_y.css">
|
||||
<script type="module" crossorigin src="/orchestrator/assets/index-yQWQlH69.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/orchestrator/assets/index-C4iBAJs_.css">
|
||||
</head>
|
||||
<!-- Sin clase de fondo: el color lo pone body en style.css desde los tokens,
|
||||
que son los que cambian con el tema. Una utilidad acá le ganaba a la
|
||||
|
||||
Reference in New Issue
Block a user