Chatui
Hoje vou falar sobre o Chatui, um projeto que desenvolvi esses dias para praticar um pouco de Golang e tem sido um aprendizado muito interessante. O Chatui é um chat utilizando websockets com uma TUI (Terminal User Interface) para a interface do usuário.
O que é o Chatui?
Eu queria tirar a ferrugem de Golang e, com a popularidade das TUIs, achei interessante desenvolver um chat em modo texto. Com isso, revisei goroutines, canais e pratiquei comunicação em tempo real via websockets, além de aprender melhor a biblioteca Bubble Tea, bem popular para TUI em Go.
Estrutura do Projeto
chatui/
├── cmd/
│ ├── server/
│ │ └── main.go
│ └── client/
│ └── main.go
├── internal/
│ ├── server/
│ │ └── server.go
│ └── client/
│ ├── client.go
│ ├── cmd.go
│ ├── model.go
│ ├── update.go
│ └── view.go
├── protocol/
│ └── message.gocmd: entradas do servidor e do cliente.internal/server: lógica do servidor (hub, registro, desconexão, broadcast).internal/client: TUI com Bubble Tea (rede, update, view separados).protocol: definição das mensagens trocadas.
Envelope das mensagens:
const (
TypeChatMessage MessageType = "chat_message"
TypeLoginResponse MessageType = "login_response"
TypeUserListUpdate MessageType = "user_list_update"
TypeLoginRequest MessageType = "login_request"
)
type Envelope struct {
Type MessageType `json:"type"`
Data json.RawMessage `json:"data"`
}Como funciona
O servidor mantém um Hub com canais para registrar/desconectar clientes e encaminhar mensagens (broadcast ou destino específico). O cliente TUI:
- Abre tela de login para digitar usuário.
- Envia login e espera resposta.
- Se sucesso, mostra a tela principal com lista de chats e mensagens.
- Envia mensagens e recebe em tempo real.
- Alterna chats; ao focar um chat, vê o histórico e zera notificações não lidas.
Perrengues de TUI (o que parece simples não é)
- Fundo sólido sem quebrar cursor: tirar bordas padrão, preencher fundos de textarea e mensagens; evitar sequências ANSI extras que cancelam o blink do cursor.
- Blink e foco visíveis: blink mais rápido, destaque da seleção no sidebar piscando quando focado, e hint de atalho no título ("(Tab: focus)").
- Largura e preenchimento: sidebar ocupa toda a largura prevista, textos centralizados, e textarea preenche todo o fundo.
- Separação de responsabilidades: o monolito virou
model.go(estado/tipos/init),view.go(render/layout),update.go(mensagens/teclas),cmd.go(efeitos/IO/tempo). Ficou bem mais fácil de ajustar estilo e lógica sem quebrar o resto.
Funcionalidades atuais
- Login com checagem de nome único e válido.
- Lista de chats (ALL + privados).
- Contador de não lidas por chat; ao focar, zera o contador.
- Envio e recepção em tempo real.
- Atalhos: Tab alterna foco (sidebar/chat), Enter envia,
/quitsai.
No vídeo abaixo, é possível ver o chat em ação, com três clientes conectados e trocando mensagens, apresentando todas as funcionalidades descritas acima:
Possíveis incrementos
- Persistir histórico em disco.
- Mostrar timestamps e indicator de “digitando…”.
- Suporte a emojis.
- Temas claro/escuro.
- Scrollback com busca.
Conclusão
O Chatui é simples, mas rendeu bastante aprendizado: websockets em Go, TUIs com Bubble Tea, e várias sutilezas de UI em terminal (um ANSI fora do lugar e o cursor para de piscar). O código está no github; contribuições são bem-vindas.