AlbertZJ/dsh-genui-stateful

Génère une UI interactive structurée directement dans les réponses via la balise `dsh-ui`. Utilise-la pour des points clés, comparaisons, processus, statuts et interactions chaque fois qu'une présentation structurée est plus claire que du texte brut.

Qu'est-ce que dsh-genui-stateful ?

Cette compétence, nommée « GenUI — Spécification de sortie UI générative », permet à un agent IA (comme Claude, Cursor ou Cursorless) d'enrichir ses réponses avec des composants d'interface utilisateur interactifs et structurés. Contrairement à du texte classique, l'agent peut insérer un bloc de code (` ```dsh-ui`) contenant un JSON qui définit des éléments tels que des boutons, des graphiques, des tableaux, des formulaires, des cartes, des chronologies ou des visualisations de données. La spécification est très détaillée et offre une multitude de types de composants : textes hiérarchisés (text), grilles flexibles (grid, col, row), graphiques simples (chart) ou avancés via ECharts (echart), éléments interactifs (button, input, select, submit) pour des sondages ou des quiz, et même des affichages spéciaux comme des arbres de fichiers ou des diagrammes de flux. L'objectif principal est de rendre les réponses plus visuelles, exploitables et claires, en remplaçant les longues descriptions textuelles par des tableaux, des graphiques ou des formulaires que l'utilisateur peut consulter et parfois même interagir directement, le tout en conservant la langue de la conversation. La compétence inclut des règles strictes sur le formatage du bloc de code ( ```dsh-ui), l'utilisation des identifiants de composants et la gestion des actions (action) pour les interactions locales ou nécessitant un nouveau tour de l'agent.

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

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

GenUI — Spécification de sortie UI générative

Langue : Adopte la langue demandée par l'utilisateur (sinon celle de son message), dans le texte environnant et dans tout le texte visible de l'UI : titres, étiquettes, contenu, options et explications. Les exemples en chinois ci-dessous illustrent uniquement le schéma ; ne bascule pas la conversation en chinois après avoir chargé cette compétence. Conserve les clés JSON, les types de composants, les ID et les actions inchangés. Une requête en anglais génère un texte anglais ; une requête en chinois génère un texte chinois. Les jetons <user-language ...> ne sont que des méta-placeholders. Remplace chacun d'eux par du contenu réel dans la langue de la conversation ; n'émet jamais ces placeholders littéralement.

Tu peux insérer des composants d'interface utilisateur interactifs au milieu de ta réponse : écris un bloc dsh-ui (bloc de code avec le tag dsh-ui), contenant une spécification JSON. Le moteur de rendu transformera ce bloc en composants réels, et le texte s'écoulera normalement autour. Le composant fait partie intégrante de la réponse, ce n'est pas un appel d'outil.

{"title":"<texte dans la langue de l'utilisateur>","gap":14,"items":[...]}

Règles absolues pour l'écriture du bloc (à lire en premier)

Le tag de langue sur la première ligne du bloc doit toujours être dsh-ui. N'ajoute pas de ponctuation, de guillemets ou de commentaires. Le moteur tolère la casse, les espaces de début/fin et une partie de la ponctuation de fin ; tout autre tag ne sera pas interprété comme une UI GenUI et l'utilisateur verra un bloc de code normal.

ÉcritureRésultat
`` dsh-ui✅ Rendu
`` dsh-ui>✅ Rendu (toléré) ; à l'écriture, retire le >
`` dsh-ui avec espaces en fin de ligne✅ Rendu (trim automatique)
`` DSH-UI✅ Rendu (insensible à la casse)
`dshui` / ds-ui❌ Tag invalide

Auparavant, un même modèle dans une même session a écrit 182 blocs `dsh-ui>` qui ont échoué silencieusement ; une tolérance universelle pour cette ponctuation de fin a maintenant été ajoutée. En écrivant un bloc, **écris uniquement les six caractères `dsh-ui`**, sans aucun signe de ponctuation, guillemet ou commentaire ; la fermeture doit être un isolé sur sa propre ligne.

Emplacement du bloc : Écris-le uniquement dans le corps de la réponse. Écrire dans le bloc reasoning/thinking ne sera pas rendu et l'utilisateur ne le verra pas.

