
Slik strømmer du en direkterapport uten flimmer
Når møtet ditt er over, fylles GeekByes oppsummering nå inn live i stedet for å tvinge deg til å stirre på en spinner. Å få et strømmende grensesnitt til å føles rolig i stedet for hakkete krevde at vi løste flimmer to ganger — én gang for strukturerte felter, én gang for markdown — og den andre løsningen var en renderer vi allerede eide.
Før denne releasen betydde det å avslutte et GeekBye-møte at du satt og så på en spinner. Appen sendte hele transkriptet ditt av gårde for analyse, ventet på hele oppsummeringen — score, nøkkelpunkter, oppgaver, alt sammen — og slapp det så på skjermen på én gang. Funksjonelt, men det føltes tregt, fordi du stirret på ingenting mens den jobbet.
GeekBye v1.6.13–v1.6.15 gjorde det om til en rapport som strømmer inn live, felt for felt, etter hvert som den genereres. Det interessante er ikke strømmingen — det er alt vi måtte gjøre for å hindre at strømmingen så hakkete ut. Rolig strømming og hakkete strømming er den samme funksjonen med svært ulik utførelse.
Flimmer, del én: ikke la hver chunk hamre løs på React
Oppsummeringen er ikke én klump — det er strukturerte felter (en samlet score, nøkkelpunkter, oppgaver, og så videre) som backenden sender ut ett om gangen over server-sent events. Den naive måten å rendre det på er å oppdatere React-state ved hver hendelse som ankommer.
Gjør det, og du får flimmer. Hvert felt som ankommer utløser sin egen re-render; komponenter som viste "tomt" monteres, avmonteres, så remonteres som "lastet"; layouten rykker til ved hver pakke. Rapporten setter seg sammen selv foran deg på verst tenkelige måte — synlig, nervøst.
Løsningen er en todelt disiplin. Først, akkumuler de innkommende feltene i en ref — en muterbar beholder som ikke utløser renders — og publiser til React-state én gang per chunk med en fersk kopi, slik at treet oppdateres bevisst i stedet for ved hver mikrohendelse. Deretter, rendr ett stabilt komponenttre hele tiden, enten det strømmer eller er ferdig, med plassholdere på plass for felter som ennå ikke har ankommet:
// accumulate in a ref, publish once per chunk
partialRef.current = { ...partialRef.current, [field]: value }
setPartial({ ...partialRef.current })
// one tree, never swapped; missing fields show a placeholder in place
const report = isStreaming ? partial : saved
<Score>{report.overallScore ?? '…'}</Score>
Komponenten som viser scoren avmonteres aldri — den viser bare … til tallet lander, deretter tallet. Ingenting rykker til. Og det fullt sammensatte objektet caches fortsatt til den lokale databasen til slutt, slik at strømmende grensesnitt og varig lagring sameksisterer uten å slåss.
Flimmer, del to: rendereren vi allerede eide
Det andre flimmeret er mer subtilt og bor i selve markdownen. Rapporten rendrer rik markdown — overskrifter, fet skrift, lister, kodeblokker. Men markdown som ankommer midt i strømmen er, i et hvilket som helst øyeblikk, halvferdig. En liste med ett punkt så langt. En kodegjerde som er åpnet, men ikke lukket. En fet-markør uten partner ennå.
En konvensjonell markdown-renderer re-parser hele strengen ved hvert token, og den halvferdige tilstanden parses til noe forskjellig hver gang — så listen flimrer, kodeblokken blinker åpen og lukket, layouten hopper etter hvert som tokens fullføres. Det er den samme nervøse sammensetningen som før, ett lag lenger ned.
Løsningen var nesten pinlig tilgjengelig: vi brukte allerede en strømmingsbevisst markdown-renderer til den live AI-chatten. En renderer bygget for å tåle ufullstendige tokens — for å rendre halvferdig markdown stabilt og bare la den falle på plass etter hvert som tokens fullføres — i stedet for å re-parse fra bunnen av hver gang. Rapporten trengte bare å bruke den samme. Vi hadde løst akkurat dette problemet for chatten måneder tidligere; "løsningen" for rapporten var å innse at vi allerede hadde verktøyet og peke det mot enda en flate. Gjenbruk slo gjenoppbygging.
Bonusen: rik kopiering er et utklippstavle-formatproblem
Da rapporten så bra ut, ville folk lime den inn — i et dokument, en e-post — og beholde formateringen og skjermbildene. Instinktet er å serialisere rapporten til en markdown-streng og håpe at destinasjonen rendrer den. Det gjør den vanligvis ikke.
Det virkelige svaret er at utklippstavlen holder flere representasjoner samtidig. Så vi skriver to: en HTML-versjon med formateringen intakt og skjermbildene innebygd som inline-bilder, og en ren tekst-reserve for destinasjoner som ikke tar HTML. Lim inn i en rik editor og du får den formaterte rapporten med bilder; lim inn i en ren tekstboks og du får ren tekst. "Rik kopiering" var aldri et serialiseringsproblem — det var et velg-riktig-utklippstavle-format-problem.
Den samme releasen ga også rapporten Me / Them-etiketter i chat-stil med de to talerne justert til hver sin side, slik at et transkript leses som samtalen det var, og flyttet dine egne skjermbilder til sin egen side for å matche. (Én release i dette vinduet, v1.6.14, var en ren gjenoppbygging — ingen historie der, og det er ærlig å si det.)
Tre ting strømmende grensesnitt lærte oss
- Buffre strømmede felter, publiser én gang per chunk. Å la hver server-sent event drive sin egen state-oppdatering er flimmeret. En ref-akkumulator pluss én enkelt publisering per chunk, inn i et tre som aldri avmonteres, gjør nervøs sammensetning om til rolig utfylling.
- Alt som strømmer trenger en strømmingsbevisst renderer. En renderer som re-parser hele strengen per token vil flimre på halvferdig markdown. Bruk en bygget for ufullstendig input — og hvis du allerede har en for en annen flate, gjenbruk den før du bygger nummer to.
- Rik kopiering handler om utklippstavle-formater, ikke serialisering. Skriv
text/htmlmed innebygde bilder og entext/plain-reserve. Utklippstavlen ble designet for å bære begge; bruk det.
Dette er det femte kapitlet i historien om pålitelighet og finpuss som blir GeekBye v2. For det forrige kapitlet, se earbuds-buggen (v1.6.12); for hvor den samme server-sent-events-idéen senere ble en hel reserve-transport, live transkribering når brannmuren blokkerer WebSockets (v2.0.8); og for hele buen, anatomien av å levere programvare til perfeksjon.