Steven
Steven9 min de leitura

Redesenhar o dashboard em torno de um gráfico de onda

A GeekBye v1.8.2 transformou o dashboard de um controle remoto de gravação em uma visão analítica somente leitura — uma barra de estatísticas e um gráfico de onda de 7 dias. Ambos os elementos característicos foram segundos rascunhos, lançados em até vinte minutos após seus primeiros rascunhos, na mesma tarde.

Engenharia
Design
Dashboard
Lançamentos da GeekBye
Redesenhar o dashboard em torno de um gráfico de onda

Alguns lançamentos adicionam uma feature. A GeekBye v1.8.2 sobretudo removeu uma — e a remoção é a história. Em uma única tarde de fevereiro de 2026, dezoito commits transformaram a tela inicial do dashboard, de um controle remoto de gravação em uma visão analítica somente leitura: quatro estatísticas e um gráfico de atividade. No caminho, ela lançou um gráfico de barras que viveu vinte e dois minutos, uma grade de cards de estatísticas que viveu vinte e uma onda SVG feita à mão que substituiu ambos. Toda a busca está preservada nos timestamps dos commits, que é o melhor tipo de documentação de design que existe.

O dashboard deixou de ser um controle remoto

Antes deste lançamento, a tela inicial do dashboard era um painel de controle. Ela abria com um card hero — um grande widget de gravação com um selo LIVE, botões de Pausar/Retomar/Parar, manchas de gradiente e um hook useSessionState ligando tudo ao gravador. O dashboard tentava ser duas coisas ao mesmo tempo: o lugar onde você olha suas reuniões e o lugar onde você controla o gravador. Dois produtos, uma tela, competindo em silêncio.

A v1.8.2 escolheu um. Apagou o card hero e a fiação do useSessionState — incluindo um efeito que te levava automaticamente para a página de detalhe de uma reunião no instante em que uma sessão terminava, que é exatamente o tipo de salto «prestativo» que surpreende as pessoas no meio do fluxo. Iniciar e ocultar o gravador foram movidos para um simples botão da navbar (um botão reaproveitado que perdeu cerca de cem linhas ao passar de controlador de sessão para interruptor de liga/desliga), apoiado por um novo handler IPC navbar:hide. O que sobrou foi um dashboard que faz uma coisa só: mostrar o que você gravou. Somente leitura. Calmo.

Há um pequeno bug, bem humano, nessa transição que vale a pena apontar, porque é o tipo de coisa que um redesign introduz sem alarde e que um bom olho pega na mesma hora. Os dois novos botões da barra lateral — Iniciar e Ocultar — saíram com o mesmo ícone genérico de grade, então eram visualmente idênticos. Doze minutos depois, um follow-up deu ao Iniciar um ícone de play, deu ao Ocultar um ícone de olho cortado e tingiu o Ocultar de âmbar, para que «fazer o app sumir» se leia como uma ação deliberada em vez do gêmeo neutro de «iniciar». Ícones são texto. Dois botões que parecem iguais dizem a mesma coisa, e estes não significavam a mesma coisa.

O gráfico de barras que viveu vinte e dois minutos

Aqui está a parte que eu amo, e ela é totalmente legível no git log. Às 16:29:34 o primeiro rascunho do redesign aterrissou: um gráfico de atividade de 7 dias desenhado como barras — colunas animadas, uma altura mínima para que os dias minúsculos continuassem visíveis, um traço apagado de 2 pixels para os dias com zero. Razoável. Convencional. O tipo de gráfico ao qual você recorre sem pensar.

Às 16:51:21 — vinte e dois minutos depois — ele foi apagado. O commit de substituição diz exatamente o que fez: «replace bar chart with smooth wave area chart for 7-day activity» (substituir o gráfico de barras por um gráfico de área de onda suave para a atividade de 7 dias). E às 16:52:21, um minuto depois, outro commit tirou o card arredondado e a borda em volta da onda, «for cleaner look» (para um visual mais limpo).

Por quê? Os commits só dizem o que mudou, então o porquê é uma leitura — mas a forma dos dados torna isso óbvio. Esta é uma ferramenta em que um usuário típico tem zero, uma ou duas reuniões em qualquer dia. Sete barras, a maioria de 2 pixels de altura, parecem um estacionamento quase vazio. Os mesmos sete números desenhados como uma área preenchida contínua parecem uma tendência — uma onda suave que sobe onde a semana foi movimentada. Nada foi falseado; os valores em y são idênticos. O redesign apenas escolheu a codificação que permite que dados esparsos e honestos se leiam como uma forma em vez de como ausência.

As estatísticas receberam o mesmo tratamento no mesmo relógio. O primeiro rascunho (16:29) pôs quatro números numa grade 2×4 de cards. Às 16:49 a grade colapsou numa única barra de estatísticas em linha — uma única linha com borda e divisores finíssimos, tipografia menor, um contêiner em vez de quatro. Cinco caixas na tela (quatro cards mais um card de gráfico) viraram duas (uma barra e uma onda). O PR seguinte adicionou mais um toque honesto: o rótulo «Total Time» virou «Time in Meetings» (Tempo em reuniões), porque total-de-quê era uma pergunta justa que ninguém deveria ter que fazer.

