Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Spécification air-tui — noyau 1.0

Document de spécification — couche 4

Rôle du document

Ce document précise le noyau opérationnel du framework air-tui après les décisions d’architecture actées dans :

  • ADR-008 — trois backends ;
  • ADR-009 — API déclarative sans coordonnées physiques ;
  • ADR-018 — modèle cellulaire pour le mode console ;
  • ADR-026 — contrat 1.0 d’air-tui.

Ce document ne fige pas encore une API Rust complète ni les signatures exactes des types publics. Il fixe en revanche trois contrats sans lesquels le framework ne peut pas être implémenté proprement :

  • le modèle Capabilities ;
  • le modèle de focus ;
  • le noyau des commandes sémantiques.

1. Périmètre

air-tui est le framework console de la couche 4. Il s’appuie sur :

  • un tronc déclaratif commun avec air-ui (air-view) ;
  • un renderer cellulaire (air-ui-render-tui) ;
  • trois backends de transport :
    • backend console local via air-console ;
    • backend terminal Air via protocole privé ;
    • backend compatible terminal tiers/SSH via Kitty keyboard protocol ou ANSI.

Le présent document couvre uniquement la sémantique commune à ces trois backends. Tout détail spécifique à un backend ne peut exister que comme capacité optionnelle ou extension interne, jamais comme hypothèse implicite du framework.

2. Modèle Capabilities

2.1 Objectif

Le type Capabilities décrit ce que l’environnement d’exécution garantit à une application air-tui.

Il ne décrit pas :

  • les préférences utilisateur ;
  • les entitlements applicatifs ;
  • l’état dynamique de l’application ;
  • les permissions de sécurité système.

Il décrit seulement les capacités d’interaction et de rendu du backend TUI.

2.2 Principes

Le modèle suit cinq principes :

  1. Une capacité décrit une garantie positive ou une absence explicite, jamais une supposition implicite.
  2. Une capacité doit être stable sémantiquement à travers les backends.
  3. Une capacité doit être interrogeable au runtime.
  4. Une capacité peut être consommée au build pour du capability-gating statique ultérieur, mais le runtime reste la source de vérité.
  5. Une capacité ne doit jamais prétendre émuler honnêtement ce qu’un backend ne sait pas fournir.

2.3 Forme conceptuelle

Le modèle conceptuel attendu est :

  • un profil global de backend ;
  • un ensemble de drapeaux ou enums fins par famille ;
  • une distinction entre guaranteed, optional, unsupported quand cela a du sens.

Exemple purement illustratif :

#![allow(unused)]
fn main() {
struct Capabilities {
    backend_profile: BackendProfile,
    keyboard: KeyboardCapabilities,
    pointer: PointerCapabilities,
    text: TextCapabilities,
    clipboard: ClipboardCapabilities,
    focus: FocusCapabilities,
    viewport: ViewportCapabilities,
}
}

Les noms exacts restent ouverts ; la structure logique, non.

2.4 Profils globaux

Trois profils globaux sont normatifs.

FullConsole

Profil local premium sous air-console.

Garanties minimales :

  • KeyDown et KeyUp fiables ;
  • modifiers complets et layout-aware ;
  • souris native ;
  • resize fiable ;
  • focus structuré ;
  • couleurs riches ;
  • Unicode sérieux ;
  • paste fiable ;
  • scroll événementiel réel.

AirTerminal

Profil transporté via terminal Air.

Garanties minimales :

  • mêmes invariants applicatifs que FullConsole ;
  • latence et qualité proches ;
  • transport indirect sans perte sémantique substantielle.

Différence clé :

  • le backend n’est pas le compositeur local lui-même.

CompatibleTerminal

Profil dégradé sur terminal tiers ou SSH.

Garanties minimales :

  • texte rendu en grille ;
  • KeyDown pour un sous-ensemble significatif du clavier ;
  • resize généralement disponible ;
  • paste et souris possibles mais non garantis ;
  • KeyUp potentiellement absent ;
  • modifiers parfois incomplets ;
  • rendu Unicode et largeur de cellules dépendants du terminal.

2.5 Familles de capacités

Le framework doit exposer au minimum les familles suivantes.

Capacités clavier

Doivent permettre de répondre explicitement aux questions suivantes :

  • KeyUp est-il garanti ?
  • les modifiers gauche/droite sont-ils distingués ?
  • les touches de navigation sont-elles fiables ?
  • la répétition clavier est-elle distinguée d’une nouvelle pression ?
  • les combinaisons avec Alt, Ctrl, Shift, Super sont-elles fiables ?
  • la composition IME est-elle disponible ?

Capacités pointeur

Doivent permettre de répondre explicitement aux questions suivantes :

  • un pointeur est-il disponible ?
  • les clics sont-ils disponibles ?
  • le mouvement est-il disponible ?
  • le scroll est-il disponible ?
  • la capture/glisse continue est-elle disponible ?

Capacités texte

Doivent permettre de répondre explicitement aux questions suivantes :

  • Unicode est-il pris en charge sérieusement ?
  • la largeur de cellule est-elle fiable pour les caractères larges ?
  • le shaping complexe est-il pris en charge ?
  • le bidi est-il pris en charge ?
  • la coloration riche est-elle disponible ?

Le framework ne doit pas promettre le même niveau de sophistication textuelle entre FullConsole et CompatibleTerminal.

Capacités viewport

Doivent permettre de répondre explicitement aux questions suivantes :

  • la taille de grille est-elle fiable ?
  • les événements de resize sont-ils fiables ?
  • les régions invalidées partielles sont-elles honorées ?
  • le mode écran alternatif est-il disponible ?

Capacités clipboard / paste

Doivent distinguer au minimum :

  • unsupported
  • paste_only
  • read_write

Capacités focus

Doivent permettre de répondre explicitement aux questions suivantes :

  • le framework peut-il garantir un focus unique stable ?
  • les changements de focus peuvent-ils être annoncés fiablement ?
  • un focus pointeur et un focus clavier coexistent-ils ou non ?

2.6 Règles de dégradation

Une application air-tui doit pouvoir :

  • interroger ses capacités au démarrage ;
  • réagir à leur variation si le backend le permet ;
  • publier des dégradations contrôlées par composant ;
  • refuser proprement un mode si une capacité indispensable est absente.

Une vue peut exiger une capacité forte, mais elle doit alors :

  • le déclarer explicitement ;
  • fournir un fallback ;
  • ou échouer proprement avec diagnostic.

Le framework ne doit jamais transformer silencieusement une garantie absente en succédané trompeur.

2.7 API conceptuelle de Capabilities

L’API publique exacte reste à écrire, mais Capabilities doit offrir au minimum les opérations conceptuelles suivantes.

Identification du profil

L’application doit pouvoir connaître le profil courant :

  • backend_profile() -> FullConsole | AirTerminal | CompatibleTerminal

Cette information sert :

  • au diagnostic ;
  • à la télémétrie locale éventuelle de debug ;
  • aux choix de fallback de haut niveau ;
  • aux tests de conformité inter-backends.

Elle ne doit pas être utilisée comme substitut aux capacités fines quand une question précise peut être posée directement.

Interrogation des familles de capacités

L’application doit pouvoir accéder à des sous-objets ou vues immuables sur :

  • keyboard()
  • pointer()
  • text()
  • clipboard()
  • focus()
  • viewport()

Chaque sous-objet doit offrir des prédicats ou enums stables. Exemples :

  • keyboard.key_up()
  • keyboard.left_right_modifiers()
  • pointer.scroll()
  • text.bidi()
  • viewport.alternate_screen()

Le document ne fige pas la forme exacte (bool, enum, bitflags), mais fige la nécessité d’une interrogation explicite, lisible et stable.

Vérification de prérequis

Le framework doit offrir une manière standard de vérifier qu’un ensemble de capacités minimales est satisfait.

Exemples conceptuels :

  • supports(requirements)
  • missing(requirements)
  • assert_supported(requirements)

Cette couche est importante pour :

  • éviter la répétition de conditions ad hoc dans les apps ;
  • produire des diagnostics homogènes ;
  • documenter les prérequis d’une vue ou d’un widget.

Observation des changements

Quand le backend supporte des changements dynamiques, l’application doit pouvoir être notifiée.

Cas typiques :

  • resize ;
  • bascule clipboard disponible / indisponible ;
  • variation du mode pointeur ;
  • perte d’une capacité sur transport dégradé.

Le contrat minimal est :

  • soit les capacités sont statiques pour la session ;
  • soit les changements sont observables explicitement.

Un changement silencieux non observable est interdit pour une capacité exposée.

Exigences déclaratives des vues

Une vue ou un widget doit pouvoir déclarer :

  • des capacités requises ;
  • des capacités améliorant le rendu ou l’interaction ;
  • un fallback quand les capacités requises ne sont pas remplies.

Le framework devra donc fournir un mécanisme conceptuel du type :

  • requires(...)
  • prefers(...)
  • fallback(...)

La syntaxe exacte reste ouverte, le contrat non.

Diagnostic standard

Le framework doit pouvoir produire un diagnostic lisible à partir de Capabilities.

Ce diagnostic doit au minimum répondre à :

  • quel backend suis-je en train d’utiliser ?
  • quelles capacités majeures sont garanties ?
  • quelles capacités manquent par rapport au mode premium ?
  • pourquoi telle vue s’est-elle dégradée ?

Le diagnostic standard est un outil de debug, de support utilisateur, et de test.

3. Modèle de focus

3.1 Objectif

Le focus est un concept de premier rang dans air-tui. Il ne peut pas être un effet de bord du rendu ni un simple index de widget actif.

