XUI — le xer comme renderer
Spec v0 du contrat entre le sens (ploxions) et les surfaces (desktop, montre, ESP32…)
Résumé
Le xerboxion doit tourner N'IMPORTE OÙ : desktop, téléphone, montre, cube à 6 faces, écran rond, OLED 1-bit, LCD caractères, ESP32, STM32. Ça n'est possible que si on sépare ce que le xerboxion EST (bions, ploxions, tsoins — le sens) de comment on le dessine (le xer — un renderer). Ce document gèle le contrat entre les deux : un ploxion décrit son état par priorité, une surface révèle ce que ses capacités permettent, une action passe par le bus — jamais par un callback. Directive RS-7 : « un TYPE d'UI complètement différent, pas un thème ».
J0bot · Août 20...
Ce que ce document gèle
Quatre choses, et rien d'autre :
- La séparation être/dessiner : le xerboxion = le sens ; le xer = un renderer d'une SURFACE. Un nouveau facteur de forme = un nouveau renderer, zéro modification des ploxions (§1).
- Le vocabulaire des capacités d'une surface : résolution, profondeur, forme, entrée, compute (§2).
- Le contrat XUI v0 : la fonction
describe()pure d'un ploxion, ses items typés et priorisés 1..5, sa variante compacte XUIC pour micro-écrans (§3). - Deux mécaniques transverses : la révélation progressive (trier par
p, couper où la surface s'arrête, §4) et les actions par le bus (unact= un topic jOSPorts, §6).
Ce qui n'est PAS gelé : le layout riche (grilles, mise en page), les images et glyphes sur micro-écran, les kinds au-delà des cinq de base. Ouvert en §7.
§1Être et dessiner
[POSÉ] Le péché originel des UI classiques : l'état vit dans le dessin. Un ploxion qui
stocke sa vérité dans son DOM ne peut exister que là où ce DOM existe — il est prisonnier de
son écran. La règle Core/UI du repo (l'état-vérité ne vit jamais dans un UI) était déjà la
moitié du chemin ; XUI en est la seconde moitié, côté sortie :
- Le xerboxion, c'est le sens : les bions (capacités), les ploxions (compositions), les tsoins (l'histoire), le bus jOSPorts (les événements). Tout ça existe indépendamment de tout pixel.
- Le xer, c'est un renderer : un programme qui connaît les capacités d'UNE surface et qui dessine dessus une description abstraite. Le xer desktop dessine en HTML. Le xer montre dessine 12 caractères. Le xer ESP32 pousse des segments sur un OLED. Le xer cube répartit sur 6 faces. Même description, N dessins.
Conséquence structurelle : un ploxion n'a plus « une UI ». Il a un état et une fonction
qui le décrit (§3). L'UI web riche existante (les app.blade.php) reste ce qu'elle est —
le rendu de luxe de la surface navigateur — mais elle devient UN rendu parmi d'autres, plus LE
ploxion. C'est la règle « ploxion = son bion, pas son UI », poussée jusqu'au bout.
§2Les capacités d'une surface
[POSÉ] Une surface se déclare par cinq axes. C'est le profil que le renderer connaît et que
le ploxion ignore (le ploxion décrit, il ne s'adapte pas — c'est le renderer qui coupe) :
{
"resolution": { "w": 128, "h": 64 },
"profondeur": "1bit",
"forme": "rect",
"entree": ["2-boutons"],
"compute": "micro"
}
- resolution : pixels utiles (
w,h) — ou colonnes×lignes pour un LCD caractères. - profondeur :
couleur·gris·1bit. Un renderer 1-bit n'a ni dégradé ni couleur d'état : il rend une gauge en barre pleine/vide, un booléen en glyphe plein/creux. - forme :
rect·rond(montre, hublot) ·bande(ticker, barre LED) ·cube-6-faces(ledion : six surfaces rect solidaires — le renderer décide quoi mettre sur quelle face). - entree :
tactile·molette·d-pad·2-boutons·voix·aucune. Une surfaceaucuneest un pur afficheur : elle montre, les actions se déclenchent d'ailleurs (§6). - compute :
navigateur(JS complet, appel direct dedescribe()) ·micro(ESP32/STM32 : pas de JS — il reçoit du XUIC déjà sérialisé, §5).
§3Le contrat XUI v0
[POSÉ] — c'est la clé de voûte, identique pour tous. Un ploxion peut exposer :
window.jOSApps[id].describe = function(){ return XUI; }
où :
XUI = { v:1, id, title, icon, items:[ ITEM… ] }
ITEM = { p: <1..5 priorité : 1 = l'essentiel absolu (la montre ne montre que p1),
5 = détail desktop>,
kind: 'stat'|'gauge'|'action'|'list'|'text',
label: <string court>,
value?: <string|number>, unit?: <string>,
min?/max?: <pour gauge>,
items?: [{label, value?, act?}] (pour kind list, max ~7),
act?: { port:<topic jOSPorts à émettre>, payload?:{} }
(pour kind action — l'exécution passe par le bus, JAMAIS par un
callback : un ESP32 doit pouvoir déclencher pareil) }
RÈGLES gelées :
describe()est PUR (pas d'effet de bord), rapide (<5 ms), et retourne l'état COURANT — on peut l'appeler à chaque frame ou une fois par minute, même réponse pour le même état.- Tout texte court : une montre = ~12 caractères utiles. Le label est un télégramme, pas une phrase.
- La révélation progressive = trier par
p, couper où la surface s'arrête (§4). Pas de logique par-device dans le ploxion. - Le registre
window.jOSAppsexiste déjà (resources/views/ploxions/_manifest.blade.php) :describes'y greffe, on n'invente pas un deuxième annuaire.
Version compacte pour micro-écrans (MQTT/série) — arrays, pas d'objets, parse trivial sur ESP32 :
XUIC = [id, title, [[p,kind,label,value,unit]…]]
Exemple 1 — un ploxion météo :
{ "v": 1, "id": "meteo", "title": "Météo", "icon": "⛅",
"items": [
{ "p": 1, "kind": "stat", "label": "Ext", "value": 21.4, "unit": "°C" },
{ "p": 2, "kind": "stat", "label": "Humidité", "value": 62, "unit": "%" },
{ "p": 2, "kind": "gauge", "label": "Pluie 24h", "value": 3, "min": 0, "max": 40, "unit": "mm" },
{ "p": 3, "kind": "list", "label": "Prévision",
"items": [ { "label": "Mar", "value": "☀ 24°" },
{ "label": "Mer", "value": "🌧 17°" },
{ "label": "Jeu", "value": "⛅ 20°" } ] },
{ "p": 4, "kind": "text", "label": "Tendance",
"value": "Front froid vendredi, bise samedi." },
{ "p": 5, "kind": "action", "label": "Rafraîchir",
"act": { "port": "ploxion.meteo.refresh" } }
] }
et son XUIC (ce que reçoit l'OLED — p1-2 seulement, déjà coupé) :
["meteo", "Météo", [
[1, "stat", "Ext", 21.4, "°C"],
[2, "stat", "Humidité", 62, "%"],
[2, "gauge", "Pluie 24h", 3, "mm"]
]]
Exemple 2 — le ploxion xerbot (les act collent au contrat xerbot v1,
resources/data/xerbot-contract.json) :
{ "v": 1, "id": "xerbot", "title": "Xerbot", "icon": "🤖",
"items": [
{ "p": 1, "kind": "stat", "label": "Rover", "value": "en ligne" },
{ "p": 1, "kind": "gauge", "label": "Batterie", "value": 87, "min": 0, "max": 100, "unit": "%" },
{ "p": 2, "kind": "action", "label": "Stop",
"act": { "port": "ploxion.xerbot.rover-1.cmd",
"payload": { "cmd": "stop", "src": "xui" } } },
{ "p": 3, "kind": "action", "label": "Avance 10m",
"act": { "port": "ploxion.xerbot.rover-1.cmd",
"payload": { "cmd": "avance", "args": { "d": 10 }, "src": "xui" } } },
{ "p": 3, "kind": "stat", "label": "Cap", "value": 142, "unit": "°" },
{ "p": 4, "kind": "list", "label": "Flotte",
"items": [
{ "label": "rover-1", "value": "87%" },
{ "label": "drone-1", "value": "au sol",
"act": { "port": "ploxion.xerbot.drone-1.cmd",
"payload": { "cmd": "takeoff", "args": { "alt": 10 }, "src": "xui" } } } ] },
{ "p": 5, "kind": "text", "label": "Dernier ack", "value": "stop ok 14:02:11" }
] }
La montre du xerbot montre donc exactement : Rover en ligne + la barre de batterie. Et son
bouton unique déclenche le premier action visible — le Stop. C'est déjà une télécommande
d'urgence complète.
§4La révélation progressive
[POSÉ] L'algorithme du renderer, en entier : trier les items par p croissant, dessiner
dans l'ordre, s'arrêter quand la surface est pleine. C'est tout. Pas de media queries, pas
de breakpoints, pas de version « mobile » du ploxion — une seule description, N coupes.
| Surface | Capacités typiques | Révèle |
|---|---|---|
| Montre | ~64px rond, 1 molette | p1 seulement |
| OLED 128×64 | 1-bit, 2 boutons | p1–2 |
| E-ink | gris, lent, souvent sans entrée | p1–3 |
| Téléphone | couleur, tactile | p1–4 |
| Desktop | couleur, clavier/souris | tout (p1–5) |
Écrire un ploxion XUI, c'est donc répondre à une seule question honnête : « si l'être n'a qu'un coup d'œil, que doit-il voir ? » → c'est le p1. Le reste est de l'enrichissement progressif. Un ploxion dont tous les items sont p1 a raté l'exercice (la montre coupera arbitrairement) ; la bonne pyramide est pointue : 1–2 items p1, le gros du contenu en p3–4.
Cas des formes non-rect : le rond rogne les coins (le renderer centre et raccourcit les
labels) ; la bande ne montre qu'un item à la fois et défile dans l'ordre des p ; le
cube-6-faces distribue — p1 sur la face avant, la suite sur les autres faces.
§5Le transport
Cadrage RS-7 (13.08) : v0 = tout en web, et le XUI est une affaire de XER — il n'est PAS couplé au bus xerbot. Le xer est le renderer ; le canal qui l'alimente lui appartient.
-
Web (RÉEL, le seul chemin gelé en v0) : le renderer tourne dans la même page que le ploxion → appel direct de
window.jOSApps[id].describe(). Zéro sérialisation, zéro latence. Le xer facteur-de-forme (⌚ montre, 📺 tv, 🖲 borne) et le simulateur de surfaces consomment XUI comme ça, aujourd'hui. -
Micro-écrans (TURFU, non gelé) : quand un xer devra vivre sur une puce (ESP32, STM32, e-ink autonome), le format compact XUIC —
[id, title, [[p,kind,label,value,unit]…]], arrays plats, un parseur de ~50 lignes suffit — voyagera sur le canal PROPRE de ce xer (MQTT, série, peu importe). Ce canal n'est volontairement pas spécifié ici, et il est DÉCOUPLÉ du contrat xerbot (le xerbot pilote des devices ; le xer dessine des surfaces — deux affaires). Une esquisse de firmware de référence existe hors repo, jamais flashée ; elle attend que RS-7 ouvre le chantier matériel. -
Rappel kiosk (RÉEL) : un onglet ouvert en plein écran verrouillé (le xer 🖲 borne) est déjà « un écran dédié » — en pur web, sans transport.
§6Les actions par le bus
[POSÉ] Un act n'est jamais un callback. C'est { port, payload? } : le renderer qui
déclenche l'action émet payload sur le topic port via jOSPorts
(resources/views/partials/_ploxion_ports.blade.php) — et c'est tout ce qu'il sait faire.
Pourquoi c'est non négociable : un callback JS n'existe que dans la page qui l'a créé. Un
topic, lui, est déclenchable depuis n'importe quelle surface — le desktop, la montre, un
ESP32 à 2 boutons qui publie sur MQTT (le pont retraduit xerbot/... en ploxion.xerbot...,
contrat v1), même une entrée voix qui mappe un mot sur un act. La surface la plus pauvre
(2-boutons, ou aucune + un bouton physique ailleurs) a exactement le même pouvoir que le
desktop : émettre un message. L'égalité des surfaces, c'est ça.
Convention de mapping entrée→act (renderer) : 2-boutons = bouton A cycle les items, bouton
B déclenche l'act de l'item courant ; molette = tourner cycle, presser déclenche ; d-pad
pareil avec navigation 2D dans les list. Le ploxion n'en sait rien — il a juste posé des
act triés par priorité.
Sécurité, dit tel quel : XUI v0 ne définit pas d'auth par item. Ce qui protège, c'est ce
qui protège déjà le bus (SSO devant les ploxions, token-gating de l'ingress côté MQTT). Un
act destructif ne doit pas exister en p1–2 sur une surface publique — règle d'auteur, pas
de mécanisme, assumé en v0 (voir §7).
§7Régimes honnêtes, et ce qui reste ouvert
- RÉEL (cette vague) : le contrat de ce document ;
describe()sur les ploxions web ; le xer facteur-de-forme + le simulateur de surfaces (rendre le même XUI en montre / OLED / e-ink / téléphone / desktop dans le navigateur, avec les vraies coupes §4) ; la chaîne XUIC → MQTT en mode mock jOSMqtt. - PRÉSENT (à câbler, non testable ici) : le firmware ESP32 qui s'abonne à
xerbot/<screen>/show, parse XUIC, dessine sur OLED/LCD, publie sesactsur 2 boutons. Le protocole est prêt et gelé ; le métal manque sur ce boxion — on le dit tel quel, pas de démo simulée déguisée en matériel. - TURFU (lane RS-3, pas cette nuit) : le renderer bare-metal — xerboxion-os qui dessine du XUI sans navigateur du tout. Le contrat est conçu pour : XUIC se parse sans allocateur sophistiqué, les actions sont des messages, rien ne suppose un DOM.
Ouvert (explicitement PAS gelé en v0) :
- Layout riche : XUI v0 est une liste priorisée, pas une mise en page. Grilles, zones, compositions multi-ploxions sur un même écran → v1, seulement quand des usages réels l'exigeront.
- Images et glyphes sur micro : un micro-écran 1-bit pourrait recevoir mieux que du
texte. La piste sérieuse : le codec glyphes AST⟷SVG
(
resources/views/partials/_glyph_codec.blade.php) — un glyphe est déjà une forme compacte, vectorielle et sémantique, exactement ce qu'un OLED peut tramer. Non spécifié ici : il faudra un format raster/vecteur minimal dans XUIC ([p,'glyph',label,ast]?). - Auth par item : marquer un
actcomme demandant un grade xion. V0 s'en remet aux murailles existantes du bus (§6). - Kinds supplémentaires (
chart,map, …) : interdits en v0 — un renderer qui reçoit un kind inconnu l'ignore silencieusement (règle de forward-compat, elle, gelée).
Statut : v0 · Licence : AGPL-3.0
· 13.08.2026 · xui