CommunityProgramación y desarrollogithub.com

AlbertZJ/dsh-genui-stateful

Renderiza una interfaz de usuario interactiva y estructurada en línea a través del bloque dsh-ui. Úsalo para puntos clave, énfasis, comparaciones, flujos, pasos, estado, datos, demostraciones e interacciones siempre que la presentación estructurada sea más clara que la prosa. Conserva el idioma de la conversación para todo el texto visible al usuario.

¿Qué es dsh-genui-stateful?

Este skill permite a agentes de IA (como Claude Code, Cursor o Codex) generar componentes de interfaz de usuario interactivos y enriquecidos directamente en el texto de las respuestas. Utiliza un bloque de código marcado con `dsh-ui` que contiene una especificación JSON que el cliente renderiza como componentes reales, sin requerir herramientas externas. Es ideal para presentar información de forma más clara que la prosa simple: tablas, gráficos, diagramas, formularios, pasos, líneas de tiempo y más. Soporta múltiples tipos de componentes, desde texto y diseño básico hasta gráficos complejos (ECharts), diagramas Mermaid, escenas 3D, formularios interactivos y componentes de retroalimentación local como autoevaluación. Todo el texto visible al usuario debe estar en el idioma de la conversación, respetando el idioma solicitado por el usuario.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/AlbertZJ/dsh-genui-stateful/tree/main

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

GenUI — Especificación de salida de UI generativa

Idioma: Debe coincidir con el idioma solicitado por el usuario (o el idioma de su mensaje) tanto en la prosa circundante como en todo el texto de la interfaz visible al usuario: títulos, etiquetas, contenido, opciones y explicaciones. Los ejemplos en chino a continuación solo ilustran el esquema; no cambies la conversación al chino después de cargar este skill. Mantén las claves JSON, los tipos de componentes, los ID y las acciones sin cambios. Una solicitud en inglés recibe prosa y texto de UI en inglés; una solicitud en chino recibe prosa y texto de UI en chino. Los tokens <user-language ...> son solo meta-placeholders. Reemplaza cada uno con contenido real en el idioma de la conversación; nunca emitas estos placeholders literalmente.

Puedes generar componentes de UI interactivos en medio de tu respuesta: escribe un bloque dsh-ui (bloque de código con la etiqueta de idioma dsh-ui) que contenga una especificación JSON; el renderizador pintará este bloque como componentes reales y el texto se intercalará normalmente. El componente es parte de la respuesta, no una llamada a herramienta.

{"title":"<texto en el idioma del usuario>","gap":14,"items":[...]}

Reglas estrictas para escribir bloques (lee esta sección primero)

La etiqueta de idioma en la línea de apertura del bloque debe ser siempre dsh-ui, sin puntuación adicional, comillas ni comentarios. El renderizador tolerará mayúsculas/minúsculas, espacios al inicio/final y algunos signos de puntuación al final; otras etiquetas de idioma no serán interpretadas como GenUI y el usuario verá un bloque de código normal.

EscrituraResultado
````dsh-ui`✅ Renderizado
````dsh-ui>`✅ Renderizado con tolerancia; al generarlo, sigue quitando >
````dsh-ui ` con espacio al final✅ Renderizado (trim automático)
```` DSH-UI`✅ Renderizado (ignora mayúsculas/minúsculas)
dshui` / ds-ui`❌ No es una etiqueta válida