Vinte minutos, do primeiro rascunho ao final, tudo dentro de um único pull request. Se aquilo tivesse sido esmagado num único commit «redesign dashboard», a busca seria invisível. Como não foi, você pode ver um designer convergir em tempo real.

A onda, mecanicamente

Não há nenhuma biblioteca de gráficos aqui. A onda é um SVG em linha construído em JSX, e vale uma olhada porque é uma receita pequena e honesta que você pode roubar para qualquer sparkline.

A suavização é o truque. Para cada par de pontos adjacentes, o path usa um segmento cubic-bezier cujos dois pontos de controle ficam no ponto médio horizontal entre eles — cpx = (prev.x + curr.x) / 2 — com os pontos de controle mantidos na altura própria de cada ponta. Essa é a clássica suavização de tangente plana: a curva passa suavemente por cada ponto de dados sem nunca ultrapassar acima do valor mais alto nem abaixo do mais baixo, o que uma spline Catmull-Rom ingênua faria. Para um gráfico sobre contagem de reuniões, «nunca desenhar uma saliência que implique um valor que não aconteceu» não é um mimo; é correção. A área preenchida é a mesma curva fechada por baixo e pintada com um gradiente vertical (ciano, esmaecendo de 30% para quase nada), e a linha se desenha sozinha com uma animação pathLength do framer-motion, da esquerda para a direita.

Embaixo do SVG está o encanamento de dados, e ele é o herói silencioso. Um novo método getAggregateStats() no repositório de transcrições roda três prepared statements contra a tabela local do SQLite transcript_sessions: totais de todos os tempos (contagem, duração somada, duração média), uma contagem móvel de sete dias e um bucket por dia com GROUP BY date(created_at). Então — e este é o detalhe que torna o gráfico robusto — um pequeno loop de JavaScript percorre os últimos sete dias do calendário e preenche cada dia que a query não retornou, empurrando um zero. O gráfico, portanto, tem garantidos exatamente sete pontos, sempre.

Essa garantia é o que faz os casos limite sumirem em vez de precisar de verificações espalhadas pela view. Uma semana vazia? Sete zeros, e um Math.max(count, 1) no denominador mantém a linha plana embaixo em vez de dividir por zero. Um único ponto de dados que faria a fórmula de espaçamento em x i / (n − 1) produzir NaN? Não pode acontecer — o repositório nunca emite menos de sete. Os rótulos dos dias da semana até parseiam cada bucket em T12:00:00 em vez da meia-noite crua, para que uma string YYYY-MM-DD não escorregue para o dia anterior num fuso horário de UTC negativo. Conserte a forma dos dados na origem e o código de renderização pode se dar ao luxo de ser ingênuo de propósito.

Tudo fica na máquina. A query de estatísticas lê o banco de dados local, retorna por um canal IPC tipado transcript:get-stats e anima em quatro números que contam para cima ao montar. Nada do seu histórico de reuniões sai do laptop para desenhar este gráfico — o que, para uma ferramenta que grava suas conversas, é a única maneira aceitável de construir uma visão analítica.

Mais um bug, porque redesigns os arrastam para a luz

Empacotado no mesmo lançamento ia uma correção que não tinha nada a ver com o dashboard e tudo a ver com o tipo de dívida em que um redesign tropeça. O estilo da scrollbar escura do app tinha derivado em duas definições CSS concorrentes da mesma classe — uma mais antiga, roxa, e uma mais nova, branca sobre escuro — e qual vencia dependia da ordem da cascata. Pior, só os elementos que lembravam de adicionar a classe recebiam algum estilo; todo o resto caía de volta na scrollbar clara padrão, e scrollbars horizontais nunca foram estilizadas porque as regras antigas definiam só uma largura, nunca uma altura. A correção apagou as duas definições, estilizou a scrollbar globalmente em todos os elementos e removeu a classe agora morta dos dez componentes que a vinham carregando. É um diff de duas linhas repetido dez vezes — o imposto sem glamour de ter estilizado a mesma coisa duas vezes e deixado as duas sobreviverem.

Três coisas que este lançamento nos ensinou

  1. Subtraia para focar. O dashboard melhorou fazendo menos — perdendo os controles de gravação de que nunca precisou e virando uma coisa clara. Uma tela que é ao mesmo tempo um visualizador e um controle remoto são dois meio-produtos; escolha um e mova o outro para onde ele pertence.
  2. Escolha a codificação que seus dados merecem. Contagens esparsas se leem como ausência em barras e como tendência num gráfico de área, a partir de números idênticos. Isso não é maquiagem — é escolher a forma honesta que deixa dados reais e escassos comunicarem.
  3. Garanta a forma dos dados lá em cima. O loop de preenchimento-até-sete é a razão de o código do gráfico não ter verificações de null espalhadas: divisão por zero, NaN de ponto único e dias faltantes desaparecem todos porque a query promete exatamente sete buckets. Renderização robusta começa uma camada abaixo.

Para o capítulo anterior da história v1, quatro lançamentos em vinte e seis horas (v1.7.6–v1.8.1); e para todo o arco, a anatomia de entregar software até a perfeição.