Le modèle de focus sert cinq objectifs :

  • navigation clavier déterministe ;
  • accessibilité ;
  • dispatch des commandes ;
  • cohérence entre backends ;
  • testabilité.

3.2 Invariants

Les invariants suivants sont normatifs.

  1. Une scène air-tui possède au plus un focus clavier primaire.
  2. Le focus clavier primaire appartient soit à un élément focusable, soit à aucun élément.
  3. Un élément non focusable ne peut jamais devenir focus primaire.
  4. La perte du focus sur un élément doit générer une transition explicite.
  5. Le focus ne dépend jamais de coordonnées physiques publiques.
  6. L’ordre de tabulation doit être déterministe pour un arbre donné.
  7. Un conteneur peut déléguer le focus à un descendant, jamais le dupliquer.

3.3 Types conceptuels de focus

Le framework doit distinguer conceptuellement :

  • keyboard focus — destination des événements clavier et commandes ;
  • pointer hover — élément survolé quand un pointeur existe ;
  • active scope — sous-arbre actuellement actif pour les commandes ;
  • modal focus — capture temporaire par dialogue, palette ou menu.

Ces concepts peuvent coexister, mais ne doivent pas être confondus.

3.4 Éléments focusables

Un élément focusable doit déclarer au minimum :

  • s’il accepte le focus ;
  • s’il accepte l’activation ;
  • s’il participe à l’ordre de tabulation ;
  • s’il expose un rôle d’accessibilité cohérent ;
  • quelles commandes il consomme prioritairement.

Exemples d’éléments focusables :

  • bouton ;
  • champ de texte ;
  • éditeur ;
  • liste sélectionnable ;
  • table navigable ;
  • arbre navigable ;
  • palette de commandes.

Exemples d’éléments non focusables par défaut :

  • texte statique ;
  • séparateur ;
  • conteneur purement décoratif.

3.5 Navigation de focus

Le framework doit fournir un noyau de navigation indépendant du backend :

  • focus suivant ;
  • focus précédent ;
  • focus premier ;
  • focus dernier ;
  • entrée dans un scope ;
  • sortie d’un scope ;
  • focus vers panneau voisin ;
  • restauration du focus précédent lors de fermeture d’un modal.

Ces transitions deviennent ensuite des commandes sémantiques, jamais des conventions cachées.

3.6 Scopes de focus

Un focus scope est un sous-arbre déclaratif qui :

  • définit une frontière de navigation ;
  • peut mémoriser son dernier descendant actif ;
  • peut piéger temporairement le focus ;
  • peut se déclarer modal ou non modal.

Cas typiques :

  • dialogue ;
  • palette de commandes ;
  • panneau latéral ;
  • split view ;
  • formulaire.

3.7 Politique clavier / souris

Le framework est keyboard-first.

Cela implique :

  • un clic pointeur peut déplacer le focus si la capacité pointeur existe ;
  • aucun élément critique ne doit être accessible uniquement à la souris ;
  • toute action de souris importante doit avoir une équivalence clavier ;
  • l’absence de pointeur ne doit pas casser l’interaction fondamentale.

3.8 API conceptuelle de FocusScope

FocusScope est la brique de structuration du focus. L’API publique exacte reste ouverte, mais les responsabilités minimales sont fixées ici.

Création et rôle

Un scope doit pouvoir être déclaré comme :

  • scope standard ;
  • scope mémorisant son dernier focus descendant ;
  • scope modal ;
  • scope piégeant la navigation ;
  • scope racine logique d’un panneau ou d’un dialogue.

Le scope n’est pas un simple conteneur visuel. C’est une frontière d’interaction.

Insertion dans l’arbre

Un scope doit pouvoir :

  • contenir des descendants focusables ou d’autres scopes ;
  • participer à la hiérarchie de dispatch ;
  • annoncer s’il est actif, modal, ou dormant ;
  • définir s’il veut une navigation séquentielle, directionnelle, ou les deux.

Opérations minimales

Le modèle conceptuel doit supporter au minimum :

  • contains(focus_target)
  • is_active()
  • is_modal()
  • activate()
  • deactivate()
  • focus_first()
  • focus_last()
  • focus_next()
  • focus_previous()
  • restore_last_focus()
  • clear_focus()

Les noms exacts restent ouverts ; les opérations, non.

Mémoire du focus

Un scope peut mémoriser son dernier descendant focalisé pour permettre :

  • restauration à la réouverture d’un panneau ;
  • retour cohérent après fermeture d’un modal ;
  • navigation plus naturelle dans les vues fractionnées.

Cette mémoire doit être explicite, jamais implicite ou accidentelle.

Piège modal

Un scope modal doit pouvoir :

  • capturer le focus primaire ;
  • empêcher la fuite de navigation hors du scope ;
  • relâcher le focus proprement à sa fermeture ;
  • restaurer le focus précédent si possible.

Les dialogues et palettes de commandes utilisent ce comportement par défaut.

Politique de sortie

Un scope doit pouvoir préciser ce qu’il advient quand la navigation atteint sa frontière :

  • boucle interne ;
  • sortie vers le parent ;
  • blocage explicite ;
  • délégation à une politique de scène.

Cette politique doit être déterministe et testable.

Relation avec les commandes

Un scope doit pouvoir enregistrer ou résoudre des commandes locales :

  • commandes de navigation internes ;
  • commandes de confirmation/annulation ;
  • commandes propres à un conteneur complexe.

Lors du dispatch, le scope actif constitue un niveau intermédiaire stable entre le widget focalisé et la scène.

4. Modèle d’événements bruts

4.1 Objectif

Le modèle d’événements bruts est la couche d’entrée commune à tous les backends air-tui.

Il sert à :

  • transporter les faits d’interaction observés ;
  • alimenter la résolution des commandes sémantiques ;
  • fournir une base de tests inter-backends ;
  • exprimer honnêtement les dégradations des terminaux tiers.

Un événement brut n’est ni une commande, ni une intention applicative. C’est un fait d’entrée structuré.

4.2 Principes

Le modèle suit six principes :

  1. Un événement brut décrit ce qui s’est produit, pas ce que l’application doit faire.
  2. La sémantique d’un événement doit rester stable entre backends.
  3. Une information absente ne doit pas être inventée silencieusement.
  4. Les timestamps et métadonnées d’origine sont autorisés, mais secondaires par rapport au sens interactionnel.
  5. Les événements doivent être utilisables en replay de test.
  6. Le backend doit traduire son protocole local vers ce modèle, jamais exposer directement un flux ANSI ou propriétaire au framework.

4.3 Familles d’événements

Le noyau 1.0 doit couvrir au minimum les familles suivantes :

  • clavier ;
  • pointeur ;
  • texte/paste ;
  • focus ;
  • viewport ;
  • système d’interaction.

4.4 Événements clavier

Les événements clavier minimaux sont :

  • KeyDown
  • KeyUp
  • KeyRepeat
  • ModifiersChanged

Un événement clavier doit pouvoir exprimer au minimum :

  • une identité de touche stable ;
  • l’état des modifiers ;
  • éventuellement le texte produit ;
  • l’origine backend ;
  • le caractère répété ou non.

Règles :

  • KeyRepeat ne doit pas être confondu avec une succession de KeyDown si le backend sait faire la distinction ;
  • en CompatibleTerminal, l’absence de KeyUp doit être déclarée par Capabilities, pas cachée ;
  • la résolution en commande n’a pas lieu à ce niveau.

4.5 Événements pointeur

Les événements pointeur minimaux sont :

  • MouseDown
  • MouseUp
  • MouseMove
  • Scroll
  • MouseEnter
  • MouseLeave

Un événement pointeur doit pouvoir exprimer au minimum :

  • position logique dans le viewport ;
  • bouton ou direction pertinente ;
  • état des modifiers ;
  • granularité du scroll si connue.

Règles :

  • un backend qui ne sait pas fournir MouseEnter / MouseLeave ne doit pas les simuler à l’aveugle ;
  • un scroll continu et un scroll discret peuvent partager la même famille d’événements tout en gardant une granularité distincte.

4.6 Événements texte et paste

Les événements minimaux sont :

  • Paste
  • ImePreedit si supporté plus tard
  • ImeCommit

Règles :

  • Paste transporte un payload textuel brut ;
  • Paste ne doit pas être réémis sous forme de succession artificielle de KeyDown si le backend sait distinguer le collage ;
  • ImeCommit doit rester distinct de toute commande d’insertion tant qu’aucun binding n’a été résolu.

4.7 Événements de focus

Les événements minimaux sont :

  • FocusGained
  • FocusLost
  • HoverGained
  • HoverLost

Règles :

  • FocusGained / FocusLost concernent le focus clavier ;
  • HoverGained / HoverLost concernent le survol pointeur ;
  • le backend ne décide pas seul de la politique de focus globale ; il signale seulement les faits observables.

4.8 Événements de viewport

Les événements minimaux sont :

  • Resize
  • ViewportExposed si une exposition partielle existe

Resize doit exprimer au minimum :

  • taille logique en cellules du viewport ;
  • information de validité/cohérence ;
  • éventuellement la cause si connue.

4.9 Événements système d’interaction

Le noyau 1.0 doit permettre au moins :

  • CapabilitiesChanged
  • BackendSuspended
  • BackendResumed

Ces événements ne sont pas des commandes et ne doivent pas être dispatchés comme telles.

4.10 Type conceptuel RawEvent

La future API devra matérialiser un type conceptuel RawEvent couvrant l’ensemble des familles ci-dessus.