Formules : $...$ / \(...\) pour les formules en ligne, $$...$$ / \[...\] pour les formules autonomes ; prend en charge les matrices, les fonctions par morceaux, les dérivations alignées sur plusieurs lignes, ainsi que les formules dans le texte en gras/surbrillance. Cette capacité est partagée par le corps du texte, les listes, les paires clé-valeur, les en-têtes/cellules de tableau, les titres de cartes/étapes/chronologies/onglets, les questions et explications, les étiquettes de boutons et de formulaires, les indicateurs, les légendes de médias et les titres de graphiques. Les barres obliques inverses dans les chaînes JSON doivent être doublées, ex. "content": "\(\frac{a}{b}\)". Le code source, les valeurs d'entrée/placeholders, les options natives des listes déroulantes et le texte de dessin interne du moteur de graphiques suivent toujours leur format natif et ne sont pas analysés pour le texte enrichi. La syntaxe mathématique est basée sur KaTeX ; n'exécute pas de HTML, de ressources externes ou de scripts ; ne prend pas en charge la compilation de documents LaTeX ou le chargement de packages arbitraires.

Vocabulaire des composants (seuls ces types sont autorisés)

Disposition : text row col grid card carousel divider spacer arrow hero Affichage : stat badge progress list table keyvalue avatar image image-group audio video timeline file-tree breadcrumb diff json code callout steps icon blocks Graphiques : chart (bars/line/donut, multi-séries) plot (graphique de fonction mathématique) echart (graphique ECharts complet) Interaction : button input select checkbox radio switch slider textarea tabs accordion copy link submit Avancé : svg mermaid diagram scene3d quiz

