3 pontos por GN⁺ 2024-08-12 | 1 comentários | Compartilhar no WhatsApp
  • O processo de desenvolvimento do Textual mostrou que é possível criar animações TUI suaves mesmo sobre protocolos de terminal antigos, mas é preciso tratar com cuidado a forma de atualização da tela e a sincronização da saída
  • Para reduzir flicker, é importante sobrescrever sem limpar a tela, escrever na saída padrão de uma só vez e usar o protocolo Synchronized Output
  • As views dict.items() do Python e @lru_cache são úteis para calcular diferenças de renderização e otimizar pequenas funções chamadas com alta frequência, e a implementação em C do CPython sustenta o desempenho do cache
  • Objetos quase imutáveis, diagramas com caracteres Unicode de caixa e fractions.Fraction aumentam, respectivamente, a precisão em raciocínio, caching e testes; compreensão da documentação; e cálculo de layout
  • Emojis no terminal são difíceis de prever por causa de diferenças em versão do Unicode, largura dos caracteres e renderização de múltiplos code points; na prática, Emojis simples no nível do Unicode 9 são mais estáveis

Animações no terminal e controle de flicker

  • Emuladores de terminal modernos funcionam sobre protocolos antigos, mas muitas implementações usam técnicas gráficas também empregadas em videogames
  • Efeitos visuais no terminal são propensos a flicker ou tearing, mas é possível implementar animações suaves se algumas condições forem respeitadas
  • A primeira forma de reduzir flicker é sobrescrever o conteúdo existente, em vez de “limpar e depois desenhar”
    • Se a tela for limpa e o novo conteúdo for adicionado depois, um quadro vazio ou parcialmente desenhado pode aparecer por um instante
  • A segunda forma é escrever as atualizações na saída padrão com uma única chamada de write
    • Se file.write for chamado várias vezes, atualizações parciais podem ficar visíveis para o usuário
  • A terceira forma é usar o protocolo Synchronized Output
    • Ao informar ao terminal o início e o fim de um quadro, o terminal pode usar isso para aplicar atualizações sem flicker
  • O Textual usa 60 fps como referência; acima disso, a diferença perceptível pode não ser grande
  • Nem toda animação tem o mesmo valor
    • Um efeito como uma sidebar deslizando a partir da esquerda é visualmente agradável, mas pode não trazer benefício real para a experiência do usuário
    • Rolagem suave ajuda a manter a posição atual em textos longos
    • Por esse motivo, o Textual pretende incluir um mecanismo para desativar animações específicas

Encontrando diferenças de renderização com DictView

  • keys() e items() de um dict em Python retornam, respectivamente, KeysView e ItemsView, que oferecem uma interface semelhante à de conjuntos
  • O processo de layout do Textual gera um mapa de renderização, que é um mapeamento entre Widgets e posições na tela
  • Na abordagem anterior, mesmo que a posição de apenas um Widget mudasse, a tela inteira era atualizada de forma desperdiçada
  • Usar a diferença simétrica entre duas ItemsView permite obter itens novos ou alterados
    • Essa operação é processada no nível de C
    • Quando uma propriedade CSS muda, o Textual usa isso para encontrar as áreas alteradas da tela e aplicar atualizações otimizadas

lru_cache e otimização de pequenas funções

  • @lru_cache, da biblioteca padrão functools, é um decorador que faz cache dos valores retornados por uma função
  • Definir maxsize permite limitar uma situação em que o cache cresceria indefinidamente
  • O lru_cache do CPython usa uma implementação em C, sendo rápido tanto em acertos quanto em falhas de cache
  • O Textual tem pequenas funções que não são lentas, mas são chamadas com muita frequência, e muitas delas eram adequadas para cache
  • Em geral, definir maxsize entre 1000 e 4000 foi suficiente para fazer a maioria das chamadas resultar em acertos de cache
  • Ao usar cache, é necessário verificar cache_info()
    • Em um cache eficaz, espera-se que hits cresça mais rapidamente que misses
  • O método intersection, que calcula a parte sobreposta de duas áreas retangulares, não executa muito trabalho por si só, mas é chamado milhares de vezes e se beneficia do caching

A simplicidade trazida por objetos imutáveis

  • Python não tem objetos verdadeiramente imutáveis, mas é possível obter muitos benefícios com tuple, NamedTuple e dataclasses frozen
  • A restrição de não poder alterar um objeto pode parecer uma limitação arbitrária, mas no código real costuma ajudar em muitos casos
  • No Textual, o código que usa objetos imutáveis é mais fácil de raciocinar, além de favorecer caching e testes
  • O principal motivo é que fica mais fácil escrever código sem efeitos colaterais
    • Ao passar uma instância de classe para uma função, é mais difícil evitar efeitos colaterais da mesma forma

