- Accueil
- Documentation
- TUI
- Composants internes du runtime TUI
Composants internes du runtime TUI
Ce document décrit le chemin d’exécution non-thème depuis l’entrée terminal jusqu’à la sortie rendue en mode interactif. Il se concentre sur le comportement dans packages/tui et son intégration depuis les contrôleurs de packages/coding-agent.
Couches du runtime et propriété
Section intitulée « Couches du runtime et propriété »- Moteur
packages/tui: cycle de vie du terminal, normalisation de stdin, routage du focus, planification du rendu, peinture différentielle, composition des superpositions, positionnement matériel du curseur. - Mode interactif de
packages/coding-agent: construit l’arbre des composants, lie les rappels de l’éditeur et les mappages de touches, réagit aux événements agent/session, et traduit l’état du domaine (streaming, exécution d’outils, nouvelles tentatives, mode plan) en composants UI.
Règle de délimitation : le moteur TUI est indépendant des messages. Il ne connaît que Component.render(width), handleInput(data), le focus et les superpositions. La sémantique de l’agent reste dans les contrôleurs interactifs.
Fichiers d’implémentation
Section intitulée « Fichiers d’implémentation »../src/modes/interactive-mode.ts../src/modes/controllers/event-controller.ts../src/modes/controllers/input-controller.ts../src/modes/components/custom-editor.ts../../tui/src/tui.ts../../tui/src/terminal.ts../../tui/src/editor-component.ts../../tui/src/stdin-buffer.ts../../tui/src/components/loader.ts
Démarrage et assemblage de l’arbre de composants
Section intitulée « Démarrage et assemblage de l’arbre de composants »InteractiveMode construit TUI(new ProcessTerminal(), showHardwareCursor) et crée des conteneurs persistants :
chatContainerpendingMessagesContainerstatusContainertodoContainerstatusLineeditorContainer(contientCustomEditor)
init() câble l’arbre dans cet ordre, donne le focus à l’éditeur, enregistre les gestionnaires d’entrée via InputController, démarre le TUI et demande un rendu forcé.
Un rendu forcé (requestRender(true)) réinitialise les caches de lignes précédentes et la gestion du curseur avant le repeint.
Cycle de vie du terminal et normalisation de stdin
Section intitulée « Cycle de vie du terminal et normalisation de stdin »ProcessTerminal.start() :
- Active le mode brut et le collage entre crochets.
- Attache le gestionnaire de redimensionnement.
- Crée un
StdinBufferpour découper les fragments d’échappement partiels en séquences complètes. - Interroge la prise en charge du protocole clavier Kitty (
CSI ? u), puis active les indicateurs de protocole si pris en charge. - Sous Windows, tente l’activation de l’entrée VT via les indicateurs de mode
kernel32.
Comportement de StdinBuffer :
- Tamponne les séquences d’échappement fragmentées (CSI/OSC/DCS/APC/SS3).
- Émet
datauniquement lorsqu’une séquence est complète ou vidée par délai d’expiration. - Détecte le collage entre crochets et émet un événement
pasteavec le texte collé brut.
Cela empêche les fragments d’échappement partiels d’être mal interprétés comme des touches normales.
Routage des entrées et modèle de focus
Section intitulée « Routage des entrées et modèle de focus »Chemin d’entrée :
stdin -> ProcessTerminal -> StdinBuffer -> TUI.#handleInput -> focusedComponent.handleInput
Détails du routage :
- Le TUI exécute d’abord les écouteurs d’entrée enregistrés (
addInputListener), permettant un comportement de consommation/transformation. - Le TUI gère le raccourci de débogage global (
shift+ctrl+d) avant la distribution aux composants. - Si le composant en focus appartient à une superposition désormais masquée/invisible, le TUI réassigne le focus à la prochaine superposition visible ou au focus pré-superposition sauvegardé.
- Les événements de relâchement de touche sont filtrés, sauf si le composant en focus définit
wantsKeyRelease = true. - Après la distribution, le TUI planifie le rendu.
setFocus() active/désactive également Focusable.focused, ce qui contrôle si les composants émettent CURSOR_MARKER pour le positionnement matériel du curseur.
Répartition de la gestion des touches : éditeur vs contrôleur
Section intitulée « Répartition de la gestion des touches : éditeur vs contrôleur »CustomEditor intercepte d’abord les combinaisons à haute priorité (échappement, ctrl-c/d/z, ctrl-v, variantes ctrl-p, ctrl-t, alt-haut, touches personnalisées d’extension) et délègue le reste au comportement de base de Editor (édition de texte, historique, autocomplétion, déplacement du curseur).
InputController.setupKeyHandlers() lie ensuite les rappels de l’éditeur aux actions du mode :
- annulation / sorties de mode sur
Escape - arrêt sur double
Ctrl+CouCtrl+Davec éditeur vide - suspension/reprise sur
Ctrl+Z - raccourcis de commande slash et sélecteur
- bascules de suivi/défilement et bascules d’expansion
Cela maintient l’analyse des touches et la mécanique de l’éditeur dans packages/tui et la sémantique du mode dans les contrôleurs de coding-agent.
Boucle de rendu et stratégie de diff
Section intitulée « Boucle de rendu et stratégie de diff »TUI.requestRender() est soumis à un anti-rebond pour limiter à un rendu par tick en utilisant process.nextTick. Plusieurs changements d’état dans le même tour sont fusionnés.
Pipeline de #doRender() :
- Rendu de l’arbre de composants racine vers
newLines. - Composition des superpositions visibles (le cas échéant).
- Extraction et suppression de
CURSOR_MARKERdes lignes visibles de la fenêtre d’affichage. - Ajout de suffixes de réinitialisation de segment pour les lignes sans image.
- Choix entre repeint complet ou correction différentielle :
- première image
- changement de largeur
- rétrécissement avec
clearOnShrinkactivé et sans superpositions - modifications au-dessus de la fenêtre d’affichage précédente
- Pour les mises à jour différentielles, correction uniquement de la plage de lignes modifiées et effacement des lignes finales obsolètes si nécessaire.
- Repositionnement du curseur matériel pour la prise en charge de l’IME.
Les écritures de rendu utilisent le mode de sortie synchronisée (CSI ? 2026 h/l) pour réduire le scintillement/déchirement.
Contraintes de sûreté du rendu
Section intitulée « Contraintes de sûreté du rendu »Vérifications de sûreté critiques dans TUI :
- Les lignes rendues sans image ne doivent pas dépasser la largeur du terminal ; un dépassement lève une exception et écrit des diagnostics d’incident.
- La composition des superpositions inclut une troncature défensive et une vérification de largeur post-composition.
- Les changements de largeur forcent un redessin complet car la sémantique du retour à la ligne change.
- La position du curseur est contrainte avant le déplacement.
Ces contraintes constituent une application à l’exécution, et non de simples conventions.
Gestion du redimensionnement
Section intitulée « Gestion du redimensionnement »Les événements de redimensionnement sont pilotés par événements depuis ProcessTerminal vers TUI.requestRender().
Effets :
- Tout changement de largeur déclenche un redessin complet.
- Le suivi de la fenêtre d’affichage/du haut (
#previousViewportTop,#maxLinesRendered) évite les calculs de curseur relatifs invalides lors de changements de contenu ou de taille du terminal. - La visibilité des superpositions peut dépendre des dimensions du terminal (
OverlayOptions.visible) ; le focus est corrigé lorsque les superpositions deviennent non visibles après redimensionnement.
Streaming et mises à jour UI incrémentales
Section intitulée « Streaming et mises à jour UI incrémentales »EventController s’abonne aux AgentSessionEvent et met à jour l’UI de manière incrémentale :
agent_start: démarre le chargeur dansstatusContainer.message_startassistant : créestreamingComponentet le monte.message_update: met à jour le contenu assistant en streaming ; crée/met à jour les composants d’exécution d’outils au fur et à mesure que les appels d’outils apparaissent.tool_execution_update/end: met à jour les composants de résultat d’outil et l’état de complétion.message_end: finalise le flux assistant, gère les annotations abandonnées/erreur, marque les arguments d’outil en attente comme complets lors d’un arrêt normal.agent_end: arrête les chargeurs, efface l’état du flux transitoire, vide le changement de modèle différé, émet une notification de complétion si mis en arrière-plan.
Le regroupement des outils de lecture est intentionnellement avec état (#lastReadGroup) pour fusionner les appels d’outils de lecture consécutifs en un seul bloc visuel jusqu’à ce qu’une interruption non-lecture se produise.
Orchestration du statut et du chargeur
Section intitulée « Orchestration du statut et du chargeur »Propriété de la voie de statut :
statusContainercontient les chargeurs transitoires (loadingAnimation,autoCompactionLoader,retryLoader).statusLineaffiche les indicateurs de statut/hooks/plan persistants et pilote les mises à jour de bordure supérieure de l’éditeur.
Comportement du chargeur :
Loaderse met à jour toutes les 80 ms via un intervalle et demande un rendu à chaque image.- Les gestionnaires d’échappement sont temporairement remplacés pendant la compaction automatique et la nouvelle tentative automatique pour annuler ces opérations.
- Sur les chemins de fin/annulation, les contrôleurs restaurent les gestionnaires d’échappement précédents et arrêtent/effacent les composants du chargeur.
Transitions de mode et mise en arrière-plan
Section intitulée « Transitions de mode et mise en arrière-plan »Modes d’entrée Bash/Python
Section intitulée « Modes d’entrée Bash/Python »Les préfixes de texte d’entrée basculent les indicateurs de mode de bordure de l’éditeur :
!-> mode bash$(préfixe non-littéral de template) -> mode python
L’échappement quitte le mode inactif en effaçant le texte de l’éditeur et en restaurant la couleur de la bordure ; lorsque l’exécution est active, l’échappement abandonne la tâche en cours à la place.
Mode plan
Section intitulée « Mode plan »InteractiveMode suit les indicateurs de mode plan, l’état de la ligne de statut, les outils actifs et le changement de modèle. L’entrée/sortie met à jour les entrées de mode de session et l’état du statut/UI, y compris le changement de modèle différé si le streaming est actif.
Suspension/reprise (Ctrl+Z)
Section intitulée « Suspension/reprise (Ctrl+Z) »InputController.handleCtrlZ() :
- Enregistre un gestionnaire
SIGCONTà usage unique pour redémarrer le TUI et forcer le rendu. - Arrête le TUI avant la suspension.
- Envoie
SIGTSTPau groupe de processus.
Mode arrière-plan (/background ou /bg)
Section intitulée « Mode arrière-plan (/background ou /bg) »handleBackgroundCommand() :
- Rejette lorsqu’inactif.
- Bascule le contexte UI des outils vers non-interactif (
hasUI=false) afin que les outils UI interactifs échouent rapidement. - Arrête les chargeurs/la ligne de statut et se désabonne du gestionnaire d’événements au premier plan.
- S’abonne au gestionnaire d’événements en arrière-plan (attend principalement
agent_end). - Arrête le TUI et envoie
SIGTSTP(chemin de contrôle de tâche POSIX).
Sur agent_end en arrière-plan sans travail en file d’attente, le contrôleur envoie une notification de complétion et s’arrête.
Chemins d’annulation
Section intitulée « Chemins d’annulation »Entrées d’annulation principales :
Escapependant le chargeur de flux actif : restaure les messages en file d’attente dans l’éditeur et abandonne l’agent.Escapependant l’exécution bash/python : abandonne la commande en cours.Escapependant la compaction automatique/nouvelle tentative : invoque des méthodes d’abandon dédiées via des gestionnaires d’échappement temporaires.Ctrl+Cpression unique : effacer l’éditeur ; double pression dans les 500 ms : arrêt.
L’annulation est conditionnelle à l’état ; la même touche peut signifier abandon, sortie de mode, déclencheur de sélecteur ou aucune action selon l’état du runtime.
Comportement piloté par événements vs comportement à débit limité
Section intitulée « Comportement piloté par événements vs comportement à débit limité »Mises à jour pilotées par événements :
- Événements de session agent (
EventController) - Rappels d’entrée de touches (
InputController) - Rappel de redimensionnement du terminal
- Observateurs de thème/branche dans
InteractiveMode
Chemins à débit limité/anti-rebond :
- Le rendu TUI est soumis à un anti-rebond par tick (fusion de
requestRender). - L’animation du chargeur est à intervalle fixe (80 ms), chaque image demandant un rendu.
- Les mises à jour d’autocomplétion de l’éditeur (dans
Editor) utilisent des minuteries anti-rebond, réduisant le recalcul excessif lors de la frappe.
Le runtime combine donc des transitions d’état pilotées par événements avec une cadence de rendu bornée pour maintenir l’interactivité réactive sans tempêtes de repeint.