Exigences minimales :

  • somme typée ou équivalent ;
  • inspectable ;
  • sérialisable pour tests ;
  • utilisable en replay ;
  • non ambigu entre texte, clavier, focus, pointeur et système.

4.11 EventContext

La future API devra aussi matérialiser un contexte léger associé aux événements bruts, distinct de CommandContext.

EventContext sert à fournir :

  • horodatage éventuel ;
  • backend d’origine ;
  • viewport courant ;
  • capabilities courantes ;
  • métadonnées de tracing ou de replay.

Règle importante :

  • EventContext appartient à la couche de traitement des événements ;
  • CommandContext appartient à la couche de dispatch des commandes ;
  • les deux peuvent partager des données, mais ne doivent pas être fusionnés sans nécessité.

4.12 Pipeline normative

La pipeline minimale devient :

RawEvent -> normalisation backend -> focus/hit-testing -> binding -> Command -> dispatch

Les étapes peuvent être implémentées différemment, mais aucune ne peut être supprimée conceptuellement sans réintroduire de l’ambiguïté.

4.13 Règles de tests

Le modèle d’événements doit être testable au minimum sur :

  • distinction KeyDown / KeyRepeat / KeyUp quand disponible ;
  • absence honnête de KeyUp en terminal tiers ;
  • cohérence des événements Resize ;
  • distinction Paste vs saisie clavier ;
  • conservation de l’ordre des événements en replay.

5. Commandes sémantiques

5.1 Objectif

Les commandes sémantiques forment le contrat d’action de air-tui.

Une commande n’est pas une touche. C’est une intention stable, rebindable, journalisable, testable, et dispatchable dans l’arbre de vues.

5.2 Pipeline

La pipeline normative est :

événement brut -> binding -> commande -> dispatch -> action

Le binding dépend :

  • du backend ;
  • du layout clavier ;
  • du contexte de focus ;
  • des préférences utilisateur ;
  • du scope modal actif.

La commande produite doit être indépendante de ces détails.

5.3 Niveaux de commandes

Le framework doit distinguer au moins quatre niveaux :

  • commandes d’application ;
  • commandes de scène ;
  • commandes de conteneur ;
  • commandes de widget.

Commandes d’application

Exemples :

  • app.quit
  • app.help
  • app.preferences
  • app.refresh

Commandes de scène

Exemples :

  • scene.focus_next
  • scene.focus_previous
  • scene.cancel
  • scene.confirm
  • scene.open_palette

Commandes de conteneur

Exemples :

  • pane.focus_left
  • pane.focus_right
  • split.resize_increase
  • tabs.next
  • tabs.previous

Commandes de widget

Exemples :

  • list.move_next
  • list.move_previous
  • list.page_down
  • tree.expand
  • tree.collapse
  • editor.insert_newline
  • editor.delete_backward
  • editor.save

5.4 Noyau minimal 1.0

Le noyau minimal de commandes garanties par le framework doit couvrir au moins les familles suivantes.

  • scene.focus_next
  • scene.focus_previous
  • scene.confirm
  • scene.cancel
  • scene.open_palette
  • nav.up
  • nav.down
  • nav.left
  • nav.right
  • nav.page_up
  • nav.page_down
  • nav.home
  • nav.end

Édition de texte

  • text.insert
  • text.delete_backward
  • text.delete_forward
  • text.move_left
  • text.move_right
  • text.move_word_left
  • text.move_word_right
  • text.select_all
  • text.copy
  • text.cut
  • text.paste

Listes / tables / arbres

  • collection.move_next
  • collection.move_previous
  • collection.activate
  • collection.expand
  • collection.collapse
  • collection.select_toggle

Fenêtrage logique / panneaux

  • pane.focus_left
  • pane.focus_right
  • pane.focus_up
  • pane.focus_down
  • pane.close

5.5 Dispatch

Le dispatch suit l’ordre conceptuel suivant :

  1. widget focalisé ;
  2. scope de focus actif ;
  3. conteneurs parents ;
  4. scène ;
  5. application.

Une commande non gérée peut :

  • être ignorée explicitement ;
  • déclencher un feedback standard ;
  • remonter jusqu’à l’application ;
  • être réinterprétée par un fallback déclaré.

Le framework ne doit pas inventer de comportement caché non inspectable.

5.6 Binding par défaut

Les bindings concrets ne sont pas tous figés ici, mais les règles suivantes le sont :

  • ils doivent rester cohérents avec les conventions TUI modernes ;
  • ils doivent être remappables ;
  • ils ne doivent pas dépendre exclusivement de touches indisponibles sur CompatibleTerminal ;
  • ils doivent avoir des alternatives quand une capacité manque.

Exemple :

  • si KeyUp n’existe pas, aucun binding critique ne doit en dépendre exclusivement ;
  • si la souris n’existe pas, aucune commande de navigation primaire ne doit lui être réservée.

6. Types publics conceptuels

Cette section introduit les types conceptuels minimaux que la future API publique air-tui devra matérialiser. Les noms exacts et signatures exactes restent ouverts, mais leur rôle ne l’est plus.

6.1 CapabilityRequirements

Rôle

CapabilityRequirements représente un ensemble déclaratif d’exigences qu’une vue, un widget, ou une fonctionnalité veut imposer à son environnement d’exécution.

Ce type sert à :

  • documenter les prérequis d’un composant ;
  • centraliser la logique de support/fallback ;
  • produire des diagnostics homogènes ;
  • éviter les branches ad hoc fondées directement sur le backend.

Contenu conceptuel minimal

CapabilityRequirements doit pouvoir exprimer :

  • des capacités requises ;
  • des capacités préférées ;
  • des capacités explicitement non nécessaires ;
  • éventuellement un niveau minimal de profil quand c’est justifié.

Le type ne doit pas être une simple liste de booléens. Il doit permettre une description structurée par famille :

  • clavier ;
  • pointeur ;
  • texte ;
  • clipboard ;
  • focus ;
  • viewport.

Opérations minimales

Le modèle conceptuel doit supporter au minimum :

  • empty()
  • require(...)
  • prefer(...)
  • merge(...)
  • is_satisfied_by(capabilities)
  • missing_from(capabilities)

Résultat de vérification

La vérification d’un CapabilityRequirements ne doit pas se limiter à un booléen quand un diagnostic plus riche est possible.

Le framework doit pouvoir produire au minimum :

  • satisfait / non satisfait ;
  • liste des capacités manquantes ;
  • liste des capacités seulement dégradées ;
  • raison normalisée du fallback.

Règles

  • Un widget du noyau 1.0 ne doit pas exiger un backend nominal si une exigence plus fine suffit.
  • Une exigence doit être formulée en termes de capacité, pas de marqueur d’implémentation.
  • Les exigences doivent être composables entre vues parentes et enfants.

6.2 FocusTarget

Rôle

FocusTarget représente une cible focusable adressable par le système de focus.

Il n’est pas nécessairement un widget visible autonome ; il peut aussi représenter :

  • un sous-élément focusable d’un widget complexe ;
  • un point d’entrée logique dans un scope ;
  • une cible virtuelle restaurable.

Invariants

Un FocusTarget doit être :

  • stable pour la durée de vie logique de son élément ;
  • comparable pour déterminer l’égalité de cible ;
  • utilisable dans les diagnostics et tests ;
  • indépendant de coordonnées physiques publiques.

Propriétés conceptuelles minimales

Un FocusTarget doit pouvoir exposer ou permettre d’obtenir :

  • son scope parent ;
  • sa focusability actuelle ;
  • son rôle d’interaction ;
  • son ordre relatif de navigation ;
  • son état activable ou non.

Opérations minimales

Le modèle conceptuel doit supporter au minimum :

  • is_focusable()
  • scope()
  • role()
  • activation_kind()

Un FocusTarget n’a pas à muter directement le focus lui-même ; cette responsabilité appartient au moteur de focus et aux scopes.

Cas des widgets complexes

Un widget complexe comme Table, Tree, Editor, ou CommandPalette peut choisir entre deux stratégies :

  • un focus target unique avec curseur interne ;
  • plusieurs focus targets descendants explicites.

La stratégie choisie doit rester cohérente avec :

  • l’accessibilité ;
  • la testabilité ;
  • la stabilité des commandes ;
  • la simplicité de navigation.

6.3 Command

Rôle

Command représente une intention sémantique dispatchable.

Il ne représente ni :

  • une touche ;
  • un événement brut ;
  • une callback liée à un widget particulier.

Structure conceptuelle minimale

Un Command doit pouvoir exprimer au minimum :

  • une identité stable ;
  • une catégorie ;
  • éventuellement une payload typée ou contextualisée ;
  • une origine ou cause logique si utile au debug.

Exemples :

  • commande sans payload : scene.cancel
  • commande paramétrée : text.insert(character)
  • commande contextualisée : collection.activate(current_selection)

Identité

L’identité d’une commande doit être :

  • stable dans le temps ;
  • sérialisable pour debug/tests ;
  • utilisable dans les diagnostics, la palette de commandes et les bindings.

Le document ne fige pas encore si cette identité est :

  • une enum fermée ;
  • un identifiant hiérarchique string-like ;
  • un couple catégorie + verbe ;
  • ou une combinaison de ces approches.

Mais il fige qu’elle doit être stable et inspectable.

Opérations minimales

Le modèle conceptuel doit supporter au minimum :

  • id()
  • category()
  • payload()
  • is_repeatable()
  • is_text_input()

Ces opérations n’imposent pas la forme finale, seulement la sémantique attendue.

Règles

  • Une commande du noyau 1.0 doit pouvoir être remappée.
  • Une commande critique doit avoir un sens stable entre backends.
  • Une commande ne doit pas dépendre d’un widget concret pour exister.