Disposition

  • text : {"type":"text","size":"display|h1|h2|h3|body|muted|caption","content":"...","align":"left|center|right"?} (tone:"danger|success|warning|info|muted" donne une couleur sémantique à la ligne entière, sans utiliser badge ; mono:true utilise une police à chasse fixe pour les hexadécimaux/binaires/identifiants, sans fond de code en ligne). (size:"display" est un cran au-dessus du titre, pour une phrase d'introduction/accroche ; ≤1 par réponse ; center:true est l'ancienne écriture, équivalente à align:"center", utilise toujours align dans les nouvelles écritures)
  • row / col : {"type":"row"|"col","items":[...],"wrap":true?,"spacer":true?,"gap":n?} ; row.justify:"center" pour centrer sur l'axe principal (comme l'en-tête icône+titre) ; col.width:n (1-100) donne à cette colonne un pourcentage de la largeur de la ligne, sinon la largeur est basée sur le contenu.
  • grid : {"type":"grid","cols":n,"items":[...]}
  • carousel : {"type":"carousel","title":"<titre dans la langue de l'utilisateur>","items":[{"type":"card","title":"...","items":[...]}],"itemWidth":240?,"gap":3?} — Défilement horizontal des nœuds natifs ; jusqu'à 24 éléments, largeur de 80 à 640 px et ne dépassant pas 85 % de la largeur de la piste ; largeur par défaut 240 px. La valeur gap est multipliée par 4 px, si omise la valeur est de 12 px, les valeurs négatives sont traitées comme 0. Le bouton de navigation fait défiler 85 % de la largeur de la piste à chaque fois, ne s'affiche que lorsque le contenu déborde ; les appareils tactiles font défiler la piste directement. Pas de lecture automatique. Lorsque la piste est ciblée, les touches fléchées gauche/droite, Home/End sont actives ; les formulaires à l'intérieur des cartes suivent les règles de soumission d'origine. La navigation et le défilement n'envoient pas d'action ; les actions déclarées par les composants enfants sont déclenchées selon les règles d'interaction existantes. En sortie streaming, les cartes sont ajoutées uniquement à la fin pour préserver les chemins de saisie existants.
  • hero : {"type":"hero","title":"<titre dans la langue de l'utilisateur>","subtitle":"<texte dans la langue de l'utilisateur>","value":"99.96%","label":"<indicateur dans la langue de l'utilisateur>","delta":"+0.02%","spark":[...],"tone":"accent|success|warning|danger"} — Bloc de couverture : sourcil + très grand nombre (52 px, avec compteur d'entrée) + titre + sous-titre + fond dégradé de tonalité. Maximum un par réponse, à placer tout en haut comme point d'ancrage visuel.
  • span : N'importe quel nœud peut ajouter "span":2 (nombre de colonnes qu'un nœud enfant de grid occupe) — Le seul primitive pour la mise en page en bento : une large carte en span:2 associée à une carte étroite, bien plus esthétique qu'une pile de carrés.
  • card : {"type":"card","title":"...","items":[...]} ; "tone":"info|success|warning|danger" donne un fond sémantique (pour les cartes de conclusion/risque) ; "accent":"#f59e0b" spécifie une couleur d'accent (bordure + titre + fond très pâle) ; "flat":true supprime l'ombre, remplacée par un fond neutre très pâle — les panneaux dans les figures doivent utiliser flat, sinon chaque couche ressemble à une carte flottante, et deux couches deviennent sales ; "eyebrow":"<petit titre introductif dans la langue de l'utilisateur>" ajoute une petite ligne meta au-dessus du titre (à utiliser pour séparer des chapitres entre cartes — ne pas ajouter à chaque carte d'un même chapitre, ≤3 par réponse).
  • palette : chart / echart supportent tous "palette":["#ff8800","#3ecf8e"] pour remplacer la palette de couleurs par défaut (suit le thème de l'hôte par défaut). Écris-la uniquement lorsque la couleur est sémantiquement nécessaire (coût=rouge, profit=vert), sinon il est plus sûr de suivre le thème.
  • divider : {"type":"divider"} ; avec nom de section {"type":"divider","label":"<nom de section dans la langue de l'utilisateur>"} (ligne + petite étiquette centrée, pour les changements de section : rapports passant du résumé aux données, ouverture d'un nouveau chapitre dans un album de graphiques — ne pas utiliser à l'intérieur des cartes/listes ou entre des contenus thématiques continus, ≤3 par réponse). spacer : {"type":"spacer"}

Affichage

  • stat : {"type":"stat","label":"...","value":"...","delta":"+12.4%|-3%"} (- au début = rouge automatique, + = vert) ; optionnel "spark":[3,5,4,8,6] pour une micro-courbe de tendance (2 à 60 valeurs numériques finies) ; "size":"hero" rend un très grand nombre (à utiliser une fois maximum par réponse, comme point d'ancrage visuel).
  • badge : {"type":"badge","label":"...","tone":"success|warn|danger|accent","icon":"emoji?"}
  • progress : {"type":"progress","label":"...","value":0-100,"valueLabel":"70%"} ; "variant":"ring" pour une jauge circulaire, "target":70 pour marquer un repère sur la barre.
  • avatar : {"type":"avatar","name":"...","color":"#hex?"}
  • image : {"type":"image","src":"/mmx-files/result.png","alt":"<description dans la langue de l'utilisateur>"} — Affiche une image http(s) accessible par le navigateur ou une adresse relative de même origine ; chargement différé ; ne supporte pas les protocoles file:/data: locaux ou actifs ; "caption":"<légende dans la langue de l'utilisateur>" ajoute une ligne d'explication en dessous de l'image (source/interprétation/credit ; alt est l'étiquette au-dessus de l'image, les deux ont des rôles différents).
  • image-group : {"type":"image-group","title":"<titre dans la langue de l'utilisateur>","items":[{"src":"/mmx-files/a.png","alt":"<description>","caption":"<caption>"},{"src":"/mmx-files/b.png","alt":"<description>"}]} — Jusqu'à 24 images disposées automatiquement en fonction du rapport hauteur/largeur ; clic pour ouvrir dans une lightbox native, touches fléchées gauche/droite / Home / End pour naviguer, Esc / bouton de fermeture pour revenir et restaurer le focus. Reprend les limitations d'URL http(s)/relatives d'image ; n'accepte pas les protocoles file/data/javascript. Uniquement pour la navigation locale, n'envoie pas d'action ; pour des actions nécessitant le modèle, place un bouton indépendant à côté. L'ajout / la réorganisation conserve le src actuel ; le retrait de l'image actuelle ferme la visionneuse ; les src identiques sont différenciés par leur ordre d'apparition. Les listes vides et les échecs de chargement d'image ont des états clairs. L'export HTML n'intègre pas les octets de l'image, celle-ci dépend toujours de son adresse d'origine. Utilisation complète et recette.
  • audio : {"type":"audio","src":"/mmx-files/result.mp3","alt":"<description dans la langue de l'utilisateur>","loop":true?} — Contrôle natif ; lecture déclenchée par l'utilisateur, pas automatique ; uniquement http(s) ou adresse relative de même origine.
  • video : {"type":"video","src":"/mmx-files/result.mp4","alt":"<description dans la langue de l'utilisateur>","poster":"/mmx-files/poster.jpg"?,"loop":true?,"muted":true?,"aspectRatio":"16:9|4:3|1:1|9:16"?} — Contrôle natif de lecture/volume/plein écran ; pas de lecture automatique.
  • list : {"type":"list","items":["..."] ou [{"title":"...","desc":"..."}] ou nœuds imbriqués (ex. {"type":"badge","label":"TS"})} — Les nœuds peuvent être imbriqués en ligne (comptés dans le budget des nœuds) ; label/name dans les enregistrements sont normalisés en title, description/content/text/body/detail sont normalisés en desc ; si seul le corps du texte est présent, il est utilisé comme titre ; les champs inconnus déclencheront un avertissement.
  • table : {"type":"table","columns":["..."],"rows":[["...","..."]],"types":["text|num|delta|bar|badge"]?,"details":[[...]]?,"total":true?} — Tri local par clic sur l'en-tête (ascendant/descendant/réinitialisation, zéro aller-retour) ; perception numérique : les séparateurs de milliers (1 234), k/m/b, 万/亿, %, les symboles monétaires sont tous comparés en tant que valeurs numériques réelles, les colonnes purement numériques sont automatiquement alignées à droite ; les cellules avec signe sont automatiquement colorées (+12,4% vert, -3 rouge, sans champ supplémentaire) ; types peut spécifier par colonne : bar (barre de progression en ligne 0-100), ring (petit cercle 0-100), spark (la cellule contient "3,5,4,8" pour une micro-courbe), badge (étiquette capsule), delta (couleur forcée hausse/baisse), num (alignement forcé à droite), index (numéro de ligne), group (première colonne comme titre de groupe : si seule la première cellule de la ligne a du contenu, elle est rendue comme un sous-titre s'étendant sur toutes les colonnes) ; "total":true ajoute une ligne de total (somme automatique des colonnes numériques) ; "export":true : deux petits boutons « Copier en Markdown / Copier en CSV » apparaissent au-dessus du tableau (presse-papiers local uniquement, pas de requête) ; "filter":"id_de_champ_de_saisie" : lie le tableau à un champ input/select, le lecteur peut filtrer instantanément en tapant (filterColumn peut limiter la colonne) — quand il y a beaucoup de données, il faut par défaut en mettre un ; "sortField":"id_de_liste_deroulante" utilise la valeur (nom de colonne) de la liste déroulante pour trier ; "details" est dans le même ordre que rows, le i-ème élément est le contenu développé pour cette ligne (peut contenir n'importe quel composant, null = la ligne ne peut pas être développée) — un chevron apparaît dans la première colonne, un clic affiche les détails en dessous de la ligne entière, parfait pour « tableau principal + détails ».
  • keyvalue : {"type":"keyvalue","pairs":[{"key":"...","value":"..."}]}
  • timeline : {"type":"timeline","items":[{"title":"...","desc":"...","time":"..."}]}
  • file-tree : {"type":"file-tree","items":[{"name":"...","type":"file|dir","children":[...]?}]} — Les lignes de répertoire peuvent être cliquées pour se replier/développer (local, zéro aller-retour).
  • breadcrumb : {"type":"breadcrumb","items":["<texte dans la langue de l'utilisateur>","<texte dans la langue de l'utilisateur>","<texte dans la langue de l'utilisateur>"]}
  • diff : {"type":"diff","diffs":[{"path":"...","oldText":"..."|null,"newText":"..."}]}
  • json : {"type":"json","value":...} (visualiseur d'arbre JSON)
  • code : {"type":"code","lang":"ts","code":"..."}
  • callout : {"type":"callout","tone":"info|success|warning|error","title":"...","content":"..."} — Pour une seule mise en valeur, un rappel ou un résumé de risque ; content est une chaîne de texte, utilise "\n" en JSON pour les sauts de ligne. Pour plusieurs éléments indépendants, utilise list avec {title,desc} pour conserver la hiérarchie.
  • steps : {"type":"steps","current":n,"steps":[{"title":"...","desc":"..."}]}
  • arrow : {"type":"arrow","dir":"down|right","length":16-96,"label":"..."} — Connecteur fin entre deux blocs dans une figure (ligne de 1,4 px + flèche en V, longueur fixe). Dans les diagrammes de flux verticaux, utilise toujours celui-ci ; ne pas utiliser de « ↓ » en texte : la flèche caractère suit la ligne de base du texte et ne crée pas d'espacement.
  • icon : {"type":"icon","name":"Nodes","tone":"info|success|warning|danger|muted|default","size":16|20|24|32,"tile":true,"label":"<nom accessible dans la langue de l'utilisateur>"} — Ensemble d'icônes intégrées (749 rendues fidèlement ; 64 en ligne, rendues instantanément, les autres chargées à la première utilisation, liste complète dans ICON-NAMES.md). tile:true le rend comme un fond carré arrondi pâle, pour les cartes d'étape/marqueurs sémantiques ; sans tile, c'est une icône en ligne de la couleur du texte. label peut être omis (purement décoratif), lorsqu'ils apparaissent par paire, un seul doit avoir un label.
  • blocks : {"type":"blocks","title":"...","rows":[{"label":"initial","cells":["0","1","2"],"color":"#64748b"}],"columns":n?,"caption":"..."} — Diagramme en blocs colorés : cellules regroupées par lignes. Trois formes avec la même structure : ① Diagramme en escalier (chaque ligne a un nombre différent de cellules, chaque ligne divise sa largeur également, ex. 8→4→2→1) ② Blocs égaux (4 blocs par ligne) ③ Diagramme en couloir (columns fixe le nombre de colonnes, écris "—" ou laisse vide dans cells). Si color est omis, utilise un fond pâle de la couleur d'accent ; la couleur du texte est inversée automatiquement en fonction de la luminosité (texte blanc sur fond sombre / texte noir sur fond clair). Utilise-le pour toute structure qui doit « être divisée en plusieurs parties, progresser par tours, ou être comparée en groupes », n'écris pas de coordonnées SVG à la main. caption est obligatoire : une phrase expliquant ce que chaque cellule représente.
    • Au niveau de la ligne : "variant":"outline" : bordure fine sans remplissage, pour les matrices (chaque cellule est un cadre) ; "variant":"plain" : pas de cadre du tout, pour les chaînes de bits (1 0 1 0 0 0 0 0 se lit comme un nombre, pas un tableau) — seules les cellules avec tone/color auront un fond.
    • Les cellules peuvent être écrites sous forme d'objet : {"text":"1","tone":"danger","sub":"SF"} — tone (accent/success/warning/danger/muted) donne une couleur sémantique à une seule cellule, sub ajoute une petite ligne de texte en dessous de la cellule (nom de bit, numéro, rôle). color peut spécifier explicitement un hexadécimal, prioritaire sur tone.
    • Exemple : {"columns":8,"rows":[{"variant":"outline","cells":[{"text":"1","tone":"danger","sub":"SF"},"0","1","1","0","0","1","0","0"]}],"caption":"Le bit de poids fort est 1, donc SF=1."}

Noms d'icônes intégrés : les 64 suivants sont intégrés dans le paquet, rendus immédiatement, choisis de préférence parmi ceux-ci :

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

Les 685 autres sont chargés à la demande (environ 1 Mo téléchargé à la première utilisation, puis toute cette page est disponible). La liste complète se trouve dans ICON-NAMES.md dans le même répertoire que cette compétence — lis-le ou grep-le si tu as besoin de deviner un nom, ne fais pas d'énumération dans la conversation.

Graphiques

  • chart : {"type":"chart","kind":"bars|line|donut","data":[{"label":"...","value":n,"color":"#hex?"}],"series":[{"label":"...","data":[...]}]?,"horizontal":true?} — bars par défaut ; line pour les tendances ; donut pour les proportions ; series : bars pour les barres groupées, line pour les lignes multi-séries ; chaque series[].data utilise un tableau d'objets [{"label":"Q1","value":30},{"label":"Q2","value":45}], pas un tableau de nombres purs [30,45] ; horizontal:true pour des barres horizontales (idéal pour les classements/étiquettes longues) ; stacked:true pour empiler les séries (composant/part dans le temps) ; lorsque les segments empilés sont suffisamment hauts, les valeurs sont imprimées directement à l'intérieur du segment, le survol de n'importe quelle barre/segment/point/secteur fait apparaître une info-bulle instantanée (affichant la valeur du segment + le total). Rendu v3 : largeur adaptative, 1/2/5 graduations sur l'axe Y, les valeurs négatives en série unique sont dessinées réellement sous la ligne zéro, line avec dégradé de surface et étiquettes X espacées, légende donut avec valeurs et pourcentages. Utilise chart pour une comparaison rapide de ≤8 points ; pour les séries multiples, le zoom/l'interaction ou les grands volumes de données, utilise echart.
  • 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":"..."} — Graphique de fonction SVG ; series peut avoir "kind":"line|area|scatter" (line par défaut ; area remplit jusqu'à la ligne de base ; scatter pour les points) ; params est rendu sous forme de curseurs en temps réel (le glissement redessine instantanément, l'axe Y est verrouillé = seule la courbe change, pas l'axe) ; les paramètres animateTo affichent un bouton de lecture (démonstration animée automatique) ; le SVG peut être glissé/déplacé, zoomé avec la molette ; les expressions prennent en charge sin/cos/tan/asin/acos/atan/sqrt/cbrt/exp/log/ln/abs/floor/ceil/round/min/max/pow, les constantes pi/e/tau, la variable x (les autres lettres sont des paramètres).
  • echart : {"type":"echart","title":"...","height":300,"preset":"bar|line|area|pie|scatter","data":[{"label":"...","value":n}],"series":[...]?} — Graphique ECharts complet, avec un rendu visuel bien supérieur à chart (dégradés, info-bulles, animations, interactions de légende) ; mode preset : utilise les mêmes formats data/series que chart, construit automatiquement une configuration ECharts thématisée (couleurs suivant le thème de l'hôte) ; Aperçu des presets (écris uniquement preset + data/series/links, le thème suit automatiquement) : bar · line · area · pie · scatter · radar (un polygone par série, les indicateurs proviennent des labels de la première série) · gauge (une jauge par datum, idéal pour un seul KPI) · funnel (entonnoir/conversion) · treemap (volume/hiérarchie) · sankey (flux, utilise links:[{from,to,value}]) · graph (graphe relationnel, piloté par links, taille des nœuds proportionnelle au nombre de connexions) · heatmap (series comme lignes, les labels de la première série comme colonnes) · bigline (longue série + zoom intégré) · wordCloud (nuage de mots, data:[{label,value}] où value est le poids ; les couleurs suivent la palette ou la palette de couleurs du thème). Mode option complète : passe "option":{...} pour écrire directement la configuration native ECharts (supporte les graphiques intégrés ECharts et l'extension wordCloud enregistrée ; les autres extensions tierces ne sont pas garanties). Les fonctions dans option sont filtrées (seules les données sont acceptées). Principe de sélection : chart est léger (pas de téléchargement supplémentaire) idéal pour une comparaison rapide ≤8 points ; echart est plus riche visuellement (dégradés, info-bulles, interactions de légende, dataZoom), mais télécharge environ 1 Mo de moteur à la demande, prioritaire pour les scénarios multi-séries, grands écrans et interactifs.

Interaction

Local d'abord (v2.6) : Les changements d'état que l'UI peut gérer par elle-même — correction, notation, réinitialisation, développement, sélection — sont tous effectués localement et immédiatement, zéro aller-retour vers le modèle. L'action est utilisée uniquement pour les choses qui nécessitent l'intervention du modèle (générer du nouveau contenu, exécuter un outil, suggérer la prochaine étape). Les composants interactifs doivent avoir une action : un bouton sans action est rendu dans un état désactivé, l'utilisateur ne peut pas cliquer ; un bouton avec action donne un retour local « déclenché » après le clic.

  • button : {"type":"button","label":"...","tone":"primary|danger|success|ghost","full":true?,"small":true?,"icon":"emoji?","action":"refresh"?}
  • Interdiction absolue : Ne demande ni ne génère de mots de passe, clés API, jetons d'accès, codes de récupération ou autres entrées secrètes ; face à une telle demande, refuse directement et explique.
  • input : {"type":"input","label":"...","placeholder":"...","inputType":"text|email|color|number","value":"...","action":"name"?,"id":"field-id"?} — color utilise le sélecteur de couleur natif du navigateur, la valeur utilise #RRGGBB ; number peut être utilisé comme champ de saisie numérique Computed ; l'action est déclenchée au flou ET à l'appui sur Entrée (l'appui sur Entrée inclut submit:true) ; le flou n'est envoyé que si la valeur a changé (le fait de se concentrer et de quitter sans changer ne génère pas d'aller-retour vide) ; le payload inclut id pour aider le modèle à localiser le champ ; les valeurs avec id sont conservées après un rafraîchissement et collectées par submit dans fields.
  • select : {"type":"select","label":"...","options":["...","..."],"selected":indice?,"action":"pick"?,"id":"field-id"?} — selected pré-sélectionne une option (par défaut, affiche « Veuillez sélectionner... » comme espace réservé, ne présélectionne pas silencieusement la première option) ; les sélections avec id sont conservées entre les rafraîchissements et incluses dans les fields de submit.
  • checkbox : {"type":"checkbox","label":"<option dans la langue de l'utilisateur>","checked":true?,"action":"toggle"?,"group":"group-id"?} — Le comportement par défaut est de déclencher une action à chaque changement ; l'ajout de group active le mode d'agrégation multi-sélection : les cases à cocher du même groupe peuvent être cochées/décochées à plusieurs reprises, les changements sont enregistrés localement sans action individuelle, un submit frère envoie en une seule fois les labels cochés de ce groupe sous forme de tableau de chaînes dans answers (ex. {"styles":["<option>","<option>"]}).
  • slider : {"type":"slider","label":"...","min":0,"max":100,"step":1,"value":n?,"action":"name"?,"id":"field-id"?} — Curseur de formulaire numérique : affiche la valeur en temps réel ; les curseurs avec id sont conservés entre les rafraîchissements et inclus dans les fields de submit (le glissement est anti-rebond et fusionné en une seule action).
  • radio : {"type":"radio","label":"<label dans la langue de l'utilisateur>","options":["<option dans la langue de l'utilisateur>","<option dans la langue de l'utilisateur>"],"selected":n?,"action":"pick"?} — Sélection unique ; l'ajout de "group":"group-id" active le mode d'agrégation : la sélection est uniquement enregistrée localement, sans aller-retour ; on peut définir "answer":indice ou label correct et "explanation":"<explication dans la langue de l'utilisateur>", pour une correction locale en mode radio pur sans autre état de formulaire en attente d'envoi.
  • link : {"type":"link","label":"...","href":"https://..."?} — Seuls les protocoles http(s)/mailto sont acceptés ; sans href, il est rendu comme du texte simple (ne fait pas semblant d'être cliquable).
  • submit : {"type":"submit","label":"<action dans la langue de l'utilisateur>","action":"grade","groups":["q1","styles"],"resetAction":"redo"?} — Bouton d'agrégation : groups référence les clés membres de soumission dans le bloc actuel, les sources sont radio.group, checkbox.group, input.id, textarea.id, select.id, slider.id ; pour les radio, une option doit être sélectionnée ; pour les groupes checkbox, au moins un élément doit être coché ; pour les champs normaux, la valeur doit être non vide après trim. groups contrôle les membres requis pour la soumission et la progression ; s'il n'est pas défini, au moins un membre complété suffit pour soumettre. Pour les radio purs avec answer et sans payload externe, une correction locale peut être effectuée ; dans les autres cas, un [genui-action] est envoyé, le payload collecte l'état du formulaire actuellement rempli dans le bloc.

Skills associés