- Home
- Documentation
- TUI
- Componenti interni del runtime TUI
Componenti interni del runtime TUI
Questo documento mappa il percorso del runtime non-tema dall’input del terminale all’output renderizzato in modalità interattiva. Si concentra sul comportamento in packages/tui e sulla sua integrazione dai controller di packages/coding-agent.
Livelli del runtime e responsabilità
Sezione intitolata “Livelli del runtime e responsabilità”- Engine
packages/tui: ciclo di vita del terminale, normalizzazione stdin, instradamento del focus, pianificazione del rendering, pittura differenziale, composizione degli overlay, posizionamento hardware del cursore. - Modalità interattiva di
packages/coding-agent: costruisce l’albero dei componenti, associa callback dell’editor e keymaps, reagisce agli eventi agente/sessione e traduce lo stato del dominio (streaming, esecuzione degli strumenti, tentativi, modalità piano) in componenti UI.
Regola di confine: l’engine TUI è agnostico rispetto ai messaggi. Conosce solo Component.render(width), handleInput(data), il focus e gli overlay. La semantica dell’agente rimane nei controller interattivi.
File di implementazione
Sezione intitolata “File di implementazione”../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
Avvio e assemblaggio dell’albero dei componenti
Sezione intitolata “Avvio e assemblaggio dell’albero dei componenti”InteractiveMode costruisce TUI(new ProcessTerminal(), showHardwareCursor) e crea contenitori persistenti:
chatContainerpendingMessagesContainerstatusContainertodoContainerstatusLineeditorContainer(contieneCustomEditor)
init() collega l’albero in quest’ordine, mette a fuoco l’editor, registra i gestori di input tramite InputController, avvia TUI e richiede un rendering forzato.
Un rendering forzato (requestRender(true)) azzera le cache delle righe precedenti e la contabilità del cursore prima di ridipingere.
Ciclo di vita del terminale e normalizzazione stdin
Sezione intitolata “Ciclo di vita del terminale e normalizzazione stdin”ProcessTerminal.start():
- Abilita la modalità raw e il paste tra parentesi.
- Associa il gestore di ridimensionamento.
- Crea uno
StdinBufferper suddividere i blocchi di escape parziali in sequenze complete. - Interroga il supporto del protocollo tastiera Kitty (
CSI ? u), quindi abilita i flag di protocollo se supportati. - Su Windows, tenta l’abilitazione dell’input VT tramite i flag di modalità
kernel32.
Comportamento di StdinBuffer:
- Bufferizza le sequenze di escape frammentate (CSI/OSC/DCS/APC/SS3).
- Emette
datasolo quando una sequenza è completa o viene svuotata per timeout. - Rileva il paste tra parentesi ed emette un evento
pastecon il testo incollato grezzo.
Ciò impedisce che i blocchi di escape parziali vengano interpretati erroneamente come normali pressioni di tasti.
Instradamento dell’input e modello di focus
Sezione intitolata “Instradamento dell’input e modello di focus”Percorso dell’input:
stdin -> ProcessTerminal -> StdinBuffer -> TUI.#handleInput -> focusedComponent.handleInput
Dettagli dell’instradamento:
- TUI esegue prima i listener di input registrati (
addInputListener), consentendo comportamenti di consumo/trasformazione. - TUI gestisce la scorciatoia di debug globale (
shift+ctrl+d) prima del dispatch al componente. - Se il componente con focus appartiene a un overlay ora nascosto/invisibile, TUI riassegna il focus al prossimo overlay visibile o al focus pre-overlay salvato.
- Gli eventi di rilascio tasto vengono filtrati a meno che il componente con focus non imposti
wantsKeyRelease = true. - Dopo il dispatch, TUI pianifica il rendering.
setFocus() alterna anche Focusable.focused, che controlla se i componenti emettono CURSOR_MARKER per il posizionamento hardware del cursore.
Suddivisione della gestione dei tasti: editor vs controller
Sezione intitolata “Suddivisione della gestione dei tasti: editor vs controller”CustomEditor intercetta prima le combinazioni ad alta priorità (escape, ctrl-c/d/z, ctrl-v, varianti ctrl-p, ctrl-t, alt-su, tasti personalizzati delle estensioni) e delega il resto al comportamento base di Editor (modifica del testo, cronologia, completamento automatico, movimento del cursore).
InputController.setupKeyHandlers() associa quindi i callback dell’editor alle azioni di modalità:
- annullamento / uscita dalla modalità con
Escape - arresto con doppio
Ctrl+CoCtrl+Dcon editor vuoto - sospensione/ripresa con
Ctrl+Z - hotkey di comandi slash e selettori
- attivazione/disattivazione dei toggle follow-up/dequeue e toggle di espansione
Questo mantiene l’analisi dei tasti/meccaniche dell’editor in packages/tui e la semantica della modalità nei controller di coding-agent.
Loop di rendering e strategia di diffing
Sezione intitolata “Loop di rendering e strategia di diffing”TUI.requestRender() è de-rimbalzato a un rendering per tick usando process.nextTick. Più cambiamenti di stato nello stesso turno si fondono insieme.
Pipeline #doRender():
- Renderizza l’albero dei componenti radice in
newLines. - Compone gli overlay visibili (se presenti).
- Estrae e rimuove
CURSOR_MARKERdalle righe del viewport visibile. - Aggiunge suffissi di reset del segmento per le righe non-immagine.
- Sceglie tra ridipintura completa e patch differenziale:
- primo frame
- cambio di larghezza
- riduzione con
clearOnShrinkabilitato e nessun overlay - modifiche sopra il viewport precedente
- Per gli aggiornamenti differenziali, corregge solo l’intervallo di righe modificate e cancella le righe finali obsolete quando necessario.
- Riposiziona il cursore hardware per il supporto IME.
Le scritture di rendering utilizzano la modalità di output sincronizzato (CSI ? 2026 h/l) per ridurre lo sfarfallio/tearing.
Vincoli di sicurezza del rendering
Sezione intitolata “Vincoli di sicurezza del rendering”Controlli di sicurezza critici in TUI:
- Le righe renderizzate non-immagine non devono superare la larghezza del terminale; l’overflow genera un’eccezione e scrive diagnostiche di crash.
- La composizione degli overlay include troncamento difensivo e verifica della larghezza post-composizione.
- I cambiamenti di larghezza forzano un ridisegno completo perché la semantica del wrapping cambia.
- La posizione del cursore viene limitata prima del movimento.
Questi vincoli sono applicazioni a runtime, non semplici convenzioni.
Gestione del ridimensionamento
Sezione intitolata “Gestione del ridimensionamento”Gli eventi di ridimensionamento sono guidati dagli eventi da ProcessTerminal a TUI.requestRender().
Effetti:
- Qualsiasi cambio di larghezza attiva un ridisegno completo.
- Il tracciamento del viewport/top (
#previousViewportTop,#maxLinesRendered) evita calcoli relativi del cursore non validi quando il contenuto o le dimensioni del terminale cambiano. - La visibilità degli overlay può dipendere dalle dimensioni del terminale (
OverlayOptions.visible); il focus viene corretto quando gli overlay diventano non visibili dopo il ridimensionamento.
Aggiornamenti UI in streaming e incrementali
Sezione intitolata “Aggiornamenti UI in streaming e incrementali”EventController si iscrive a AgentSessionEvent e aggiorna l’UI in modo incrementale:
agent_start: avvia il loader instatusContainer.message_startassistant: creastreamingComponente lo monta.message_update: aggiorna il contenuto dell’assistant in streaming; crea/aggiorna i componenti di esecuzione degli strumenti man mano che appaiono le chiamate agli strumenti.tool_execution_update/end: aggiorna i componenti del risultato degli strumenti e lo stato di completamento.message_end: finalizza lo stream dell’assistant, gestisce le annotazioni di interruzione/errore, contrassegna gli argomenti degli strumenti in sospeso come completi all’arresto normale.agent_end: arresta i loader, cancella lo stato di stream transitorio, svuota il cambio di modello differito, emette la notifica di completamento se in background.
Il raggruppamento degli strumenti di lettura è intenzionalmente stateful (#lastReadGroup) per unire chiamate consecutive agli strumenti di lettura in un unico blocco visivo fino a quando non si verifica un’interruzione non-read.
Orchestrazione di stato e loader
Sezione intitolata “Orchestrazione di stato e loader”Proprietà della corsia di stato:
statusContainercontiene i loader transitori (loadingAnimation,autoCompactionLoader,retryLoader).statusLinerenderizza gli indicatori persistenti di stato/hook/piano e guida gli aggiornamenti del bordo superiore dell’editor.
Comportamento del loader:
Loadersi aggiorna ogni 80ms tramite intervallo e richiede il rendering a ogni frame.- I gestori Escape vengono temporaneamente sovrascritti durante la compattazione automatica e il tentativo automatico per annullare tali operazioni.
- Nei percorsi di fine/annullamento, i controller ripristinano i gestori escape precedenti e arrestano/cancellano i componenti loader.
Transizioni di modalità e background
Sezione intitolata “Transizioni di modalità e background”Modalità di input Bash/Python
Sezione intitolata “Modalità di input Bash/Python”I prefissi del testo di input attivano i flag di modalità del bordo dell’editor:
!-> modalità bash$(prefisso non-template literal) -> modalità python
Escape esce dalla modalità inattiva cancellando il testo dell’editor e ripristinando il colore del bordo; quando l’esecuzione è attiva, escape interrompe invece l’attività in corso.
Modalità piano
Sezione intitolata “Modalità piano”InteractiveMode tiene traccia dei flag della modalità piano, dello stato della riga di stato, degli strumenti attivi e del cambio di modello. L’entrata/uscita aggiorna le voci della modalità sessione e lo stato UI/stato, incluso il cambio di modello differito se lo streaming è attivo.
Sospensione/ripresa (Ctrl+Z)
Sezione intitolata “Sospensione/ripresa (Ctrl+Z)”InputController.handleCtrlZ():
- Registra un gestore
SIGCONTone-shot per riavviare TUI e forzare il rendering. - Arresta TUI prima della sospensione.
- Invia
SIGTSTPal gruppo di processi.
Modalità background (/background o /bg)
Sezione intitolata “Modalità background (/background o /bg)”handleBackgroundCommand():
- Rifiuta quando inattivo.
- Cambia il contesto UI degli strumenti a non-interattivo (
hasUI=false) in modo che gli strumenti UI interattivi falliscano rapidamente. - Arresta i loader/la riga di stato e annulla l’iscrizione al gestore di eventi in primo piano.
- Si iscrive al gestore di eventi in background (principalmente in attesa di
agent_end). - Arresta TUI e invia
SIGTSTP(percorso di controllo lavori POSIX).
All’evento agent_end in background senza lavoro in coda, il controller invia la notifica di completamento e si arresta.
Percorsi di annullamento
Sezione intitolata “Percorsi di annullamento”Input primari di annullamento:
Escapedurante il loader di stream attivo: ripristina i messaggi in coda nell’editor e interrompe l’agente.Escapedurante l’esecuzione bash/python: interrompe il comando in esecuzione.Escapedurante la compattazione automatica/tentativo: richiama metodi di interruzione dedicati tramite gestori escape temporanei.Ctrl+Cpressione singola: cancella l’editor; doppia pressione entro 500ms: arresto.
L’annullamento è condizionato dallo stato; la stessa chiave può significare interruzione, uscita dalla modalità, attivazione del selettore o nessuna operazione a seconda dello stato a runtime.
Comportamento guidato dagli eventi vs a limitazione di frequenza
Sezione intitolata “Comportamento guidato dagli eventi vs a limitazione di frequenza”Aggiornamenti guidati dagli eventi:
- Eventi della sessione agente (
EventController) - Callback di input dei tasti (
InputController) - Callback di ridimensionamento del terminale
- Osservatori di tema/branch in
InteractiveMode
Percorsi a limitazione di frequenza/de-rimbalzo:
- Il rendering TUI è de-rimbalzato per tick (fusione di
requestRender). - L’animazione del loader è a intervallo fisso (80ms), con ogni frame che richiede il rendering.
- Gli aggiornamenti di completamento automatico dell’editor (all’interno di
Editor) utilizzano timer de-rimbalzo, riducendo il ricalcolo durante la digitazione.
Il runtime combina quindi transizioni di stato guidate dagli eventi con una cadenza di rendering limitata per mantenere la reattività dell’interazione senza tempeste di ridipintura.