6.4 CommandContext

Rôle

CommandContext représente l’environnement sémantique dans lequel une commande est évaluée et dispatchée.

Il sert à éviter que les widgets ou l’application aillent puiser directement des informations globales dispersées.

Contenu conceptuel minimal

Un CommandContext doit fournir au minimum l’accès à :

  • Capabilities actives ;
  • focus primaire courant ;
  • scope actif ;
  • informations de viewport utiles ;
  • état modal éventuel ;
  • métadonnées d’origine de la commande ;
  • canal de feedback ou de rejet standard.

Opérations minimales

Le modèle conceptuel doit supporter au minimum :

  • capabilities()
  • focused_target()
  • active_scope()
  • viewport()
  • origin()
  • reject(reason)
  • emit_feedback(feedback)

Origine de commande

Le contexte doit distinguer conceptuellement l’origine d’une commande :

  • clavier ;
  • pointeur ;
  • paste ;
  • accessibilité ;
  • automation/test ;
  • commande programmatiquement injectée.

Cette distinction est utile pour :

  • diagnostiquer des comportements ;
  • adapter certains feedbacks ;
  • tester la cohérence inter-backends.

Règles

  • CommandContext ne doit pas devenir un objet fourre-tout applicatif.
  • Il transporte le strict nécessaire au dispatch et au feedback.
  • Toute donnée métier applicative doit rester hors de ce type.

6.5 Relations entre les types

Les relations minimales suivantes sont normatives :

  • CapabilityRequirements s’évalue contre Capabilities.
  • FocusTarget vit dans un FocusScope.
  • Command est dispatchée à partir d’un FocusTarget et d’un CommandContext.
  • CommandContext expose le FocusTarget focalisé et les Capabilities actives.

Schéma conceptuel :

Capabilities ----> CapabilityRequirements
      |
      v
CommandContext ---> FocusScope ---> FocusTarget
      |
      v
   Command

6.6 Non-objectifs à ce stade

Cette section ne fige pas encore :

  • les traits Rust exacts ;
  • le niveau de généricité ;
  • la sérialisation wire de ces types ;
  • la séparation exacte entre types air-view communs et types air-tui spécifiques.

Ces points viendront plus tard. Le but ici est de verrouiller la sémantique, pas la syntaxe API.

7. Relations avec les widgets 1.0

Le noyau de widgets défini par ADR-026 impose déjà quelques attentes.

Widgets devant obligatoirement participer au focus

  • Input
  • Editor
  • List
  • Table
  • Tree
  • Tabs
  • Dialog
  • CommandPalette

Widgets pouvant être focusables selon configuration

  • Block / Panel
  • StatusBar
  • Progress quand interactif

Widgets non focusables par défaut

  • Text
  • Span
  • Chart purement passif

8. Premier lot de widgets 1.0

Cette section précise le premier lot de widgets à considérer comme noyau implémentable d’air-tui. Le but n’est pas d’épuiser le futur widget set, mais de fixer un socle cohérent avec Capabilities, FocusScope et les commandes sémantiques.

Pour chaque widget, quatre dimensions comptent :

  • rôle principal ;
  • contrat de focus ;
  • commandes minimales ;
  • dégradations acceptées.

8.1 Text

Rôle. Affichage de texte statique ou riche en spans stylés.

Focus. Non focusable par défaut.

Commandes minimales. Aucune commande d’interaction obligatoire.

Dégradations acceptées.

  • réduction de richesse colorimétrique ;
  • wrapping différent selon backend ;
  • shaping/bidi réduit en CompatibleTerminal.

Dégradations interdites en silence.

  • texte tronqué sans signalement quand une vue exige explicitement l’intégrité ;
  • largeur de cellule incohérente cassant la mise en page.

8.2 Block / Panel

Rôle. Conteneur logique et visuel.

Focus. Non focusable par défaut ; peut devenir focusable s’il représente un panneau navigable ou sélectionnable.

Commandes minimales.

  • aucune en mode passif ;
  • scene.focus_next / scene.focus_previous si focusable ;
  • pane.focus_* quand utilisé comme panneau d’une vue fractionnée.

Dégradations acceptées.

  • bordures simplifiées ;
  • styles de box-drawing réduits ;
  • titres plus sobres.

8.3 Input

Rôle. Champ texte mono-ligne.

Focus. Toujours focusable.

Commandes minimales.

  • scene.confirm
  • scene.cancel
  • text.insert
  • text.delete_backward
  • text.delete_forward
  • text.move_left
  • text.move_right
  • text.move_word_left
  • text.move_word_right
  • text.select_all
  • text.copy
  • text.cut
  • text.paste

Dégradations acceptées.

  • sélection visuelle plus simple ;
  • clipboard limité ;
  • IME absent sur backend dégradé.

Dégradations interdites en silence.

  • perte d’édition clavier de base ;
  • focus instable ;
  • paste destructif ou ambigu sans signalement.

Machine d’état minimale.

  • idle : champ non focalisé.
  • focused : champ focalisé, curseur visible.
  • editing : insertion/suppression active.
  • selecting : sélection active si supportée.
  • disabled : visible mais non éditable.

Le passage focused -> editing peut être implicite dès la première entrée texte. Le passage focused -> disabled n’est autorisé que sur changement d’état explicite de l’application.

Modèle de focus.

  • un Input correspond par défaut à un unique FocusTarget ;
  • l’entrée dans le focus place le curseur selon la politique du composant : début, fin, ou dernière position mémorisée ;
  • la sortie du focus doit figer proprement la sélection et le curseur ;
  • un Input dans un Dialog ne doit pas pouvoir perdre le focus hors du scope modal sans commande explicite.

Sémantique de commandes.

  • text.insert ajoute le contenu au point d’insertion courant ;
  • text.delete_backward et text.delete_forward opèrent de manière déterministe sur sélection ou curseur ;
  • scene.confirm valide le champ si le contexte l’autorise, sinon remonte ;
  • scene.cancel annule l’édition en cours seulement si le composant le supporte explicitement ; sinon la commande remonte.

Feedback minimal.

  • focus visible ;
  • curseur visible ;
  • état désactivé perceptible ;
  • erreur de validation perceptible mais non bloquante par défaut.

Pré-requis de capacités.

  • requis : keyboard, focus, viewport.resize fiable ;
  • préférés : clipboard, text.bidi, ime.

8.4 Editor

Rôle. Édition texte multi-ligne.

Focus. Toujours focusable ; constitue généralement son propre scope d’interaction.

Commandes minimales.

  • toutes celles de Input ;
  • nav.up
  • nav.down
  • nav.page_up
  • nav.page_down
  • nav.home
  • nav.end
  • editor.insert_newline
  • editor.save si l’éditeur expose cette action

Dégradations acceptées.

  • rendu de sélection simplifié ;
  • gouttière ou numéros de ligne absents ;
  • shortcuts avancés indisponibles sur backend dégradé.

Dégradations interdites en silence.

  • désynchronisation curseur / texte ;
  • perte de navigation fondamentale ;
  • modifications de texte non déterministes.

Machine d’état minimale.

  • idle : éditeur non focalisé ;
  • focused : éditeur focalisé ;
  • editing : insertion/suppression active ;
  • selecting : sélection active ;
  • navigating : déplacement sans mutation ;
  • readonly : navigation autorisée, mutation interdite ;
  • disabled : ni édition ni navigation active.

Un éditeur ne doit jamais mélanger silencieusement readonly et disabled.

Stratégie de focus.

Deux stratégies sont permises :

  • un unique FocusTarget avec curseur interne ;
  • plusieurs cibles de focus internes si l’éditeur expose des sous-zones distinctes.

Pour le noyau 1.0, la stratégie recommandée est le focus target unique avec curseur interne, plus simple et plus stable pour les commandes.

Sémantique de navigation.

  • nav.up / nav.down déplacent le curseur verticalement ;
  • nav.page_up / nav.page_down déplacent le viewport logique ;
  • nav.home / nav.end agissent au minimum sur la ligne courante ;
  • le maintien d’une colonne désirée lors des déplacements verticaux doit être déterministe.

Sémantique d’édition.

  • text.insert et editor.insert_newline doivent produire des mutations ordonnées et reproductibles ;
  • les suppressions doivent avoir une sémantique définie sur sélection, caractère, ou unité logique future ;
  • editor.save n’est disponible que si le composant annonce explicitement cette action ; sinon la commande remonte.

Feedback minimal.

  • curseur visible ;
  • viewport cohérent autour du curseur ;
  • état readonly perceptible ;
  • sélection perceptible quand elle est supportée ;
  • erreur ou rejet de commande signalé via feedback standard.

Pré-requis de capacités.

  • requis : keyboard, focus, viewport.resize ;
  • préférés : clipboard, text.wide_cell_width, text.bidi, ime.

8.5 List

Rôle. Collection linéaire navigable.

Focus. Focusable si interactive.

Commandes minimales.

  • collection.move_next
  • collection.move_previous
  • nav.page_up
  • nav.page_down
  • nav.home
  • nav.end
  • collection.activate
  • collection.select_toggle si multi-sélection

Dégradations acceptées.

  • marqueurs de sélection simplifiés ;
  • hover absent ;
  • scroll moins fin.

Machine d’état minimale.

  • idle : liste non focalisée ;
  • focused : liste focalisée ;
  • navigating : déplacement de l’élément actif ;
  • selecting : changement de sélection ;
  • empty : liste vide ;
  • disabled : liste visible mais non interactive.

Une liste peut être focused et empty ; elle doit alors rester navigable au sens du focus global, sans action de collection disponible.

