📟 DEC Private Mode Sequences
const DEC = {
CURSOR_VISIBLE: 25,
ALT_SCREEN: 47, // older; NAO salva/restaura cursor
ALT_SCREEN_CLEAR: 1049, // modern: save cursor + switch + clear
MOUSE_NORMAL: 1000, // button press/release + wheel
MOUSE_BUTTON: 1002, // adds drag (button-motion)
MOUSE_ANY: 1003, // adds all-motion (hover)
MOUSE_SGR: 1006, // SGR format
FOCUS_EVENTS: 1004,
BRACKETED_PASTE: 2004,
SYNCHRONIZED_UPDATE:2026,
}
💡 Por que 1049 e nao 47?
DEC mode 1049 salva posicao do cursor antes de trocar e restaura na saida. O antigo mode 47 troca buffers sem preservar cursor. Claude Code usa 1049 exclusivamente para preservar a posicao do cursor do shell.
🖱️ Mouse Tracking & Teardown
Os 4 modos mouse sao habilitados em ordem crescente e desabilitados em ordem reversa (outer para inner):
Enable: 1000 -> 1002 -> 1003 -> 1006
Disable: 1006 -> 1003 -> 1002 -> 1000
Desabilitar em ordem reversa garante teardown limpo. Se o processo crashar entre dois decreset, nao deixa modo parcial ativo.
| Env var | Desabilita | Ainda funciona |
|---|---|---|
NO_FLICKER=0 | Alt-screen + mouse tracking | Terminal scrollback normal |
DISABLE_MOUSE=1 | Mouse capture (wheel+click) | Alt-screen + keyboard nav |
DISABLE_MOUSE_CLICKS=1 | Click e drag | Alt-screen + wheel scroll |
🔍 Logica de Deteccao Fullscreen
flowchart TD
A["isFullscreenActive()"] --> B{"getIsInteractive()"}
B -->|"false (headless/SDK)"| Z["return false"]
B -->|"true"| C["isFullscreenEnvEnabled()"]
C --> D{"NO_FLICKER\nexplicitly falsy?"}
D -->|"yes (=0)"| Z
D -->|"no"| E{"NO_FLICKER\nexplicitly truthy?"}
E -->|"yes (=1)"| Y["return true"]
E -->|"no"| F{"isTmuxControlMode()?"}
F -->|"yes (iTerm2 -CC)"| Z
F -->|"no"| G{"USER_TYPE === 'ant'?"}
G -->|"yes"| Y
G -->|"no"| Z
🔌 tmux -CC Probe Sincrono
probeTmuxControlModeSync() chama spawnSync('tmux', ['display-message', '-p', '#{client_control_mode}']) -- subprocess sincrono bloqueando o event loop por ~5ms.
⚠️ Por que sincrono?
Um probe async competiu com o React render e perdeu: usuarios de SSH + tmux -CC terminavam com alt-screen ativo e mouse wheel morto. O custo de 5ms uma vez no startup evita um bug de UX irrecuperavel. So dispara quando $TMUX esta setado E $TERM_PROGRAM esta ausente.
⚛️ AlternateScreen React Component
Limite React que escreve sequencias DEC no terminal. Constrainte critico: escapes devem chegar ao terminal antes do primeiro frame renderizado.
useInsertionEffect vs useLayoutEffect
useInsertionEffect dispara durante a mutation phase, antes de resetAfterCommit do Ink. Com useLayoutEffect, o primeiro onRender dispararia ANTES deste efeito -- pintando um frame na tela principal que seria preservado como view quebrada ao sair do alt-screen.
Height Constraint
Alt-screen nao tem scrollback. AlternateScreen pina sua altura em size.rows de TerminalSizeContext, forcando todo overflow a ser tratado pelo flexbox do Ink.
🏗️ FullscreenLayout: 5 Slots
💡 OffscreenFreeze
Explora bail-out por identidade de objeto do React para produzir zero diff output para conteudo que scrollou para o scrollback do terminal. Usa 'use no memo' para opt-out do React Compiler, porque memoizacao automatica derrotaria o mecanismo de freeze.
🔄 Ciclo de Vida Completo
sequenceDiagram
participant Startup
participant fullscreen.ts
participant AlternateScreen
participant Ink
participant Terminal
Startup->>fullscreen.ts: isFullscreenActive()?
fullscreen.ts-->>Startup: true
Startup->>AlternateScreen: mount (useInsertionEffect)
AlternateScreen->>Terminal: ENTER_ALT_SCREEN + clear + ENABLE_MOUSE
AlternateScreen->>Ink: setAltScreenActive(true, mouseTracking)
Note over Ink: renderer clamps cursor to viewport
loop Every render frame
Ink->>Terminal: diff output within alt-screen bounds
end
Startup->>AlternateScreen: unmount (cleanup)
AlternateScreen->>Ink: setAltScreenActive(false)
AlternateScreen->>Terminal: DISABLE_MOUSE + EXIT_ALT_SCREEN
Note over Terminal: main screen + cursor restored
📋 Resumo do Modulo
useInsertionEffect garante que ENTER_ALT_SCREEN chega ao terminal antes do primeiro frame do Ink