Spécification d’API air-tui-runtime
Document de spécification — couche 4
Rôle du document
Ce document décrit la surface d’API conceptuelle de air-tui-runtime.
Il complète air-tui-core.md, qui fixe :
- les contrats d’interaction ;
- le modèle d’événements ;
- les commandes ;
- les widgets ;
- l’architecture des crates ;
- l’architecture interne de
air-tui-runtime.
Le présent document change de focale : il s’adresse d’abord à un développeur qui veut comprendre ce que le runtime lui offre pour construire des applications ou des composants de framework, sans devoir relire toute l’architecture interne.
L’objectif n’est pas encore de figer mot pour mot une API Rust stable 1.0. L’objectif est de définir :
- les points d’entrée attendus ;
- les types publics majeurs ;
- les responsabilités de chaque objet ;
- le style d’usage attendu ;
- les invariants ergonomiques de l’API.
1. Principes d’API
L’API publique de air-tui-runtime doit respecter les principes suivants.
1.1 Développeur-first
Un développeur d’application ne doit pas avoir à comprendre :
- un protocole backend ;
- un format terminal ;
- une boucle de focus implicite ;
- une mécanique d’invalidation artisanale ;
- un système de dispatch caché.
Ces détails existent, mais le runtime doit les absorber.
1.2 Contrats explicites
Les capacités, le focus, les commandes, les événements et le feedback doivent être exposés par des types nommés et des opérations explicites.
Pas de magie cachée.
1.3 Lecture simple, puissance graduelle
L’API doit être praticable à trois niveaux :
- usage simple pour une application standard ;
- usage intermédiaire pour des composants personnalisés ;
- usage avancé pour des widgets ou conteneurs complexes.
Le développeur doit pouvoir commencer simple sans être enfermé.
1.4 Stabilité sémantique
Même si les signatures exactes évolueront avant 1.0, les concepts suivants doivent rester stables :
CapabilitiesCapabilityRequirementsRawEventCommandCommandContextFocusScopeFocusTarget
2. Modèle mental de l’API
Le développeur doit pouvoir raisonner ainsi :
- Le backend livre des événements.
- Le runtime les normalise.
- Le runtime maintient les capacités et le focus.
- Le runtime résout des commandes sémantiques.
- Les vues/widgets réagissent aux commandes.
- Le runtime programme le rerendu.
Formule compacte :
events in -> commands resolved -> state updated -> render scheduled
3. Points d’entrée publics
L’API conceptuelle doit exposer au minimum les points d’entrée suivants.
3.1 Runtime
Runtime est le point d’entrée principal.
Rôle :
- posséder la boucle d’exécution ;
- recevoir des événements ;
- maintenir le focus et les capacités ;
- dispatcher les commandes ;
- programmer le rendu.
Attentes conceptuelles minimales :
- création simple ;
- configuration lisible ;
- accès aux capacités ;
- injection d’événements ;
- exécution d’un cycle ;
- accès au rendu produit.
Exemple conceptuel :
#![allow(unused)]
fn main() {
let mut runtime = Runtime::new(config);
runtime.mount(app_view);
runtime.push_event(raw_event);
runtime.tick();
let frame = runtime.take_frame();
}
Les noms exacts sont ouverts ; ce style d’usage est l’attendu.
3.2 RuntimeConfig
RuntimeConfig décrit les choix globaux du runtime.
Doit pouvoir couvrir au minimum :
- stratégie de bindings ;
- options de focus ;
- politique de feedback ;
- budget de rendu ou d’invalidation ;
- politique de diagnostics.
Il ne doit pas devenir une poubelle de flags hétérogènes.
3.3 MountedView ou équivalent
Le runtime doit pouvoir monter un arbre racine de vue ou un composant racine.
Le type exact peut varier, mais la sémantique suivante doit exister :
- une racine unique logique ;
- une durée de vie claire ;
- un canal de mise à jour de l’état ;
- un point de démontage propre.
4. Types publics centraux
4.1 Capabilities
Déjà défini conceptuellement dans air-tui-core.md.
Attentes API :
- lecture simple ;
- interrogation par famille ;
- diagnostic standard ;
- vérification contre
CapabilityRequirements.
Exemple conceptuel :
#![allow(unused)]
fn main() {
let caps = runtime.capabilities();
if caps.keyboard().key_up().is_guaranteed() {
// comportement premium
}
}
4.2 CapabilityRequirements
Usage attendu :
- déclaration par widgets et composites ;
- vérification par le runtime ;
- fallback documenté.
Exemple conceptuel :
#![allow(unused)]
fn main() {
let req = CapabilityRequirements::new()
.require(KeyboardCapability::KeyDown)
.prefer(PointerCapability::Scroll)
.prefer(TextCapability::Bidi);
}
4.3 RawEvent
RawEvent est la surface d’entrée stable des interactions.
L’application standard ne manipule pas forcément RawEvent souvent, mais :
- les composants avancés peuvent l’observer ;
- les tests en ont besoin ;
- les backends doivent le produire ;
- le runtime doit le consommer.
4.4 Command
Command est l’unité d’action stable.
Attentes API :
- identité stable ;
- catégorie lisible ;
- payload éventuelle ;
- sérialisation utile au debug/tests.
Exemple conceptuel :
#![allow(unused)]
fn main() {
match command.id() {
CommandId::SceneConfirm => { /* ... */ }
CommandId::CollectionMoveNext => { /* ... */ }
_ => {}
}
}
4.5 CommandContext
CommandContext est la fenêtre d’information disponible au moment du dispatch.
Il doit être :
- léger ;
- lisible ;
- centré sur l’interaction ;
- sans pollution métier.
4.6 FocusScope et FocusTarget
Le développeur n’a pas toujours besoin de construire des scopes explicitement. Mais dès qu’il écrit :
- un conteneur complexe ;
- un modal ;
- un éditeur ;
- une navigation maître/détail ;
il doit disposer d’objets stables pour raisonner sur le focus.
5. Objets et rôles de haut niveau
L’API publique doit permettre de distinguer au moins les objets suivants.
5.1 Runtime
Orchestre l’ensemble.
5.2 EventSource ou équivalent
Abstraction sur une source d’événements backend.
Usage :
- backend console ;
- backend terminal Air ;
- backend ANSI ;
- tests synthétiques.
5.3 CommandHandler ou équivalent
Abstraction pour les composants qui consomment des commandes.
Le nom exact peut changer, mais le runtime doit pouvoir appeler un point d’entrée cohérent de traitement des commandes.
5.4 RenderOutput ou équivalent
Représente la sortie rendue ou le diff prêt à être envoyé au backend.
Le développeur d’application n’a pas toujours à le manipuler, mais :
- les intégrateurs backend, si ;
- les tests snapshot, si ;
- le debug outillé, si.
6. Style d’API attendu
6.1 API lisible
Le style attendu est plus proche de :
#![allow(unused)]
fn main() {
runtime.capabilities().pointer().scroll().is_supported()
}
que de :
#![allow(unused)]
fn main() {
runtime.flags() & 0x0400 != 0
}
6.2 API orientée types
Préférer :
- enums ;
- nouveaux types ;
- objets contextuels explicites ;
- méthodes nommées ;
à :
- chaînes magiques ;
- entiers nus ;
- drapeaux opaques ;
- callbacks anonymes partout.
6.3 API centrée commandes, pas touches
L’API applicative ne doit pas pousser les développeurs vers :
- inspection de séquences clavier brutes ;
- branches par backend ;
- logique de keycodes dans chaque widget.
Le bon chemin doit être :
- déclarer ou consommer des commandes sémantiques.
7. Parcours développeur attendu
7.1 Application simple
Un développeur doit pouvoir :
- créer un runtime ;
- monter une vue racine ;
- brancher un backend ;
- laisser le runtime faire le reste.
Exemple conceptuel :
#![allow(unused)]
fn main() {
let mut runtime = Runtime::new(RuntimeConfig::default());
runtime.mount(root_view());
while let Some(event) = backend.next_event() {
runtime.push_event(event);
runtime.tick();
if let Some(frame) = runtime.take_frame() {
backend.render(frame);
}
}
}
7.2 Composant personnalisé
Un développeur de composant doit pouvoir :
- déclarer ses
CapabilityRequirements; - déclarer sa politique de focus ;
- consommer des
Command; - demander une invalidation propre.
Exemple conceptuel :
#![allow(unused)]
fn main() {
impl CommandHandler for MyWidget {
fn requirements(&self) -> CapabilityRequirements { /* ... */ }
fn handle_command(
&mut self,
command: &Command,
cx: &mut CommandContext,
) -> CommandResult {
// ...
}
}
}
7.3 Intégrateur backend
Un développeur backend doit pouvoir :
- produire des
RawEventcohérents ; - déclarer les capacités ;
- consommer la sortie rendue ;
- ne jamais avoir à reconstituer la logique de focus ou de commandes.
8. Feedback et diagnostics
Le runtime doit offrir un canal uniforme pour :
- rejeter une commande ;
- signaler une capacité manquante ;
- expliquer un fallback ;
- inspecter l’état courant du focus ;
- journaliser la suite d’événements/commandes utile au debug.
L’objectif explicite est de produire une future documentation développeur crédible et agréable, pas seulement un moteur techniquement juste.
9. Exemples de surface d’API conceptuelle
Les snippets suivants ne figent pas la syntaxe finale, mais illustrent le style attendu.
9.1 Lecture des capacités
#![allow(unused)]
fn main() {
let caps = runtime.capabilities();
if caps.text().bidi().is_supported() {
render_rich_help();
} else {
render_fallback_help();
}
}
9.2 Déclaration de prérequis
#![allow(unused)]
fn main() {
fn requirements() -> CapabilityRequirements {
CapabilityRequirements::new()
.require(KeyboardCapability::KeyDown)
.require(FocusCapability::PrimaryFocus)
.prefer(PointerCapability::Scroll)
}
}
9.3 Dispatch d’une commande
#![allow(unused)]
fn main() {
runtime.dispatch(Command::scene_confirm());
}
9.4 Inspection du focus
#![allow(unused)]
fn main() {
let focused = runtime.focus().primary_target();
let scope = runtime.focus().active_scope();
}
10. Anti-objectifs
La future API publique ne doit pas ressembler à :
- un simple wrapper d’événements terminal bruts ;
- une API centrée sur les backends ;
- une collection de callbacks sans contrat ;
- une hiérarchie opaque de services globaux ;
- un framework où chaque widget reprogramme le focus et le binding.
11. Relations avec la future documentation développeur
Ce document doit servir plus tard de base à trois familles de documents :
- guide “démarrage rapide” développeur ;
- guide “écrire un widget personnalisé” ;
- guide “écrire un backend TUI”.
Autrement dit, cette spec n’est pas seulement une note d’architecture interne. Elle prépare directement la qualité perçue du SDK Air.
12. Points laissés ouverts
- noms exacts des traits et structs publics ;
- politique de lifetime/ownership des arbres montés ;
- style exact de configuration (
builder, struct simple, traits) ; - séparation précise entre API application et API backend ;
- niveau de réexport depuis
air-tuivsair-tui-runtime.
Licence du document : MPL 2.0 Statut : Spécification d’API conceptuelle pré-implémentation. Édition directe autorisée en phase de design pré-ouverture publique ; après ouverture publique, évolution via RFC si impact contractuel.