Modèle interne minimal.

Une List interactive doit distinguer explicitement :

  • l’élément actif ;
  • l’ensemble sélectionné si la multi-sélection existe ;
  • l’ancre de scroll logique.

L’élément actif et la sélection ne doivent pas être confondus.

Stratégie de focus.

Pour le noyau 1.0, la stratégie recommandée est :

  • un unique FocusTarget pour la liste ;
  • un curseur/élément actif interne.

La stratégie “un focus target par ligne” est autorisée plus tard, mais alourdit fortement l’accessibilité, le dispatch et les tests.

Sémantique de commandes.

  • collection.move_next / collection.move_previous déplacent l’élément actif sans ambiguïté ;
  • collection.activate active l’élément courant ;
  • collection.select_toggle modifie la sélection sans casser l’élément actif ;
  • nav.page_up / nav.page_down déplacent au moins l’ancre visible.

Feedback minimal.

  • élément actif perceptible ;
  • sélection perceptible si présente ;
  • état vide perceptible ;
  • impossibilité d’activation signalée proprement.

Pré-requis de capacités.

  • requis : keyboard, focus ;
  • préférés : pointer, viewport.resize, pointer.scroll.

8.6 Table

Rôle. Données tabulaires navigables.

Focus. Focusable si interactive.

Commandes minimales.

  • toutes les commandes de List ;
  • nav.left
  • nav.right

Dégradations acceptées.

  • colonnes tronquées ou repliées ;
  • absence de resize interactif des colonnes ;
  • styles de séparation simplifiés.

Dégradations interdites en silence.

  • ambiguïté non signalée sur cellule active ;
  • navigation incohérente ligne/colonne.

Machine d’état minimale.

  • idle : table non focalisée ;
  • focused : table focalisée ;
  • navigating_rows : déplacement vertical actif ;
  • navigating_columns : déplacement horizontal actif ;
  • selecting : changement de sélection si supporté ;
  • empty : table vide ;
  • disabled : table visible mais non interactive.

Modèle interne minimal.

Une Table interactive doit distinguer explicitement :

  • la ligne active ;
  • la colonne active ou cellule active selon le mode ;
  • la sélection courante ;
  • l’ancre de scroll verticale ;
  • l’ancre de scroll horizontale.

Le modèle “ligne active” et le modèle “cellule active” sont tous deux permis, mais le composant doit en choisir un explicitement et le maintenir de façon cohérente.

Stratégie de focus.

Pour le noyau 1.0, la stratégie recommandée est :

  • un unique FocusTarget pour la table ;
  • une cellule ou ligne active gérée en interne.

Sémantique de commandes.

  • collection.move_next / collection.move_previous déplacent au minimum la ligne active ;
  • nav.left / nav.right déplacent la colonne ou cellule active ;
  • collection.activate agit sur l’élément logique actuellement actif ;
  • nav.page_up / nav.page_down déplacent la fenêtre visible de façon déterministe.

Feedback minimal.

  • ligne ou cellule active perceptible ;
  • séparation visuelle suffisante des colonnes ;
  • état vide perceptible ;
  • ambiguïté de navigation signalée si le mode change.

Pré-requis de capacités.

  • requis : keyboard, focus, viewport.resize ;
  • préférés : pointer, pointer.scroll, text.wide_cell_width.

8.7 Tree

Rôle. Hiérarchie expandable/collapsible.

Focus. Focusable si interactive.

Commandes minimales.

  • collection.move_next
  • collection.move_previous
  • collection.expand
  • collection.collapse
  • collection.activate

Dégradations acceptées.

  • glyphes d’expansion simplifiés ;
  • indentation plus sobre ;
  • absence d’animation d’ouverture/fermeture.

Machine d’état minimale.

  • idle : arbre non focalisé ;
  • focused : arbre focalisé ;
  • navigating : déplacement du nœud actif ;
  • expanding : ouverture logique d’un nœud ;
  • collapsing : fermeture logique d’un nœud ;
  • empty : arbre vide ;
  • disabled : arbre visible mais non interactif.

Modèle interne minimal.

Un Tree interactive doit distinguer explicitement :

  • le nœud actif ;
  • l’état ouvert/fermé de chaque nœud concerné ;
  • la profondeur logique ;
  • l’ancre de scroll visible ;
  • la sélection si elle existe.

Stratégie de focus.

Comme pour List, la stratégie recommandée est :

  • un unique FocusTarget pour l’arbre ;
  • un nœud actif interne.

Sémantique de commandes.

  • collection.move_next / collection.move_previous naviguent dans l’ordre visible actuel ;
  • collection.expand ouvre le nœud actif s’il est ouvrable ;
  • collection.collapse ferme le nœud actif s’il est ouvert, ou remonte au parent selon la politique du composant ;
  • collection.activate active le nœud courant.

Feedback minimal.

  • nœud actif perceptible ;
  • état ouvert/fermé perceptible ;
  • hiérarchie perceptible malgré les dégradations visuelles ;
  • impossibilité d’expansion/collapse signalée proprement.

Pré-requis de capacités.

  • requis : keyboard, focus ;
  • préférés : pointer, pointer.scroll, viewport.resize.

8.8 ScrollView

Rôle. Fenêtre défilante sur un contenu plus grand que le viewport.

Focus. Peut être focusable ou déléguer le focus à son contenu.

Commandes minimales.

  • nav.up
  • nav.down
  • nav.page_up
  • nav.page_down
  • nav.home
  • nav.end

Dégradations acceptées.

  • scroll seulement par page ;
  • indicateurs de scroll absents ;
  • inertie inexistante.

Machine d’état minimale.

  • idle : non focalisée si le composant prend le focus ;
  • focused : focalisée ;
  • scrolling : déplacement du viewport logique ;
  • delegating : laisse le focus ou les commandes au contenu ;
  • disabled : visible mais non interactive.

Rôle de focus.

Deux modes sont normatifs :

  • focus owner : la ScrollView reçoit le focus et interprète les commandes de scroll ;
  • focus delegator : le contenu reçoit le focus, la ScrollView ne fait que clipper et translater.

Le composant doit déclarer explicitement quel mode il adopte.

Sémantique de commandes.

  • nav.up / nav.down déplacent le viewport ou sont déléguées au contenu selon le mode ;
  • nav.page_up / nav.page_down déplacent au minimum d’un viewport logique ;
  • nav.home / nav.end déplacent à l’origine ou à l’extrémité logique.

Feedback minimal.

  • position de scroll perceptible si possible ;
  • début et fin de contenu détectables ;
  • impossibilité de scroller davantage signalée proprement.

Pré-requis de capacités.

  • requis : keyboard si focus owner ;
  • préférés : pointer.scroll, viewport.resize.

8.9 Tabs

Rôle. Sélection entre plusieurs panneaux mutuellement exclusifs.

Focus. Focusable ; constitue souvent un scope de focus avec restauration du dernier focus par onglet.

Commandes minimales.

  • tabs.next
  • tabs.previous
  • scene.confirm

Dégradations acceptées.

  • apparence de tab simplifiée ;
  • raccourcis directs aux onglets absents ;
  • overflow transformé en liste ou palette.

Machine d’état minimale.

  • idle : onglets non focalisés ;
  • focused : barrette ou sélecteur d’onglets focalisé ;
  • switching : transition logique entre deux onglets ;
  • disabled : visible mais non interactive.

Modèle interne minimal.

Un Tabs doit distinguer explicitement :

  • l’onglet sélectionné ;
  • l’onglet focalisé si distinct ;
  • le dernier focus descendant mémorisé pour chaque panneau quand cette fonctionnalité est activée.

Stratégie de focus.

Le modèle recommandé est :

  • un FocusTarget pour la barrette d’onglets ;
  • un FocusScope pour le panneau actif ;
  • restauration du dernier focus du panneau lors du retour sur un onglet.

Sémantique de commandes.

  • tabs.next / tabs.previous changent l’onglet sélectionné ;
  • scene.confirm peut entrer dans le panneau actif si la barrette a le focus ;
  • scene.focus_next / scene.focus_previous peuvent alterner entre barrette et contenu selon la politique du composant.

Feedback minimal.

  • onglet sélectionné perceptible ;
  • onglet focalisé perceptible s’il diffère ;
  • overflow perceptible et navigable.

Pré-requis de capacités.

  • requis : keyboard, focus ;
  • préférés : viewport.resize, pointer.

8.10 StatusBar

Rôle. Affichage d’état, de hints de commandes, ou de messages contextuels.

Focus. Non focusable par défaut ; peut devenir focusable si interactive.

Commandes minimales.

  • aucune en mode passif ;
  • scene.confirm ou commande dédiée si elle expose des actions.

8.11 Dialog

Rôle. Interaction modale courte : confirmation, alerte, petit formulaire.

Focus. Toujours encapsulé dans un FocusScope modal.

Commandes minimales.

  • scene.confirm
  • scene.cancel
  • scene.focus_next
  • scene.focus_previous

Dégradations acceptées.

  • bordure ou ombrage simplifiés ;
  • réduction du chrome visuel ;
  • mise en page plus compacte.

Dégradations interdites en silence.

  • fuite du focus hors du modal ;
  • impossibilité de confirmer ou d’annuler au clavier.

Machine d’état minimale.

  • opening : apparition logique du dialogue ;
  • active : dialogue modal actif ;
  • submitting : validation en cours si l’action n’est pas immédiate ;
  • closing : fermeture logique ;
  • closed : hors scène active.

Même sans animation visuelle, ces états logiques doivent exister pour cadrer le focus et les commandes.

Scope modal.

