Repository navigation
Description
Contexto
Fatia 3 do epic #24 (Practice/Solo mode §V.5). Depois do Treino Livre (#25) e do modo contra bots (fatia 2), o próximo degrau de retenção é competir contra o próprio recorde — sem precisar de login/servidor (produto é sem cadastro), um "ghost" replica o melhor run já feito naquela combinação linguagem+dificuldade.
Escopo
- Ao terminar um treino em
PracticeView.tsxcom um resultado melhor que o PB salvo (ou sem PB ainda) para a combinaçãolanguage+difficulty, gravar um replay leve emlocalStorage: sequência de amostras{ tMs, progress }do run (não precisa ser frame-a-frame — poucas amostras por segundo bastam para reconstituir uma curva suave). - Nos treinos seguintes na mesma combinação, se existir PB salvo, reproduzir o ghost como uma segunda barra/trilha de progresso avançando conforme o replay gravado, em paralelo ao jogador (mesmo
startedAt). - Tela de conclusão mostra jogador vs ghost (bateu o PB? por quanto?) e, se bateu, substitui o PB salvo.
- Chave de storage isolada por
language+difficulty(ex.:coderacer:pb:<language>:<difficulty>); sem PII, só métricas + curva de progresso.
Acceptance criteria (verificáveis)
- Primeiro treino numa combinação nova → grava PB automaticamente (sem UI extra, best-effort).
- Segundo treino na mesma combinação → ghost do PB visível e sincronizado ao
startedAtdo jogador. - Run que bate o PB → localStorage atualizado com o novo replay; run que não bate → PB antigo preservado.
- Tela de conclusão comunica claramente o resultado (bateu/não bateu o PB, diferença de WPM/tempo).
- Falha de
localStorage(indisponível/quota) é best-effort silencioso — nunca quebra o treino (mesmo padrão defensivo delocalStorage.getItemjá usado emRoomView.tsx/NameForm). - Zero chamada a
/api/rooms/Supabase — dado fica só no navegador do jogador. -
pnpm typecheck+pnpm buildverdes.
Dica de abordagem
Reuse o formato de progresso que a fatia 2 (bot) já vai estabelecer para a "segunda trilha" visual, se a fatia 2 for puxada antes — dá pra compartilhar o componente de trilha fantasma entre bot e ghost (progresso ao longo do tempo é a mesma forma de dado). Funções de amostragem/reconstrução do replay devem ser puras e testáveis (Vitest), como os builders de src/lib/room.ts.
Refs: spec docs/UI-AAA-OVERHAUL.md §V.5 (modo 2, "contra seu ghost (PB)"); epic #24; Treino Livre atual em src/components/PracticeView.tsx.
Activity