Kit de componentes y UI
Componentes, tematización, estado, expresiones y acciones de las extensiones.
En esta página
- Un componente, una página
- Componentes de página completa
- Reglas de tematización
- Un ejemplo completo
- Catálogo de componentes
- Estructura
- Texto
- Acciones
- Entradas y formularios
- Datos
- Navegación
- Estado y avisos
- Superpuestos
- Gráficos
- Lógica
- Estado y binding
- Expresiones
- Acciones
- CALL_BACKEND: reintento y resultado
- Validación y errores
- Formato antiguo y migración
- Iconos de extensiones
- Logotipos de marca disponibles
- Iconos funcionales recomendados
Las extensiones describen su interfaz como JSON ({ "type", "props", "children" }). BloomX lo dibuja con un kit de componentes propio que hereda, sin que la extensión haga nada, la paleta, el radio, la tipografía y el modo claro/oscuro de la empresa. Esta página es la referencia de componentes, tematización, estado, expresiones y acciones; las herramientas para escribirlos y probarlos están en Herramientas de extensiones.
Un componente, una página#
Cada componente del kit tiene su página propia (/docs/extension-ui/<componente>, por ejemplo BUTTON, FORM o TABLE) con la tabla de props generada del esquema, vista previa en vivo con el renderer real, un simulador (editor de props, validación, estado, contexto, backend simulado y registro de eventos) y un ejemplo tomado de un manifest real. El índice de abajo agrupa todos por categoría.
Una página por componente con su tabla de props, vista previa en vivo, simulador y ejemplo en un manifest real. Generadas desde el esquema. · 75 componentes
Estructura
- STACKApila hijos en vertical con un espacio de la escala.6 props
- ROWColoca hijos en horizontal.5 props
- GRIDRejilla de columnas iguales; en movil se reduce sola.3 props
- CARDTarjeta con titulo opcional. Hereda radio y paleta del tema.7 props
- SECTIONSeccion con encabezado; puede plegarse.5 props
- PAGE_HEADERCabecera de una pagina completa: migas de pan, titulo (h1), estado, descripcion y acciones; muestra su propio estado de carga y error con Reintentar.9 props
- SPLIT_PANEDos paneles (p. ej. lista y detalle): lado a lado en escritorio y apilados en pantallas estrechas. Redimensionable con raton o teclado si `resizable`.8 props
- DIVIDERLinea separadora, con etiqueta opcional.3 props
- SPACEREspacio vacio de la escala.1 props
Texto
- TEXTParrafo de texto plano (nunca HTML).9 props
- HEADINGEncabezado semantico (h1-h6).5 props
- CODECodigo en linea o bloque, con boton de copiar opcional.5 props
- LINKEnlace seguro (solo http, https, mailto, tel o ruta interna).4 props
- MARKDOWNMarkdown seguro (negrita, cursiva, codigo, listas, enlaces). El HTML crudo se muestra como texto.2 props
- ICONIcono decorativo: Lucide, "brand:<slug>" (logotipo de una app integrada) o "initials:<XY>".4 props
Acciones
- BUTTONBoton. Color segun `tone` + `variant`; contraste garantizado por el tema.15 props
- ICON_BUTTONBoton solo con icono; `label` es obligatorio (nombre accesible y tooltip).9 props
- BUTTON_GROUPAgrupa botones; `attached` los une.4 props
- MENUBoton que abre un menu de acciones (teclado: flechas, Enter, Escape).9 props
- IMAGE_BUTTONImagen pulsable (https).3 props
- SMART_REPLY_CHIPSFila de sugerencias de respuesta pulsables.2 props
Entradas y formularios
- INPUTCampo de texto de una linea.23 props
- TEXTAREATexto de varias lineas.17 props
- SELECTLista desplegable nativa.17 props
- CHECKBOXCasilla de verificacion.15 props
- RADIO_GROUPGrupo de opciones excluyentes (flechas para moverse).16 props
- TOGGLEInterruptor (switch) on/off.14 props
- SLIDERControl deslizante numerico.18 props
- DATE_PICKERFecha (valor AAAA-MM-DD).16 props
- TIME_PICKERHora (valor HH:MM).15 props
- COLOR_PICKERSelector de color SOLO para datos del usuario (etiquetas, agendas). El valor es un hex en `state`; nunca estiliza la interfaz.14 props
- FILE_INPUTSeleccion de archivos con limite de tamano; puede leerlos como base64/texto.22 props
- TAG_INPUTLista de etiquetas/valores (Enter o coma anade, Retroceso quita).17 props
- CONTACT_PICKERSelector de contactos de la libreta (correos). El valor es un arreglo de correos.17 props
- FORMFormulario con validacion declarativa y estados (cargando/error/exito) automaticos.9 props
Datos
- TABLETabla con orden, paginacion y seleccion.20 props
- LISTLista de elementos: con `itemTemplate` + `items` (una plantilla por dato) o con hijos LIST_ITEM.7 props
- LIST_ITEMElemento de lista con titulo, descripcion, icono y acciones finales (hijos).7 props
- TIMELINELinea de tiempo vertical de eventos (fecha, titulo, descripcion, icono y tono).3 props
- TREEArbol navegable accesible (WAI-ARIA tree: flechas, Inicio/Fin, Intro). Nodos {id, label, icon?, badge?, children?}.7 props
- KPI_CARDIndicador clave (KPI): valor destacado, variacion, mini-tendencia, estado de carga y error. Pulsable con onClick.13 props
Navegación
- TABSPestanas accesibles (flechas, Inicio/Fin).6 props
- TAB_ITEMPanel de TABS (forma heredada con hijos).2 props
- ACCORDIONSecciones plegables (Enter/Espacio).2 props
- ACCORDION_ITEMSeccion de ACCORDION (forma heredada con hijos).1 props
- STEPPERIndicador de los pasos de un proceso (completado, actual, pendiente, error). Solo informa; para mostrar contenido por pasos usa WIZARD.4 props
- WIZARDAsistente por pasos con indicador de progreso.6 props
Estado y avisos
- BADGEEtiqueta corta de estado.4 props
- CHIPChip (filtro/valor) opcionalmente pulsable o eliminable.7 props
- AVATARAvatar con imagen (https) o iniciales.6 props
- STATMetrica destacada con variacion.7 props
- PROGRESSBarra de progreso (role=progressbar).7 props
- SKELETONMarcador de carga.3 props
- EMPTYEstado vacio con mensaje y accion.5 props
- ALERTAviso en linea (role=alert para danger/warning).6 props
- CALLOUTNota destacada (consejo, advertencia) con hijos.3 props
- LOADINGIndicador de carga.2 props
Superpuestos
Gráficos
- BAR_CHARTBarras simples en SVG propio con colores de tema.6 props
- CHARTGrafico de lineas, barras, area, sectores o anillo con varias series, leyenda y tooltips accesibles (raton y teclado). SVG propio con colores del tema, sin librerias.16 props
- SPARKLINELinea de tendencia minima.5 props
- DONUTAnillo de proporciones con leyenda.5 props
Lógica
- CONDITIONALMuestra `true` o `false` segun `condition`.3 props
- CONDITIONComo CONDITIONAL con `if` y `else`.4 props
- FOR_EACHRepite `template` por cada elemento de `items`.5 props
- REPEATRepite los hijos N veces o por item.4 props
- SWITCHElige un caso segun `value` (forma `cases` o hijos CASE/DEFAULT).3 props
- CASECaso de SWITCH (forma con hijos).1 props
- DEFAULTCaso por defecto de SWITCH.0 props
- SET_VARFija una variable de `state` (no visible).2 props
- HEADLESSSin interfaz: solo ejecuta `onLoad`.0 props
- DEBUGSolo desarrollo: muestra contexto y estado.0 props
Componentes de página completa#
Pensados para un mount PAGE (una página completa en /extensions/<path>). Cada uno tiene su página con vista previa y simulador con datos simulados (sin backend real). Exigen la capacidad `ui.pages.v1` en requires.capabilities; los clientes antiguos no reciben esas versiones.
| Componente | Qué es |
|---|---|
PAGE_HEADER | Cabecera de una pagina completa: migas de pan, titulo (h1), estado, descripcion y acciones; muestra su propio estado de carga y error con Reintentar. |
SPLIT_PANE | Dos paneles (p. ej. lista y detalle): lado a lado en escritorio y apilados en pantallas estrechas. Redimensionable con raton o teclado si resizable. |
KPI_CARD | Indicador clave (KPI): valor destacado, variacion, mini-tendencia, estado de carga y error. Pulsable con onClick. |
CHART | Grafico de lineas, barras, area, sectores o anillo con varias series, leyenda y tooltips accesibles (raton y teclado). SVG propio con colores del tema, sin librerias. |
TIMELINE | Linea de tiempo vertical de eventos (fecha, titulo, descripcion, icono y tono). |
TREE | Arbol navegable accesible (WAI-ARIA tree: flechas, Inicio/Fin, Intro). Nodos {id, label, icon?, badge?, children?}. |
STEPPER | Indicador de los pasos de un proceso (completado, actual, pendiente, error). Solo informa; para mostrar contenido por pasos usa WIZARD. |
La TABLE también ganó, con la misma capacidad: searchable, filtros por columna (columns[].filter), celdas link y status (toneMap, hrefKey), bulkActions, defaultSort y los estados error + onRetry.
Para construir una página completa y enlazarla desde la navegación, sigue Página completa y navegación.
Reglas de tematización#
Regla clave: una extensión no elige colores ni estilos. Solo usa propiedades semánticas y el renderer las traduce a los tokens del tema activo (bg-primary, text-success, border-destructive...), con el contraste exigido por el contrato de temas (Temas empresariales).
| Propiedad | Valores | Uso |
|---|---|---|
tone | neutral primary success warning danger info | Intención: color de marca, éxito, aviso, peligro, información o neutro |
variant | solid soft outline ghost link (botones) · solid soft outline (badges) | Aspecto de la superficie |
size | xs sm md lg xl | Tamaño en escala (botones, iconos, avatares, campos) |
density | compact comfortable spacious | Relleno de tarjetas y filas de tablas |
align / justify | start center end stretch · between around | Alineación en layouts |
gap / padding / spacing | 0 1 2 3 4 5 6 8 10 12 | Escala de 4 px (0, 4, 8, 12, 16, 20, 24, 32, 40, 48 px) |
width | sm md lg xl full | Ancho de modales y paneles |
Lo que se rechaza
Cualquier className, style, color, bg, border, shadow, html, dangerouslySetInnerHTML, evento DOM (onclick...), url(), HTML en textos o color hex en una prop semántica. El validador lo marca como error con su ruta; en ejecución se elimina con aviso (nunca llega al DOM). COLOR_PICKER solo maneja un hex como dato del usuario (etiquetas, agendas), nunca estiliza la interfaz.
- Contraste garantizado: cada par texto/fondo que pinta el kit está declarado y un test lo comprueba en los 8 temas genéricos y en las paletas de empresa de prueba (claro y oscuro). Las superficies "suaves" usan la pareja
card/card-foregroundcon borde de la intención, no tintes translucidos que podrían bajar de 4.5:1. - Valores inválidos no rompen: un
tonedesconocido, ungapfuera de escala o un texto donde se espera un número se corrigen al valor por defecto en ejecución y se avisan en el validador. - Accesible por defecto: roles ARIA, nombres accesibles, foco visible, teclado (flechas, Enter, Espacio, Escape) y foco atrapado en diálogos.
Un ejemplo completo#
{
"type": "STACK",
"props": { "gap": 3 },
"children": [
{ "type": "HEADING", "props": { "level": 3, "content": "Nueva nota" } },
{ "type": "FORM", "props": {
"submitLabel": "Guardar",
"successMessage": "Guardado",
"fields": [
{ "name": "title", "label": "Título", "required": true, "rules": { "maxLength": 80 } },
{ "name": "tags", "label": "Etiquetas", "type": "tags" }
],
"onSubmit": { "action": "CALL_BACKEND", "function": "saveNote",
"retry": { "attempts": 3, "delayMs": 400 } } } },
{ "type": "ALERT", "props": { "tone": "danger", "message": "${state.$error.saveNote}", "hidden": "${!state.$error.saveNote}" } }
]
}Catálogo de componentes#
Generado desde ui-schema.ts. Todas las props admiten expresiones ${...} salvo las de tipo nodo o acción. Cada componente admite además hidden (expresión booleana), onLoad, onLoadWhen y ariaLabel. La galería de componentes los muestra en vivo con ejemplos copiables y en todos los temas.
Estructura#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
STACK | Apila hijos en vertical con un espacio de la escala. | gap(0|1|2|3|4|5|…) align(start|center|end|stretch|baseline) justify(start|center|end|between|around) wrap padding(0|1|2|3|4|5|…) fullWidth |
ROW | Coloca hijos en horizontal. | gap(0|1|2|3|4|5|…) align(start|center|end|stretch|baseline) justify(start|center|end|between|around) wrap padding(0|1|2|3|4|5|…) |
GRID | Rejilla de columnas iguales; en movil se reduce sola. | columns(1|2|3|4|5|6) gap(0|1|2|3|4|5|…) maxHeight(sm|md|lg|xl) |
CARD | Tarjeta con titulo opcional. Hereda radio y paleta del tema. | title description icon tone(neutral|primary|success|warning|danger|info) variant(outline|flat|elevated) density(compact|comfortable|spacious) footer |
SECTION | Seccion con encabezado; puede plegarse. | title description collapsible defaultOpen gap(0|1|2|3|4|5|…) |
PAGE_HEADER | Cabecera de una pagina completa: migas de pan, titulo (h1), estado, descripcion y acciones; muestra su propio estado de carga y error con Reintentar. | title description icon breadcrumbs status actions loading error onRetry* |
SPLIT_PANE | Dos paneles (p. ej. lista y detalle): lado a lado en escritorio y apilados en pantallas estrechas. Redimensionable con raton o teclado si resizable. | startPane endPane ratio(1:3|1:2|1:1|2:1|3:1) resizable gap(0|1|2|3|4|5|…) sticky startLabel endLabel |
DIVIDER | Linea separadora, con etiqueta opcional. | label orientation(horizontal|vertical) spacing(0|1|2|3|4|5|…) |
SPACER | Espacio vacio de la escala. | size(0|1|2|3|4|5|…) |
Texto#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
TEXT | Parrafo de texto plano (nunca HTML). | content variant(default|muted|caption|label|quote) tone(neutral|primary|success|warning|danger|info) size(xs|sm|md|lg|xl) weight(normal|medium|semibold|bold) align(start|center|end) truncate lines mono |
HEADING | Encabezado semantico (h1-h6). | content level(1|2|3|4|5|6) size(xs|sm|md|lg|xl) tone(neutral|primary|success|warning|danger|info) align(start|center|end) |
CODE | Codigo en linea o bloque, con boton de copiar opcional. | content code block copyable language |
LINK | Enlace seguro (solo http, https, mailto, tel o ruta interna). | label url tone(primary|neutral) onClick* |
MARKDOWN | Markdown seguro (negrita, cursiva, codigo, listas, enlaces). El HTML crudo se muestra como texto. | content size(xs|sm|md|lg|xl) |
ICON | Icono decorativo: Lucide, "brand:<slug>" (logotipo de una app integrada) o "initials:<XY>". | name size(xs|sm|md|lg|xl) tone(neutral|primary|success|warning|danger|info) label |
Acciones#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
BUTTON | Boton. Color segun tone + variant; contraste garantizado por el tema. | label icon iconPosition(start|end) tone(neutral|primary|success|warning|danger|info) variant(solid|soft|outline|ghost|link) size(xs|sm|md|lg) fullWidth align(start|center) loading disabled submit showLabel onClick* menuOptions toolbar |
ICON_BUTTON | Boton solo con icono; label es obligatorio (nombre accesible y tooltip). | icon label tone(neutral|primary|success|warning|danger|info) variant(solid|soft|outline|ghost|link) size(xs|sm|md|lg) loading disabled onClick* toolbar |
BUTTON_GROUP | Agrupa botones; attached los une. | attached gap(0|1|2|3|4|5|…) wrap align(start|center|end|between|around) |
MENU | Boton que abre un menu de acciones (teclado: flechas, Enter, Escape). | label icon tone(neutral|primary|success|warning|danger|info) variant(solid|soft|outline|ghost|link) size(xs|sm|md|lg) showLabel items align(start|end) toolbar |
IMAGE_BUTTON | Imagen pulsable (https). | src alt onClick* |
SMART_REPLY_CHIPS | Fila de sugerencias de respuesta pulsables. | suggestions onSelect* |
Entradas y formularios#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
INPUT | Campo de texto de una linea. | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* type(text|email|password|number|search|tel|…) maxLength min max step autoFocus multiline rows onSubmit* |
TEXTAREA | Texto de varias lineas. | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* rows maxLength mono |
SELECT | Lista desplegable nativa. | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* options valueKey labelKey |
CHECKBOX | Casilla de verificacion. | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* checked |
RADIO_GROUP | Grupo de opciones excluyentes (flechas para moverse). | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* options orientation(vertical|horizontal) |
TOGGLE | Interruptor (switch) on/off. | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* |
SLIDER | Control deslizante numerico. | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* min max step showValue |
DATE_PICKER | Fecha (valor AAAA-MM-DD). | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* min max |
TIME_PICKER | Hora (valor HH:MM). | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* step |
COLOR_PICKER | Selector de color SOLO para datos del usuario (etiquetas, agendas). El valor es un hex en state; nunca estiliza la interfaz. | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* |
FILE_INPUT | Seleccion de archivos con limite de tamano; puede leerlos como base64/texto. | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* accept multiple maxSizeMb readAs(none|base64|text) onSelect* onUpload* onSuccess* onError* |
TAG_INPUT | Lista de etiquetas/valores (Enter o coma anade, Retroceso quita). | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* max validate(none|email) suggestions |
CONTACT_PICKER | Selector de contactos de la libreta (correos). El valor es un arreglo de correos. | name label helperText placeholder bind bindTo value defaultValue required disabled readOnly size(xs|sm|md|lg) rules onChange* multiple max contacts |
FORM | Formulario con validacion declarativa y estados (cargando/error/exito) automaticos. | fields submitLabel cancelLabel onSubmit* onCancel* successMessage resetOnSuccess validateOn(submit|blur|change) gap(0|1|2|3|4|5|…) |
Datos#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
TABLE | Tabla con orden, paginacion y seleccion. | columns data rows rowKey selectable(none|single|multiple) bind pageSize density(compact|comfortable|spacious) emptyText loading actions onRowClick* onSelect* caption searchable searchPlaceholder defaultSort bulkActions error onRetry* |
LIST | Lista de elementos: con itemTemplate + items (una plantilla por dato) o con hijos LIST_ITEM. | items itemTemplate empty columns(1|2|3|4) gap(0|1|2|3|4|5|…) variant(plain|divided|cards) maxHeight(sm|md|lg|xl) |
LIST_ITEM | Elemento de lista con titulo, descripcion, icono y acciones finales (hijos). | title description meta icon tone(neutral|primary|success|warning|danger|info) selected onClick* |
TIMELINE | Linea de tiempo vertical de eventos (fecha, titulo, descripcion, icono y tono). | items emptyText loading |
TREE | Arbol navegable accesible (WAI-ARIA tree: flechas, Inicio/Fin, Intro). Nodos {id, label, icon?, badge?, children?}. | items bind label defaultExpanded onSelect* emptyText loading |
KPI_CARD | Indicador clave (KPI): valor destacado, variacion, mini-tendencia, estado de carga y error. Pulsable con onClick. | label value unit delta trend(up|down|flat) invertTrend description icon tone(neutral|primary|success|warning|danger|info) sparkline loading error onClick* |
Navegación#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
TABS | Pestanas accesibles (flechas, Inicio/Fin). | tabs bind variant(underline|pills) onChange* value label |
TAB_ITEM | Panel de TABS (forma heredada con hijos). | label value |
ACCORDION | Secciones plegables (Enter/Espacio). | sections multiple |
ACCORDION_ITEM | Seccion de ACCORDION (forma heredada con hijos). | title |
STEPPER | Indicador de los pasos de un proceso (completado, actual, pendiente, error). Solo informa; para mostrar contenido por pasos usa WIZARD. | steps current orientation(horizontal|vertical) onSelect* |
WIZARD | Asistente por pasos con indicador de progreso. | steps nav(auto|manual) backLabel nextLabel finishLabel onFinish* |
Estado y avisos#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
BADGE | Etiqueta corta de estado. | label tone(neutral|primary|success|warning|danger|info) variant(solid|soft|outline) size(sm|md) |
CHIP | Chip (filtro/valor) opcionalmente pulsable o eliminable. | label tone(neutral|primary|success|warning|danger|info) icon selected removable onClick* onRemove* |
AVATAR | Avatar con imagen (https) o iniciales. | src name initials alt size(xs|sm|md|lg|xl) tone(neutral|primary|success|warning|danger|info) |
STAT | Metrica destacada con variacion. | label value delta trend(up|down|flat) description icon tone(neutral|primary|success|warning|danger|info) |
PROGRESS | Barra de progreso (role=progressbar). | value max label tone(neutral|primary|success|warning|danger|info) size(xs|sm|md|lg) showValue indeterminate |
SKELETON | Marcador de carga. | variant(text|circle|rect) lines size(xs|sm|md|lg|xl) |
EMPTY | Estado vacio con mensaje y accion. | icon title description action* actionLabel |
ALERT | Aviso en linea (role=alert para danger/warning). | tone(neutral|primary|success|warning|danger|info) title message description icon dismissible |
CALLOUT | Nota destacada (consejo, advertencia) con hijos. | tone(neutral|primary|success|warning|danger|info) title icon |
LOADING | Indicador de carga. | label size(xs|sm|md|lg) |
Superpuestos#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
MODAL | Marco de un OVERLAY (titulo + contenido). Con open/bind se comporta como dialogo propio accesible. | title description icon width(sm|md|lg|xl|full) open bind footer onLoad* onLoadWhen onClose* |
DRAWER | Panel lateral accesible. | title side(left|right) width(sm|md|lg) open bind footer onClose* |
POPOVER | Contenido flotante anclado a un disparador. | trigger triggerLabel title align(start|end) |
TOOLTIP | Ayuda al pasar el raton o enfocar (Escape cierra). | text |
Gráficos#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
BAR_CHART | Barras simples en SVG propio con colores de tema. | data orientation(vertical|horizontal) showValues height(sm|md|lg) tone(neutral|primary|success|warning|danger|info) title |
CHART | Grafico de lineas, barras, area, sectores o anillo con varias series, leyenda y tooltips accesibles (raton y teclado). SVG propio con colores del tema, sin librerias. | kind(line|bar|area|pie|donut) labels series data stacked showLegend showGrid valueFormat(number|compact|percent) unit height(sm|md|lg) centerLabel title description emptyText loading error |
SPARKLINE | Linea de tendencia minima. | values tone(neutral|primary|success|warning|danger|info) height(sm|md|lg) area label |
DONUT | Anillo de proporciones con leyenda. | data centerLabel size(xs|sm|md|lg|xl) showLegend title |
Lógica#
| Componente | Qué es | Props (enums entre paréntesis; `*` = acción) |
|---|---|---|
CONDITIONAL | Muestra true o false segun condition. | condition true false |
CONDITION | Como CONDITIONAL con if y else. | if true false else |
FOR_EACH | Repite template por cada elemento de items. | items as index template empty |
REPEAT | Repite los hijos N veces o por item. | count items as index |
SWITCH | Elige un caso segun value (forma cases o hijos CASE/DEFAULT). | value cases default |
CASE | Caso de SWITCH (forma con hijos). | value |
DEFAULT | Caso por defecto de SWITCH. | — |
SET_VAR | Fija una variable de state (no visible). | name value |
HEADLESS | Sin interfaz: solo ejecuta onLoad. | — |
DEBUG | Solo desarrollo: muestra contexto y estado. | — |
Estado y binding#
statedel manifest define el valor inicial; cada mount y cada overlay tiene su propio estado. Lee con${state.a.b}y escribe conSET_STATE(keyadmite rutas"a.b").- Binding bidireccional:
bind: "form.email"en cualquier entrada lee y escribestate.form.email;defaultValuese aplica una vez si aún no hay valor;onChangerecibevalue. - Dentro de un
FORM, las entradas connamese registran solas: validan al enviar y sus valores viajan enformData. - Carga y error automáticos:
CALL_BACKEND/CALL_APImantienenstate.$loading.<clave>ystate.$error.<clave>(la clave eskeyo el nombre de la función). UnBUTTONcuyoonClickllama al backend muestra solo su carga, se bloquea y evita el doble envío; unFORMmuestra cargando, el error del backend ysuccessMessage.
{
"state": { "query": "", "rows": [] },
"mounts": [{ "point": "PAGE", "path": "notas", "component": {
"type": "STACK", "children": [
{ "type": "INPUT", "props": { "label": "Buscar", "type": "search", "bind": "query" } },
{ "type": "BUTTON", "props": { "label": "Buscar",
"onClick": { "action": "CALL_BACKEND", "function": "search", "args": { "q": "${state.query}" },
"resultKey": "rows" } } },
{ "type": "TABLE", "props": { "rows": "${state.rows}", "columns": [ { "key": "title", "label": "Título", "sortable": true } ],
"loading": "${state.$loading.search}", "pageSize": 10 } }
] } }]
}Expresiones#
Las cadenas ${...} se evalúan sin `eval` con un lenguaje pequeño (los argumentos de filtro son expresiones: los textos van entre comillas, como pluck:'id'; un identificador suelto es una variable): literales, context.x, state.x, variables del evento (value, result, formData, row, error, item), acceso a.b, a?.b, a[0], operadores + - * / % == != < > <= >= && || ! ?: y filtros con |. No se pueden llamar funciones ni salir del alcance dado.
| Filtros | Ejemplo |
|---|---|
truncate:N upper lower trim capitalize replace:'a':'b' split:',' | ${context.subject | truncate:40} |
length first last slice:a:b join:', ' includes:x pluck:'clave' sum keys | ${state.rows | pluck:'id' | join:', '} |
round:N floor ceil abs number date datetime | ${result.total | round:2} |
default:'x' yesno:'si':'no' plural:'uno':'otros' not json | ${state.n | plural:'mensaje':'mensajes'} |
|| devuelve el primer valor no vacío (el valor indefinido, null y la cadena vacía son vacíos; 0 y false no). Una expresión inválida produce vacío en ejecución y un error con ruta y posición en el validador (checkExpression).
Acciones#
Una acción es { "action": "NOMBRE", ... }, un arreglo o { "actions": [...] }; se ejecutan en orden y cada paso ve el estado de los anteriores. onSuccess recibe result; onError recibe error. Sin onError, un fallo muestra un aviso con el motivo (toastOnError: false lo silencia) y se registra para el autor.
| Acción | Obligatorios | Opcionales |
|---|---|---|
SET_STATE | key | value |
MERGE_STATE | key | value |
MAP_ARRAY | source template | target |
FILTER_ARRAY | source condition | target |
SET_LOADING | key | value |
OPEN_OVERLAY | targetId | extensionId overlays passArgs passResult |
CLOSE_OVERLAY | — | — |
OPEN_URL | url | — |
NAVIGATE | path | — |
REFRESH | — | — |
DELAY | — | ms |
CONFIRM | — | message onConfirm onCancel |
CALL_BACKEND | function | args params key resultKey retry toastOnError onSuccess onError |
CALL_API | url | method headers body args params emitEvent key resultKey retry toastOnError onSuccess onError |
TOAST | message | variant tone |
COPY_TO_CLIPBOARD | text | successMessage |
INSERT_CONTENT | content | closeOverlay |
APPEND_BODY | content | closeOverlay |
SET_SUBJECT | — | subject value ifEmpty |
ADD_ATTACHMENT | — | attachment url filename mimeType contentBase64 size |
SET_CONTEXT_VALUE | key | value |
NEXT_STEP | — | — |
PREV_STEP | — | — |
SECURE_SAVE | key | value onSuccess onError |
SECURE_READ | key | targetState onSuccess onError |
OAUTH_CONNECT | provider | url |
OAUTH_DISCONNECT | provider | onSuccess |
CALL_BACKEND: reintento y resultado#
retry: { attempts: 1-5, delayMs: 0-10000, backoff: "none"|"linear"|"exponential" }reintenta ante error de red osuccess:false.resultKeyguarda el resultado enstate[resultKey];argsgana sobre los campos del formulario que disparó la acción.NAVIGATEsolo admite rutas internas (/...),OPEN_URLsolohttps/http/mailto/tel,CALL_APIsolo rutas del propio origen. El resto se bloquea con un aviso claro.CONFIRMabre un diálogo accesible y encadenaonConfirm/onCancel.
Validación y errores#
- Antes de pintar,
ExtensionLoadervalida el manifest y el UI. Un mount con errores se sustituye por un estado de error amable (el resto de la extensión y de la aplicación siguen funcionando) y el problema se registra para el autor: lo ves en /extensions (errores de ejecución por extensión) y en la consola. - Los errores llevan la ruta exacta:
mounts[0].component.children[2].props.tone: Valor no permitido "sucsess"; permitidos: neutral, primary, ... (¿quisiste decir success?). - Límites duros por UI: maxNodes 600 · maxDepth 24 · maxString 20000 · maxArray 500 · maxBytes 262144.
- Formularios:
rulesadmiterequired,minLength,maxLength,min,max,minItems,maxItems,pattern,email,urlymessage(sustituye al texto genérico).validateOn:submit,blur(por defecto) ochange. Los patrones con cuantificadores anidados se rechazan.
Formato antiguo y migración#
Los manifests existentes siguen funcionando: migrateLegacyUi los adapta al cargar y avisa de lo obsoleto (sin tocar el manifest). Conviene migrarlos con el botón Migrar del playground o con el validador.
| Antes | Ahora |
|---|---|
COLUMN, FLEX, BOX, BLOCK | STACK / ROW con gap, align, justify |
SEPARATOR, EMPTY_STATE, DATA_TABLE, CODE_BLOCK, FILE_UPLOAD, CODE_EDITOR | DIVIDER, EMPTY, TABLE, CODE, FILE_INPUT, TEXTAREA (con mono) |
BUTTON variant: "primary"|"secondary"|"destructive" | tone + variant (solid soft outline ghost link) |
BUTTON con menuOptions | MENU con items |
TEXT variant: "error"|"success"|"h4"|"body" | ALERT tone · HEADING · TEXT variant: "quote" |
ALERT variant, BADGE variant | tone (+ variant de superficie) |
bindTo, INPUT multiline | bind, TEXTAREA |
TABS/ACCORDION con hijos TAB_ITEM/ACCORDION_ITEM | tabs / sections con content |
className, style, color, bg... | Se eliminan con aviso: usa tone, variant, size, gap |
IFRAME (HTML en una prop) | Ya no se admite: usa MARKDOWN o componentes |
Iconos de extensiones#
El campo icon (en el manifest y en cualquier prop icon o name de un icono) admite tres formas: `brand:<slug>` (logotipo de una app integrada), `lucide:<Nombre>` (icono funcional) e `initials:<XY>` (1 a 3 letras). El nombre Lucide sin esquema ("Mail") sigue funcionando. No hay URLs de imagen arbitrarias: los logotipos se cargan de forma asíncrona desde el backend de BloomX (/api/extensions/icons/…, con un marcador de letra instantáneo y sin saltos de maquetación), así que cambiar un logo no exige desplegar ni recargar. El icon también puede ser un data URL data:image/png|webp|svg+xml;base64,… (máx. 64 KB, validado).
{
"id": "acme-zoom",
"name": "Acme Zoom",
"icon": "brand:zoom",
"i18n": {
"es": { "name": "Zoom", "description": "Crea reuniones de Zoom" },
"en": { "name": "Zoom", "description": "Create Zoom meetings" }
},
"mounts": [{ "point": "COMPOSER_TOOLBAR", "component": { "type": "BUTTON", "props": {
"label": "Zoom", "icon": "brand:zoom",
"toolbar": {
"label": { "es": "Reunión de Zoom", "en": "Zoom meeting" },
"description": { "es": "Crea una reunión de Zoom", "en": "Create a Zoom meeting" }
},
"onClick": { "action": "OPEN_OVERLAY", "targetId": "zoom-modal" } } } }]
}Los logotipos se pintan con su color oficial sobre una ficha neutra. Si ese color no contrasta al menos 3:1 con el tema activo (Notion negro en un tema oscuro, por ejemplo) se usa una variante más clara u oscura del mismo tono; en menús densos y botones deshabilitados se usa el modo monocromo. El icono es decorativo: el nombre accesible lo da el botón. name y description son el respaldo; i18n.<idioma> los localiza, y el tooltip de una acción usa toolbar.label y toolbar.description por idioma (aparece a los 400 ms, máximo 280 px, el texto envuelve y Esc lo cierra).
Logotipos de marca disponibles#
brand:googlemeet (Google Meet), brand:googlecalendar (Google Calendar), brand:googledrive (Google Drive), brand:googlesheets (Google Sheets), brand:googledocs (Google Docs), brand:googlegemini (Google Gemini), brand:gmail (Gmail), brand:google (Google), brand:zoom (Zoom), brand:notion (Notion), brand:trello (Trello), brand:hubspot (HubSpot), brand:giphy (GIPHY), brand:zoho (Zoho), brand:github (GitHub), brand:gitlab (GitLab), brand:jira (Jira), brand:linear (Linear), brand:asana (Asana), brand:clickup (ClickUp), brand:airtable (Airtable), brand:stripe (Stripe), brand:dropbox (Dropbox), brand:todoist (Todoist), brand:calendly (Calendly), brand:discord (Discord), brand:whatsapp (WhatsApp), brand:telegram (Telegram), brand:anthropic (Anthropic), brand:microsoft (Microsoft), brand:microsoftteams (Microsoft Teams), brand:slack (Slack)
Marcas que no están en el conjunto de logotipos libres (retiradas a petición del titular): se muestran como una ficha neutra con su inicial y nunca se dibujan a mano. brand:microsoftoutlook (Microsoft Outlook), brand:salesforce (Salesforce), brand:openai (OpenAI).
Iconos funcionales recomendados#
| Icono | Función |
|---|---|
lucide:ShieldCheck | Prevencion de fuga de datos (DLP) |
lucide:PenLine | Firma |
lucide:Languages | Traductor |
lucide:Sparkles | Resumen con IA |
lucide:FolderTree | Organizador |
lucide:Lock | Sellado (candado) |
lucide:Users | Grupos de correo |
lucide:Webhook | Webhooks |
lucide:SquareSlash | Comandos con / |
lucide:Reply | Respuesta inteligente |
lucide:WandSparkles | Asistente de redaccion |
lucide:CalendarClock | Citas |
lucide:RefreshCw | Sincronizacion |
lucide:Video | Videollamada |
lucide:CalendarDays | Calendario |
lucide:Users | Contactos |
lucide:Bot | Asistente de IA |
lucide:Workflow | Automatizacion |
lucide:Puzzle | Extension generica |
Marcas registradas
Los logotipos pertenecen a sus titulares y se usan solo para identificar la integración con ese servicio; no implican patrocinio ni afiliación. Proceden de simple-icons (CC0-1.0).
Límites
El HTML sigue siendo texto: MARKDOWN solo admite un subconjunto (negrita, cursiva, código, listas, encabezados, enlaces seguros). Los gráficos son SVG propios sin ejes complejos ni animaciones. CONTACT_PICKER usa la libreta del usuario (/api/contacts/suggestions). El backend compartido valida el manifest pero aún no valida el UI al publicar: la comprobación de UI la hacen el validador CLI y el frontend al cargar.