Un Dialog crée par défaut un FocusScope modal qui :

  • capture le focus primaire à l’ouverture ;
  • mémorise le focus précédent ;
  • piège scene.focus_next / scene.focus_previous à l’intérieur ;
  • restaure le focus précédent à la fermeture si la cible existe encore.

Structure interne minimale.

Un Dialog 1.0 peut contenir au minimum :

  • un titre optionnel ;
  • un contenu textuel ou un petit arbre de widgets ;
  • une zone d’actions explicites ;
  • éventuellement un widget focalisable primaire (Input, List, etc.).

Sémantique de commandes.

  • scene.confirm déclenche l’action par défaut si elle existe ;
  • scene.cancel ferme le dialogue ou remonte un rejet explicite ;
  • scene.focus_next / scene.focus_previous naviguent dans le scope modal ;
  • les commandes non locales remontent seulement si la politique du dialogue le permet explicitement.

Feedback minimal.

  • frontière modale perceptible ;
  • action par défaut identifiable ;
  • fermeture ou rejet perceptible ;
  • erreur de validation ou d’envoi perceptible.

Pré-requis de capacités.

  • requis : keyboard, focus ;
  • préférés : pointer, viewport.resize.

8.12 CommandPalette

Rôle. Point d’entrée universel des commandes et de la navigation globale.

Focus. Scope modal dédié.

Commandes minimales.

  • scene.cancel
  • scene.confirm
  • collection.move_next
  • collection.move_previous
  • text.insert
  • text.delete_backward

Dégradations acceptées.

  • matching plus simple ;
  • absence de preview ;
  • rendu de liste simplifié.

Dégradations interdites en silence.

  • impossibilité d’invoquer la palette ;
  • résultats activables uniquement à la souris.

Machine d’état minimale.

  • closed : hors scène active ;
  • opening : entrée logique dans la modalité ;
  • querying : saisie active de la requête ;
  • browsing : navigation dans les résultats ;
  • executing : exécution logique de la commande choisie ;
  • closing : fermeture logique ;

Scope modal.

La CommandPalette crée un FocusScope modal par défaut, au même titre qu’un Dialog, avec capture du focus primaire et restauration du focus précédent à la fermeture.

Structure interne minimale.

La palette contient au minimum :

  • un champ de requête ;
  • une liste de résultats ;
  • une sélection active ;
  • un mécanisme d’activation de résultat.

Le champ et la liste peuvent être modélisés comme sous-composants internes, mais le widget doit rester cohérent comme unité d’interaction unique.

Sémantique de commandes.

  • text.insert et text.delete_backward modifient la requête ;
  • collection.move_next / collection.move_previous déplacent le résultat actif ;
  • scene.confirm exécute le résultat actif ;
  • scene.cancel ferme la palette sans exécution.

Feedback minimal.

  • requête visible ;
  • résultat actif perceptible ;
  • absence de résultat perceptible ;
  • exécution ou rejet perceptible.

Pré-requis de capacités.

  • requis : keyboard, focus ;
  • préférés : viewport.resize, clipboard, pointer.

8.13 Progress

Rôle. Indication d’avancement déterminé ou indéterminé.

Focus. Non focusable par défaut ; focusable seulement si l’élément expose des actions.

Commandes minimales. Aucune en mode passif.

Dégradations acceptées.

  • style de barre simplifié ;
  • mode indéterminé réduit à indicateur textuel ;
  • absence d’animation.

8.14 Chart

Rôle. Visualisation textuelle simple : barres, sparklines, jauges, petits indicateurs.

Focus. Non focusable par défaut ; peut devenir focusable si des points ou séries sont inspectables.

Commandes minimales.

  • aucune en mode passif ;
  • nav.left / nav.right / scene.confirm si inspectable.

Dégradations acceptées.

  • résolution visuelle réduite ;
  • couleurs simplifiées ;
  • légende tronquée ou repliée.

9. Contrats transverses du lot 1.0

Tous les widgets 1.0 interactifs doivent respecter les règles suivantes.

9.1 Clavier intégral

Toute action critique doit être réalisable au clavier.

9.2 Focus visible

Un widget focusable doit avoir un état focalisé perceptible dans tous les profils supportés, même si la richesse visuelle varie.

9.3 Commandes inspectables

Les commandes consommées par un widget doivent pouvoir être documentées et, à terme, introspectées par le framework.

9.4 Dégradation explicable

Quand un widget se dégrade, le framework doit pouvoir expliquer :

  • quelle capacité manque ;
  • quelle variante a été choisie ;
  • si cette variante reste pleinement fonctionnelle ou non.

10. Patterns composites 1.0

Les widgets de base ne suffisent pas à décrire une application air-tui crédible. Le framework doit fournir ou au minimum normaliser quelques patterns composites de haut niveau, récurrents dans air-base.

Un pattern composite :

  • assemble plusieurs widgets du noyau ;
  • impose une politique de focus cohérente ;
  • expose un petit contrat de commandes ;
  • définit ses dégradations acceptées.

10.1 Form

Rôle. Saisie structurée de plusieurs champs, sections et actions.

Composition minimale.

  • texte d’aide ou labels ;
  • Input et/ou Editor ;
  • actions confirm / cancel ;
  • sections optionnelles.

Focus.

  • scope dédié ;
  • navigation séquentielle stable ;
  • possibilité de mémoriser le dernier champ invalide ou focalisé.

Commandes minimales.

  • scene.focus_next
  • scene.focus_previous
  • scene.confirm
  • scene.cancel

Dégradations acceptées.

  • layout plus compact ;
  • aides inline déplacées en dessous ;
  • indicateurs visuels de validation simplifiés.

Machine d’état minimale.

  • idle : formulaire présent mais non focalisé ;
  • focused : un champ ou une action interne détient le focus ;
  • editing : au moins un champ est en interaction active ;
  • validating : validation locale ou globale en cours ;
  • submitting : soumission logique en cours ;
  • invalid : au moins une contrainte empêche la soumission ;
  • disabled : formulaire visible mais non interactif.

Le formulaire peut être focused et invalid en même temps. L’état invalid ne doit jamais être implicite : il doit pouvoir être dérivé d’erreurs de validation explicites.

Modèle interne minimal.

Un Form doit distinguer explicitement :

  • l’ordre de tabulation des champs ;
  • les sections éventuelles ;
  • le premier champ invalide ;
  • les actions primaires et secondaires ;
  • l’état sale (dirty) si la vue choisit de l’exposer.

Focus et validation.

  • le focus se déplace séquentiellement entre champs et actions ;
  • scene.confirm sur un champ n’implique pas automatiquement soumission globale si le composant ne l’annonce pas ;
  • à échec de validation, le focus doit pouvoir être déplacé de manière déterministe vers le premier champ invalide ou rester sur le champ courant selon la politique du formulaire ;
  • un formulaire dans un Dialog hérite du scope modal parent sans dupliquer sa logique.

Sémantique de commandes.

  • scene.focus_next / scene.focus_previous naviguent entre champs et actions ;
  • scene.confirm soumet si le contexte l’autorise et si la validation passe ;
  • scene.cancel annule ou remonte selon la politique du conteneur ;
  • les commandes widget-level (text.*, editor.*) sont traitées par le champ focalisé avant toute logique de formulaire.

Feedback minimal.

  • champ focalisé perceptible ;
  • champs invalides perceptibles ;
  • état de soumission ou de validation perceptible ;
  • action primaire identifiable.

Pré-requis de capacités.

  • requis : keyboard, focus ;
  • préférés : viewport.resize, clipboard, ime, text.bidi.

10.2 NavigationSplit

Rôle. Navigation maître/détail en mode texte.

Composition minimale.

  • un panneau latéral de navigation (List ou Tree) ;
  • un panneau de détail (Text, Form, Editor, etc.) ;
  • éventuellement une barre de séparation logique.

Focus.

  • deux scopes principaux ;
  • commandes pane.focus_left / pane.focus_right normatives ;
  • restauration du dernier focus dans chaque panneau recommandée.

Dégradations acceptées.

  • collapse du panneau latéral ;
  • passage temporaire en navigation empilée ;
  • séparation visuelle simplifiée.

Machine d’état minimale.

  • idle : composite visible mais aucun panneau focalisé ;
  • master_focused : panneau de navigation focalisé ;
  • detail_focused : panneau de détail focalisé ;
  • collapsed_master : panneau maître replié ;
  • disabled : composite visible mais non interactif.

Modèle interne minimal.

Un NavigationSplit doit distinguer explicitement :

  • le panneau maître ;
  • le panneau détail ;
  • le dernier focus interne de chaque panneau ;
  • l’item de navigation actuellement sélectionné ;
  • la politique de collapse du panneau maître.

Politique de focus.

  • pane.focus_left et pane.focus_right sont des commandes normatives ;
  • le changement de sélection dans le panneau maître peut remplacer le contenu du détail sans déplacer automatiquement le focus ;
  • quand le focus revient dans un panneau, la restauration du dernier focus interne est recommandée ;
  • en mode collapsed_master, le panneau maître doit rester accessible via une commande explicite.

Sémantique de commandes.

  • les commandes collection.* dans le panneau maître s’appliquent à la vue de navigation ;
  • les commandes du panneau détail sont résolues localement tant que ce panneau garde le focus ;
  • scene.confirm dans le panneau maître peut soit activer l’élément, soit déplacer le focus au détail selon la politique du composant.

Feedback minimal.

  • panneau focalisé perceptible ;
  • item maître sélectionné perceptible ;
  • état collapsed / expanded perceptible ;
  • séparation logique entre navigation et détail perceptible même en dégradé.

