
Επανασχεδιάζοντας το Dashboard γύρω από ένα Wave Chart
Το GeekBye v1.8.2 μετέτρεψε το dashboard από τηλεχειριστήριο εγγραφής σε έναν αναλυτικό μόνο-για-ανάγνωση χώρο — μια λωρίδα στατιστικών και ένα wave chart 7 ημερών. Και τα δύο χαρακτηριστικά στοιχεία ήταν δεύτερα προσχέδια, που κυκλοφόρησαν μέσα σε είκοσι λεπτά από τα πρώτα προσχέδιά τους, το ίδιο απόγευμα.
Κάποιες εκδόσεις προσθέτουν μια λειτουργία. Το GeekBye v1.8.2 κυρίως αφαίρεσε μία — και η αφαίρεση είναι η ιστορία. Σε ένα μόνο απόγευμα τον Φεβρουάριο του 2026, δεκαοκτώ commits μετέτρεψαν την αρχική του dashboard από τηλεχειριστήριο εγγραφής σε έναν αναλυτικό μόνο-για-ανάγνωση χώρο: τέσσερα στατιστικά και ένα chart δραστηριότητας. Στην πορεία κυκλοφόρησε ένα bar chart που έζησε είκοσι δύο λεπτά, ένα grid από κάρτες στατιστικών που έζησε είκοσι, και ένα χειροποίητο SVG wave που αντικατέστησε και τα δύο. Όλη η αναζήτηση διατηρείται στις χρονοσφραγίδες των commits, που είναι το καλύτερο είδος τεκμηρίωσης design που υπάρχει.
Το dashboard έπαψε να είναι τηλεχειριστήριο
Πριν από αυτή την έκδοση, η αρχική του dashboard ήταν ένας πίνακας ελέγχου. Ξεκινούσε με μια hero κάρτα — ένα μεγάλο widget εγγραφής με ένα LIVE badge, κουμπιά Pause/Resume/Stop, gradient blobs, και ένα hook useSessionState που τα συνέδεε όλα με τον recorder. Το dashboard προσπαθούσε να είναι δύο πράγματα ταυτόχρονα: το μέρος όπου βλέπεις τις συναντήσεις σου και το μέρος από όπου οδηγείς τον recorder. Δύο προϊόντα, μία οθόνη, σε σιωπηλό ανταγωνισμό.
Το v1.8.2 διάλεξε ένα. Έσβησε την hero κάρτα και τη σύνδεση useSessionState — συμπεριλαμβανομένου ενός effect που σε πήγαινε αυτόματα στη σελίδα λεπτομερειών μιας συνάντησης τη στιγμή που τελείωνε μια συνεδρία, ακριβώς το είδος του «βοηθητικού» άλματος που ξαφνιάζει τους ανθρώπους στη μέση της ροής. Το ξεκίνημα και η απόκρυψη του recorder μετακινήθηκαν σε έναν απλό navbar διακόπτη (ένα επαναχρησιμοποιημένο κουμπί που έχασε περίπου εκατό γραμμές πηγαίνοντας από ελεγκτής συνεδρίας σε διακόπτη on/off), υποστηριζόμενο από έναν νέο navbar:hide IPC handler. Αυτό που έμεινε ήταν ένα dashboard που κάνει ένα πράγμα: σου δείχνει τι έχεις καταγράψει. Μόνο για ανάγνωση. Ήρεμο.
Υπάρχει ένα μικρό, ανθρώπινο bug σε αυτή τη μετάβαση που αξίζει να αναφερθεί, γιατί είναι από αυτά που ένας επανασχεδιασμός εισάγει σιωπηλά και ένα καλό μάτι πιάνει την ίδια ώρα. Τα δύο νέα κουμπιά της sidebar — Start και Hide — κυκλοφόρησαν με το ίδιο γενικό grid εικονίδιο, οπότε ήταν οπτικά πανομοιότυπα. Δώδεκα λεπτά αργότερα ένα follow-up έδωσε στο Start ένα play εικονίδιο, έδωσε στο Hide ένα εικονίδιο ματιού-κλειστού, και έβαψε το Hide κεχριμπαρένιο, ώστε το «κάνε την εφαρμογή να εξαφανιστεί» να διαβάζεται ως σκόπιμη ενέργεια αντί για ουδέτερο δίδυμο του «start». Τα εικονίδια είναι κείμενο. Δύο κουμπιά που δείχνουν ίδια λένε το ίδιο πράγμα, και αυτά δεν σήμαιναν το ίδιο πράγμα.
Το bar chart που έζησε είκοσι δύο λεπτά
Να το κομμάτι που αγαπώ, και είναι εντελώς ευανάγνωστο από το git log. Στις 16:29:34 προσγειώθηκε το πρώτο προσχέδιο του επανασχεδιασμού: ένα chart δραστηριότητας 7 ημερών σχεδιασμένο ως μπάρες — κινούμενες στήλες, ένα ελάχιστο ύψος ώστε οι μικρές μέρες να παραμένουν ορατές, ένα αχνό κολόβωμα 2 pixel για τις μέρες με μηδέν. Λογικό. Συμβατικό. Το είδος του chart που πιάνεις χωρίς να σκέφτεσαι.
Στις 16:51:21 — είκοσι δύο λεπτά αργότερα — διαγράφηκε. Το commit αντικατάστασης λέει ακριβώς τι έκανε: «replace bar chart with smooth wave area chart for 7-day activity» (αντικατέστησε το bar chart με ομαλό wave area chart για τη δραστηριότητα 7 ημερών). Και στις 16:52:21, ένα λεπτό μετά, ένα άλλο commit αφαίρεσε τη στρογγυλεμένη κάρτα και το περίγραμμα γύρω από το wave, «for cleaner look» (για πιο καθαρή όψη).
Γιατί; Τα commits λένε μόνο τι άλλαξε, οπότε το γιατί είναι μια ανάγνωση — αλλά το σχήμα των δεδομένων το κάνει προφανές. Πρόκειται για ένα εργαλείο όπου ένας τυπικός χρήστης έχει μηδέν, μία ή δύο συναντήσεις σε οποιαδήποτε δεδομένη μέρα. Επτά μπάρες, οι περισσότερες ύψους 2 pixel, μοιάζουν με έναν σχεδόν άδειο χώρο. Οι ίδιοι επτά αριθμοί σχεδιασμένοι ως μια συνεχής γεμισμένη area μοιάζουν με τάση — ένα απαλό κύμα που ανεβαίνει εκεί όπου η εβδομάδα ήταν φορτωμένη. Τίποτα δεν παραποιήθηκε· οι τιμές του άξονα y είναι πανομοιότυπες. Ο επανασχεδιασμός απλώς διάλεξε την κωδικοποίηση που αφήνει τα αραιά, τίμια δεδομένα να διαβάζονται ως σχήμα αντί για απουσία.
Τα στατιστικά πήραν την ίδια μεταχείριση στο ίδιο ρολόι. Το πρώτο προσχέδιο (16:29) έβαλε τέσσερις αριθμούς σε ένα 2×4 grid από κάρτες. Στις 16:49 το grid συμπτύχθηκε σε μία inline λωρίδα στατιστικών — μία γραμμή με περίγραμμα, με λεπτούς διαχωριστές, μικρότερη γραμματοσειρά, ένα container αντί για τέσσερα. Πέντε κουτιά στην οθόνη (τέσσερις κάρτες συν μία κάρτα chart) έγιναν δύο (μία λωρίδα και ένα wave). Το επόμενο PR πρόσθεσε ακόμη μια τίμια πινελιά: η ετικέτα «Total Time» έγινε «Time in Meetings» (Χρόνος σε συναντήσεις), επειδή σύνολο-του-τίνος ήταν μια δίκαιη ερώτηση που κανείς δεν θα έπρεπε να χρειάζεται να κάνει.
Είκοσι λεπτά, από το πρώτο προσχέδιο ως το τελικό, όλα μέσα σε ένα pull request. Αν αυτά είχαν γίνει squash σε ένα μόνο commit «redesign dashboard», η αναζήτηση θα ήταν αόρατη. Επειδή δεν έγιναν, μπορείς να παρακολουθήσεις έναν designer να συγκλίνει σε πραγματικό χρόνο.
Το wave, μηχανικά
Εδώ δεν υπάρχει charting library. Το wave είναι ένα inline SVG χτισμένο σε JSX, και αξίζει μια ματιά επειδή είναι μια μικρή, τίμια συνταγή που μπορείς να κλέψεις για οποιοδήποτε sparkline.
Το smoothing είναι το κόλπο. Για κάθε ζεύγος γειτονικών σημείων, το path χρησιμοποιεί ένα cubic-bezier segment του οποίου τα δύο control points κάθονται στο οριζόντιο μέσο μεταξύ τους — cpx = (prev.x + curr.x) / 2 — με τα control points κρατημένα στο δικό του ύψος κάθε άκρου. Αυτό είναι το κλασικό smoothing επίπεδης εφαπτομένης: η καμπύλη περνά ομαλά μέσα από κάθε σημείο δεδομένων χωρίς ποτέ να ξεπερνά πάνω από την υψηλότερη ή κάτω από τη χαμηλότερη τιμή, κάτι που ένα αφελές Catmull-Rom spline θα έκανε. Για ένα chart σχετικά με αριθμούς συναντήσεων, το «μη ζωγραφίζεις ποτέ ένα εξόγκωμα που υπονοεί μια τιμή που δεν συνέβη» δεν είναι φινέτσα· είναι ορθότητα. Η γεμισμένη area είναι η ίδια καμπύλη κλεισμένη κατά μήκος του κάτω μέρους και βαμμένη με ένα κατακόρυφο gradient (cyan, ξεθωριάζοντας από 30% σχεδόν στο τίποτα), και η γραμμή σχεδιάζεται μόνη της με ένα framer-motion pathLength animation, από αριστερά προς τα δεξιά.
Κάτω από το SVG είναι η υδραυλική των δεδομένων, και είναι ο σιωπηλός ήρωας. Μια νέα μέθοδος getAggregateStats() στο transcript repository τρέχει τρία prepared statements ενάντια στον τοπικό SQLite πίνακα transcript_sessions: σύνολα όλων των εποχών (πλήθος, αθροισμένη διάρκεια, μέση διάρκεια), ένα κυλιόμενο πλήθος επτά ημερών, και ένα bucket ανά ημέρα με GROUP BY date(created_at). Έπειτα — και αυτή είναι η λεπτομέρεια που κάνει το chart ανθεκτικό — ένας μικρός βρόχος JavaScript διατρέχει τις τελευταίες επτά ημερολογιακές ημέρες και συμπληρώνει (back-fill) κάθε μέρα που το query δεν επέστρεψε, σπρώχνοντας ένα μηδέν. Το chart είναι επομένως εγγυημένα ακριβώς επτά σημεία, πάντα.
Αυτή η εγγύηση είναι που κάνει τις ακραίες περιπτώσεις να εξαφανίζονται αντί να χρειάζονται φύλακες σκορπισμένους μέσα στο view. Άδεια εβδομάδα; Επτά μηδενικά, και ένα Math.max(count, 1) στον παρονομαστή κρατά τη γραμμή επίπεδη στον πάτο αντί να διαιρεί με το μηδέν. Ένα μοναδικό σημείο δεδομένων που θα έκανε τον τύπο x-spacing i / (n − 1) να παράγει NaN; Δεν μπορεί να συμβεί — το repository δεν εκπέμπει ποτέ λιγότερα από επτά. Οι ετικέτες των ημερών μάλιστα parse-άρουν κάθε bucket στο T12:00:00 αντί για σκέτο μεσάνυχτα, ώστε ένα YYYY-MM-DD string να μη γλιστρήσει στην προηγούμενη μέρα σε μια ζώνη ώρας αρνητικού UTC. Διόρθωσε το σχήμα των δεδομένων στην πηγή και ο κώδικας rendering επιτρέπεται να είναι αφελής επίτηδες.
Όλα μένουν στο μηχάνημα. Το stats query διαβάζει την τοπική βάση δεδομένων, επιστρέφει μέσω ενός τυποποιημένου transcript:get-stats IPC channel, και εμψυχώνεται σε τέσσερις αριθμούς count-up κατά το mount. Τίποτα από το ιστορικό των συναντήσεών σου δεν φεύγει από το laptop για να σχεδιαστεί αυτό το chart — κάτι που, για ένα εργαλείο που καταγράφει τις συνομιλίες σου, είναι ο μόνος αποδεκτός τρόπος να χτίσεις έναν αναλυτικό χώρο.
Ένα ακόμη bug, επειδή οι επανασχεδιασμοί τα σέρνουν στο φως
Πακεταρισμένο στην ίδια έκδοση ήταν ένα fix που δεν είχε καμία σχέση με το dashboard και κάθε σχέση με το είδος του χρέους πάνω στο οποίο σκοντάφτει ένας επανασχεδιασμός. Το styling του σκούρου scrollbar της εφαρμογής είχε παρασυρθεί σε δύο ανταγωνιστικούς CSS ορισμούς της ίδιας κλάσης — έναν παλαιότερο μοβ και έναν νεότερο άσπρο-σε-σκούρο — και ποιος κέρδιζε εξαρτιόταν από τη σειρά του cascade. Χειρότερα, μόνο τα στοιχεία που θυμόντουσαν να προσθέσουν την κλάση αποκτούσαν καθόλου styling· όλα τα άλλα έπεφταν πίσω στον προεπιλεγμένο ανοιχτό scrollbar, και οι οριζόντιοι scrollbars δεν αποκτούσαν ποτέ styling επειδή οι παλιοί κανόνες όριζαν μόνο πλάτος, ποτέ ύψος. Το fix έσβησε και τους δύο ορισμούς, έκανε styling στον scrollbar καθολικά σε κάθε στοιχείο, και αφαίρεσε την πλέον νεκρή κλάση από τα δέκα components που την κουβαλούσαν. Είναι ένα diff δύο γραμμών επαναλαμβανόμενο δέκα φορές — ο άχαρος φόρος του να έχεις κάνει styling στο ίδιο πράγμα δύο φορές και να άφησες και τα δύο να επιβιώσουν.
Τρία πράγματα που μας δίδαξε αυτή η έκδοση
- Αφαίρεσε για να εστιάσεις. Το dashboard έγινε καλύτερο κάνοντας λιγότερα — χάνοντας τα controls εγγραφής που ποτέ δεν χρειαζόταν και γινόμενο ένα καθαρό πράγμα. Μια οθόνη που είναι και θεατής και τηλεχειριστήριο είναι δύο μισά προϊόντα· διάλεξε ένα και μετακίνησε το άλλο εκεί που ανήκει.
- Διάλεξε την κωδικοποίηση που αξίζουν τα δεδομένα σου. Τα αραιά πλήθη διαβάζονται ως απουσία σε μπάρες και ως τάση σε area chart, από πανομοιότυπους αριθμούς. Αυτό δεν είναι ωραιοποίηση — είναι η επιλογή του τίμιου σχήματος που αφήνει τα πραγματικά, ισχνά δεδομένα να επικοινωνήσουν.
- Εγγυήσου το σχήμα των δεδομένων ανάντη. Ο βρόχος back-fill-σε-επτά είναι ο λόγος που ο κώδικας του chart δεν έχει σκορπισμένους null ελέγχους: η διαίρεση με το μηδέν, το NaN από ένα σημείο, και οι μέρες που λείπουν εξαφανίζονται όλα επειδή το query υπόσχεται ακριβώς επτά buckets. Το ανθεκτικό rendering ξεκινά ένα επίπεδο πιο κάτω.
Για το προηγούμενο κεφάλαιο της ιστορίας του v1, τέσσερις εκδόσεις σε είκοσι έξι ώρες (v1.7.6–v1.8.1)· και για ολόκληρη την πορεία, η ανατομία της παράδοσης λογισμικού στην τελειότητα.