- A suposição de que apps de terminal são inerentemente acessíveis por serem baseados em texto não se sustenta nos TUIs modernos, e frameworks como Ink, Bubble Tea e tcell podem criar um ambiente ainda mais hostil para usuários de leitores de tela
- O CLI acumula saída em ordem cronológica como um fluxo linear de
stdin/stdout, mas o TUI trata o terminal como uma grade 2D de células de caracteres, o que dificulta para leitores de tela acompanhar o fluxo - O
gemini-clifaz o Ink redesenhar a árvore de componentes React para ajustá-la à grade do terminal, movendo o cursor entre spinner, timer e histórico da conversa, o que pode provocar leitura repetida, travamentos e atraso de entrada no Speakup e no NVDA - Ferramentas antigas como
nano,vim,menuconfige Irssi reduzem o ruído de atualizações de coordenadas e minimizam a interferência com a linha de entrada usando ocultação do cursor, foco em coluna única e áreas de rolagem do VT100 - Para criar ferramentas de terminal acessíveis, é preciso evitar frameworks de UI declarativa que tratam o terminal como um canvas e evitar redesenhos agressivos, garantindo um comportamento mais próximo de fluxos de CLI simples e lineares
O equívoco de que “por ser texto, é acessível”
- A suposição de que aplicações executadas no terminal são inerentemente acessíveis não corresponde ao uso real
- A expectativa de que leitores de tela consigam interpretar facilmente texto ASCII bruto por não haver gráficos, DOM complexo ou canvas WebGL se rompe nos TUIs modernos
- Frameworks de UI para terminal como Ink (JS/React), Bubble Tea (Go) e tcell tentam melhorar a experiência do desenvolvedor (DX), mas podem criar um ambiente mais hostil para usuários com deficiência visual
- Em muitos casos, TUIs modernos são piores em acessibilidade do que interfaces gráficas mal implementadas
Diferença estrutural entre CLI e TUI
-
CLI: fluxo linear
- CLI funciona com base em
stdin/stdout; você digita um comando, o resultado é adicionado abaixo e o cursor desce - Como a saída é linear e se acumula em ordem cronológica, ela se adapta bem a leitores de tela em nível de kernel como o Speakup
- CLI funciona com base em
-
TUI: grade 2D
- TUI trata a janela do terminal não como um fluxo de texto, mas como uma grade 2D em que cada célula de caractere é usada como se fosse um pixel
- Ao abandonar o fluxo temporal e priorizar o layout espacial, cria-se uma estrutura difícil de acompanhar para leitores de tela
O problema exposto no gemini-cli
- O
gemini-clié uma ferramenta escrita em Node.js com o framework Ink e, por fora, parece apenas uma interface de chat simples - Internamente, o Ink tenta ajustar a árvore de componentes React à grade do terminal
- Ao usar com Speakup (Linux) ou NVDA (Windows), o aplicativo não apenas falha: ele despeja continuamente conteúdo para o leitor de tela ler
-
Uma tela que se comporta como um canvas reativo
- Como o framework trata a tela como um canvas reativo, toda atualização provoca um redesenho
- Quando a IA está “pensando”, para atualizar um timer ou spinner, ele move o cursor de hardware até a posição do timer, escreve o novo tempo e depois volta ao ponto original
- Para usuários videntes, isso passa instantaneamente, mas para usuários de leitores de tela soa como “Responding... Time elapsed 1s... Responding... Time elapsed 2s...” repetidamente
- À medida que o cursor se desloca momentaneamente entre o indicador de status, o spinner e o histórico da conversa, o Speakup tenta ler o que estiver sob o cursor naquele instante
- Como resultado, atualizações do timer e trechos da conversa se misturam, tornando difícil focar no que está sendo digitado de fato
-
Instabilidade com NVDA e colagem
- No Windows, ao abrir um terminal com NVDA, conectar por SSH a uma máquina Linux e entrar em uma sessão
screen, colar texto pode fazer o NVDA travar imediatamente ou deixar o sistema altamente instável - Sempre que um caractere é digitado ou um texto é colado, o estado da aplicação muda, e o framework decide que precisa renderizar a interface novamente
- Se o histórico da conversa faz parte do estado, ele pode tentar redesenhar ou recalcular instantaneamente o layout de milhares de linhas de texto
- Quanto mais mensagens houver na conversa, mais frequentemente esse problema ocorre
- Nem a combinação
Insert+5, usada para evitar anúncios de conteúdo dinâmico, consegue evitar isso
- No Windows, ao abrir um terminal com NVDA, conectar por SSH a uma máquina Linux e entrar em uma sessão
-
O loop de atraso de entrada
- Quando frameworks como Ink rodam em ambientes de thread única como o Node.js, a degradação de desempenho cresce conforme o histórico aumenta
- Ao colar grandes blocos de texto, é preciso calcular diferenças para milhares de linhas
- O sistema fica ocupado calculando como redesenhar a tela, e o processamento da entrada atrasa
- Mesmo ao pressionar uma única tecla, pode ser preciso esperar até 10 segundos para o caractere reaparecer na tela
Por que ferramentas antigas funcionam
- Ferramentas como
nano,vimemenuconfignão são usadas por serem sempre perfeitas em acessibilidade - O ponto central é que elas conseguem ocultar totalmente o cursor ou reduzir o ruído gerado pelo rastreamento da posição do cursor
-
nanoevim: ocultação do cursor- Se o
nanofor executado com opções que exibem a posição do cursor, como--constantshow, ou se ovimfor usado sem determinadas configurações, a usabilidade pode se deteriorar - Quando o cursor fica visível e o rastreamento está ativo, o Speakup prioriza atualizações da posição do cursor em vez do eco de caracteres
- Quando o usuário digita “a”, em vez de ouvir “a”, ele ouve “Column 2”; ao digitar “b”, ouve “Column 3”
- Essas ferramentas antigas podem ser configuradas para suprimir atualizações do cursor visual ou da barra de status, fazendo o leitor de tela depender do fluxo de entrada de caracteres em vez de atualizações de coordenadas
- Frameworks modernos normalmente não oferecem modo “no-cursor” ou “headless” e partem do princípio de que o cursor visual é obrigatório
- Se o
-
menuconfig: foco em coluna única- O
menuconfigdo kernel Linux funciona porque mantém um foco estrito em uma única coluna - Mesmo com bordas e títulos, a área ativa é uma lista vertical, e o cursor permanece preso a essa lista
- O cursor não sai para o canto inferior direito para atualizar um relógio e depois vai ao canto superior esquerdo para atualizar um título
- A complexidade espacial se mantém baixa, então o leitor de tela não perde a referência
- O
-
Irssi: uso de áreas de rolagem
- O Irssi não é acessível por sorte; é uma ferramenta de chat que há mais de 20 anos usa áreas de rolagem do VT100 por meio de um mecanismo de renderização próprio
- Quando chega uma nova mensagem, ele instrui o driver do terminal a “definir da linha 1 à linha 23 como área de rolagem”
- Em seguida, envia o comando de “rolar para cima”, o terminal move o conteúdo para cima e desenha o novo texto na parte inferior dessa área
- Esse método minimiza a interferência com a linha de entrada
- Em vez de reescrever manualmente todos os caracteres da tela, ele depende de recursos de hardware do terminal
- Frameworks modernos ignoram esses recursos de hardware e preferem calcular diferenças de estado da tela para reescrever caracteres, o que custa mais computacionalmente e é hostil à acessibilidade
O problema no tratamento dos issues do gemini-cli
- O Google e os mantenedores do
gemini-cliparecem se importar com acessibilidade, mas regressões importantes de acessibilidade estão sendo deixadas de lado no repositório - Regressões de acessibilidade como a Issue #3435 e a Issue #11305 não têm discussão, roadmap nem correção
- A Issue #1553 foi aberta para rastrear esse tipo de falha de acessibilidade, mas não foi resolvida e acabou fechada automaticamente por um bot
- O bot encerra o issue com uma mensagem genérica dizendo que ele foi fechado por inatividade prolongada e para gerenciamento de backlog
- Fechar relatos de acessibilidade porque os mantenedores não tocaram neles por meses não é organização; é como esconder evidências
- Isso passa o sinal de que, se um bug for ignorado tempo suficiente, ele deixa de existir, enquanto o software continua inutilizável para usuários com deficiência visual
- A métrica de “Closed Issues” do projeto pode melhorar, mas os problemas de acessibilidade seguem sem solução
Conclusão para criar ferramentas de terminal acessíveis
- Se acessibilidade realmente importa em aplicações para terminal, é preciso parar de usar frameworks de UI declarativa que tratam o terminal como um canvas
- As stacks de TUI “modernas” foram otimizadas para facilitar ao desenvolvedor escrever código como se estivesse usando React, sacrificando a capacidade da máquina de renderizar texto com eficiência
- Se a aplicação não garante que o usuário possa ocultar o cursor, ou depende de redesenhos agressivos para mostrar spinners e timers, ela se torna uma ferramenta inacessível
- Para usuários com deficiência visual, um fluxo de CLI simples e linear é muito melhor do que um TUI “inteligente” que atrasa, despeja leitura contínua e espalha o cursor pela tela inteira
1 comentários
Comentários no Lobste.rs
Este texto, como muitos outros posts de blog, tem um forte cheiro de texto escrito com ajuda de IA
LLMs adoram títulos desse tipo: “The Architectural Flaw”, “The Lag Loop”, “Why The ‘Old Guard’ Works”, “The Lost Art of Scrolling Regions”, “The ‘Stale Bot’ excuse: A Case Study in Neglect”
Poderia ter sido um excelente post de blog, mas se parece que o autor jogou um esboço no ChatGPT e parou por aí, isso é ruim tanto para o leitor quanto para o autor
Chamar um problema muito específico e pontual de problema “clássico” é a mesma coisa
Isso é realmente deprimente. Em resumo, existem TUIs acessíveis como o Irssi, mas os frameworks modernos de TUI ignoram esses precedentes e dependem de diff de grade e movimentação do cursor
Leitores de tela leem o conteúdo da posição para a qual o cursor se move, então o resultado vira uma bagunça ou gera uma quantidade absurda de spam de leitura
Fico em dúvida se a explicação técnica aqui está totalmente correta
Em especial, o Ink por muito tempo não suportou renderização incremental nenhuma, e a maioria dos apps que usam Ink ainda não a ativa. E mesmo essa renderização incremental é baseada em linhas, então ela não move o cursor até a posição real do timer
O Gemini CLI exige uso de buffer alternativo para ativar a renderização incremental, e isso é desativado quando o modo interno compatível com leitor de tela está ligado. A documentação da opção relacionada está aqui
Além disso, rich/textual em Python muitas vezes é bem mais rápido que Ink, mesmo rodando sobre uma linguagem mais lenta e em geral de thread única. Fazer diff de milhares de linhas não é necessariamente tão lento assim, e certamente não a ponto de levar 10 segundos
Não duvido que a experiência de usuário esteja frustrante e quebrada, mas a causa exata apresentada parece possivelmente ser uma alucinação de LLM ou baseada em informação incompleta. A renderização incremental do Ink, mesmo quando ativada, não funciona como foi descrito
Na prática, é bem provável que o redesenho da tela inteira confunda leitores de tela, e que o redesenho baseado em linhas seja ruim por fazer reler fragmentos arbitrários e quebrados de texto sem relação com a mudança
Culpar só a TUI não é justo
O problema real é que o suporte a acessibilidade de quase toda a pilha é péssimo
Primeiro, a maioria dos emuladores de terminal com renderização por GPU não usa nem um pouco as APIs de acessibilidade fornecidas pelo sistema. Quando o texto é renderizado pela GPU, ferramentas de acessibilidade não conseguem “ler”; ele parece só uma imagem. Kitty, Alacritty e WezTerm entram nisso. Meu terminal, Ghostty, pode ser lido pela API de acessibilidade no macOS, e iTerm2 e Terminal.app também
Segundo, não existe nenhuma sequência de terminal nem movimento padronizado para uma TUI transmitir informação de acessibilidade ao emulador de terminal. Precisaríamos de algo equivalente a anotações ao estilo ARIA para células do terminal, intervalos de execução e regiões, mas não há nenhuma iniciativa disso. Mesmo que a TUI lide bem com o cursor, ainda vai dar problema em muitos casos de uso
Por exemplo, no Ghostty temos trabalhado para integrar OSC133 com a API de acessibilidade, expondo cada prompt do shell, entrada e comando como elementos estruturalmente significativos, e não apenas caixas de texto simples. Isso mostra que especificação de terminal, TUI e emulador de terminal precisam se encaixar juntos
A pilha inteira está apodrecida, e quase ninguém quer consertar isso de verdade. Eu também só faço o melhor que posso com tempo limitado, mas este é um tema enorme que ainda exige política de ecossistema, então é difícil de enfrentar
Como bônus, a realidade ao mesmo tempo interessante e terrível é que a IA está ajudando a melhorar a acessibilidade aqui. Muitas ferramentas de IA usam ou abusam das APIs de acessibilidade para ler listas de janelas e realizar entrada. Então mais aplicativos começaram a levar integração de acessibilidade muito mais a sério por causa dos casos de uso de IA
Fico irritado todos os dias porque o Claude Code e o gemini-cli não são baseados em readline
Eles colocaram algumas teclas parecidas, mas falta a longa cauda dos atalhos readline já familiares
A Anthropic poderia admitir que decidir “fazer como desenvolvimento web” foi um erro e recomeçar com readline
A ideia de que a experiência familiar de desenvolvimento para quem cria essas ferramentas é mais importante do que a experiência familiar de uso para quem as usa está errada
Na prática também quase não existem soluções de terceiros famosas e bem mantidas. Se você precisa de uma caixa de entrada flexível, acaba tendo de construir do zero
Em contraste com o excelente widget Input do Textual ou com outra biblioteca do ecossistema JS, o OpenTUI
Eu não gosto de LLMs, então pessoalmente considero a UI ruim uma vantagem, mas pode haver um motivo para não usarem readline
Editores de terminal como kakoune e helix provavelmente teriam dificuldade para passar em critérios de acessibilidade, a menos que usem o truque de “esconder o cursor”
Ainda assim, provavelmente não seriam tão acessíveis quanto o VS Code
Fora o VS Code, que IDE-lite ou IDE multiplataforma acessível existe? Não gosto da postura cada vez mais hostil do VS Code. Talvez os IDEs da JetBrains
O lado ruim é que, embora o Emacs em si seja multiplataforma, o emacspeak pode ter uma dependência fraca de Linux por causa de TTS. Ou talvez não. Nunca tentei no Windows
Se for acessibilidade para pessoas cegas, você precisa de emacspeak ou das ferramentas de acessibilidade para deficiência visual da própria plataforma
Acessibilidade é um espectro, não uma caixa de seleção
O Links tem um modo de terminal braille separado, que troca elementos de GUI falsos por um menu de tela cheia mais simples e também muda a navegação por setas para ser linha a linha
Outro caso interessante é o edbrowse. É um navegador em modo texto criado por Karl Dahlke, que é cego, e ao contrário de navegadores web em modo texto mais populares, ele não usa TUI, e sim uma interface de linha de comando no estilo do ed
Se for o framework Ink, isso provavelmente explica por que a CLI usa 100% da CPU, trava para sempre e continua redesenhando um histórico longo de chat. Uma pena