Pré-requis de capacités.

  • requis : keyboard, focus ;
  • préférés : viewport.resize, pointer.

10.3 SettingsView

Rôle. Présentation ordonnée de préférences et réglages.

Composition minimale.

  • groupes ou sections ;
  • Form ;
  • aides contextuelles ;
  • actions de validation si nécessaire.

Focus.

  • scope principal ;
  • navigation séquentielle et éventuellement par sections.

Commandes minimales.

  • celles de Form ;
  • navigation directionnelle si panneau latéral de catégories.

Machine d’état minimale.

  • browsing_categories : catégorie active en navigation ;
  • editing_section : section de réglages active ;
  • validating : validation locale ou globale ;
  • submitting : application ou enregistrement en cours.

Politique de focus.

  • si une liste de catégories existe, elle forme un scope latéral analogue à NavigationSplit ;
  • sinon, la vue se comporte comme un Form étendu.

Dégradations acceptées.

  • catégories repliées en liste simple ;
  • aide contextuelle déplacée en dessous ;
  • validation différée moins riche visuellement.

10.4 HelpView

Rôle. Aide textuelle structurée, raccourcis, documentation in-app.

Composition minimale.

  • Text riche ;
  • ScrollView ;
  • éventuellement Tabs ou List de rubriques.

Focus.

  • peut être passive ou interactive ;
  • si interactive, focus owner généralement au niveau ScrollView ou de la liste de rubriques.

Dégradations acceptées.

  • styles typographiques simplifiés ;
  • table des matières repliée ;
  • recherche absente en backend dégradé.

Machine d’état minimale.

  • reading : lecture du contenu principal ;
  • navigating_index : navigation dans la table des matières si présente ;
  • searching : recherche active si le composant la supporte.

Politique de focus.

  • en version passive, aucun focus propre hors conteneur parent ;
  • en version interactive, le focus alterne entre index, recherche et contenu scrollable selon la structure choisie.

Commandes minimales.

  • nav.up
  • nav.down
  • nav.page_up
  • nav.page_down
  • scene.focus_next
  • scene.focus_previous

10.5 CommandBrowser

Rôle. Variante persistante ou non modale de la CommandPalette pour explorer les actions disponibles.

Composition minimale.

  • Input de filtre optionnel ;
  • List ou Table de commandes ;
  • panneau d’aide ou description optionnel.

Focus.

  • un ou plusieurs scopes selon la mise en page ;
  • la stratégie recommandée est un focus principal sur la liste avec filtre accessible par commande dédiée.

10.6 Règles communes aux composites

  • un composite ne doit pas introduire de commandes cachées incohérentes avec le noyau ;
  • un composite doit définir sa politique de focus en termes de FocusScope et FocusTarget ;
  • un composite doit pouvoir expliquer ses dégradations via Capabilities.

11. Architecture des crates air-tui

Cette section traduit les contrats précédents en découpage d’implémentation. Elle ne fige pas encore tous les noms exacts des modules internes, mais fixe les responsabilités de chaque crate et leurs relations de dépendance.

Le principe directeur est simple :

  • séparer sémantique, rendu et transport ;
  • ne pas laisser un backend dicter l’API publique ;
  • garder les types de contrat au-dessus des détails de protocole ;
  • permettre des tests croisés du même comportement sur plusieurs backends.

11.1 Vue d’ensemble

Le découpage cible est le suivant :

air-view
   |
   +--> air-tui
   |       |
   |       +--> air-tui-widgets
   |       +--> air-ui-render-tui
   |       +--> air-tui-runtime
   |
   +--> air-tui-backend-console
   +--> air-tui-backend-terminal
   +--> air-tui-backend-ansi

Ce schéma doit se lire ainsi :

  • air-view porte le tronc déclaratif commun ;
  • air-tui expose la façade framework console ;
  • air-tui-widgets porte le widget set TUI ;
  • air-ui-render-tui porte le renderer cellulaire ;
  • air-tui-runtime porte la boucle d’input, la normalisation d’événements, le binding et le dispatch ;
  • les crates air-tui-backend-* adaptent chacune un environnement d’exécution concret.

11.2 air-tui

Rôle. Façade publique du framework console.

La crate air-tui doit réexporter :

  • les widgets TUI publics ;
  • les types de contrat publics (Capabilities, CapabilityRequirements, Command, CommandContext, FocusScope, FocusTarget, RawEvent selon la répartition retenue) ;
  • les points d’entrée de runtime nécessaires à une application ;
  • le minimum de traits ou fonctions de bootstrap nécessaires à l’exécution.

air-tui ne doit pas :

  • contenir directement un backend concret ;
  • dépendre d’un terminal ANSI concret ;
  • contenir de logique de protocole air-console ;
  • exposer des détails de transport comme surface publique principale.

11.3 air-tui-widgets

Rôle. Implémentation du widget set TUI standard.

Cette crate porte au minimum :

  • les widgets du lot 1.0 ;
  • les patterns composites 1.0 ;
  • leurs machines d’état ;
  • leur logique de focus locale ;
  • leur traduction des Command en mutations d’état.

Elle dépend conceptuellement de :

  • air-view pour la composition ;
  • air-tui-runtime pour les contrats d’interaction ;
  • air-ui-render-tui seulement via interfaces stables, jamais via couplage au backend concret.

air-tui-widgets ne doit pas embarquer directement de logique spécifique à air-console, au terminal Air, ou à l’ANSI.

11.4 air-ui-render-tui

Rôle. Renderer cellulaire.

Cette crate reçoit un arbre de vues ou une représentation normalisée et produit une scène cellulaire rendable par les backends.

Responsabilités minimales :

  • layout cellulaire ;
  • glyphes, attributs et styles ;
  • clipping ;
  • dirty regions ;
  • scroll logique ;
  • calcul de hit-testing logique si cette responsabilité n’est pas ailleurs.

Sortie conceptuelle :

  • grille de cellules ;
  • métadonnées de focus/hover si nécessaires au debug ;
  • informations de diff pour éviter le repaint complet.

air-ui-render-tui ne doit pas :

  • parser un flux terminal ;
  • parler AirCom directement ;
  • gérer un PTY ;
  • décider de la politique de binding clavier.

11.5 air-tui-runtime

Rôle. Cœur interactionnel du framework console.

C’est la crate la plus structurante après air-tui lui-même. Elle doit porter le pipeline suivant :

  • réception des événements bruts ;
  • normalisation backend ;
  • mise à jour des Capabilities ;
  • focus et scopes ;
  • hit-testing logique ;
  • binding événement -> commande ;
  • dispatch de commande ;
  • feedback et rejet standard.

air-tui-runtime est le bon lieu pour matérialiser ou héberger conceptuellement les types suivants, sous une forme ou une autre :

  • RawEvent
  • EventContext
  • Command
  • CommandContext
  • FocusScope
  • FocusTarget
  • Capabilities

Cette crate ne doit pas dépendre d’un backend concret. Les backends doivent dépendre d’elle, ou implémenter les interfaces qu’elle expose.

11.6 air-tui-backend-console

Rôle. Adaptateur du mode plein bypass via air-console.

Responsabilités minimales :

  • connexion au protocole privé d’air-console ;
  • réception des événements riches natifs ;
  • émission de la grille cellulaire ou de son diff ;
  • signalement fidèle des capacités FullConsole.

Ce backend est la référence premium pour le mode texte Air.

Il ne doit pas contenir :

  • la logique de widget ;
  • la politique de focus globale ;
  • des fallbacks ANSI.

11.7 air-tui-backend-terminal

Rôle. Adaptateur du terminal Air riche, hors air-console direct.

Responsabilités minimales :

  • parler le protocole privé du terminal Air ;
  • traduire les événements et capacités en profil AirTerminal ;
  • transmettre la sortie du renderer.

Ce backend doit fournir une sémantique applicative proche de air-tui-backend-console, avec seule différence notable : la couche de transport.

11.8 air-tui-backend-ansi

Rôle. Adaptateur du terminal tiers ou SSH.

Responsabilités minimales :

  • parler ANSI/VT et Kitty keyboard protocol quand disponible ;
  • détecter honnêtement les capacités réellement présentes ;
  • produire des RawEvent dégradés mais cohérents ;
  • transmettre la sortie rendue sous une forme acceptable par le terminal tiers.

Ce backend est explicitement un backend de compatibilité, pas le centre de gravité du design.

11.9 Dépendances et direction du flux

Les dépendances conceptuelles doivent respecter au minimum les règles suivantes :

  • air-tui dépend de air-view, air-tui-runtime, air-tui-widgets, air-ui-render-tui ;
  • air-tui-widgets dépend de air-view et air-tui-runtime ;
  • air-ui-render-tui ne dépend d’aucun backend concret ;
  • chaque air-tui-backend-* dépend de air-tui-runtime et consomme la sortie du renderer ;
  • aucun backend ne doit être requis pour compiler les types centraux du framework.

Flux conceptuel d’exécution :

backend -> RawEvent -> air-tui-runtime -> Command -> widgets/state
widgets/state -> air-ui-render-tui -> cell grid -> backend

11.10 Testabilité du découpage

Ce découpage est choisi aussi pour ses propriétés de test :

  • tests unitaires de air-tui-runtime sans backend réel ;
  • tests de widgets sans protocole terminal ;
  • tests du renderer sur snapshots de grilles ;
  • tests d’adaptateurs backend avec fixtures de flux d’entrée/sortie ;
  • tests end-to-end identiques sur console, terminal, ansi.

11.11 Points laissés ouverts

