Steven
Steven6 min di lettura

Come trasmettere un report dal vivo senza lo sfarfallio

Quando la tua riunione finisce, il riassunto di GeekBye ora si riempie dal vivo invece di farti fissare uno spinner. Rendere l'interfaccia in streaming calma anziché nervosa ha richiesto di risolvere lo sfarfallio due volte — una per i campi strutturati, una per il markdown — e la seconda soluzione era un renderer che già possedevamo.

Ingegneria
Frontend
Streaming
Rilasci GeekBye
Come trasmettere un report dal vivo senza lo sfarfallio

Prima di questo rilascio, terminare una riunione GeekBye significava fissare uno spinner. L''app inviava l''intera trascrizione per l''analisi, aspettava il riassunto completo — punteggio, punti chiave, azioni da svolgere, tutto — e poi lo scaricava sullo schermo tutto in una volta. Funzionale, ma sembrava lento, perché fissavi il nulla mentre lavorava.

GeekBye v1.6.13–v1.6.15 ha trasformato tutto ciò in un report che si trasmette dal vivo, campo per campo, mentre viene generato. La parte interessante non è lo streaming — è tutto quello che abbiamo dovuto fare per evitare che lo streaming apparisse nervoso. Lo streaming calmo e lo streaming a scatti sono la stessa funzionalità con esecuzioni molto diverse.

Sfarfallio, parte uno: non lasciare che ogni chunk scuota React

Il riassunto non è un unico blocco — sono campi strutturati (un punteggio complessivo, punti chiave, azioni da svolgere e così via) che il backend emette uno alla volta tramite server-sent events. Il modo ingenuo di renderizzarlo è aggiornare lo stato di React a ogni evento non appena arriva.

Fallo, e ottieni sfarfallio. Ogni campo che arriva innesca la propria ri-renderizzazione; componenti che mostravano "vuoto" si montano, poi si smontano, poi si rimontano come "caricato"; il layout sobbalza a ogni pacchetto. Il report si assembla da solo davanti a te nel modo peggiore possibile — in modo visibile, nervoso.

La soluzione è una disciplina in due parti. Primo, accumula i campi in arrivo in un ref — un contenitore mutabile che non innesca renderizzazioni — e pubblica sullo stato di React una sola volta per chunk con una copia nuova, così l''albero si aggiorna in modo deliberato anziché a ogni micro-evento. Secondo, renderizza un unico albero di componenti stabile per tutto il tempo, sia in streaming sia completato, con segnaposto in linea per i campi non ancora arrivati:

// 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>

Il componente che mostra il punteggio non si smonta mai — mostra semplicemente finché il numero non atterra, poi il numero. Niente sobbalza. E l''oggetto completamente assemblato viene comunque memorizzato nella cache del database locale alla fine, così l''interfaccia in streaming e l''archiviazione durevole coesistono senza litigare.

Sfarfallio, parte due: il renderer che già possedevamo

Il secondo sfarfallio è più sottile e vive nel markdown stesso. Il report renderizza markdown formattato — titoli, grassetto, liste, blocchi di codice. Ma il markdown che arriva a metà flusso è, in ogni istante dato, a metà. Una lista con un solo punto per ora. Un recinto di codice aperto ma non chiuso. Un marcatore di grassetto ancora senza partner.

Un renderer di markdown convenzionale ri-analizza l''intera stringa a ogni token, e quello stato a metà si analizza in qualcosa di diverso ogni volta — così la lista sfarfalla, il blocco di codice lampeggia aprendosi e chiudendosi, il layout salta mentre i token si completano. È lo stesso assemblaggio nervoso di prima, un livello più in basso.

La soluzione era quasi imbarazzante nella sua disponibilità: stavamo già usando un renderer di markdown consapevole dello streaming per la chat IA dal vivo. Un renderer costruito per tollerare token incompleti — per renderizzare markdown a metà in modo stabile e assestarlo solo quando i token si completano — invece di ri-analizzare da zero ogni volta. Al report bastava usare lo stesso. Avevamo risolto esattamente questo problema per la chat mesi prima; la "soluzione" per il report è stata riconoscere che avevamo già lo strumento e puntarlo su una seconda superficie. Riutilizzare ha battuto ricostruire.

Il bonus: la copia formattata è un problema di formato degli appunti