Arte Unicode e documentação

  • Quando é difícil explicar um assunto técnico apenas com palavras, diagramas feitos com caracteres Unicode de caixa ajudam muito na compreensão da documentação
  • As docstrings do Textual usam diagramas com caracteres de caixa para explicar um método que divide uma área em quatro subáreas
  • Esses diagramas não substituem uma docstring bem escrita, mas, usados em conjunto, podem aumentar a compreensão
  • A ferramenta monodraw é usada para criar os diagramas
    • O Monodraw é exclusivo para macOS
    • Pode haver alternativas em outras plataformas

Reduzindo erros de layout com fractions.Fraction

  • O módulo fractions da biblioteca padrão do Python existe desde o Python 2.6
  • Fraction é outra forma de representar números e, depois de criado, pode ser usado no lugar de float
  • Números de ponto flutuante têm limitações bem conhecidas
    • Por exemplo, 0.1 + 0.1 + 0.1 == 0.3 é False
  • No Textual, erros de arredondamento de ponto flutuante se tornaram um problema em layouts que dividem a tela em várias proporções
    • Em uma situação com um painel ocupando 1/3 da largura da tela e os 2/3 restantes sendo divididos novamente, podia surgir uma lacuna de um caractere
  • Trocar float por Fraction evita esse tipo de erro de arredondamento
    • A soma de três Fraction(1, 10) é igual a Fraction(3, 10)
  • Em um exemplo que divide 24 caracteres em 7 partes, a versão com float ficava com um caractere a menos, enquanto a versão com Fraction preenchia todo o comprimento

As dificuldades dos Emojis no terminal

  • Suporte a Emojis é um problema recorrente desde os primeiros dias do Rich, e o Textual herdou o mesmo problema
  • No terminal, caracteres normalmente ocupam uma ou duas células, e alguns caracteres têm largura zero
    • Caracteres CJK ocupam o dobro do espaço de alfabetos ocidentais
    • Para fazer formatações básicas, como centralização ou desenho de caixas, é preciso saber a largura que o texto impresso ocupará no terminal
    • Portanto, len(text) por si só não informa a largura ocupada no terminal
  • O banco de dados Unicode tem mapeamentos entre caracteres de largura simples e dupla
    • Rich e Textual consultam esse banco de dados para cada caractere exibido
    • Esse trabalho não é leve, mas pode ser rápido o suficiente com engenharia e caching
  • Mesmo quando Emojis estão incluídos no banco de dados Unicode, novos Emojis são adicionados a cada nova versão do Unicode, tornando o resultado difícil de prever
    • Ao imprimir um Emoji novo no terminal, ele pode aparecer com uma ou duas células de largura
    • Também pode não ser renderizado corretamente
  • Não há um método padrão para detectar de forma confiável a versão do Unicode usada por um emulador de terminal
    • Também não há variável de ambiente padrão
    • Existem heurísticas que imprimem uma sequência específica e consultam a posição do cursor, mas nos testes, mesmo quando se acreditava conhecer o banco de dados Unicode, o terminal renderizava Emojis de forma imprevisível
  • Emojis com múltiplos code points tornam o problema ainda mais complexo
    • Por exemplo, 👨🏻‍🦰 é composto por 4 code points
    • Um terminal pode renderizá-lo como 4 caracteres, 2 caracteres, 1 caractere, uma ou duas células, ou ainda como quatro ?
  • Na prática, usar Emojis do Unicode 9 é relativamente estável em várias plataformas
    • É melhor evitar Emojis novos e caracteres com múltiplos code points, mesmo que apareçam corretamente no terminal atual

Contratações na Textualize

  • A Textualize está contratando para criar um framework TUI que possa substituir parte do papel do navegador
  • As vagas podem ser vistas em Jobs