Restent ouverts à ce stade :

  • l’existence ou non d’une crate air-tui-types dédiée ;
  • la séparation exacte entre runtime, focus et commandes en sous-crates ;
  • la place exacte de certains types communs dans air-view vs air-tui-runtime ;
  • la stratégie de réexport depuis la façade air-tui.

12. Architecture interne de air-tui-runtime

Cette section descend d’un cran sous le niveau crate. Elle décrit les sous-systèmes conceptuels de air-tui-runtime, leur ordre de responsabilité, et les points où ils s’articulent avec les widgets, le renderer et les backends.

Principe directeur :

  • normaliser tôt ;
  • décider tard ;
  • ne jamais mélanger transport, focus, binding et rendu ;
  • rendre chaque étape testable isolément.

12.1 Vue d’ensemble

Le runtime peut se lire comme une pipeline en anneau :

backend adapter
    -> event ingress
    -> normalizer
    -> capability tracker
    -> focus engine
    -> hit-testing / target resolution
    -> binding engine
    -> command dispatcher
    -> state mutation / feedback
    -> invalidation scheduler
    -> renderer bridge
    -> backend adapter

Toutes ces étapes peuvent cohabiter dans une seule crate, mais elles doivent rester séparables conceptuellement et testables individuellement.

12.2 event ingress

Rôle. Point d’entrée des événements provenant d’un backend.

Responsabilités minimales :

  • recevoir des événements source dans l’ordre ;
  • préserver l’ordre logique ;
  • associer un EventContext minimal ;
  • rejeter les événements manifestement invalides ;
  • alimenter la phase de normalisation.

event ingress ne doit pas :

  • résoudre des commandes ;
  • décider du focus global ;
  • déclencher du rendu directement.

12.3 normalizer

Rôle. Convertir les événements backend-spécifiques en RawEvent canoniques.

Responsabilités minimales :

  • transformer les protocoles de backend en événements normés ;
  • distinguer honnêtement KeyDown, KeyRepeat, KeyUp, Paste, etc. ;
  • enrichir les événements avec les métadonnées stables disponibles ;
  • éliminer les ambiguïtés de transport sans inventer d’information absente.

Le normalizer est le point où se matérialise le contrat “les backends ne dictent pas l’API”.

12.4 capability tracker

Rôle. Maintenir l’état courant de Capabilities.

Responsabilités minimales :

  • exposer le profil courant ;
  • suivre les capacités stables et dynamiques ;
  • émettre CapabilitiesChanged si nécessaire ;
  • fournir des snapshots cohérents au reste du runtime.

Le capability tracker doit être la source de vérité interne pour toute question du type “peut-on faire X sur ce backend maintenant ?”.

12.5 focus engine

Rôle. Maintenir et faire évoluer l’état de focus.

Responsabilités minimales :

  • conserver le focus primaire ;
  • gérer les FocusScope ;
  • appliquer les politiques d’entrée/sortie de scope ;
  • restaurer les focus mémorisés ;
  • arbitrer les transitions FocusGained / FocusLost.

Le focus engine ne doit pas être un effet secondaire du renderer. Il est un sous-système autonome.

12.6 hit-testing / target resolution

Rôle. Résoudre les cibles logiques concernées par un événement ou une commande.

Responsabilités minimales :

  • associer un événement pointeur à un FocusTarget ou sous-arbre logique ;
  • déterminer le widget focalisé ou survolé ;
  • coopérer avec le renderer pour les positions logiques si nécessaire ;
  • fournir au binding et au dispatch un contexte de cible stable.

Cette étape peut dépendre de métadonnées calculées au rendu, mais ne doit pas vivre dans le backend.

12.7 binding engine

Rôle. Transformer RawEvent + contexte en Command.

Responsabilités minimales :

  • appliquer les bindings par défaut ;
  • respecter les remappings utilisateur ;
  • distinguer insertion de texte, navigation, activation, annulation ;
  • tenir compte du scope modal actif ;
  • produire zéro, une, ou plusieurs commandes si le modèle le permet.

Le binding engine ne doit pas muter directement l’état des widgets. Il produit des intentions, pas des effets.

12.8 command dispatcher

Rôle. Acheminer une Command vers le bon niveau de l’arbre.

Responsabilités minimales :

  • construire le CommandContext ;
  • envoyer d’abord la commande au widget focalisé ;
  • remonter au scope actif, aux parents, à la scène, puis à l’application ;
  • recueillir rejet, consommation, ou remapping ;
  • déclencher le feedback standard quand rien ne gère la commande.

Le command dispatcher est le gardien de la cohérence de propagation.

12.9 state mutation / feedback

Rôle. Appliquer les mutations d’état résultant du dispatch et produire les signaux de retour.

Responsabilités minimales :

  • muter les machines d’état des widgets concernés ;
  • produire des rejets standardisés ;
  • produire les feedbacks visuels ou sonores abstraits ;
  • marquer les zones ou sous-arbres invalidés.

Le runtime ne doit pas imposer la logique métier d’application, mais il doit fournir le cadre pour que les mutations d’UI restent cohérentes.

12.10 invalidation scheduler

Rôle. Décider quoi rerendre, quand, et avec quel niveau de granularité.

Responsabilités minimales :

  • agréger les invalidations ;
  • distinguer invalidation locale et rerendu complet ;
  • coopérer avec les dirty regions du renderer ;
  • éviter les rerenders inutiles ;
  • garantir qu’une mutation visible mène à un rendu cohérent.

L’invalidation est un sous-système important parce que le mode texte riche peut facilement dégénérer en rafraîchissements complets coûteux si elle n’est pas structurée.

12.11 renderer bridge

Rôle. Frontière entre runtime et air-ui-render-tui.

Responsabilités minimales :

  • préparer l’arbre ou snapshot à rendre ;
  • transmettre les contraintes de viewport ;
  • récupérer la grille ou son diff ;
  • transmettre au runtime les métadonnées utiles au hit-testing et au debug.

Cette frontière doit rester assez mince pour éviter le couplage fort entre runtime et renderer.

12.12 Boucle d’exécution

La boucle d’exécution conceptuelle d’air-tui-runtime est :

  1. recevoir un événement backend ;
  2. le normaliser en RawEvent ;
  3. mettre à jour les capacités si nécessaire ;
  4. résoudre focus et cible ;
  5. produire une ou plusieurs commandes ;
  6. dispatcher les commandes ;
  7. appliquer mutations et feedback ;
  8. planifier l’invalidation ;
  9. demander un rendu si nécessaire ;
  10. pousser la sortie vers le backend.

Cette boucle peut être synchrone ou intégrée à un runtime async plus large, mais sa sémantique doit rester stable.

12.13 États internes minimaux

Le runtime doit maintenir au minimum :

  • snapshot courant de Capabilities ;
  • focus primaire et scopes actifs ;
  • état de hover si pertinent ;
  • queue ou flux d’événements entrants ;
  • invalidations en attente ;
  • dernier snapshot rendu ou diff de référence si nécessaire ;
  • configuration de bindings active.

12.14 Interfaces attendues entre sous-systèmes

Les interfaces conceptuelles minimales sont :

  • backend -> event ingress : émission d’événements source ordonnés ;
  • normalizer -> capability tracker : métadonnées de capacité observées ;
  • focus engine <-> hit-testing : cibles et transitions ;
  • binding engine -> command dispatcher : Command + origine ;
  • state mutation -> invalidation scheduler : sous-arbres ou régions sales ;
  • renderer bridge -> backend : grille ou diff rendus.

12.15 Testabilité interne

Chaque sous-système doit être testable séparément :

  • normalizer avec fixtures de backend ;
  • capability tracker avec transitions de profil ;
  • focus engine avec arbres et scopes synthétiques ;
  • binding engine avec matrices d’événements et de remappings ;
  • command dispatcher avec arbres de widgets factices ;
  • invalidation scheduler avec scénarios de mutations locales.

12.16 Points laissés ouverts

Restent ouverts à ce stade :

  • architecture mono-crate interne vs sous-modules très marqués ;
  • éventuelle scission future en air-tui-focus, air-tui-command, etc. ;
  • politique exacte de buffering des événements ;
  • articulation précise avec le runtime async de couche 1/2 ;
  • place exacte du hit-testing entre runtime et renderer.

13. Tests attendus

L’implémentation future doit être testée au minimum selon quatre axes.

Tests de capacités

  • capacité correctement détectée par backend ;
  • absence de fausse déclaration ;
  • fallback correct d’une vue selon capacité manquante.

Tests de focus

  • unicité du focus primaire ;
  • ordre de tabulation déterministe ;
  • restauration correcte après fermeture d’un modal ;
  • navigation cohérente entre scopes.

Tests de commandes

  • binding produit la bonne commande ;
  • dispatch remonte correctement dans l’arbre ;
  • commande critique toujours accessible dans les profils supportés.

Tests inter-backends

Une même app de test doit être exercée sur :

  • FullConsole
  • AirTerminal
  • CompatibleTerminal

avec vérification que :

  • le comportement critique reste cohérent ;
  • seules les dégradations explicitement autorisées apparaissent.

14. Questions laissées ouvertes

Les points suivants restent à instruire plus tard sans remettre en cause cette spec :

  • noms exacts des types publics ;
  • sérialisation précise de l’arbre logique pour debug/tests ;
  • stratégie IME détaillée en mode console ;
  • politique de shaping/bidi exacte selon backend ;
  • modèle de thèmes et de styles textuels ;
  • widgets additionnels après le noyau 1.0.

Licence du document : MPL 2.0 Statut : Spécification de design pré-implémentation. Édition directe autorisée en phase de design pré-ouverture publique ; après ouverture publique, évolution via RFC si impact contractuel.