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
Il silenzio era portante
Steven
Steven8 min di lettura

Il silenzio era portante

Le ultime due release di GeekBye v1 ruotano attorno alla stessa verità scomoda: la trascrizione in tempo reale su una rete vera non è senza perdite, e la mossa onesta è smettere di fingere che lo sia. La v1.8.20 teneva su disco una copia di ogni chunk audio prima di scartarlo durante una riconnessione, e ha iniziato a segnare ad alta voce i buchi nella trascrizione. La v1.9.0 ha smesso di inviare silenzio per risparmiare banda — e ha scoperto che il silenzio era proprio il segnale che il trascrittore usava per sapere che una frase era finita. Due release sul costo di buttare via le cose.

Ingegneria
Audio
Affidabilità
I tre verbi che tengono vivo il Web Audio
Steven
Steven9 min di lettura

I tre verbi che tengono vivo il Web Audio

Due release puntuali di GeekBye, a due mesi di distanza e in due file diversi, hanno insegnato al nostro codice audio la stessa lezione da estremità opposte: smettere di trattare l'AudioContext del browser come usa e getta. Una release ha imparato a fare resume() di un contesto che macOS aveva silenziosamente sospeso a metà registrazione; l'altra ha imparato a fare suspend() invece di close() così che sessioni consecutive smettano di schiantarsi contro il tetto di circa sei contesti di Chromium. Resume, suspend, close — è tutta qui la trama.

Ingegneria
Audio
Desktop