
Repenser le dashboard autour d'un seul graphique en vague
GeekBye v1.8.2 a transformé le dashboard d'une télécommande d'enregistrement en une vue d'analytics en lecture seule — une bande de statistiques et un graphique en vague sur 7 jours. Les deux éléments signature étaient des seconds jets, livrés dans les vingt minutes suivant leurs premiers jets, le même après-midi.
Certaines releases ajoutent une feature. GeekBye v1.8.2 en a surtout supprimé une — et c'est la suppression qui fait l'histoire. En un seul après-midi de février 2026, dix-huit commits ont transformé la page d'accueil du dashboard, d'une télécommande d'enregistrement en une vue d'analytics en lecture seule : quatre statistiques et un graphique d'activité. Au passage, elle a livré un graphique en barres qui a vécu vingt-deux minutes, une grille de cartes de statistiques qui a vécu vingt, et une vague SVG faite main qui a remplacé les deux. Toute la recherche est préservée dans les horodatages des commits, ce qui est la meilleure documentation de design qui soit.
Le dashboard a cessé d'être une télécommande
Avant cette release, la page d'accueil du dashboard était un panneau de contrôle. Elle s'ouvrait sur une carte hero — un gros widget d'enregistrement avec un badge LIVE, des boutons Pause/Reprendre/Arrêter, des taches de dégradé, et un hook useSessionState qui câblait le tout à l'enregistreur. Le dashboard essayait d'être deux choses à la fois : l'endroit où vous regardez vos réunions et l'endroit où vous pilotez l'enregistreur. Deux produits, un écran, en concurrence discrète.
La v1.8.2 a choisi. Elle a supprimé la carte hero et le câblage useSessionState — y compris un effet qui vous emmenait automatiquement sur la page de détail d'une réunion à l'instant où une session se terminait, exactement le genre de saut « serviable » qui surprend les gens en plein travail. Démarrer et masquer l'enregistreur ont été déplacés vers un simple bouton de la navbar (un bouton réaffecté qui a perdu une centaine de lignes en passant de contrôleur de session à interrupteur marche/arrêt), soutenu par un nouveau handler IPC navbar:hide. Ce qui restait, c'était un dashboard qui fait une seule chose : vous montrer ce que vous avez enregistré. Lecture seule. Calme.
Il y a un petit bug, très humain, dans cette transition qui vaut la peine d'être signalé, parce que c'est le genre de chose qu'un redesign introduit sans bruit et qu'un bon œil attrape dans l'heure. Les deux nouveaux boutons de la barre latérale — Démarrer et Masquer — sont sortis avec la même icône de grille générique, si bien qu'ils étaient visuellement identiques. Douze minutes plus tard, un correctif de suivi a donné à Démarrer une icône de lecture, a donné à Masquer une icône d'œil barré, et a teinté Masquer en ambre, pour que « faire disparaître l'app » se lise comme une action délibérée plutôt que comme le jumeau neutre de « démarrer ». Les icônes sont du texte. Deux boutons qui se ressemblent disent la même chose, et ceux-là ne voulaient pas dire la même chose.
Le graphique en barres qui a vécu vingt-deux minutes
Voici la partie que j'adore, et elle est entièrement lisible dans le git log. À 16:29:34 le premier jet du redesign a atterri : un graphique d'activité sur 7 jours dessiné en barres — colonnes animées, une hauteur minimale pour que les jours minuscules restent visibles, un moignon estompé de 2 pixels pour les jours à zéro. Raisonnable. Conventionnel. Le genre de graphique vers lequel on tend sans réfléchir.
À 16:51:21 — vingt-deux minutes plus tard — il a été supprimé. Le commit de remplacement dit exactement ce qu'il a fait : « replace bar chart with smooth wave area chart for 7-day activity » (remplacer le graphique en barres par un graphique en aire vague lissée pour l'activité sur 7 jours). Et à 16:52:21, une minute après, un autre commit a retiré la carte arrondie et la bordure autour de la vague, « for cleaner look » (pour un rendu plus propre).
Pourquoi ? Les commits disent seulement ce qui a changé, donc le pourquoi est une lecture — mais la forme des données le rend évident. C'est un outil où un utilisateur typique a zéro, une ou deux réunions un jour donné. Sept barres, la plupart hautes de 2 pixels, ressemblent à un parking presque vide. Les mêmes sept chiffres dessinés comme une aire pleine continue ressemblent à une tendance — une vague douce qui monte là où la semaine a été chargée. Rien n'a été falsifié ; les valeurs en y sont identiques. Le redesign a simplement choisi l'encodage qui permet à des données éparses et honnêtes de se lire comme une forme plutôt que comme une absence.
Les statistiques ont eu le même traitement sur la même horloge. Le premier jet (16:29) a mis quatre chiffres dans une grille 2×4 de cartes. À 16:49, la grille s'est effondrée en une seule bande de statistiques en ligne — une unique rangée bordée avec des séparateurs très fins, une typo plus petite, un conteneur au lieu de quatre. Cinq boîtes à l'écran (quatre cartes plus une carte de graphique) sont devenues deux (une bande et une vague). La PR suivante a ajouté une touche d'honnêteté de plus : le libellé « Total Time » est devenu « Time in Meetings » (Temps en réunion), parce que total-de-quoi était une question légitime que personne ne devrait avoir à poser.
Vingt minutes, du premier jet à la version finale, le tout dans une seule pull request. Si tout cela avait été écrasé en un unique commit « redesign dashboard », la recherche serait invisible. Comme ça ne l'a pas été, on peut regarder un designer converger en temps réel.
La vague, mécaniquement
Il n'y a aucune librairie de graphiques ici. La vague est un SVG en ligne construit en JSX, et elle vaut le coup d'œil parce que c'est une petite recette honnête que vous pouvez voler pour n'importe quel sparkline.
Le lissage est l'astuce. Pour chaque paire de points adjacents, le path utilise un segment cubic-bezier dont les deux points de contrôle se placent au milieu horizontal entre eux — cpx = (prev.x + curr.x) / 2 — les points de contrôle étant maintenus à la hauteur propre de chaque extrémité. C'est le classique lissage à tangente plate : la courbe passe en douceur par chaque point de données sans jamais dépasser au-dessus de la valeur la plus haute ni en dessous de la plus basse, ce que ferait une spline Catmull-Rom naïve. Pour un graphique sur des nombres de réunions, « ne jamais dessiner une bosse qui implique une valeur qui n'a pas eu lieu » n'est pas une coquetterie ; c'est de la justesse. L'aire pleine est la même courbe fermée le long du bas et peinte avec un dégradé vertical (cyan, s'estompant de 30 % à presque rien), et la ligne se dessine toute seule avec une animation pathLength de framer-motion, de gauche à droite.
Sous le SVG se trouve la plomberie de données, et c'est l'héroïne discrète. Une nouvelle méthode getAggregateStats() sur le repository de transcriptions exécute trois requêtes préparées contre la table SQLite locale transcript_sessions : les totaux de toujours (nombre, durée cumulée, durée moyenne), un décompte glissant sur sept jours, et un bucket par jour avec GROUP BY date(created_at). Ensuite — et c'est le détail qui rend le graphique robuste — une petite boucle JavaScript parcourt les sept derniers jours calendaires et remplit chaque jour que la requête n'a pas renvoyé, en poussant un zéro. Le graphique reçoit donc à coup sûr exactement sept points, toujours.
C'est cette garantie qui fait disparaître les cas limites au lieu d'exiger des gardes éparpillés dans la vue. Une semaine vide ? Sept zéros, et un Math.max(count, 1) au dénominateur garde la ligne plate en bas au lieu de diviser par zéro. Un point de données unique qui ferait produire NaN à la formule d'espacement en x i / (n − 1) ? Impossible — le repository n'émet jamais moins de sept. Les libellés des jours de la semaine parsent même chaque bucket à T12:00:00 plutôt qu'à minuit tout court, pour qu'une chaîne YYYY-MM-DD ne glisse pas au jour précédent dans un fuseau horaire à UTC négatif. Corrigez la forme des données à la source et le code de rendu peut se permettre d'être naïf exprès.
Tout reste sur la machine. La requête de statistiques lit la base de données locale, revient via un canal IPC typé transcript:get-stats, et s'anime en quatre chiffres qui comptent vers le haut au montage. Rien de votre historique de réunions ne quitte le laptop pour dessiner ce graphique — ce qui, pour un outil qui enregistre vos conversations, est la seule façon acceptable de construire une vue d'analytics.
Un bug de plus, parce que les redesigns les tirent au grand jour
Empaqueté dans la même release se trouvait un correctif qui n'avait rien à voir avec le dashboard et tout à voir avec le genre de dette sur laquelle un redesign trébuche. Le style de la scrollbar sombre de l'app avait dérivé vers deux définitions CSS concurrentes de la même classe — une plus ancienne, violette, et une plus récente, blanche sur fond sombre — et laquelle l'emportait dépendait de l'ordre de la cascade. Pire, seuls les éléments qui pensaient à ajouter la classe recevaient un style ; tout le reste retombait sur la scrollbar claire par défaut, et les scrollbars horizontales n'ont jamais été stylées parce que les vieilles règles ne fixaient qu'une largeur, jamais une hauteur. Le correctif a supprimé les deux définitions, stylé la scrollbar globalement sur chaque élément, et retiré la classe désormais morte des dix composants qui la trimballaient. C'est un diff de deux lignes répété dix fois — la taxe peu glamour d'avoir stylé la même chose deux fois et laissé les deux survivre.
Trois choses que cette release nous a apprises
- Soustraire pour se concentrer. Le dashboard s'est amélioré en faisant moins — en perdant les contrôles d'enregistrement dont il n'avait jamais eu besoin et en devenant une chose claire. Un écran qui est à la fois une visionneuse et une télécommande, ce sont deux demi-produits ; choisissez-en un et déplacez l'autre là où il a sa place.
- Choisissez l'encodage que vos données méritent. Des décomptes épars se lisent comme une absence en barres et comme une tendance dans un graphique en aire, à partir de chiffres identiques. Ce n'est pas de l'enjolivement — c'est choisir la forme honnête qui laisse de vraies données ténues communiquer.
- Garantissez la forme des données en amont. La boucle de remplissage-jusqu'à-sept est la raison pour laquelle le code du graphique n'a pas de vérifications de null éparpillées : division par zéro, NaN à point unique et jours manquants disparaissent tous parce que la requête promet exactement sept buckets. Un rendu robuste commence une couche plus bas.
Pour le chapitre précédent de l'histoire v1, quatre releases en vingt-six heures (v1.7.6–v1.8.1) ; et pour tout l'arc, l'anatomie de livrer un logiciel jusqu'à la perfection.