Pular para o conteúdo
Projetos
No ar2026

Cyber Typing

Teste de digitação com WPM, precisão, consistência e progressão infinita de níveis.

Função
Frontend Engineer
Status
No ar
Cyber Typing
// Visão geral

O Cyber Typing é um teste de velocidade de digitação que faz parte do ecossistema pablobanker.dev — ele reusa os design tokens do portfólio (o mesmo fundo escuro, o accent cyan e a tipografia Geist). Em três telas (home, teste, resultados), mede WPM líquido e bruto, precisão e consistência, acompanha sua evolução e sobe de nível conforme você joga. O princípio central é que nenhuma lógica de negócio vive dentro de componentes Svelte. Uma classe TypingEngine pura e independente de framework recebe as teclas e expõe snapshots imutáveis; métricas, progressão de nível e patente por WPM são módulos puros; e uma camada de services orquestra tudo sem tocar na UI. A progressão é infinita: cada nível escala quantidade de palavras, tamanho das palavras, maiúsculas, pontuação e números, e você só avança batendo a meta de 90% de precisão. A patente é derivada da média de WPM líquido dos 10 testes mais recentes. Não há backend: histórico, preferências e o nível atual ficam no LocalStorage com chaves versionadas, e cada registro é revalidado no carregamento com type guards escritos à mão. A captura de digitação é segura para acentos e IME (dead keys e eventos de composição são tratados explicitamente), colar é bloqueado para as métricas não serem burladas, e tudo é coberto por testes unitários com Vitest e um fluxo end-to-end com Playwright rodando contra o build de produção.

Principais recursos

  • Feedback ao vivo por caractere: correto, incorreto, extra e não digitado.
  • WPM líquido e bruto, precisão e consistência — fórmulas puras e protegidas.
  • Níveis infinitos escalando quantidade de palavras, tamanho, maiúsculas, pontuação e números.
  • Só avança batendo a meta de 90% de precisão; senão, repete o nível.
  • Patente por WPM derivada da média dos 10 testes mais recentes.
  • Listas de palavras em português e inglês com geração determinística por seed.
  • Histórico, preferências e nível persistidos em chaves versionadas no LocalStorage, validados no load.
  • Captura segura para acentos/IME (dead keys, eventos de composição); colar bloqueado.
  • Atalhos de teclado e View Transitions entre as telas.
  • Animações respeitam prefers-reduced-motion.
// Arquitetura

Arquitetura

Nenhuma lógica de negócio dentro de componentes Svelte: uma classe TypingEngine pura guarda o estado do teste e expõe snapshots imutáveis, módulos puros calculam métricas, progressão e patente, e uma camada de services orquestra resultados, estatísticas e persistência — os componentes só renderizam.

  • Fluxo: a home entrega o nível → a tela de teste espelha snapshots do engine em estado com runes → os resultados montam um TestResult → o histórico persiste → a home volta populada.
  • O TypingEngine é independente de framework e 100% testável em Node, sem DOM.
  • Os caracteres são avaliados por code point, então palavras acentuadas funcionam corretamente.
  • As métricas (wpm, precisão, consistência via coeficiente de variação) são módulos puros protegidos contra divisão por zero.
  • Progressão infinita: levelConfig(n) escala a dificuldade suavemente em quatro eixos.
  • Persistência SSR-safe: todo acesso ao LocalStorage passa por guarda de browser e a hidratação é lazy, só no cliente.
  • Dados persistidos nunca são confiados — type guards validam cada registro e descartam os inválidos individualmente.
  • Vitest cobre engine, métricas e services; Playwright roda o fluxo real de três telas contra o build de produção.
// Desafios e soluções

Desafios e soluções

Acentos e IME sem perder teclas

Problema

Dead keys (ABNT2) e composição de IME disparam eventos em ordens diferentes entre navegadores, o que pode perder ou contar acentos em dobro.

Solução

A digitação é lida do buffer de um input transparente sobreposto: durante uma composição o buffer não é limpo e o commit acontece no compositionend, com normalização NFC e iteração por code point.

Manter a lógica de digitação testável

Problema

Lógica de digitação misturada aos componentes é difícil de testar e fácil de quebrar.

Solução

O engine é uma classe pura que não conhece Svelte: recebe press/space/backspace, guarda o estado interno e expõe um snapshot imutável que a UI espelha — então o núcleo roda e é testado inteiramente em Node.

Persistência sem backend

Problema

Todo o histórico vive no LocalStorage, onde os dados podem estar velhos, malformados ou de uma versão antiga do app.

Solução

Chaves versionadas e type guards escritos à mão que revalidam cada registro no load: entradas inválidas são descartadas individualmente, resultados antigos são complementados em vez de jogados fora, e o histórico é limitado aos 100 mais recentes.

// O que aprendi
  • Como construir UI reativa com runes do Svelte 5 em cima de um núcleo independente de framework.
  • Como desenhar fórmulas de métricas (WPM líquido/bruto, precisão, consistência via CV) como módulos puros e protegidos.
  • Como tratar dead keys, composição de IME e code points Unicode na captura de digitação.
  • Como persistir com segurança no LocalStorage com chaves versionadas e validação no carregamento.
  • Como cobrir um app inteiro com testes unitários e um fluxo end-to-end contra o build de produção.
// Tecnologias
SvelteKitSvelte 5TypeScriptGSAPVitestPlaywrightLocalStorageView Transitions