Una volta che il report aveva un bell''aspetto, la gente voleva incollarlo — in un documento, in un''e-mail — mantenendo la formattazione e gli screenshot. L''istinto è serializzare il report in una stringa di markdown e sperare che la destinazione la renderizzi. Di solito non lo fa.

La vera risposta è che gli appunti contengono più rappresentazioni contemporaneamente. Quindi ne scriviamo due: una versione HTML con la formattazione intatta e gli screenshot incorporati come immagini in linea, e un ripiego in testo semplice per le destinazioni che non accettano HTML. Incolla in un editor formattato e ottieni il report formattato con le immagini; incolla in una casella di testo semplice e ottieni testo pulito. La "copia formattata" non è mai stata un problema di serializzazione — era un problema di scegliere-il-formato-di-appunti-giusto.

Lo stesso rilascio ha anche dato al report etichette in stile chat Io / Loro con i due interlocutori allineati su lati opposti, così una trascrizione si legge come la conversazione che era, e ha spostato i tuoi screenshot sul loro lato per fare pendant. (Un rilascio in questa finestra, v1.6.14, era una pura ricompilazione — nessuna storia lì, ed è onesto dirlo.)

Tre cose che l''interfaccia in streaming ci ha insegnato

  1. Bufferizza i campi trasmessi, pubblica una volta per chunk. Lasciare che ogni server-sent event guidi il proprio aggiornamento di stato è lo sfarfallio. Un accumulatore con ref più un''unica pubblicazione per chunk, in un albero che non si smonta mai, trasforma l''assemblaggio nervoso in un riempimento calmo.
  2. Tutto ciò che si trasmette ha bisogno di un renderer consapevole dello streaming. Un renderer che ri-analizza l''intera stringa per token sfarfallerà sul markdown a metà. Usane uno costruito per input incompleti — e se ne hai già uno per un''altra superficie, riutilizzalo prima di costruirne un secondo.
  3. La copia formattata riguarda i formati degli appunti, non la serializzazione. Scrivi text/html con immagini incorporate e un ripiego text/plain. Gli appunti sono stati progettati per trasportare entrambi; usali.

Questo è il quinto capitolo della storia di affidabilità e rifinitura che diventa GeekBye v2. Per il capitolo precedente, vedi il bug degli auricolari (v1.6.12); per il punto in cui la stessa idea dei server-sent events è poi diventata un intero trasporto di riserva, trascrizione dal vivo quando il firewall blocca i WebSocket (v2.0.8); e per l''intero arco, l''anatomia del rilasciare software fino alla perfezione.

Articoli Correlati

La tua app Mac concede l’accesso al microfono — poi lo dimentica a ogni avvio
Steven
Steven6 min di lettura

La tua app Mac concede l’accesso al microfono — poi lo dimentica a ogni avvio

GeekBye ha chiesto il permesso del microfono, tu l’hai concesso e ha funzionato. All’avvio successivo: sparito. E l’app non compariva mai in Impostazioni di Sistema → Microfono. La colpevole era una funzione di sicurezza di macOS che eseguiva l’app di nascosto da un percorso che svanisce — ecco la diagnosi e la correzione in un solo avviso.

macOS
Autorizzazioni
Ingegneria
Una connessione caduta non dovrebbe mandare in crash tutta la tua app — ma la nostra lo faceva
Steven
Steven7 min di lettura

Una connessione caduta non dovrebbe mandare in crash tutta la tua app — ma la nostra lo faceva

Quando il nostro backend è andato offline nel mezzo di una riunione, non ha solo messo in pausa la trascrizione — ha mandato in crash l'intera app. La causa era un singolo evento non gestito, e la correzione erano dieci righe. Questo è il gruppo di release che ha reso GeekBye capace di restare connesso all'account e restare connesso alla rete attraverso le cose che prima lo uccidevano.

Ingegneria
Affidabilità
Electron
L'anatomia di un software rilasciato alla perfezione: come la code review ha catturato ciò che i test non potevano
Steven
Steven7 min di lettura

L'anatomia di un software rilasciato alla perfezione: come la code review ha catturato ciò che i test non potevano

Lungo tutta la serie GeekBye v2 si ripete la stessa cosa: una correzione supera ogni test sulla macchina dello sviluppatore, e poi la code review dimostra che sarebbe fallita per quasi tutti. Questo è il flusso di lavoro dietro nove release: il cancello della review, le catture "fix-first" e la disciplina del "testa-prima-di-rilasciare" che trasforma "funziona da me" in "funziona".

Ingegneria
Processo
Code Review