
Redissenyar el dashboard al voltant d'una sola gràfica d'ona
GeekBye v1.8.2 va convertir el dashboard d'un comandament a distància per gravar en una vista d'analítica de només lectura — una tira d'estadístiques i una gràfica d'ona de 7 dies. Tots dos elements distintius van ser segones versions, publicades dins dels vint minuts posteriors a les seves primeres versions, la mateixa tarda.
Algunes releases afegeixen una funcionalitat. La GeekBye v1.8.2 en va eliminar una sobretot — i l'eliminació és la història. En una sola tarda del febrer de 2026, divuit commits van convertir la home del dashboard d'un comandament a distància per gravar en una vista d'analítica de només lectura: quatre estadístiques i una gràfica d'activitat. Pel camí va publicar una gràfica de barres que va viure vint-i-dos minuts, una graella de targetes d'estadístiques que en va viure vint, i una ona SVG feta a mà que va substituir totes dues. Tota la cerca queda preservada a les marques de temps dels commits, que és la millor mena de documentació de disseny que hi ha.
El dashboard va deixar de ser un comandament a distància
Abans d'aquesta release, la home del dashboard era un tauler de control. Encapçalava amb una hero card — un gran widget de gravació amb una insígnia LIVE, botons Pause/Resume/Stop, taques de degradat, i un hook useSessionState que ho connectava tot al gravador. El dashboard intentava ser dues coses alhora: el lloc on mires les teves reunions i el lloc on controles el gravador. Dos productes, una pantalla, competint en silenci.
La v1.8.2 en va triar una. Va eliminar la hero card i la connexió de useSessionState — inclòs un efecte que et navegava automàticament a la pàgina de detall d'una reunió en el moment que acabava una sessió, que és exactament la mena de salt "útil" que sorprèn la gent enmig del flux. Iniciar i amagar el gravador va sortir cap a un simple toggle de la navbar (un botó reaprofitat que va perdre uns cent línies passant de controlador de sessió a interruptor d'engegada/aturada), sostingut per un nou handler IPC navbar:hide. El que va quedar va ser un dashboard que fa una sola cosa: mostrar-te què has gravat. Només lectura. Tranquil.
Hi ha un petit bug, humà, en aquesta transició que val la pena assenyalar, perquè és la mena de cosa que un redisseny introdueix en silenci i un bon ull detecta la mateixa hora. Els dos botons nous de la barra lateral — Start i Hide — es van publicar amb la mateixa icona genèrica de graella, així que eren visualment idèntics. Dotze minuts després, una correcció posterior va donar a Start una icona de play, va donar a Hide una icona d'ull tancat, i va tenyir Hide d'ambre, així que "fer desaparèixer l'app" es llegeix com una acció deliberada i no com un bessó neutre de "start." Les icones són text. Dos botons que semblen iguals diuen el mateix, i aquests no volien dir el mateix.
La gràfica de barres que va viure vint-i-dos minuts
Aquesta és la part que m'encanta, i és completament llegible des del git log. A les 16:29:34 va aterrar la primera versió del redisseny: una gràfica d'activitat de 7 dies dibuixada com a barres — columnes animades, una alçada mínima perquè els dies petits seguissin sent visibles, un bocí atenuat de 2 píxels per als dies amb zero. Raonable. Convencional. La mena de gràfica que fas servir sense pensar-hi.
A les 16:51:21 — vint-i-dos minuts després — es va eliminar. El commit de reemplaçament diu exactament què va fer: "substituir la gràfica de barres per una gràfica d'ona d'àrea suau per a l'activitat de 7 dies." I a les 16:52:21, un minut després, un altre commit va treure la targeta arrodonida i la vora del voltant de l'ona, "per a un aspecte més net."
Per què? Els commits només diuen què va canviar, així que el perquè és una interpretació — però la forma de les dades ho fa evident. Aquesta és una eina on un usuari típic té zero, una o dues reunions en un dia qualsevol. Set barres, la majoria de 2 píxels d'alçada, semblen un espai gairebé buit. Els mateixos set números dibuixats com una àrea plena contínua semblen una tendència — una ona suau que puja on la setmana va ser atrafegada. No es va falsejar res; els valors de l'eix y són idèntics. El redisseny simplement va triar la codificació que permet que dades escasses i honestes es llegeixin com una forma en lloc de com una absència.
Les estadístiques van rebre el mateix tractament al mateix rellotge. La primera versió (16:29) va posar quatre números en una graella 2×4 de targetes. A les 16:49 la graella es va col·lapsar en una sola tira d'estadístiques en línia — una única fila amb vora, amb separadors finíssims, tipografia més petita, un contenidor en lloc de quatre. Cinc caixes a la pantalla (quatre targetes més una targeta de gràfica) van passar a ser dues (una tira i una ona). El PR següent va afegir un toc honest més: l'etiqueta "Temps total" va passar a ser "Temps en reunions", perquè total-de-què era una pregunta justa que ningú hauria d'haver de fer.
Vint minuts, de la primera versió a la final, tot dins d'un sol pull request. Si això s'hagués aixafat en un únic commit "redisseny del dashboard", la cerca seria invisible. Com que no ho van fer, pots veure un dissenyador convergir en temps real.
L'ona, mecànicament
Aquí no hi ha cap library de gràfiques. L'ona és un SVG en línia construït en JSX, i val la pena mirar-la perquè és una recepta petita i honesta que pots robar per a qualsevol sparkline.
El suavitzat és el truc. Per a cada parell de punts adjacents, el camí fa servir un segment cubic-bezier amb dos punts de control situats al punt mig horitzontal entre ells — cpx = (prev.x + curr.x) / 2 — amb els punts de control mantinguts a l'alçada pròpia de cada extrem. Aquest és el clàssic suavitzat de tangent plana: la corba passa suaument per cada punt de dades sense mai sobrepassar per sobre del valor més alt ni per sota del més baix, cosa que una spline Catmull-Rom ingènua sí que faria. Per a una gràfica sobre recomptes de reunions, "no dibuixis mai un bony que impliqui un valor que no va passar" no és un caprici; és correcció. L'àrea plena és la mateixa corba tancada per la part de baix i pintada amb un degradat vertical (cian, esvaint-se del 30% fins a gairebé res), i la línia es dibuixa sola amb una animació pathLength de framer-motion, d'esquerra a dreta.
Sota l'SVG hi ha les canonades de dades, i són l'heroi silenciós. Un nou mètode getAggregateStats() al repositori de transcripts executa tres prepared statements contra la taula SQLite local transcript_sessions: totals de sempre (recompte, durada sumada, durada mitjana), un recompte mòbil de set dies, i un cubell per dia amb GROUP BY date(created_at). Llavors — i aquest és el detall que fa robusta la gràfica — un petit bucle de JavaScript recorre els últims set dies del calendari i reomple cada dia que la consulta no va retornar, afegint-hi un zero. La gràfica té, per tant, garantits exactament set punts, sempre.
Aquesta garantia és el que fa desaparèixer els casos límit en lloc de necessitar guardes escampades per la vista. Una setmana buida? Set zeros, i un Math.max(count, 1) al denominador manté la línia plana a baix en lloc de dividir per zero. Un únic punt de dades que faria que la fórmula d'espaiat x i / (n − 1) produís NaN? No pot passar — el repositori mai no n'emet menys de set. Les etiquetes de dia de la setmana fins i tot analitzen cada cubell a T12:00:00 en lloc de mitjanit pelada, així que una cadena YYYY-MM-DD no rellisca al dia anterior en una zona horària UTC negativa. Arregla la forma de les dades a l'origen i el codi de renderitzat es pot permetre ser ingenu a propòsit.
Tot plegat es queda a la màquina. La consulta d'estadístiques llegeix la base de dades local, retorna a través d'un canal IPC tipat transcript:get-stats, i s'anima en quatre números que compten cap amunt en muntar-se. Res del teu historial de reunions surt del portàtil per dibuixar aquesta gràfica — cosa que, per a una eina que grava les teves converses, és l'única manera acceptable de construir una vista d'analítica.
Un bug més, perquè els redissenys els arrosseguen a la llum
Empaquetada dins de la mateixa release hi havia una correcció que no tenia res a veure amb el dashboard i tot a veure amb la mena de deute amb què ensopega un redisseny. L'estil de la barra de desplaçament fosca de l'app havia derivat cap a dues definicions CSS en competència de la mateixa classe — una de morada més antiga i una de blanc-sobre-fosc més nova — i quina guanyava depenia de l'ordre de la cascada. Pitjor encara, només els elements que recordaven afegir la classe rebien cap estil; tota la resta requeia a la barra de desplaçament clara per defecte, i les barres de desplaçament horitzontals no rebien mai estil perquè les regles velles només fixaven una amplada, mai una alçada. La correcció va eliminar totes dues definicions, va donar estil a la barra de desplaçament globalment a cada element, i va treure la classe ara morta dels deu components que l'havien portat. És un diff de dues línies repetit deu vegades — l'impost poc glamurós d'haver donat estil dues vegades a la mateixa cosa i deixar sobreviure totes dues.
Tres coses que aquesta release ens va ensenyar
- Resta per enfocar. El dashboard va millorar fent menys — perdent els controls de gravació que mai no havia necessitat i esdevenint una sola cosa clara. Una pantalla que és alhora un visor i un comandament són dos mitjos productes; tria'n un i mou l'altre allà on li toca.
- Tria la codificació que les teves dades mereixen. Els recomptes escassos es llegeixen com a absència en barres i com a tendència en una gràfica d'àrea, a partir de números idèntics. Això no és propaganda — és triar la forma honesta que permet comunicar-se a dades reals i escasses.
- Garanteix la forma de les dades upstream. El bucle de reompliment-fins-a-set és el motiu que el codi de la gràfica no tingui comprovacions de null escampades: la divisió-per-zero, el NaN d'un sol punt i els dies que falten desapareixen tots perquè la consulta promet exactament set cubells. El renderitzat robust comença una capa més avall.
Per al capítol anterior de la història de la v1, quatre releases en vint-i-sis hores (v1.7.6–v1.8.1); i per a tot l'arc, l'anatomia de publicar programari fins a la perfecció.