1 comentários

 
GN⁺ 2024-08-12
Opiniões do Hacker News
  • É engraçado como todo desenvolvedor de TUI acaba esbarrando em Unicode, e como lidar corretamente com caracteres internacionais e emojis vira um projeto separado, tão grande quanto ou maior que o projeto TUI original
    A mesma coisa aconteceu no rivo/tview, e o pacote rivo/uniseg criado como resultado mostrou que mantenedores de outras bibliotecas de TUI também enfrentam o mesmo problema
    No fim, como a largura dos caracteres não é padronizada e terminais são complexos, cada um inventa sua própria solução. O autor do texto só dá suporte ao Unicode 9, mas o Unicode atual é o 15.1, então é difícil ter certeza de que seja uma boa solução, já que em algum momento surgirão reclamações de que certos emojis ou caracteres internacionais são renderizados incorretamente

    • O melhor, hoje, é detectar o mode 2027 e ativá-lo quando houver suporte. Assim dá para saber que o terminal lida corretamente com grafemas
      Mantenho duas bibliotecas de TUI que usam essa técnica, e o suporte a emojis tem sido quase excelente. Uma delas também usa a biblioteca uniseg
      https://mitchellh.com/writing/grapheme-clusters-in-terminals
    • Fico me perguntando se já existe uma biblioteca que normalize todos os emojis para um único símbolo
  • Minha maior reclamação sobre o textual é que ele tenta ser como o React. Entendo o motivo, já que é um framework popular com o qual muita gente está familiarizada, mas não acho que seja uma boa forma de criar interfaces de usuário
    O design reativo em si é um caminho comprovado, e basear o desenho do sistema em uma abordagem que se sabe que funciona bem é uma boa maneira de reduzir o risco do projeto
    Mas usar também uma variação de CSS parece exagero. O modelo do React, ao favorecer componentes padronizados, já rompeu em vários aspectos com os conceitos do CSS, e desenvolvedores customizando componentes com CSS parece mais um efeito colateral da evolução do React do que uma arquitetura legítima
    Da última vez que usei, era preciso usar CSS, e não havia bons componentes padrão, então eu precisava criá-los. Em vez de um componente ser uma única classe Python bem encapsulada, a documentação oficial mostrava algo como criar um componente de lista e estilizá-lo com uma folha de estilos externa
    Em Python, deveria haver uma forma óbvia de fazer algo — de preferência apenas uma. Ao seguir o React de perto demais, parece que estão copiando também os piores maus hábitos da comunidade JavaScript

    • Não é obrigatório usar CSS. Na verdade isso já é assim há tempos: todos os estilos podem ser definidos em código, e a documentação traz, para cada estilo, o código equivalente em CSS e em Python
      Também parece fazer bastante tempo desde que você verificou a falta de bons componentes padrão: https://textual.textualize.io/widget_gallery/
    • Não sei bem de onde vem a impressão de que ele tenta ser o React. É claro que tenta usar alguns conceitos de HTML e CSS, e talvez de JavaScript, mas para mim isso permitiu avançar muito rápido, justamente por não precisar aprender um sistema de UI totalmente novo do zero
      Eu também não precisei criar componentes próprios, e já conhecia CSS. Na verdade, o que às vezes me confundia eram algumas diferenças de layout e pontos que fugiam do CSS
    • Eu gostaria de uma forma de embutir um terminal dentro de um programa gráfico. Não preciso dar suporte a várias emulações de terminal; uma só basta, com macOS como prioridade, e seria bom se funcionasse em outras plataformas também
      Imagine um componente, dentro de uma janela GUI comum, que seja uma janela de terminal. Fico me perguntando se isso existe ou se devo simplesmente usar uma visualização de texto com fonte monoespaçada
    • Seria bom explicar melhor em que sentido o textual tenta ser como o framework React. A palavra React não aparece em nenhum lugar do texto
    • Se React, e a programação funcional reativa em geral, não são uma boa forma de construir UIs, fico curioso sobre qual abordagem você considera boa
  • Esta TUI é bonita, mas é difícil imaginar uma situação em que eu realmente a usaria e até pagaria por ela. Talvez eu simplesmente não esteja no ambiente certo, mas, pela minha experiência, as pessoas ou ficam satisfeitas com ferramentas realmente minimalistas, ou partem direto para uma GUI para satisfazer os usuários
    Por exemplo, o link do YouTube no texto mostrava uma tabela que destacava uma célula, mas não sei por que isso precisaria ser feito em uma TUI. Se for preciso mover a célula ativa destacada, no fim você também vai precisar de muitos outros recursos, e provavelmente acabará precisando de uma GUI de verdade

    • Um motivo para preferir uma interface de texto é poder usá-la rapidamente em qualquer computador, em qualquer lugar. Não é necessário um VNC lento baseado em vídeo. Se você já usa o terminal, melhorar essa experiência também é bom, e há um mercado grande o suficiente para esta empresa mirar
      Dito isso, não sei muito bem o que seria uma “GUI de verdade”. Terminais têm padrões abertos amplamente usados, e esses protocolos são mais padronizados e interoperáveis do que qualquer framework de GUI, além de serem compatíveis com todos os sistemas
      Se elementos como vídeo forem acrescentados a isso e algumas lacunas forem preenchidas, parece razoável competir com o navegador ou levar toda a experiência de computação para o terminal
      Claro que, se o terminal adicionar vídeo, estará copiando o pipeline de renderização de uma “GUI de verdade”, mas isso melhora a experiência do terminal e, se tiver grande sucesso, também pode ser visto como trazer a simplicidade ao estilo UNIX para as GUIs
    • Por exemplo, há trabalhos como entrada de cobranças médicas, em que os funcionários conhecem todos os códigos de cobrança e ainda usam consoles de tela verde AS400
      Eles trabalham com atalhos de teclado e são extremamente eficientes; toda vez que alguém tenta substituir o AS400 por uma aplicação web moderna, a produtividade cai 100 vezes
      É a mesma situação da diferença entre usuários avançados de Vim/Emacs e uma GUI visualmente elegante, mas sem atalhos de teclado
    • No momento, isso definitivamente parece um nicho. Se você precisa de algo mais amigável do que uma linha de comando pura, mas por algum motivo é arriscado oferecer uma UI web, uma TUI pode fazer sentido
      Dá para fazer o usuário se conectar ao servidor via SSH e usar o app TUI como se fosse um shell, embora eu admita que é um cenário um tanto forçado
      Pessoalmente, achei o Textual um pouco estranho de usar, mas foi melhor que ncurses. Ainda assim, não me entregou o resultado que eu queria. Aplicações TUI antigas no estilo mainframe pareciam extremamente eficientes
    • Uma GUI de verdade também é boa, mas a história muda quando é preciso fazer a mesma coisa por meio de uma conexão SSH
      Às vezes funciona bem, mas é limitado, como quando se usa encaminhamento X em uma conexão de alta largura de banda. Em outros casos, a GUI fica completamente lenta
      Uma GUI baseada em texto parece poder reunir o melhor dos dois mundos
  • A técnica de “o primeiro truque é sobrescrever em vez de apagar” é a mesma forma como os jogos anteriores ao DirectX eram escritos
    Escrevia-se diretamente no frame buffer e, em vez de apagar tudo e redesenhar, redesenhavam-se as partes alteradas e as áreas ao redor/abaixo delas. Isso porque não havia tempo para atualizar a tela inteira a tempo

    • Na época havia pelo menos mais duas técnicas
      A primeira era escrever em outro buffer e, ao fim do frame, copiar o buffer inteiro de uma só vez, de modo que cada pixel mudasse apenas uma vez. Esse buffer normalmente podia estar na RAM comum, não na RAM de vídeo
      A segunda era escrever em outro buffer dentro da RAM de vídeo e alterar registradores do hardware gráfico para fazer com que os pixels exibidos no monitor fossem gerados a partir desse buffer
      Os trade-offs eram diferentes. Copiar o buffer inteiro era caro, enquanto alterar registradores de endereço era barato. Mas os detalhes dos registradores podiam depender do hardware, e não havia um framework de drivers gráficos de verdade
      Para fazer simplesmente “buffer flipping”, ao renderizar em um buffer fora da tela ainda era preciso enviar os pixels para a RAM de vídeo, e pelo que me lembro o acesso à RAM de vídeo era mais lento que à RAM comum. Na prática, era uma arquitetura NUMA; dependendo de quantas vezes os pixels eram redesenhados, renderizar na RAM comum podia ser mais rápido no geral, mesmo incluindo a cópia final
    • Aprendi essa abordagem programando em ComputerCraft, e ela foi útil várias vezes depois, às vezes até no trabalho
      Ainda assim, acho muito mais poderosa a capacidade de informar ao terminal o início e o fim de um frame
  • Eu realmente não entendo por que engenheiros de software têm tanto interesse em TUI. Gosto de bons programas de linha de comando, mas TUI não me atrai

    • TUI é, tirando a web, praticamente mais um toolkit de GUI multiplataforma instalado universalmente
      Além disso, tem uma estética hacker. O estilo não é fortemente separado do conteúdo, e as opções de estilo ajustáveis são limitadas, então os apps TUI tendem a convergir para um único estilo. Esse estilo lembra filmes de hackers e ficção científica legal
      Também não é uma área de que designers corporativos gostem, e empresas não fecham vendas para corporações ou consumidores com base em TUI. Sem essa pressão comercial, TUI é projetada por desenvolvedores para desenvolvedores
      Por isso o meme das TUIs se reforça sozinho. Desenvolvedores veem e usam TUIs, percebem que normalmente são ferramentas pensadas para desenvolvedores, e então querem criar suas próprias TUIs
    • É uma questão de gosto. Ainda assim, poder executar um app através de SSH é uma grande vantagem. Pessoas como eu gostam de experiências rápidas e centradas no teclado, algo que apps GUI até poderiam oferecer, mas geralmente não oferecem
    • Não há muitos benefícios óbvios em usar uma TUI, mas há grandes vantagens em escrever uma TUI. Em muitos aspectos, é mais fácil
      O suporte multiplataforma vem quase de graça, não é preciso criar uma GUI perfeita em nível de pixel — e, para começo de conversa, isso nem é possível —, e não há necessidade de ícones ou gráficos, porque não dá para exibir gráficos
    • É bom quando você está executando algo dentro do terminal e precisa de mais interface de usuário do que a linha de comando oferece. Às vezes uma TUI é mais rápida que a linha de comando e mostra melhor o contexto geral
    • Uso bastante CLI, mas algumas tarefas combinam melhor com uma GUI, e TUI é um meio-termo entre GUI e CLI
      Você pode continuar usando o emulador de terminal com a fonte que prefere e ainda ter uma interface parecida com GUI
      Todos os apps TUI que usei até agora tinham baixa latência. Apps GUI em geral também são rápidos, mas todos os apps que foram irritantemente lentos eram apps GUI
  • Uma alternativa ao Monodraw é o ascii-draw
    https://github.com/Nokse22/ascii-draw

  • Quase tudo que vi depois do TurboVision era inspirado nele, mas, na prática, faltava acabamento. Quando se usa o toolkit para valer, ele até parece simpático, mas dá a sensação de inacabado
    Dito de outra forma, talvez muitos frameworks de TUI que vi fossem frameworks “opinativos”
    Talvez eu seja obtuso e pouco criativo, mas desde que migrei do DOS para o Linux nos anos 90, nunca fui tão produtivo quanto quando trabalhava com B800
    É possível que eu tenha demorado para entender a necessidade de double buffering, a diferença entre modo texto local direto e terminal, e até as sequências de escape. Mesmo assim, toda vez que tentava fazer algo diretamente com ncurses, a sensação clara de insatisfação quase me fazia desistir. Era completamente diferente do que dava para fazer com a simplicidade ideal do B800

    • Aprendi programação GUI com Win16 e depois Win32, na época em que a transição para o WinXP estava acontecendo
      Não sei se minha visão estava bloqueada como uma cidade inteira, mas todos os frameworks GUI posteriores ao message pump eram simplesmente difíceis de entender
      Em especial, eu não entendia como os frameworks orientados a objetos realmente resolviam o problema de UI multithread. No Win32, eu colocava o renderizador principal em uma thread, os renderizadores auxiliares montavam o modelo ao lado e depois só atualizavam as diferenças. O código não crescia a ponto de ficar impossível de lidar
    • Sei o que é Turbo Vision, mas fiquei curioso para saber o que é B800. Imagino que não seja o processador Celeron; é difícil até de pesquisar e não parece haver uma página na wiki
  • Se você vai rodar kitty, dá para fazer muito mais do que isso: https://m.youtube.com/watch?v=ft1Q-DwGWIs
    https://notcurses.com/

    • Prefiro WezTerm ao Kitty, por causa da postura do autor do Kitty em relação a pedidos de recursos e pull requests
      Ambos podem exibir gráficos usando o mesmo protocolo. Se você realmente precisa de gráficos, o terminal quase nunca é a solução correta. Ainda assim, às vezes é útil para coisas pequenas, como ícones
  • É interessante que estejam contratando. Fico curioso sobre como pretendem ganhar dinheiro com um framework de TUI

    • Como eles também mencionaram rodar na web no fim, imagino que queiram ajudar a migrar TUIs antigas. Há bastante dinheiro em consultoria na manutenção de software corporativo legado
    • Essas vagas foram preenchidas e depois eliminadas: https://blog.davep.org/2024/03/28/goodbye-textualize.html#go...
    • O link de contratação dá erro 404
    • Tenho a mesma dúvida. Parece que fazem um software bem legal, mas engenheiros são caros e já existem outros frameworks
  • Para referência, avaliei cerca de 12 bibliotecas TUI compiláveis, e FTXUI foi a mais fácil de usar e a mais estável
    https://github.com/ArthurSonzogni/FTXUI
    É uma boa ferramenta para criar dashboards interativos com suporte tanto a teclado quanto a mouse