Anteriormente, el mismo modelo escribió 182 bloques dsh-ui>` en una misma sesión, lo que causó fallos silenciosos; ahora se tolera esa puntuación final. Al escribir el bloque, **usa solo los seis caracteres `dsh-ui`**, sin puntuación, comillas ni comentarios; el cierre debe ser en una línea separada.

Ubicación del bloque: Solo se escribe en el cuerpo de la respuesta. Si se escribe dentro de reasoning/bloque de pensamiento, no se renderizará y el usuario no lo verá.

Fórmulas: $...$ / \(...\) para fórmulas en línea, $$...$$ / \[...\] para fórmulas independientes; admite matrices, funciones por partes, derivaciones multilínea alineadas y fórmulas dentro de negrita o resaltado. Esta capacidad se comparte en el cuerpo del texto, listas, pares clave-valor, encabezados/celdas de tabla, tarjetas/pasos/líneas de tiempo/títulos de pestañas, preguntas y explicaciones, botones y etiquetas de formularios, indicadores, descripciones de medios y títulos de gráficos. Las barras invertidas en las cadenas JSON deben duplicarse, por ejemplo "content": "\(\\frac{a}{b}\)". El código fuente, los valores de entrada/placeholders, las opciones nativas de selección y el texto de dibujo interno del motor de gráficos siguen su propio formato nativo y no analizan texto enriquecido. La sintaxis matemática sigue KaTeX; no se ejecuta HTML, recursos externos ni scripts; no se admite la compilación de documentos LaTeX ni la carga de paquetes arbitrarios.

Vocabulario de componentes (solo se permiten estos tipos)

Diseño: text row col grid card carousel divider spacer arrow hero Presentación: stat badge progress list table keyvalue avatar image image-group audio video timeline file-tree breadcrumb diff json code callout steps icon blocks Gráficos: chart (barras/línea/dona, con múltiples series) plot (gráfico de funciones matemáticas) echart (gráficos completos de ECharts) Interactivos: button input select checkbox radio switch slider textarea tabs accordion copy link submit Avanzados: svg mermaid diagram scene3d quiz

Diseño

  • text: {"type":"text","size":"display|h1|h2|h3|body|muted|caption","content":"...","align":"left|center|right"?} (tone:"danger|success|warning|info|muted" da color semántico a toda la línea, no uses badge; mono:true usa fuente monoespaciada para hexadecimal/binario/identificadores, sin fondo de código en línea) (size:"display" un escalón más grande que h1, para frases de introducción/apertura, ≤1 por respuesta; center:true es escritura antigua, equivalente a align:"center", las nuevas escrituras usan siempre align)
  • row / col: {"type":"row"|"col","items":[...],"wrap":true?,"spacer":true?,"gap":n?}; row.justify:"center" centra en el eje principal (como encabezados con icono+título); col.width:n (1-100) da a esta columna un porcentaje del ancho de la fila, si no se escribe, se ajusta al contenido
  • grid: {"type":"grid","cols":n,"items":[...]}
  • carousel: {"type":"carousel","title":"<título en idioma del usuario>","items":[{"type":"card","title":"...","items":[...]}],"itemWidth":240?,"gap":3?} — Carrusel nativo de desplazamiento horizontal; máximo 24 elementos, ancho 80–640px y no más del 85% del ancho del carril; ancho predeterminado 240px. gap se multiplica por 4px, si se omite es 12px, los valores negativos se tratan como 0. Los botones de navegación se desplazan el 85% del ancho del carril, solo se muestran cuando el contenido se desborda; en dispositivos táctiles se desliza el carril. No reproduce automáticamente. Cuando el carril está enfocado, se admiten las teclas izquierda/derecha/Inicio/Fin; los formularios dentro de las tarjetas funcionan con las reglas de envío originales. La navegación y el desplazamiento no envían acciones; las acciones declaradas por los componentes hijos se activan según las reglas de interacción existentes. La salida en streaming solo añade tarjetas al final para conservar las rutas de entrada existentes.
  • hero: {"type":"hero","title":"<título en idioma del usuario>","subtitle":"<texto en idioma del usuario>","value":"99.96%","label":"<métrica en idioma del usuario>","delta":"+0.02%","spark":[...],"tone":"accent|success|warning|danger"} — Bloque de portada: ceja + número grande (52px, con conteo de entrada) + título + subtítulo + fondo degradado de tono. Usar como máximo uno por respuesta, colócalo al principio como ancla visual
  • span: cualquier nodo puede tener "span":2 (cuántas columnas ocupa un hijo de grid) — la única primitiva para diseño bento: una tarjeta ancha con span:2 combinada con una tarjeta estrecha, mucho mejor que una columna de bloques apilados
  • card: {"type":"card","title":"...","items":[...]}; "tone":"info|success|warning|danger" da un fondo semántico a la tarjeta (para tarjetas de conclusión/riesgo); "accent":"#f59e0b" especifica un color de énfasis (borde + título + fondo muy claro); "flat":true quita la sombra y usa un fondo neutro muy claro — los paneles en figuras deben usar flat, de lo contrario, cada capa parece una tarjeta flotante, dos capas se ven desordenadas; "eyebrow":"<texto pequeño de introducción en idioma del usuario>" añade una línea de meta texto pequeño encima del título (útil para agrupar varias tarjetas en capítulos — no añadas ceja a cada tarjeta consecutiva del mismo capítulo, ≤3 por respuesta)
  • palette: chart / echart admiten "palette":["#ff8800","#3ecf8e"] para sobrescribir la paleta de colores de categoría (por defecto sigue el tema del anfitrión). Solo se escribe cuando semánticamente es necesario especificar colores (costo=rojo, ganancia=verde), de lo contrario, seguir el tema es más estable
  • divider: {"type":"divider"}; con nombre de capítulo {"type":"divider","label":"<nombre de capítulo en idioma del usuario>"} (línea + etiqueta pequeña centrada, para transiciones de capítulo: cuando un informe pasa del resumen a los datos, o se abre un nuevo capítulo en un álbum de gráficos; no usar entre tarjetas/listas o contenido consecutivo del mismo tema, ≤3 por respuesta); spacer: {"type":"spacer"}

Presentación

  • stat: {"type":"stat","label":"...","value":"...","delta":"+12.4%|-3%"} (si empieza con - automáticamente rojo, + verde); opcional "spark":[3,5,4,8,6] para dibujar una micro línea de tendencia (2–60 valores numéricos finitos); "size":"hero" renderiza números grandes (usar como máximo una vez por respuesta, como ancla visual)
  • badge: {"type":"badge","label":"...","tone":"success|warn|danger|accent","icon":"emoji?"}
  • progress: {"type":"progress","label":"...","value":0-100,"valueLabel":"70%"}; "variant":"ring" dibuja un anillo de progreso, "target":70 marca una escala objetivo en la pista
  • avatar: {"type":"avatar","name":"...","color":"#hex?"}
  • image: {"type":"image","src":"/mmx-files/result.png","alt":"<descripción en idioma del usuario>"} — Muestra direcciones de imagen accesibles por el navegador http(s) o rutas relativas del mismo origen; carga diferida; no admite protocolos file:/data: ni otros locales o activos; "caption":"<pie de foto en idioma del usuario>" añade una línea explicativa debajo de la imagen (fuente/interpretación/atribución; alt es la línea de etiqueta encima de la imagen, ambas tienen funciones diferentes)
  • image-group: {"type":"image-group","title":"<título en idioma del usuario>","items":[{"src":"/mmx-files/a.png","alt":"<descripción>","caption":"<pie>"},{"src":"/mmx-files/b.png","alt":"<descripción>"}]} — Máximo 24 imágenes que se ajustan automáticamente en filas según la relación de aspecto; al hacer clic, se abre un lightbox nativo para ampliar, con teclas izquierda/derecha/Inicio/Fin para navegar entre imágenes, y Esc/botón de cierre para volver y restaurar el foco. Usa las mismas restricciones de direcciones http(s)/relativas que image; no acepta rutas file/data/javascript. Solo para visualización local, no envía acciones; si se necesita una acción del modelo, coloca un botón independiente al lado. Agregar/reordenar conserva el src actual; cuando se elimina la imagen actual, se cierra el visor; el mismo src se distingue por orden de aparición. Las listas vacías y las imágenes que fallan al cargar tienen estados claros. La exportación a HTML no incrusta los bytes de la imagen; la imagen sigue dependiendo de la URL original. Uso completo y criterios de aceptación.
  • audio: {"type":"audio","src":"/mmx-files/result.mp3","alt":"<descripción en idioma del usuario>","loop":true?} — Control nativo de reproducción; reproducido por iniciativa del usuario, no automáticamente; solo http(s) o rutas relativas del mismo origen
  • video: {"type":"video","src":"/mmx-files/result.mp4","alt":"<descripción en idioma del usuario>","poster":"/mmx-files/poster.jpg"?,"loop":true?,"muted":true?,"aspectRatio":"16:9|4:3|1:1|9:16"?} — Controles nativos de reproducción/volumen/pantalla completa; no se reproduce automáticamente
  • list: {"type":"list","items":["..."] o [{"title":"...","desc":"..."}] o nodos anidados (ej. {"type":"badge","label":"TS"})} — Se pueden incrustar nodos en línea (cuentan en el presupuesto de nodos); label/name en los registros se normalizan a title, description/content/text/body/detail se normalizan a desc; si solo hay cuerpo del texto, se usa como título; los campos desconocidos reciben una advertencia
  • table: {"type":"table","columns":["..."],"rows":[["...","..."]],"types":["text|num|delta|bar|badge"]?,"details":[[...]]?,"total":true?} — Los encabezados se ordenan localmente al hacer clic (ascendente/descendente/restablecer, cero viajes de ida y vuelta); consciente de valores numéricos: separadores de miles (1,234), k/m/b, 万/亿, %, símbolos de moneda se comparan como valores reales, las columnas puramente numéricas se alinean a la derecha automáticamente; las celdas con signo se colorean automáticamente (+12.4% verde, -3 rojo, sin necesidad de campos adicionales); types puede especificar por columna: bar (barra de progreso en línea 0-100), ring (anillo pequeño 0-100), spark (la celda escribe "3,5,4,8" para dibujar una micro línea de tendencia), badge (etiqueta cápsula), delta (forzar color de subida/bajada), num (forzar alineación derecha), index (número de fila), group (primera columna como título de grupo: si la fila solo tiene contenido en la primera celda, se renderiza como subtítulo que abarca varias columnas); "total":true agrega una fila de totales (las columnas numéricas se suman automáticamente); "export":true: aparecen dos botones pequeños "Copiar Markdown / Copiar CSV" encima de la tabla (portapapeles local, sin enviar solicitudes); "filter":"id-del-input": vincula la tabla con un input/select, el lector puede filtrar instantáneamente al escribir (filterColumn puede limitar la columna) — cuando hay muchos datos, por defecto se debe configurar uno; "sortField":"id-del-select" usa el valor de un select (nombre de columna) para ordenar; "details" en el mismo orden que rows, el i-ésimo elemento es el contenido expandido de esa fila (puede contener cualquier componente, null = la fila no es expandible) — aparece un cheurón en la primera columna, al hacer clic se expanden los detalles debajo de toda la fila, ideal para "tabla principal + detalles"
  • keyvalue: {"type":"keyvalue","pairs":[{"key":"...","value":"..."}]}
  • timeline: {"type":"timeline","items":[{"title":"...","desc":"...","time":"..."}]}
  • file-tree: {"type":"file-tree","items":[{"name":"...","type":"file|dir","children":[...]?}]} — Las filas de directorio se pueden plegar/expandir al hacer clic (local, cero viajes de ida y vuelta)
  • breadcrumb: {"type":"breadcrumb","items":["<texto en idioma del usuario>","<texto en idioma del usuario>","<texto en idioma del usuario>"]}
  • diff: {"type":"diff","diffs":[{"path":"...","oldText":"..."|null,"newText":"..."}]}
  • json: {"type":"json","value":...} (visor de árbol JSON)
  • code: {"type":"code","lang":"ts","code":"..."}
  • callout: {"type":"callout","tone":"info|success|warning|error","title":"...","content":"..."} — Para énfasis único, aviso o resumen de riesgo; content es una cadena de texto, se pueden usar "\n" en JSON para saltos de línea. Para múltiples elementos independientes, usa list con {title,desc} para mantener la jerarquía ítem por ítem
  • steps: {"type":"steps","current":n,"steps":[{"title":"...","desc":"..."}]}
  • arrow: {"type":"arrow","dir":"down|right","length":16-96,"label":"..."} — Conector de línea fina entre dos bloques en una figura (1.4px de ancho de línea + punta de flecha en V, longitud fija). En diagramas de flujo verticales, úsalo siempre, no uses la flecha de texto "↓": las flechas de carácter siguen la línea base del texto y no abren espacio
  • icon: {"type":"icon","name":"Nodes","tone":"info|success|warning|danger|muted|default","size":16|20|24|32,"tile":true,"label":"<nombre accesible en idioma del usuario>"} — Conjunto de iconos integrados (749 se pueden renderizar fielmente; 64 se cargan instantáneamente en línea, el resto se cargan al usarlos por primera vez, la lista completa está en ICON-NAMES.md). tile:true lo dibuja como un fondo cuadrado redondeado de color claro, para tarjetas de etapa/marcas semánticas; sin tile es un icono en línea que sigue el color del texto. label se puede omitir (puramente decorativo); cuando aparecen pares, solo uno necesita label
  • blocks: {"type":"blocks","title":"...","rows":[{"label":"Inicial","cells":["0","1","2"],"color":"#64748b"}],"columns":n?,"caption":"..."} — Diagrama de bloques de colores: celdas agrupadas por filas. Tres formas con la misma estructura: ① Escalera (cada fila tiene un número diferente de celdas, cada fila divide su ancho en partes iguales, ej. 8→4→2→1) ② Bloques iguales (4 bloques por fila) ③ Diagrama de carriles (columns fijo, escribe "—" o deja vacío en cells). Si se omite color, se usa un fondo claro con el color de énfasis; el color del texto se invierte automáticamente según el brillo (fondo oscuro texto blanco/fondo claro texto negro). Úsalo para estructuras que impliquen "dividir en partes, avanzar por rondas, agrupar para comparar", no escribas coordenadas SVG a mano. caption es obligatorio: una frase que explique qué representa cada celda
    • "variant":"outline" a nivel de fila: borde fino sin relleno, para matrices (cada celda es un recuadro); "variant":"plain": ni siquiera dibuja el borde, para cadenas de bits (1 0 1 0 0 0 0 0 se lee como un número, no como una tabla) — solo las celdas con tone/color tienen fondo
    • Las celdas se pueden escribir como objetos: {"text":"1","tone":"danger","sub":"SF"} — tone (accent/success/warning/danger/muted) da color semántico a una celda individual, sub coloca una línea pequeña debajo de la celda (nombre de bit, número de secuencia, rol). color puede especificar explícitamente un hexadecimal, tiene prioridad sobre tone
    • Ejemplo: {"columns":8,"rows":[{"variant":"outline","cells":[{"text":"1","tone":"danger","sub":"SF"},"0","1","1","0","0","1","0","0"]}],"caption":"El bit más significativo es 1, por lo tanto SF=1."}

Nombres de iconos integrados: los siguientes 64 están incluidos en el paquete y se renderizan de inmediato, elígelos preferentemente de aquí:

Check CheckCircle Info InfoCircle Warning Error X Plus Minus Circle ArrowDown ArrowUp ArrowRight ArrowLeft ArrowCurvedRight Function Functions Operator Atom Nodes Connect Share Branch Grid GridAlt Stack Cube Storage Chart Trending Pulse Speed Timer Clock TableFilled Code SquareCode Terminal File FileCode Folder Link Paperclip Lightbulb Book BookOpen GraduationCap Flask Lab Brain Robot Sparkles ShieldCheck Lock Globe WebsiteNetwork Search MagnifyingGlassSearch PluginPuzzle Settings Bolt Compass

Los otros 685 se cargan bajo demanda (al usarse por primera vez se descargan ~1 MB, y luego están disponibles para toda la página). La lista completa está en ICON-NAMES.md en el mismo directorio de este SKILL — si necesitas adivinar un nombre, léelo o haz grep, no lo enumere en la conversación.

Gráficos

  • chart: {"type":"chart","kind":"bars|line|donut","data":[{"label":"...","value":n,"color":"#hex?"}],"series":[{"label":"...","data":[...]}]?,"horizontal":true?} — bars por defecto; line para tendencias; donut para proporciones; series: bars son barras agrupadas, line son líneas de múltiples series; cada series[].data usa un array de objetos como [{"label":"Q1","value":30},{"label":"Q2","value":45}], no un array de números puros como [30,45]; horizontal:true dibuja barras horizontales (ideal para clasificaciones/etiquetas largas); stacked:true apila las series (composición/proporción a lo largo del tiempo); cuando los segmentos apilados son lo suficientemente altos, el valor se imprime directamente dentro del segmento; al pasar el ratón sobre cualquier barra/segmento/punto/sector, aparece un tooltip instantáneo (apilado muestra el valor del segmento + total). Renderizado v3: ancho adaptable, escala del eje Y 1/2/5, valores negativos de serie única se dibujan realmente por debajo de la línea cero, line con degradado de área y etiquetas X adelgazadas, la leyenda de donut muestra valores y porcentajes. Usa chart para comparaciones rápidas de ≤8 puntos; usa echart para múltiples series, cuando se necesita zoom/interacción o grandes volúmenes de datos
  • plot: {"type":"plot","series":[{"expr":"a*sin(b*x)","label":"...","color":"#hex?","params":[{"name":"a","value":1,"min":0,"max":5,"animateTo":3,"durationMs":4000,"loop":true},{"name":"b","value":1,"min":0.5,"max":5}]}],"xMin":-6.28,"xMax":6.28,"title":"..."} — Gráfico de funciones SVG; series puede tener "kind":"line|area|scatter" (por defecto line; area rellena hasta la línea base; scatter puntos dispersos); params se renderiza como controles deslizantes en tiempo real (al arrastrar se redibuja al instante, el eje Y está fijo = solo cambia la curva, no el eje numérico); el parámetro animateTo muestra un botón de reproducción (animación automática de demostración); SVG se puede arrastrar para desplazar y hacer zoom con la rueda; la expresión admite sin/cos/tan/asin/acos/atan/sqrt/cbrt/exp/log/ln/abs/floor/ceil/round/min/max/pow, constantes pi/e/tau, variable x (otras letras = parámetros)
  • echart: {"type":"echart","title":"...","height":300,"preset":"bar|line|area|pie|scatter","data":[{"label":"...","value":n}],"series":[...]?} — Gráfico completo de ECharts, con efectos visuales muy superiores a chart (degradados, tooltips, animaciones, interacción con la leyenda); modo preset: usa el mismo formato data/series que chart, construye automáticamente la configuración de ECharts con el tema (colores que siguen el tema del anfitrión); Lista de presets (solo escribe preset + data/series/links, el tema se sigue automáticamente): bar · line · area · pie · scatter · radar (cada serie un polígono, los indicadores toman las etiquetas de la primera serie) · gauge (cada dato un medidor, ideal para un solo KPI) · funnel (embudo/conversión) · treemap (volumen/proporción jerárquica) · sankey (flujo, usando links:[{from,to,value}]) · graph (grafo de relaciones, impulsado por links, el tamaño de los nodos varía según el número de conexiones) · heatmap (series como filas, las etiquetas de la primera serie como columnas) · bigline (secuencia larga + zoom integrado) · wordCloud (nube de palabras, data:[{label,value}] donde value es el peso; los colores siguen palette o la paleta del tema) Modo de opción completa: pasa "option":{...} para escribir la configuración nativa de ECharts (admite los gráficos integrados de ECharts y la extensión wordCloud registrada; no se garantiza que otras extensiones de terceros funcionen), las funciones en option se filtran (solo se aceptan datos). Principio de selección: chart es ligero (sin descarga adicional) adecuado para comparaciones rápidas de ≤8 puntos; echart es visualmente más rico (degradados, tooltips, interacción con leyenda, dataZoom), pero descarga bajo demanda ~1 MB de motor; prioriza para múltiples series, pantallas grandes o escenarios interactivos

Interactivos

Local primero (v2.6): Los cambios de estado que la UI puede hacer por sí misma — evaluar, calificar, restablecer, expandir, seleccionar — se realizan localmente al instante, cero viajes de ida y vuelta del modelo. Las acciones solo se usan para cosas que requieren la participación del modelo (generar nuevo contenido, ejecutar herramientas, sugerir el siguiente paso). Los componentes interactivos deben tener action: los botones sin action se renderizan como deshabilitados, el usuario no puede hacer clic; los botones con action muestran una respuesta local de "activado" al hacer clic.

  • button: {"type":"button","label":"...","tone":"primary|danger|success|ghost","full":true?,"small":true?,"icon":"emoji?","action":"refresh"?}
  • Prohibición absoluta: No se deben solicitar ni generar contraseñas, claves de API, tokens de acceso, códigos de recuperación ni ninguna entrada secreta; ante tal solicitud, recházala directamente y explica
  • input: {"type":"input","label":"...","placeholder":"...","inputType":"text|email|color|number","value":"...","action":"name"?,"id":"field-id"?} — color usa el selector de color nativo del navegador, el valor usa #RRGGBB; number se puede usar como entrada numérica Computed; la acción se dispara al perder el foco y al presionar Enter (Enter lleva submit:true); blur solo se envía si el valor ha cambiado (no se produce un viaje vacío si se enfoca y se sale); el payload lleva id para ayudar al modelo a localizar el campo; los valores con id se conservan al refrescar y son recogidos por submit en fields
  • select: {"type":"select","label":"...","options":["...","..."],"selected":índice?,"action":"pick"?,"id":"field-id"?} — selected preselecciona una opción (por defecto muestra un placeholder "Seleccionar...", no preselecciona silenciosamente el primer elemento); las selecciones con id se conservan al refrescar y entran en fields de submit
  • checkbox: {"type":"checkbox","label":"<opción en idioma del usuario>","checked":true?,"action":"toggle"?,"group":"group-id"?} — Por defecto mantiene el comportamiento de action por cada clic; al añadir group se entra en modo de agregación multiselección: las casillas del mismo grupo se pueden marcar/desmarcar repetidamente, los cambios se registran solo localmente, no se envían acciones de ida y vuelta; un submit hermano envía de una vez las etiquetas seleccionadas de ese grupo como un array de cadenas en answers (ej. {"styles":["<opción en idioma del usuario>","<opción en idioma del usuario>"]})
  • slider: {"type":"slider","label":"...","min":0,"max":100,"step":1,"value":n?,"action":"name"?,"id":"field-id"?} — Control deslizante de formulario numérico: muestra el valor en tiempo real; los valores con id se conservan al refrescar y entran en fields de submit (el arrastre se estabiliza con debounce para combinarse en una sola acción)
  • radio: {"type":"radio","label":"<etiqueta en idioma del usuario>","options":["<opción en idioma del usuario>","<opción en idioma del usuario>"],"selected":n?,"action":"pick"?} — Selección única; al añadir "group":"group-id" se entra en modo de agregación: la selección solo se registra localmente, no se envía de ida y vuelta; se puede configurar "answer":índice o etiqueta correctos y "explanation":"<explicación en idioma del usuario>", y en modo radio puro sin otros estados de formulario pendientes, se puede calificar localmente
  • link: {"type":"link","label":"...","href":"https://..."?} — Solo se aceptan protocolos http(s)/mailto; sin href, se renderiza como texto plano (no simula ser cliqueable)
  • submit: {"type":"submit","label":"<acción en idioma del usuario>","action":"grade","groups":["q1","styles"],"resetAction":"redo"?} — Botón de agregación: groups hace referencia a las claves de los miembros de envío en el bloque actual, con fuentes en radio.group, checkbox.group, input.id, textarea.id, select.id, slider.id; las radios deben estar seleccionadas, los grupos de checkbox deben tener al menos un elemento seleccionado, los campos normales deben tener un valor no vacío después de trim. groups controla los miembros necesarios para el envío y el progreso; si no se establece, se puede enviar con al menos un miembro completado. Si el ámbito de radio puro contiene answer y no hay payload fuera del ámbito esta vez, se puede calificar localmente; en otros casos, se envía una vez [genui-action], y el payload continúa recogiendo el estado del formulario completado en el bloque actual.

Skills relacionados