GenUI — Especificação de Saída de UI Generativa
Idioma: Corresponda ao idioma solicitado pelo usuário (caso contrário, o idioma da mensagem dele) tanto na prosa ao redor quanto em todo texto da interface visível ao usuário: títulos, rótulos, conteúdo, opções e explicações. Os exemplos em chinês abaixo ilustram apenas o esquema; não mude a conversa para chinês após carregar esta skill. Mantenha chaves JSON, tipos de componente, IDs e ações inalterados. Uma solicitação em inglês gera prosa e texto de UI em inglês; uma solicitação em chinês gera prosa e texto de UI em chinês. <user-language ...> são meta-placeholders apenas. Substitua cada um por conteúdo real no idioma da conversa; nunca emita esses placeholders literalmente.
Você pode inserir componentes de UI interativos no meio do texto da resposta: escreva um fence dsh-ui (bloco de código com tag de linguagem dsh-ui) contendo uma especificação JSON. O renderizador desenhará este bloco inteiro como componentes reais, com o texto fluindo normalmente antes e depois. O componente é parte da resposta, não uma chamada de ferramenta.
{"title":"<texto no idioma do usuário>","gap":14,"items":[...]}
Regras Obrigatórias para o Fence (Leia esta seção primeiro)
A tag de linguagem na linha de abertura do fence deve ser sempre dsh-ui, sem adicionar pontuação, aspas ou comentários. O renderizador tolera diferenças de maiúsculas/minúsculas, espaços no início/fim e alguma pontuação no final; outras tags de linguagem não serão tratadas como GenUI, e o usuário verá um bloco de código comum.
| Escrita | Resultado |
|---|---|
```dsh-ui | ✅ Renderiza |
```dsh-ui> | ✅ Renderiza com tolerância; ao gerar, ainda remova > |
```dsh-ui com espaço no final da linha | ✅ Renderiza (trim automático) |
``` DSH-UI | ✅ Renderiza (ignora maiúsculas/minúsculas) |
```dshui / ```ds-ui | ❌ Não é uma tag válida |
Anteriormente, o mesmo modelo escreveu 182 fences ```dsh-ui> na mesma sessão, causando falhas silenciosas; agora a tolerância para essa pontuação final foi unificada. Ao escrever o fence, use apenas os seis caracteres dsh-ui, sem qualquer pontuação, aspas ou comentários; o fechamento deve ser uma linha separada contendo apenas ```.
Posição do fence: escreva apenas no corpo da resposta. Escrever dentro de blocos de reasoning/pensamento não será renderizado e o usuário não verá.
Fórmulas: $...$ / \(...\) para fórmulas inline, $$...$$ / \[...\] para fórmulas em bloco; suporta matrizes, funções por partes, deduções em várias linhas alinhadas e fórmulas dentro de texto em negrito/destaque. Corpo do texto, listas, pares chave-valor, cabeçalhos/células de tabela, títulos de cartões/etapas/linhas do tempo/abas, perguntas e instruções, rótulos de botões e formulários, métricas, legendas de mídia e títulos externos de gráficos compartilham essa capacidade. As barras invertidas em strings JSON devem ser duplicadas, ex.: "content": "\\(\\frac{a}{b}\\)". Código fonte, valores/placeholders de entrada, opções nativas de dropdown e texto de plotagem interno do mecanismo de gráficos ainda seguem seus próprios formatos nativos, sem análise de rich text. A sintaxe matemática segue o KaTeX; não execute HTML, recursos externos ou scripts; não suporta compilação de documentos LaTeX ou carregamento de pacotes arbitrários.
Vocabulário de Componentes (Apenas esses tipos são permitidos)
Layout: text row col grid card carousel divider spacer arrow hero
Exibição: 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/linha/donut, multi-série) plot (gráfico de função matemática) echart (gráfico ECharts completo)
Interação: button input select checkbox radio switch slider textarea tabs accordion copy link submit
Avançado: svg mermaid diagram scene3d quiz
Layout
- text:
{"type":"text","size":"display|h1|h2|h3|body|muted|caption","content":"...","align":"left|center|right"?}(tone:"danger|success|warning|info|muted"adiciona cor semântica a toda a linha, sem usar badge;mono:trueusa fonte monoespaçada, para hexadecimais/binários/identificadores, sem fundo de código inline) (size:"display"é um tamanho maior que o título, para frases de abertura/destaque, ≤1 por resposta;center:trueé a escrita antiga, equivalente aalign:"center", sempre usealignpara novas escritas) - row / col:
{"type":"row"|"col","items":[...],"wrap":true?,"spacer":true?,"gap":n?};row.justify:"center"centraliza no eixo principal (como cabeçalho de ícone + título);col.width:n(1-100) define a largura da coluna como porcentagem da largura da linha; se não for especificado, a largura é baseada no conteúdo - grid:
{"type":"grid","cols":n,"items":[...]} - carousel:
{"type":"carousel","title":"<título no idioma do usuário>","items":[{"type":"card","title":"...","items":[...]}],"itemWidth":240?,"gap":3?}— Rolagem horizontal de nós nativos; máximo de 24 itens, largura 80–640px e não excedendo 85% da largura do trilho; largura padrão 240px.gapmultiplicado por 4px, omitido é 12px, valores negativos tratados como 0. Botões de navegação rolam 85% da largura do trilho a cada clique, exibidos apenas quando o conteúdo transborda; dispositivos de toque deslizam o trilho diretamente. Sem reprodução automática. Quando o trilho está focado, suporta teclas Setas / Home / End; formulários dentro de cartões funcionam de acordo com as regras de envio originais. Navegação e deslizamento não disparam action; actions declaradas por componentes filhos são acionadas de acordo com as regras de interação existentes. Saída em streaming apenas anexa cartões ao final para preservar caminhos de entrada existentes. - hero:
{"type":"hero","title":"<título no idioma do usuário>","subtitle":"<texto no idioma do usuário>","value":"99.96%","label":"<métrica no idioma do usuário>","delta":"+0.02%","spark":[...],"tone":"accent|success|warning|danger"}— Bloco de capa: sobretítulo + número grande (52px, com contagem de entrada animada) + título + subtítulo + fundo gradiente com tom. Use no máximo um por resposta, coloque-o no início como âncora visual - span: Qualquer nó pode receber
"span":2(quantas colunas um nó filho de grid ocupa) — a única primitiva para layout bento: um cartão largospan:2combinado com um cartão estreito fica muito melhor do que uma coluna de blocos empilhados - card:
{"type":"card","title":"...","items":[...]};"tone":"info|success|warning|danger"dá ao cartão uma cor de fundo semântica (para cartões de conclusão/risco);"accent":"#f59e0b"especifica uma cor de destaque (borda + título + fundo muito claro);"flat":trueremove sombras e usa um fundo neutro muito claro — painéis em figuras devem usar flat, caso contrário cada camada parecerá um cartão flutuante, sujando o visual após algumas camadas;"eyebrow":"<pequeno subtítulo no idioma do usuário>"adiciona uma linha meta pequena acima do título (use para dividir cartões em capítulos — não coloque em todos os cartões do mesmo capítulo contínuo, ≤3 por resposta) - palette:
chart/echartsuportam"palette":["#ff8800","#3ecf8e"]para substituir a paleta de cores de categorias (padrão segue o tema do host). Escreva apenas quando semanticamente precisar especificar cores (custo=vermelho, ganho=verde), caso contrário, seguir o tema é mais estável - divider:
{"type":"divider"}; com nome de capítulo{"type":"divider","label":"<nome do capítulo no idioma do usuário>"}(linha + pequeno rótulo centralizado, para mudança de capítulo: ao passar de resumo para dados, abrir um novo capítulo em um álbum de gráficos, use uma vez; não use entre cartões/listas de conteúdo contínuo do mesmo tópico, ≤3 por resposta); spacer:{"type":"spacer"}
Exibição
- stat:
{"type":"stat","label":"...","value":"...","delta":"+12.4%|-3%"}(-no início automaticamente vermelho,+verde); opcional"spark":[3,5,4,8,6]desenha uma linha de micro-tendência (2–60 valores numéricos finitos);"size":"hero"renderiza números gigantes (use no máximo uma vez por resposta, como âncora visual) - badge:
{"type":"badge","label":"...","tone":"success|warn|danger|accent","icon":"emoji?"} - progress:
{"type":"progress","label":"...","value":0-100,"valueLabel":"70%"};"variant":"ring"desenha progresso circular;"target":70marca uma escala alvo no trilho - avatar:
{"type":"avatar","name":"...","color":"#hex?"} - image:
{"type":"image","src":"/mmx-files/result.png","alt":"<descrição no idioma do usuário>"}— Exibe endereços de imagem http(s) acessíveis pelo navegador ou relativos à mesma origem; carregamento lento; não suporta protocolos locais/ativos comofile:/data:;"caption":"<legenda no idioma do usuário>"adiciona uma linha de explicação abaixo da imagem (fonte/interpretação/atribuição;alté a linha de rótulo acima da imagem, ambas com funções diferentes) - image-group:
{"type":"image-group","title":"<título no idioma do usuário>","items":[{"src":"/mmx-files/a.png","alt":"<descrição>","caption":"<legenda>"},{"src":"/mmx-files/b.png","alt":"<descrição>"}]}— Máximo de 24 imagens que se ajustam por proporção e quebram em novas linhas; clique para abrir em lightbox nativo, teclas Seta / Home / End para navegar, Esc / botão de fechar para retornar e restaurar o foco. Segue as mesmas restrições de http(s)/URL relativa deimage; não aceitafile/data/javascript. Apenas visualização local, não dispara action; se precisar de ação do modelo, coloque um botãobuttonseparado ao lado. Anexar/reordenar preserva osrcatual; quando a imagem atual é removida, o visualizador fecha; mesmosrcé diferenciado pela ordem de ocorrência. Listas vazias e falhas de carregamento de imagem têm estados explícitos. Exportar HTML não incorpora bytes de imagem, as imagens ainda dependem do endereço original. Uso completo e verificação. - audio:
{"type":"audio","src":"/mmx-files/result.mp3","alt":"<descrição no idioma do usuário>","loop":true?}— Controles nativos; reprodução iniciada pelo usuário, não automática; apenas http(s) ou URL relativa à mesma origem - video:
{"type":"video","src":"/mmx-files/result.mp4","alt":"<descrição no idioma do usuário>","poster":"/mmx-files/poster.jpg"?,"loop":true?,"muted":true?,"aspectRatio":"16:9|4:3|1:1|9:16"?}— Controles nativos de reprodução/volume/tela cheia; não reproduz automaticamente - list:
{"type":"list","items":["..."] ou [{"title":"...","desc":"..."}] ou nós aninhados (ex.: {"type":"badge","label":"TS"})}— Nós inline podem ser incorporados (contam para o orçamento de nós); em registros,label/namesão normalizados paratitle,description/content/text/body/detailsão normalizados paradesc; se apenas o corpo do texto estiver presente, ele é usado como título; campos desconhecidos geram um aviso - table:
{"type":"table","columns":["..."],"rows":[["...","..."]],"types":["text|num|delta|bar|badge"]?,"details":[[...]]?,"total":true?}— Clique no cabeçalho para ordenação local (crescente/decrescente/restaurar, zero ida e volta); reconhecimento numérico: separador de milhares (1.234),k/m/b,万/亿,%, símbolos de moeda são comparados como valores numéricos reais, colunas puramente numéricas são automaticamente alinhadas à direita; células com sinal são coloridas automaticamente (+12.4%verde,-3vermelho, nenhum campo extra necessário);typespode especificar por coluna:bar(barra de progresso inline 0-100),ring(pequeno anel 0-100),spark(escreva"3,5,4,8"na célula para desenhar uma linha de micro-tendência),badge(rótulo cápsula),delta(força cor de variação),num(força alinhamento à direita),index(número da linha),group(primeira coluna como título de grupo: se a linha tiver conteúdo apenas na primeira célula, renderiza como subtítulo de várias colunas);"total":trueadiciona uma linha de total (colunas numéricas são somadas automaticamente);"export":true: dois pequenos botões "Copiar Markdown / Copiar CSV" aparecem acima da tabela (apenas área de transferência local, sem requisições);"filter":"id_do_input": vincula a tabela a um input/select, o leitor digita para filtrar instantaneamente (filterColumnpode limitar a coluna) — com muitos dados, deve-se configurar um por padrão;"sortField":"id_do_dropdown"usa o valor do dropdown (nome da coluna) para ordenar;detailsna mesma ordem derows, o i-ésimo item é o conteúdo expandido daquela linha (pode conter qualquer componente,null= linha não expansível) — um chevron aparece na primeira coluna, clicar expande os detalhes abaixo da linha, ideal para "tabela principal + detalhes" - keyvalue:
{"type":"keyvalue","pairs":[{"key":"...","value":"..."}]} - timeline:
{"type":"timeline","items":[{"title":"...","desc":"...","time":"..."}]} - file-tree:
{"type":"file-tree","items":[{"name":"...","type":"file|dir","children":[...]?}]}— Linhas de diretório podem ser clicadas para recolher/expandir (local, zero ida e volta) - breadcrumb:
{"type":"breadcrumb","items":["<texto no idioma do usuário>","<texto no idioma do usuário>","<texto no idioma do usuário>"]} - diff:
{"type":"diff","diffs":[{"path":"...","oldText":"..."|null,"newText":"..."}]} - json:
{"type":"json","value":...}(visualizador de árvore JSON) - code:
{"type":"code","lang":"ts","code":"..."} - callout:
{"type":"callout","tone":"info|success|warning|error","title":"...","content":"..."}— Para ênfase única, lembrete ou resumo de risco;contenté uma string de texto, pode usar"\n"do JSON para quebrar linhas. Para múltiplos itens independentes, uselistcom{title,desc}para manter a hierarquia item por item - steps:
{"type":"steps","current":n,"steps":[{"title":"...","desc":"..."}]} - arrow:
{"type":"arrow","dir":"down|right","length":16-96,"label":"..."}— Conector de linha fina entre dois blocos em um diagrama (largura de linha 1.4px + seta em V, comprimento fixo). Use sempre para fluxogramas de cima para baixo, não use o caractere↓: as setas de caractere seguem a linha de base do texto, não abrem espaçamento - icon:
{"type":"icon","name":"Nodes","tone":"info|success|warning|danger|muted|default","size":16|20|24|32,"tile":true,"label":"<nome acessível no idioma do usuário>"}— Conjunto de ícones embutidos (749 podem ser renderizados fielmente; 64 carregam instantaneamente inline, os demais são carregados no primeiro uso, lista completa em ICON-NAMES.md).tile:truedesenha como fundo quadrado arredondado claro, usado para cartões de etapa/marcação semântica; semtile, é um ícone inline que segue a cor do texto.labelé opcional (puramente decorativo); quando aparecem em pares, apenas um recebe label - blocks:
{"type":"blocks","title":"...","rows":[{"label":"Inicial","cells":["0","1","2"],"color":"#64748b"}],"columns":n?,"caption":"..."}— Diagrama de blocos coloridos: células agrupadas em linhas. Três formas, mesma estrutura: ① Diagrama de escada (cada linha tem número diferente de células, cada linha divide a largura igualmente, ex.: 8→4→2→1) ② Blocos iguais (4 blocos por linha) ③ Diagrama de pistas (número fixo de colunas emcolumns, escreva"—"ou deixe vazio emcells). Secolorfor omitido, usa fundo claro da cor de destaque; a cor do texto é invertida automaticamente com base no brilho (texto branco em fundo escuro / texto preto em fundo claro). Use para estruturas que envolvam "dividir em partes, progredir rodada a rodada, comparar grupos", não escreva coordenadas SVG manualmente.captioné obrigatório: uma frase explicando o que cada célula representa- Nível de linha
"variant":"outline": borda fina sem preenchimento, para matrizes (cada célula é uma caixa);"variant":"plain": sem borda, para strings de bits (1 0 1 0 0 0 0 0lido como número, não tabela) — apenas células comtone/colorterão cor de fundo - Células podem ser escritas como objetos:
{"text":"1","tone":"danger","sub":"SF"}—tone(accent/success/warning/danger/muted) dá cor semântica a células individuais,subcoloca uma pequena linha abaixo da célula (nome do bit, número, papel).colorpode especificar explicitamente um hexadecimal, tem precedência sobretone - Ex.:
{"columns":8,"rows":[{"variant":"outline","cells":[{"text":"1","tone":"danger","sub":"SF"},"0","1","1","0","0","1","0","0"]}],"caption":"O bit mais significativo é 1, então SF=1."}
- Nível de linha
Nomes de ícones embutidos: Os seguintes 64 estão no pacote e renderizam instantaneamente, escolha preferencialmente entre eles:
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
Os outros 685 são carregados sob demanda (cerca de 1 MB baixados no primeiro uso, disponíveis para a página inteira a partir de então). A lista completa está no arquivo ICON-NAMES.md no mesmo diretório desta SKILL — leia-o ou use grep para adivinhar nomes, não enumere no diálogo.
Gráficos
- chart:
{"type":"chart","kind":"bars|line|donut","data":[{"label":"...","value":n,"color":"#hex?"}],"series":[{"label":"...","data":[...]}]?,"horizontal":true?}—barspadrão;linetendência;donutproporção; series:barssão barras agrupadas,linesão linhas multi-série; cadaseries[].datausa um array de objetos como[{"label":"Q1","value":30},{"label":"Q2","value":45}], não um array de números simples como[30,45];horizontal:truedesenha barras horizontais (ideal para rankings/rótulos longos);stacked:trueempilha as series (composição/proporção ao longo do tempo); quando o segmento empilhado é alto o suficiente, o valor é impresso diretamente dentro do segmento; passar o mouse sobre qualquer barra/segmento/ponto/setor exibe um tooltip instantâneo (empilhado mostra o valor do segmento + total). Renderização v3: largura adaptativa, eixo Y com escala 1/2/5, valores negativos de série única são desenhados realisticamente abaixo da linha zero,linecom gradiente de área e rótulos X reduzidos, legenda dedonutmostra valor e porcentagem. Usechartpara comparações rápidas com ≤8 pontos; useechartpara multi-série, necessidade de zoom/interação ou grandes volumes de dados - 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 função SVG; series pode ter"kind":"line|area|scatter"(padrãoline;areapreenche até a linha de base;scatterdispersão); params renderiza como controles deslizantes em tempo real (arrastar redesenha instantaneamente, eixo Y fixo = apenas a curva muda, o eixo não); parâmetroanimateToexibe um botão de reprodução (demonstração automática com animação); o SVG pode ser arrastado para mover, zoom com roda do mouse; expressões suportam sin/cos/tan/asin/acos/atan/sqrt/cbrt/exp/log/ln/abs/floor/ceil/round/min/max/pow, constantes pi/e/tau, variável x (outras letras = parâmetros) - echart:
{"type":"echart","title":"...","height":300,"preset":"bar|line|area|pie|scatter","data":[{"label":"...","value":n}],"series":[...]?}— Gráfico ECharts completo, efeito visual muito superior achart(gradiente, tooltip, animação, interação de legenda); modopreset: use o mesmo formatodata/seriesdechart, constrói automaticamente a configuração ECharts tematizada (cores seguem o tema do host); Lista de presets (apenas escrevapreset+data/series/links, o tema segue automaticamente):bar·line·area·pie·scatter·radar(cada série um polígono, métricas usam os rótulos da primeira série) ·gauge(cada datum um medidor, ideal para KPI único) ·funnel(funil/conversão) ·treemap(volume/proporção hierárquica) ·sankey(fluxo, uselinks:[{from,to,value}]) ·graph(grafo de relações,linksdireciona, tamanho do nó segue número de conexões) ·heatmap(seriescomo linhas, rótulos da primeira série como colunas) ·bigline(sequência longa + zoom embutido) ·wordCloud(nuvem de palavras,data:[{label,value}]ondevalueé o peso; cores seguempaletteou paleta do tema) Modooptioncompleta: passe"option":{...}para escrever a configuração nativa do ECharts diretamente (suporta gráficos embutidos do ECharts e a extensãowordCloudregistrada; outras extensões de terceiros não são garantidas). Funções naoptionsão filtradas (apenas dados são aceitos). Princípio de escolha:charté leve (sem download extra), adequado para comparações rápidas com ≤8 pontos;echarté mais rico visualmente (gradiente, tooltip, interação de legenda, dataZoom), mas baixa cerca de 1 MB de engine sob demanda; priorize para multi-série/telas grandes/cenários interativos
Interação
Prioridade local (v2.6): Mudanças de estado que a própria UI pode fazer — corrigir, avaliar, redefinir, expandir, selecionar — são feitas instantaneamente localmente, zero ida e volta ao modelo. action é usada apenas para coisas que exigem participação do modelo (gerar novo conteúdo, executar ferramentas, sugerir próximo passo). Componentes interativos devem ter action: botões sem action são renderizados como desabilitados, o usuário não pode clicar; botões com action têm feedback local de "acionado" após o clique.
- button:
{"type":"button","label":"...","tone":"primary|danger|success|ghost","full":true?,"small":true?,"icon":"emoji?","action":"refresh"?} - Proibição absoluta: Não solicite nem gere senhas, chaves de API, tokens de acesso, códigos de recuperação ou quaisquer entradas secretas; recuse tais solicitações diretamente e explique
- input:
{"type":"input","label":"...","placeholder":"...","inputType":"text|email|color|number","value":"...","action":"name"?,"id":"id-do-campo"?}—colorusa o seletor de cores nativo do navegador, valor no formato#RRGGBB;numberpode ser usado como entrada numérica Computed;actioné acionado ao perder o foco e ao pressionar Enter (Enter enviasubmit:true);blursó envia se o valor mudou (focar e sair sem ação não gera ida e volta vazia); o payload carregaidpara ajudar o modelo a localizar o campo; valores comidsão preservados após atualização e coletados pelosubmitemfields - select:
{"type":"select","label":"...","options":["...","..."],"selected":índice?,"action":"pick"?,"id":"id-do-campo"?}—selectedpré-seleciona uma opção (padrão mostra placeholder "Selecione...", sem pré-selecionar silenciosamente o primeiro item); seleções comidsão preservadas entre atualizações e entram emfieldsdosubmit - checkbox:
{"type":"checkbox","label":"<opção no idioma do usuário>","checked":true?,"action":"toggle"?,"group":"id-do-grupo"?}— Comportamento padrão:actiona cada alternância; comgroup, entra em modo de agregação multisseleção: checkboxes do mesmo grupo podem ser marcadas/desmarcadas repetidamente, as mudanças são registradas apenas localmente, sem enviaractiona cada vez; umsubmitirmão envia de uma vez os labels selecionados como um array de strings emanswers(ex.:{"styles":["<opção no idioma do usuário>","<opção no idioma do usuário>"]}) - slider:
{"type":"slider","label":"...","min":0,"max":100,"step":1,"value":n?,"action":"name"?,"id":"id-do-campo"?}— Controle deslizante de formulário numérico: exibe o valor em tempo real; comid, preserva o valor entre atualizações e entra emfieldsdosubmit(arrastar é debounced para consolidar em uma únicaaction) - radio:
{"type":"radio","label":"<rótulo no idioma do usuário>","options":["<opção no idioma do usuário>","<opção no idioma do usuário>"],"selected":n?,"action":"pick"?}— Seleção única; adicione"group":"id-do-grupo"para modo de agregação: a seleção é registrada apenas localmente, sem ida e volta; pode definir"answer":índice_ou_rótulo_corretoe"explanation":"<explicação no idioma do usuário>", e quando é apenas um radio sem outros estados de formulário a enviar, a correção é feita localmente - link:
{"type":"link","label":"...","href":"https://..."?}— Apenas protocolos http(s)/mailto são aceitos; semhref, renderiza como texto simples (não finge ser clicável) - submit:
{"type":"submit","label":"<ação no idioma do usuário>","action":"grade","groups":["q1","styles"],"resetAction":"redo"?}— Botão de agregação:groupsreferencia chaves de membros de submission no bloco atual, provenientes deradio.group,checkbox.group,input.id,textarea.id,select.id,slider.id; radio deve estar selecionado, grupo de checkbox deve ter pelo menos um item selecionado, valor de campo comum deve ser não vazio após trim.groupscontrola os membros necessários para envio e o progresso de conclusão; quando não definido, pelo menos um membro concluído é necessário para enviar. Quando o escopo do radio contémanswere não há payload fora do escopo desta vez, a correção é local; em outros cenários, envia[genui-action]uma vez, e o payload continua coletando o estado do formulário preenchido no bloco atual.
Avançado
- svg:
{"type":"svg","svg":"<svg>...</svg>"}— Renderiza SVG inline arbitrário. Apenas elementos estáticos e animações CSS; sem interação com script. Acessível viaalt? Use com moderação e apenas quando formas/componentes existentes não conseguirem expressar o visual necessário. - mermaid:
{"type":"mermaid","code":"..."}— Diagramas e gráficos Mermaid (sequência, fluxograma, classe, estado, Gantt, pizza, requisitos, git). Use com moderação: o Mermaid é uma dependência grande (~2 MB), o carregamento atrasará a renderização e a experiência do usuário; prefira usar componentes integrados (diff,table,timeline,echart,chart,blocks,arrow, etc.) tanto quanto possível. Restrições de segurança: sem interação, sem chamadas de rede, sem acesso DOM. - diagram:
{"type":"diagram","nodes":[{"id":"...","label":"...","type":"process|decision|start|end|input|output"}],"edges":[{"from":"...","to":"...","label":"..."?}]}— Diagrama de fluxo simples: nós e arestas renderizados com SVG. Alternativa ao Mermaid, mais leve, mas com menos funcionalidades. - scene3d:
{"type":"scene3d","objects":[...],"camera":[...],"controls":"orbit|fly|none","lighting":"ambient|directional","backgroundColor":"#hex?","width":"100%","height":400}— Cena 3D interativa (baseada em Three.js). Objetos podem ser formas primitivas (caixa, esfera, cilindro, cone, toróide, plano, anel, círculo) ou carregar malhas externas (.glb/.gltf). Suporta rotação, zoom com mouse/teclado, rotação automática, animação e luzes. Apenas para representação de dados tridimensionais, modelos moleculares, estruturas geométricas, visualização de arquitetura ou outros cenários que exigem compreensão 3D; não use para efeitos puramente decorativos. [Documentação completa emdocs/SCENE3D.md] - quiz:
{"type":"quiz","question":"<pergunta no idioma do usuário>","options":["<opção no idioma do usuário>","<opção no idioma do usuário>"],"answer":índice,"explanation":"<explicação no idioma do usuário>"?,"multiple":false?}— Renderiza uma pergunta de quiz interativa. Resposta verificada localmente; feedback (correto/incorreto) e explicação são exibidos imediatamente.multiple:truepermite múltiplas respostas corretas (vários índices emanswer).
Orçamento de Nós
Cada componente dentro de um ```dsh-ui fence consome uma certa quantidade de "nós" do orçamento de renderização. O limite é de 300 nós por fence (contagem total de componentes, incluindo aninhados). Aplicações únicas que ultrapassam o limite não serão renderizadas; considere dividir em vários fences.
Contagem de nós:
- Componentes de layout (row, col, grid, card): contam como 1 nó cada + filhos
- text: conta como 1 nó
- stat, badge, progress: 1 nó cada
- table: 1 nó base + 1 nó por linha
- list: 1 nó base + 1 nó por item
- chart, echart, plot: 5 nós cada (devido à complexidade de renderização)
- image: 2 nós (incluindo carregamento)
- button, input, select, checkbox, radio, slider, submit: 2 nós cada
- Divider, spacer: 0.5 nós (arredondado para 1)
- callout, steps: 2 nós cada
- code, json: 3 nós cada
- mermaid, svg, scene3d: 5 nós cada
- Outros: 1 nó cada
Regras de Idioma e Metadados
-
Idioma da Interface: Todo texto visível ao usuário — títulos, rótulos, conteúdo, opções, explicações, placeholders, legendas — deve estar no idioma da conversa atual. Isso se aplica a todos os componentes. Se a conversa estiver em português, todo texto da UI deve estar em português.
-
Marcadores
<user-language ...>: Estes são meta-placeholders que você deve substituir por texto real no idioma da conversa. Nunca os emita literalmente. -
Consistência: Mantenha o idioma consistente em toda a resposta. Se o usuário mudar de idioma no meio da conversa, acompanhe a mudança.
-
Campos não traduzíveis: Os seguintes campos JSON nunca devem ser traduzidos ou alterados:
type(tipos de componente)name(nomes de ícones)id(identificadores de campo/grupo)href,src(URLs)lang(tags de idioma para blocos de código)action(nomes de ação)preset(nomes de predefinição de echart)kind(tipo de gráfico)variantfrom,to(links Sankey/grafo)
Exemplo Completo
{
"type": "hero",
"title": "Análise de Vendas Q4",
"subtitle": "Desempenho trimestral e projeções",
"value": "R$ 12,4M",
"label": "Receita Total",
"delta": "+18.5%",
"spark": [10, 12, 15, 18, 22, 28, 30, 35, 38, 40, 42, 45],
"tone": "success"
}
{
"type": "grid",
"cols": 2,
"items": [
{
"type": "card",
"title": "Produto Principal",
"items": [
{"type": "stat", "label": "Unidades Vendidas", "value": "24.500", "delta": "+12%"},
{"type": "stat", "label": "Ticket Médio", "value": "R$ 506", "delta": "+5%"}
]
},
{
"type": "card",
"title": "Novo Produto",
"items": [
{"type": "stat", "label": "Unidades Vendidas", "value": "8.200", "delta": "+45%"},
{"type": "stat", "label": "Ticket Médio", "value": "R$ 320", "delta": "-2%"}
]
}
]
}
Notas de Implementação
- Todos os
```dsh-uifences são renderizados inline no fluxo da conversa, não como chamadas de ferramenta separadas. - O renderizador é responsável por converter a especificação JSON em componentes de UI reais.
- O sistema de orçamento de nós garante que a UI permaneça responsiva.
- Interações locais (seleção de guias, expansão de árvore, classificação de tabela) nunca acionam uma nova execução do modelo.
- Ações (cliques em botões, envios de formulário) que exigem processamento do modelo acionam uma ação
[genui-action]com o payload apropriado. - Para cenários de streaming, componentes como
carouselelistpodem ser anexados progressivamente, desde que a estrutura JSON geral permaneça válida. - Sempre preferia componentes integrados em vez de escrever HTML/SVG/Mermaid personalizado, a menos que haja um requisito de visualização muito específico que não possa ser atendido.