dsh-live-voice-debugger 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -104,6 +104,10 @@ var init_en2 = __esm({
104
104
  commandStopSpeaking: "Stop speaking",
105
105
  completed: "Completed",
106
106
  confirmDelete: "Delete this entire recording, including its audio and transcription history? This cannot be undone. Deleting an active recording also discards its future saved audio.",
107
+ copyTranscripts: "Copy transcripts",
108
+ copyTranscriptsError: "Could not copy. Check clipboard permission or copy the text manually.",
109
+ copyTranscriptsHelp: "Copy the latest successful nonempty transcript per segment in the current list, in list order. Includes ignored speech; window and search filters apply.",
110
+ copyTranscriptsSuccess: "Transcripts copied.",
107
111
  date: "Started",
108
112
  delete: "Delete entire session",
109
113
  deleteAll: "Delete all recordings",
@@ -138,6 +142,7 @@ var init_en2 = __esm({
138
142
  exportError: "Unable to export the recording.",
139
143
  failed: "Failed",
140
144
  help: "Inspect saved audio, speech/silence boundaries and transcription attempts. Audio and transcripts may be sensitive. Playback is local; export includes audio and text. Inspection never changes capture; only explicit deletion removes recordings.",
145
+ ignoredMuted: "Ignored while muted",
141
146
  inputStopComplete: "Recognition/meter stop completed",
142
147
  interrupted: "Interrupted",
143
148
  loadError: "Unable to load recording sessions. Try refreshing.",
@@ -199,6 +204,7 @@ var init_en2 = __esm({
199
204
  storagePartial: "At least {size} \xB7 {count} session(s) unmeasured",
200
205
  storageScope: "All sources in this browser and site. Encoded saved audio only; excludes metadata, database overhead, exported files and unsaved audio. Not total browser storage usage.",
201
206
  storageSessions: "{count} session(s) across all sources",
207
+ storageTab: "Storage",
202
208
  storageTotal: "Total stored audio",
203
209
  storageUnknown: "Unavailable",
204
210
  success: "Success",
@@ -207,6 +213,10 @@ var init_en2 = __esm({
207
213
  unavailable: "Recording service unavailable. Enable recording in Live Voice settings with a compatible runtime.",
208
214
  unstar: "Remove protection",
209
215
  unstarHelp: "Allow automatic cleanup and include this recording in the retention count again. Manual deletion is still allowed. Browser storage and eviction protection are unchanged.",
216
+ viewLayout: "View",
217
+ viewList: "List",
218
+ viewSession: "Session",
219
+ viewSplit: "Split",
210
220
  voiceCommandRecognized: "Voice command recognized",
211
221
  waitCancelled: "Wait cancelled",
212
222
  waitChanged: "Wait reasons changed",
@@ -363,6 +373,10 @@ var init_es2 = __esm({
363
373
  commandStopSpeaking: "Detener voz",
364
374
  completed: "Completada",
365
375
  confirmDelete: "\xBFEliminar toda esta grabaci\xF3n, incluido el audio y el historial de transcripci\xF3n? No se puede deshacer. Eliminar una grabaci\xF3n activa tambi\xE9n descarta su audio guardado futuro.",
376
+ copyTranscripts: "Copiar transcripciones",
377
+ copyTranscriptsError: "No se pudo copiar. Revisa el permiso del portapapeles o copia manualmente.",
378
+ copyTranscriptsHelp: "Copia la \xFAltima transcripci\xF3n correcta no vac\xEDa de cada segmento de la lista actual, en su orden. Incluye voz ignorada y respeta ventana y b\xFAsqueda.",
379
+ copyTranscriptsSuccess: "Transcripciones copiadas.",
366
380
  date: "Inicio",
367
381
  delete: "Eliminar sesi\xF3n completa",
368
382
  deleteAll: "Borrar todas las grabaciones",
@@ -397,6 +411,7 @@ var init_es2 = __esm({
397
411
  exportError: "No se pudo exportar la grabaci\xF3n.",
398
412
  failed: "Fallida",
399
413
  help: "Inspeccione audio guardado, l\xEDmites de voz/silencio e intentos de transcripci\xF3n. Audio y texto pueden ser sensibles. La reproducci\xF3n es local; la exportaci\xF3n incluye audio y texto. Inspeccionar no cambia la captura; solo la eliminaci\xF3n expl\xEDcita borra grabaciones.",
414
+ ignoredMuted: "Ignorado por estar silenciado",
400
415
  inputStopComplete: "Parada de reconocimiento/medidor completada",
401
416
  interrupted: "Interrumpida",
402
417
  loadError: "No se pudieron cargar las grabaciones. Intente actualizar.",
@@ -458,6 +473,7 @@ var init_es2 = __esm({
458
473
  storagePartial: "Al menos {size} \xB7 {count} sesi\xF3n(es) sin medir",
459
474
  storageScope: "Todas las fuentes de este navegador y sitio. Solo audio codificado guardado; excluye metadatos, sobrecarga de base de datos, archivos exportados y audio sin guardar. No es el uso total del navegador.",
460
475
  storageSessions: "{count} sesi\xF3n(es) de todas las fuentes",
476
+ storageTab: "Almacenamiento",
461
477
  storageTotal: "Total de audio almacenado",
462
478
  storageUnknown: "No disponible",
463
479
  success: "\xC9xito",
@@ -466,6 +482,10 @@ var init_es2 = __esm({
466
482
  unavailable: "Servicio de grabaci\xF3n no disponible. Active la grabaci\xF3n en los ajustes de Voz en vivo con un runtime compatible.",
467
483
  unstar: "Quitar protecci\xF3n",
468
484
  unstarHelp: "Permite la limpieza autom\xE1tica e incluye de nuevo esta grabaci\xF3n en el recuento de retenci\xF3n. La eliminaci\xF3n manual sigue permitida. El almacenamiento y la protecci\xF3n contra eliminaci\xF3n del navegador no cambian.",
485
+ viewLayout: "Vista",
486
+ viewList: "Lista",
487
+ viewSession: "Sesi\xF3n",
488
+ viewSplit: "Dividida",
469
489
  voiceCommandRecognized: "Comando de voz reconocido",
470
490
  waitCancelled: "Espera cancelada",
471
491
  waitChanged: "Motivos de espera cambiados",
@@ -622,6 +642,10 @@ var init_fr2 = __esm({
622
642
  commandStopSpeaking: "Arr\xEAter la parole",
623
643
  completed: "Termin\xE9",
624
644
  confirmDelete: "Supprimer cet enregistrement entier, y compris son audio et son historique de transcription ? Cette action est irr\xE9versible. Supprimer un enregistrement actif supprime aussi son futur audio enregistr\xE9.",
645
+ copyTranscripts: "Copier les transcriptions",
646
+ copyTranscriptsError: "Copie impossible. V\xE9rifiez l\u2019autorisation du presse-papiers ou copiez manuellement.",
647
+ copyTranscriptsHelp: "Copie la derni\xE8re transcription r\xE9ussie non vide par segment de la liste actuelle, dans son ordre. Inclut la parole ignor\xE9e et respecte la fen\xEAtre et la recherche.",
648
+ copyTranscriptsSuccess: "Transcriptions copi\xE9es.",
625
649
  date: "D\xE9but",
626
650
  delete: "Supprimer toute la session",
627
651
  deleteAll: "Supprimer tous les enregistrements",
@@ -656,6 +680,7 @@ var init_fr2 = __esm({
656
680
  exportError: "Impossible d\u2019exporter l\u2019enregistrement.",
657
681
  failed: "\xC9chec",
658
682
  help: "Inspectez l\u2019audio sauvegard\xE9, les limites parole/silence et les tentatives de transcription. Audio et texte peuvent \xEAtre sensibles. Lecture locale ; l\u2019export contient l\u2019audio et le texte. L\u2019inspection ne change pas la capture ; seule une suppression explicite efface les enregistrements.",
683
+ ignoredMuted: "Ignor\xE9 en sourdine",
659
684
  inputStopComplete: "Arr\xEAt reconnaissance/indicateur termin\xE9",
660
685
  interrupted: "Interrompu",
661
686
  loadError: "Impossible de charger les enregistrements. Actualisez la liste.",
@@ -717,6 +742,7 @@ var init_fr2 = __esm({
717
742
  storagePartial: "Au moins {size} \xB7 {count} session(s) non mesur\xE9e(s)",
718
743
  storageScope: "Toutes les sources de ce navigateur et site. Audio encod\xE9 sauvegard\xE9 uniquement ; hors m\xE9tadonn\xE9es, surcharge de base de donn\xE9es, exports et audio non sauvegard\xE9. Ce n\u2019est pas le stockage total du navigateur.",
719
744
  storageSessions: "{count} session(s), toutes sources confondues",
745
+ storageTab: "Stockage",
720
746
  storageTotal: "Total de l\u2019audio stock\xE9",
721
747
  storageUnknown: "Indisponible",
722
748
  success: "R\xE9ussite",
@@ -725,6 +751,10 @@ var init_fr2 = __esm({
725
751
  unavailable: "Service d\u2019enregistrement indisponible. Activez l\u2019enregistrement dans les param\xE8tres de Voix en direct avec un runtime compatible.",
726
752
  unstar: "Retirer la protection",
727
753
  unstarHelp: "Autorise le nettoyage automatique et compte \xE0 nouveau cet enregistrement parmi ceux \xE0 conserver. La suppression manuelle reste autoris\xE9e. Le stockage et la protection contre l\u2019\xE9viction par le navigateur ne changent pas.",
754
+ viewLayout: "Vue",
755
+ viewList: "Liste",
756
+ viewSession: "Session",
757
+ viewSplit: "Partag\xE9e",
728
758
  voiceCommandRecognized: "Commande vocale reconnue",
729
759
  waitCancelled: "Attente annul\xE9e",
730
760
  waitChanged: "Motifs d\u2019attente modifi\xE9s",
@@ -881,6 +911,10 @@ var init_hi2 = __esm({
881
911
  commandStopSpeaking: "\u092C\u094B\u0932\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
882
912
  completed: "\u092A\u0942\u0930\u094D\u0923",
883
913
  confirmDelete: "\u0911\u0921\u093F\u092F\u094B \u0914\u0930 \u091F\u094D\u0930\u093E\u0902\u0938\u0915\u094D\u0930\u093F\u092A\u094D\u0936\u0928 \u0907\u0924\u093F\u0939\u093E\u0938 \u0938\u0939\u093F\u0924 \u092F\u0939 \u092A\u0942\u0930\u0940 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0939\u091F\u093E\u090F\u0901? \u0907\u0938\u0947 \u0935\u093E\u092A\u0938 \u0928\u0939\u0940\u0902 \u0932\u093E\u092F\u093E \u091C\u093E \u0938\u0915\u0924\u093E\u0964 \u0938\u0915\u094D\u0930\u093F\u092F \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0939\u091F\u093E\u0928\u0947 \u092A\u0930 \u0906\u0917\u0947 \u0938\u0939\u0947\u091C\u093E \u091C\u093E\u0928\u0947 \u0935\u093E\u0932\u093E \u0911\u0921\u093F\u092F\u094B \u092D\u0940 \u0939\u091F\u093E \u0926\u093F\u092F\u093E \u091C\u093E\u0924\u093E \u0939\u0948\u0964",
914
+ copyTranscripts: "\u092A\u094D\u0930\u0924\u093F\u0932\u0947\u0916\u0928 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902",
915
+ copyTranscriptsError: "\u0915\u0949\u092A\u0940 \u0928\u0939\u0940\u0902 \u0939\u0941\u0906\u0964 \u0915\u094D\u0932\u093F\u092A\u092C\u094B\u0930\u094D\u0921 \u0915\u0940 \u0905\u0928\u0941\u092E\u0924\u093F \u091C\u093E\u0901\u091A\u0947\u0902 \u092F\u093E \u092A\u093E\u0920 \u0938\u094D\u0935\u092F\u0902 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902\u0964",
916
+ copyTranscriptsHelp: "\u0935\u0930\u094D\u0924\u092E\u093E\u0928 \u0938\u0942\u091A\u0940 \u0915\u0947 \u0939\u0930 \u0916\u0902\u0921 \u0915\u093E \u0905\u0902\u0924\u093F\u092E \u0938\u092B\u0932 \u0917\u0948\u0930-\u0916\u093E\u0932\u0940 \u092A\u094D\u0930\u0924\u093F\u0932\u0947\u0916\u0928 \u0938\u0942\u091A\u0940 \u0915\u094D\u0930\u092E \u092E\u0947\u0902 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902\u0964 \u0905\u0928\u0926\u0947\u0916\u0940 \u0906\u0935\u093E\u091C\u093C \u092D\u0940 \u0936\u093E\u092E\u093F\u0932 \u0939\u0948; \u0935\u093F\u0902\u0921\u094B \u0914\u0930 \u0916\u094B\u091C \u0932\u093E\u0917\u0942 \u0939\u094B\u0924\u0947 \u0939\u0948\u0902\u0964",
917
+ copyTranscriptsSuccess: "\u092A\u094D\u0930\u0924\u093F\u0932\u0947\u0916\u0928 \u0915\u0949\u092A\u0940 \u0939\u0941\u0906\u0964",
884
918
  date: "\u0936\u0941\u0930\u0941\u0906\u0924",
885
919
  delete: "\u092A\u0942\u0930\u0940 \u0938\u0924\u094D\u0930 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0939\u091F\u093E\u090F\u0901",
886
920
  deleteAll: "\u0938\u092D\u0940 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0939\u091F\u093E\u090F\u0901",
@@ -915,6 +949,7 @@ var init_hi2 = __esm({
915
949
  exportError: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0928\u093F\u0930\u094D\u092F\u093E\u0924 \u0928\u0939\u0940\u0902 \u0915\u0940 \u091C\u093E \u0938\u0915\u0940\u0964",
916
950
  failed: "\u0935\u093F\u092B\u0932",
917
951
  help: "\u0938\u0939\u0947\u091C\u093E \u0911\u0921\u093F\u092F\u094B, \u0906\u0935\u093E\u091C\u093C/\u092E\u094C\u0928 \u0938\u0940\u092E\u093E\u090F\u0901 \u0914\u0930 \u091F\u094D\u0930\u093E\u0902\u0938\u0915\u094D\u0930\u093F\u092A\u094D\u0936\u0928 \u092A\u094D\u0930\u092F\u093E\u0938 \u0926\u0947\u0916\u0947\u0902\u0964 \u0911\u0921\u093F\u092F\u094B \u0914\u0930 \u091F\u0947\u0915\u094D\u0938\u094D\u091F \u0938\u0902\u0935\u0947\u0926\u0928\u0936\u0940\u0932 \u0939\u094B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902\u0964 \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0938\u094D\u0925\u093E\u0928\u0940\u092F \u0939\u0948; \u0928\u093F\u0930\u094D\u092F\u093E\u0924 \u092E\u0947\u0902 \u0911\u0921\u093F\u092F\u094B \u0914\u0930 \u091F\u0947\u0915\u094D\u0938\u094D\u091F \u0936\u093E\u092E\u093F\u0932 \u0939\u0948\u0902\u0964 \u0928\u093F\u0930\u0940\u0915\u094D\u0937\u0923 \u0915\u0948\u092A\u094D\u091A\u0930 \u0928\u0939\u0940\u0902 \u092C\u0926\u0932\u0924\u093E; \u0915\u0947\u0935\u0932 \u0938\u094D\u092A\u0937\u094D\u091F \u0939\u091F\u093E\u0928\u0947 \u0915\u0940 \u0915\u093E\u0930\u094D\u0930\u0935\u093E\u0908 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0939\u091F\u093E\u0924\u0940 \u0939\u0948\u0964",
952
+ ignoredMuted: "\u092E\u094D\u092F\u0942\u091F \u0939\u094B\u0928\u0947 \u0915\u0947 \u0915\u093E\u0930\u0923 \u0905\u0928\u0926\u0947\u0916\u093E",
918
953
  inputStopComplete: "\u092A\u0939\u091A\u093E\u0928/\u092E\u0940\u091F\u0930 \u0930\u094B\u0915\u0928\u093E \u092A\u0942\u0930\u093E \u0939\u0941\u0906",
919
954
  interrupted: "\u092C\u093E\u0927\u093F\u0924",
920
955
  loadError: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0938\u0924\u094D\u0930 \u0932\u094B\u0921 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u0947\u0964 \u0930\u0940\u092B\u093C\u094D\u0930\u0947\u0936 \u0915\u0930\u0947\u0902\u0964",
@@ -976,6 +1011,7 @@ var init_hi2 = __esm({
976
1011
  storagePartial: "\u0915\u092E \u0938\u0947 \u0915\u092E {size} \xB7 {count} \u0938\u0924\u094D\u0930\u094B\u0902 \u0915\u093E \u092E\u093E\u092A \u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902",
977
1012
  storageScope: "\u0907\u0938 \u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u0914\u0930 \u0938\u093E\u0907\u091F \u0915\u0947 \u0938\u092D\u0940 \u0938\u094D\u0930\u094B\u0924\u0964 \u0915\u0947\u0935\u0932 \u0938\u0939\u0947\u091C\u093E \u090F\u0928\u094D\u0915\u094B\u0921\u0947\u0921 \u0911\u0921\u093F\u092F\u094B; \u092E\u0947\u091F\u093E\u0921\u0947\u091F\u093E, \u0921\u0947\u091F\u093E\u092C\u0947\u0938 \u0913\u0935\u0930\u0939\u0947\u0921, \u0928\u093F\u0930\u094D\u092F\u093E\u0924 \u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u0914\u0930 \u092C\u093F\u0928\u093E \u0938\u0939\u0947\u091C\u093E \u0911\u0921\u093F\u092F\u094B \u0936\u093E\u092E\u093F\u0932 \u0928\u0939\u0940\u0902 \u0939\u0948\u0902\u0964 \u092F\u0939 \u092A\u0942\u0930\u0947 \u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u0915\u093E \u0938\u0902\u0917\u094D\u0930\u0939\u0923 \u0909\u092A\u092F\u094B\u0917 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964",
978
1013
  storageSessions: "\u0938\u092D\u0940 \u0938\u094D\u0930\u094B\u0924\u094B\u0902 \u092E\u0947\u0902 {count} \u0938\u0924\u094D\u0930",
1014
+ storageTab: "\u0938\u0902\u0917\u094D\u0930\u0939\u0923",
979
1015
  storageTotal: "\u0915\u0941\u0932 \u0938\u0939\u0947\u091C\u093E \u0917\u092F\u093E \u0911\u0921\u093F\u092F\u094B",
980
1016
  storageUnknown: "\u0905\u0928\u0941\u092A\u0932\u092C\u094D\u0927",
981
1017
  success: "\u0938\u092B\u0932",
@@ -984,6 +1020,10 @@ var init_hi2 = __esm({
984
1020
  unavailable: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0938\u0947\u0935\u093E \u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964 \u0938\u0902\u0917\u0924 \u0930\u0928\u091F\u093E\u0907\u092E \u0915\u0947 \u0938\u093E\u0925 \u0932\u093E\u0907\u0935 \u0935\u0949\u0907\u0938 \u0938\u0947\u091F\u093F\u0902\u0917\u094D\u0938 \u092E\u0947\u0902 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u091A\u093E\u0932\u0942 \u0915\u0930\u0947\u0902\u0964",
985
1021
  unstar: "\u0938\u0941\u0930\u0915\u094D\u0937\u093E \u0939\u091F\u093E\u090F\u0901",
986
1022
  unstarHelp: "\u0938\u094D\u0935\u091A\u093E\u0932\u093F\u0924 \u0938\u092B\u093C\u093E\u0908 \u0915\u0940 \u0905\u0928\u0941\u092E\u0924\u093F \u0926\u0947\u0902 \u0914\u0930 \u0907\u0938 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0915\u094B \u092B\u093F\u0930 \u0938\u0947 \u0930\u0916\u0940 \u091C\u093E\u0928\u0947 \u0935\u093E\u0932\u0940 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0915\u0940 \u0917\u093F\u0928\u0924\u0940 \u092E\u0947\u0902 \u0936\u093E\u092E\u093F\u0932 \u0915\u0930\u0947\u0902\u0964 \u092E\u0948\u0928\u094D\u092F\u0941\u0905\u0932 \u0930\u0942\u092A \u0938\u0947 \u0939\u091F\u093E\u0928\u093E \u0905\u092C \u092D\u0940 \u0938\u0902\u092D\u0935 \u0939\u0948\u0964 \u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u0915\u093E \u0938\u094D\u091F\u094B\u0930\u0947\u091C \u0914\u0930 \u0928\u093F\u0937\u094D\u0915\u093E\u0938\u0928 \u0938\u0941\u0930\u0915\u094D\u0937\u093E \u0928\u0939\u0940\u0902 \u092C\u0926\u0932\u0924\u0947\u0964",
1023
+ viewLayout: "\u0926\u0943\u0936\u094D\u092F",
1024
+ viewList: "\u0938\u0942\u091A\u0940",
1025
+ viewSession: "\u0938\u0924\u094D\u0930",
1026
+ viewSplit: "\u0935\u093F\u092D\u093E\u091C\u093F\u0924",
987
1027
  voiceCommandRecognized: "\u0935\u0949\u0907\u0938 \u0915\u092E\u093E\u0902\u0921 \u092A\u0939\u091A\u093E\u0928\u093E \u0917\u092F\u093E",
988
1028
  waitCancelled: "\u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0930\u0926\u094D\u0926",
989
1029
  waitChanged: "\u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0915\u0947 \u0915\u093E\u0930\u0923 \u092C\u0926\u0932\u0947",
@@ -1140,6 +1180,10 @@ var init_pt_BR2 = __esm({
1140
1180
  commandStopSpeaking: "Parar fala",
1141
1181
  completed: "Conclu\xEDda",
1142
1182
  confirmDelete: "Excluir esta grava\xE7\xE3o inteira, incluindo \xE1udio e hist\xF3rico de transcri\xE7\xE3o? N\xE3o \xE9 poss\xEDvel desfazer. Excluir uma grava\xE7\xE3o ativa tamb\xE9m descarta o \xE1udio salvo futuro.",
1183
+ copyTranscripts: "Copiar transcri\xE7\xF5es",
1184
+ copyTranscriptsError: "N\xE3o foi poss\xEDvel copiar. Verifique a permiss\xE3o da \xE1rea de transfer\xEAncia ou copie o texto manualmente.",
1185
+ copyTranscriptsHelp: "Copia a \xFAltima transcri\xE7\xE3o bem-sucedida n\xE3o vazia de cada segmento da lista atual, na ordem da lista. Inclui falas ignoradas e respeita a janela e a busca.",
1186
+ copyTranscriptsSuccess: "Transcri\xE7\xF5es copiadas.",
1143
1187
  date: "In\xEDcio",
1144
1188
  delete: "Excluir sess\xE3o inteira",
1145
1189
  deleteAll: "Apagar todas as grava\xE7\xF5es",
@@ -1174,6 +1218,7 @@ var init_pt_BR2 = __esm({
1174
1218
  exportError: "N\xE3o foi poss\xEDvel exportar a grava\xE7\xE3o.",
1175
1219
  failed: "Falhou",
1176
1220
  help: "Inspecione \xE1udio salvo, limites de fala/sil\xEAncio e tentativas de transcri\xE7\xE3o. \xC1udio e texto podem ser sens\xEDveis. Reprodu\xE7\xE3o local; a exporta\xE7\xE3o inclui \xE1udio e texto. Inspecionar n\xE3o altera a captura; apenas a exclus\xE3o expl\xEDcita remove grava\xE7\xF5es.",
1221
+ ignoredMuted: "Ignorado por estar no mudo",
1177
1222
  inputStopComplete: "Parada do reconhecimento/medidor conclu\xEDda",
1178
1223
  interrupted: "Interrompida",
1179
1224
  loadError: "N\xE3o foi poss\xEDvel carregar as grava\xE7\xF5es. Tente atualizar.",
@@ -1235,6 +1280,7 @@ var init_pt_BR2 = __esm({
1235
1280
  storagePartial: "Pelo menos {size} \xB7 {count} sess\xE3o(\xF5es) sem medi\xE7\xE3o",
1236
1281
  storageScope: "Todas as fontes deste navegador e site. Apenas \xE1udio codificado salvo; n\xE3o inclui metadados, espa\xE7o interno do banco, arquivos exportados nem \xE1udio ainda n\xE3o salvo. N\xE3o \xE9 o uso total do navegador.",
1237
1282
  storageSessions: "{count} sess\xE3o(\xF5es) de todas as fontes",
1283
+ storageTab: "Armazenamento",
1238
1284
  storageTotal: "Total de \xE1udio armazenado",
1239
1285
  storageUnknown: "Indispon\xEDvel",
1240
1286
  success: "Sucesso",
@@ -1243,6 +1289,10 @@ var init_pt_BR2 = __esm({
1243
1289
  unavailable: "Servi\xE7o de grava\xE7\xE3o indispon\xEDvel. Ative a grava\xE7\xE3o nas configura\xE7\xF5es da Voz ao Vivo com um runtime compat\xEDvel.",
1244
1290
  unstar: "Remover prote\xE7\xE3o",
1245
1291
  unstarHelp: "Permite a limpeza autom\xE1tica e inclui novamente esta grava\xE7\xE3o na contagem de reten\xE7\xE3o. A exclus\xE3o manual continua permitida. O armazenamento e a prote\xE7\xE3o contra remo\xE7\xE3o pelo navegador n\xE3o mudam.",
1292
+ viewLayout: "Visualiza\xE7\xE3o",
1293
+ viewList: "Lista",
1294
+ viewSession: "Sess\xE3o",
1295
+ viewSplit: "Dividido",
1246
1296
  voiceCommandRecognized: "Comando de voz reconhecido",
1247
1297
  waitCancelled: "Espera cancelada",
1248
1298
  waitChanged: "Motivos da espera alterados",
@@ -1399,6 +1449,10 @@ var init_zh2 = __esm({
1399
1449
  commandStopSpeaking: "\u505C\u6B62\u8BB2\u8BDD",
1400
1450
  completed: "\u5DF2\u5B8C\u6210",
1401
1451
  confirmDelete: "\u5220\u9664\u6574\u4E2A\u5F55\u97F3\uFF0C\u5305\u62EC\u97F3\u9891\u548C\u8F6C\u5F55\u5386\u53F2\uFF1F\u6B64\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500\u3002\u5220\u9664\u6B63\u5728\u5F55\u5236\u7684\u4F1A\u8BDD\u8FD8\u4F1A\u4E22\u5F03\u540E\u7EED\u4FDD\u5B58\u7684\u97F3\u9891\u3002",
1452
+ copyTranscripts: "\u590D\u5236\u8F6C\u5199",
1453
+ copyTranscriptsError: "\u65E0\u6CD5\u590D\u5236\u3002\u8BF7\u68C0\u67E5\u526A\u8D34\u677F\u6743\u9650\u6216\u624B\u52A8\u590D\u5236\u6587\u672C\u3002",
1454
+ copyTranscriptsHelp: "\u6309\u5F53\u524D\u5217\u8868\u987A\u5E8F\u590D\u5236\u6BCF\u4E2A\u7247\u6BB5\u6700\u65B0\u7684\u6210\u529F\u4E14\u975E\u7A7A\u8F6C\u5199\u3002\u5305\u542B\u88AB\u5FFD\u7565\u7684\u8BED\u97F3\uFF0C\u5E76\u9075\u5FAA\u65F6\u95F4\u7A97\u53E3\u548C\u641C\u7D22\u6761\u4EF6\u3002",
1455
+ copyTranscriptsSuccess: "\u8F6C\u5199\u5DF2\u590D\u5236\u3002",
1402
1456
  date: "\u5F00\u59CB\u65F6\u95F4",
1403
1457
  delete: "\u5220\u9664\u6574\u4E2A\u4F1A\u8BDD",
1404
1458
  deleteAll: "\u5220\u9664\u6240\u6709\u5F55\u97F3",
@@ -1433,6 +1487,7 @@ var init_zh2 = __esm({
1433
1487
  exportError: "\u65E0\u6CD5\u5BFC\u51FA\u5F55\u97F3\u3002",
1434
1488
  failed: "\u5931\u8D25",
1435
1489
  help: "\u67E5\u770B\u5DF2\u4FDD\u5B58\u97F3\u9891\u3001\u8BED\u97F3/\u9759\u97F3\u8FB9\u754C\u548C\u8F6C\u5F55\u5C1D\u8BD5\u3002\u97F3\u9891\u548C\u6587\u672C\u53EF\u80FD\u654F\u611F\u3002\u64AD\u653E\u5728\u672C\u5730\u8FDB\u884C\uFF1B\u5BFC\u51FA\u5305\u542B\u97F3\u9891\u548C\u6587\u672C\u3002\u67E5\u770B\u4E0D\u4F1A\u66F4\u6539\u91C7\u96C6\uFF1B\u53EA\u6709\u660E\u786E\u5220\u9664\u64CD\u4F5C\u624D\u4F1A\u79FB\u9664\u5F55\u97F3\u3002",
1490
+ ignoredMuted: "\u9759\u97F3\u65F6\u5DF2\u5FFD\u7565",
1436
1491
  inputStopComplete: "\u8BC6\u522B/\u97F3\u91CF\u8BA1\u505C\u6B62\u5DF2\u5B8C\u6210",
1437
1492
  interrupted: "\u5DF2\u4E2D\u65AD",
1438
1493
  loadError: "\u65E0\u6CD5\u52A0\u8F7D\u5F55\u97F3\u4F1A\u8BDD\u3002\u8BF7\u5237\u65B0\u91CD\u8BD5\u3002",
@@ -1494,6 +1549,7 @@ var init_zh2 = __esm({
1494
1549
  storagePartial: "\u81F3\u5C11 {size} \xB7 {count} \u4E2A\u4F1A\u8BDD\u672A\u6D4B\u91CF",
1495
1550
  storageScope: "\u6B64\u6D4F\u89C8\u5668\u548C\u7AD9\u70B9\u7684\u6240\u6709\u6765\u6E90\u3002\u4EC5\u8BA1\u7B97\u5DF2\u4FDD\u5B58\u7684\u7F16\u7801\u97F3\u9891\uFF1B\u4E0D\u542B\u5143\u6570\u636E\u3001\u6570\u636E\u5E93\u5F00\u9500\u3001\u5BFC\u51FA\u6587\u4EF6\u6216\u672A\u4FDD\u5B58\u97F3\u9891\u3002\u5E76\u975E\u6D4F\u89C8\u5668\u603B\u5B58\u50A8\u7528\u91CF\u3002",
1496
1551
  storageSessions: "\u6240\u6709\u6765\u6E90\u5171 {count} \u4E2A\u4F1A\u8BDD",
1552
+ storageTab: "\u5B58\u50A8",
1497
1553
  storageTotal: "\u5DF2\u5B58\u50A8\u97F3\u9891\u603B\u91CF",
1498
1554
  storageUnknown: "\u4E0D\u53EF\u7528",
1499
1555
  success: "\u6210\u529F",
@@ -1502,6 +1558,10 @@ var init_zh2 = __esm({
1502
1558
  unavailable: "\u5F55\u97F3\u670D\u52A1\u4E0D\u53EF\u7528\u3002\u8BF7\u5728\u517C\u5BB9\u7684\u5B9E\u65F6\u8BED\u97F3\u8BBE\u7F6E\u4E2D\u542F\u7528\u5F55\u97F3\u3002",
1503
1559
  unstar: "\u53D6\u6D88\u4FDD\u62A4",
1504
1560
  unstarHelp: "\u5141\u8BB8\u81EA\u52A8\u6E05\u7406\uFF0C\u5E76\u5C06\u6B64\u5F55\u97F3\u91CD\u65B0\u8BA1\u5165\u4FDD\u7559\u6570\u91CF\u3002\u4ECD\u53EF\u624B\u52A8\u5220\u9664\u3002\u6D4F\u89C8\u5668\u5B58\u50A8\u548C\u9632\u9010\u51FA\u4FDD\u62A4\u4FDD\u6301\u4E0D\u53D8\u3002",
1561
+ viewLayout: "\u89C6\u56FE",
1562
+ viewList: "\u5217\u8868",
1563
+ viewSession: "\u4F1A\u8BDD",
1564
+ viewSplit: "\u5206\u680F",
1505
1565
  voiceCommandRecognized: "\u5DF2\u8BC6\u522B\u8BED\u97F3\u547D\u4EE4",
1506
1566
  waitCancelled: "\u7B49\u5F85\u5DF2\u53D6\u6D88",
1507
1567
  waitChanged: "\u7B49\u5F85\u539F\u56E0\u5DF2\u6539\u53D8",
@@ -1596,7 +1656,7 @@ __export(apply_exports, {
1596
1656
  inject: () => inject
1597
1657
  });
1598
1658
  module.exports = __toCommonJS(apply_exports);
1599
- var import_react30 = __toESM(require("react"), 1);
1659
+ var import_react31 = __toESM(require("react"), 1);
1600
1660
 
1601
1661
  // ../live-voice/src/shared/design-system/buttons/IconButton.tsx
1602
1662
  var import_react2 = __toESM(require("react"), 1);
@@ -1825,7 +1885,7 @@ var import_react20 = __toESM(require("react"), 1);
1825
1885
  var import_client = require("react-dom/client");
1826
1886
 
1827
1887
  // src/DebuggerPanel.tsx
1828
- var import_react27 = __toESM(require("react"), 1);
1888
+ var import_react28 = __toESM(require("react"), 1);
1829
1889
 
1830
1890
  // ../live-voice/src/modules/core/diagnostics.ts
1831
1891
  var DIAGNOSTIC_KEY = /* @__PURE__ */ Symbol.for("dsh-live-voice.diagnostics.v1");
@@ -1960,7 +2020,8 @@ function StateTree({
1960
2020
  }
1961
2021
 
1962
2022
  // src/RecordingPanel.tsx
1963
- var import_react25 = __toESM(require("react"), 1);
2023
+ var import_react26 = __toESM(require("react"), 1);
2024
+ var import_react_dom = require("react-dom");
1964
2025
 
1965
2026
  // src/formatBytes.ts
1966
2027
  function formatBytes(bytes, locale = "en") {
@@ -2164,12 +2225,17 @@ function RecordingStorage({
2164
2225
  ));
2165
2226
  }
2166
2227
 
2167
- // src/EventTimeline.tsx
2168
- var import_react23 = __toESM(require("react"), 1);
2169
-
2170
2228
  // src/eventTimelineModel.ts
2171
- var eventCategories = ["waiting", "playback", "capture", "command", "attention"];
2229
+ var eventCategories = [
2230
+ "waiting",
2231
+ "playback",
2232
+ "capture",
2233
+ "command",
2234
+ "ignored",
2235
+ "attention"
2236
+ ];
2172
2237
  function eventCategory(kind) {
2238
+ if (kind === "transcription-ignored-muted") return "ignored";
2173
2239
  if (kind === "voice-command-recognized") return "command";
2174
2240
  if (kind.startsWith("assistant-wait")) return "waiting";
2175
2241
  if (kind.includes("error") || kind.includes("failed") || kind.includes("cancel") || kind.includes("interruption"))
@@ -2177,6 +2243,32 @@ function eventCategory(kind) {
2177
2243
  if (kind.startsWith("capture-") || kind === "input-stop-complete") return "capture";
2178
2244
  return "playback";
2179
2245
  }
2246
+ function eventIntervals(events, frontier) {
2247
+ const open = /* @__PURE__ */ new Map();
2248
+ const ranges = [];
2249
+ for (const event of events) {
2250
+ const category = event.kind.startsWith("assistant-wait") ? "waiting" : "playback";
2251
+ const starts = event.kind === "assistant-wait-start" || event.kind === "assistant-start" || event.kind === "assistant-resume";
2252
+ const ends = event.kind === "assistant-wait-end" || ["assistant-end", "assistant-pause", "assistant-cancel", "assistant-error"].includes(
2253
+ event.kind
2254
+ );
2255
+ if (starts && !open.has(category)) open.set(category, event);
2256
+ if (ends && open.has(category)) {
2257
+ const begin = open.get(category);
2258
+ open.delete(category);
2259
+ if (event.atSample > begin.atSample)
2260
+ ranges.push({ category, start: begin.atSample, end: event.atSample });
2261
+ }
2262
+ }
2263
+ for (const [category, begin] of open)
2264
+ if (frontier > begin.atSample)
2265
+ ranges.push({
2266
+ category,
2267
+ start: begin.atSample,
2268
+ end: frontier
2269
+ });
2270
+ return ranges;
2271
+ }
2180
2272
  function groupTimelineEvents(events, start, end, width) {
2181
2273
  const span = Math.max(1, end - start);
2182
2274
  const slots = Math.max(1, Math.floor(Math.max(1, width) / 36));
@@ -2204,8 +2296,9 @@ function groupTimelineEvents(events, start, end, width) {
2204
2296
  }
2205
2297
 
2206
2298
  // src/EventTimeline.tsx
2299
+ var import_react23 = __toESM(require("react"), 1);
2207
2300
  function EventGlyph({ kind }) {
2208
- const path = kind.includes("command") ? "m13 2-9 12h7l-1 8 10-12h-7l1-8Z" : kind.includes("wait") ? "M6 3h12M6 21h12M7 3v4l10 10v4M17 3v4L7 17v4" : kind.includes("error") || kind.includes("failed") ? "M12 3 2 21h20L12 3ZM12 9v5M12 17v.1" : kind.includes("cancel") ? "m6 6 12 12M6 18 18 6" : kind.includes("pause") || kind.includes("interruption") ? "M8 5v14M16 5v14" : kind.endsWith("end") || kind === "input-stop-complete" ? "M5 5h14v14H5Z" : kind.startsWith("capture") ? "M9 4a3 3 0 0 1 6 0v8a3 3 0 0 1-6 0V4ZM5 10v2a7 7 0 0 0 14 0v-2M12 19v3M8 22h8" : "m7 4 13 8-13 8Z";
2301
+ const path = kind.includes("ignored") ? "M3 3l18 18M9 9v3a3 3 0 0 0 5 2M15 8V5a3 3 0 0 0-5-2M6 10v2a6 6 0 0 0 10 4M12 18v4M8 22h8" : kind.includes("command") ? "m13 2-9 12h7l-1 8 10-12h-7l1-8Z" : kind.includes("wait") ? "M6 3h12M6 21h12M7 3v4l10 10v4M17 3v4L7 17v4" : kind.includes("error") || kind.includes("failed") ? "M12 3 2 21h20L12 3ZM12 9v5M12 17v.1" : kind.includes("cancel") ? "m6 6 12 12M6 18 18 6" : kind.includes("pause") || kind.includes("interruption") ? "M8 5v14M16 5v14" : kind.endsWith("end") || kind === "input-stop-complete" ? "M5 5h14v14H5Z" : kind.startsWith("capture") ? "M9 4a3 3 0 0 1 6 0v8a3 3 0 0 1-6 0V4ZM5 10v2a7 7 0 0 0 14 0v-2M12 19v3M8 22h8" : "m7 4 13 8-13 8Z";
2209
2302
  return /* @__PURE__ */ import_react23.default.createElement(
2210
2303
  "svg",
2211
2304
  {
@@ -2250,6 +2343,7 @@ function EventTimeline({
2250
2343
  playback: copy.eventPlayback,
2251
2344
  capture: copy.eventCapture,
2252
2345
  command: copy.eventCommand,
2346
+ ignored: copy.ignoredMuted,
2253
2347
  attention: copy.eventAttention
2254
2348
  };
2255
2349
  const timestamp = (event) => new Date(event.occurredAt).toLocaleString() + "." + new Date(event.occurredAt).getMilliseconds().toString().padStart(3, "0");
@@ -2309,12 +2403,12 @@ function EventTimeline({
2309
2403
  };
2310
2404
  }, [active, group, copy]);
2311
2405
  const show = (item, anchor) => setActive({ key: item.key, anchor });
2312
- return /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-event-timeline", ref: root }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-event-legend" }, eventCategories.map((category) => /* @__PURE__ */ import_react23.default.createElement("span", { key: category, "data-category": category }, /* @__PURE__ */ import_react23.default.createElement(
2406
+ return /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-event-timeline", ref: root }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-event-legend" }, /* @__PURE__ */ import_react23.default.createElement("span", { "data-category": "silence" }, /* @__PURE__ */ import_react23.default.createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true" }, /* @__PURE__ */ import_react23.default.createElement("path", { d: "M2 8h12", stroke: "currentColor", strokeWidth: "2" })), copy.silence), eventCategories.map((category) => /* @__PURE__ */ import_react23.default.createElement("span", { key: category, "data-category": category }, /* @__PURE__ */ import_react23.default.createElement(
2313
2407
  EventGlyph,
2314
2408
  {
2315
- kind: category === "waiting" ? "wait" : category === "capture" ? "capture-start" : category === "attention" ? "error" : category === "command" ? "voice-command" : "assistant-start"
2409
+ kind: category === "waiting" ? "wait" : category === "capture" ? "capture-start" : category === "attention" ? "error" : category === "command" ? "voice-command" : category === "ignored" ? "transcription-ignored-muted" : "assistant-start"
2316
2410
  }
2317
- ), labels[category]))), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dlvd-event-help" }, copy.eventTrackHelp), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-event-track", ref: lanes, role: "group", "aria-label": copy.events }, eventCategories.map((category) => /* @__PURE__ */ import_react23.default.createElement(
2411
+ ), labels[category]))), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dlvd-event-help" }, copy.eventTrackHelp), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-event-track", ref: lanes, role: "group", "aria-label": copy.events }, eventCategories.filter((category) => groups.some((group2) => group2.category === category)).map((category) => /* @__PURE__ */ import_react23.default.createElement(
2318
2412
  "div",
2319
2413
  {
2320
2414
  className: "dlvd-event-lane",
@@ -2454,7 +2548,7 @@ function TimelineWindow({ total, start, end, copy, format, onChange, onReset })
2454
2548
  onKeyDown: (event) => keyboard(event, part)
2455
2549
  });
2456
2550
  const percent = (value) => total > 0 ? value / total * 100 : 0;
2457
- return /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-timeline-window", role: "group", "aria-label": copy.window }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-window-selector" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-window-values" }, /* @__PURE__ */ import_react24.default.createElement("span", null, copy.windowStart, ": ", format(start)), /* @__PURE__ */ import_react24.default.createElement("span", null, copy.windowEnd, ": ", format(end))), /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-window-track", ref: track }, /* @__PURE__ */ import_react24.default.createElement(
2551
+ return /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-timeline-window", role: "group", "aria-label": copy.window }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-window-selector" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-window-values" }, /* @__PURE__ */ import_react24.default.createElement("span", null, copy.start, ": ", format(start)), /* @__PURE__ */ import_react24.default.createElement("span", null, copy.end, ": ", format(end))), /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-window-track", ref: track }, /* @__PURE__ */ import_react24.default.createElement(
2458
2552
  "button",
2459
2553
  {
2460
2554
  type: "button",
@@ -2532,6 +2626,55 @@ function TimelineWindow({ total, start, end, copy, format, onChange, onReset })
2532
2626
  ));
2533
2627
  }
2534
2628
 
2629
+ // src/CopyTranscripts.tsx
2630
+ var import_react25 = __toESM(require("react"), 1);
2631
+ function segmentTranscripts(segments) {
2632
+ return segments.map(
2633
+ (segment) => [...segment.attempts].reverse().find((attempt) => attempt.status === "success" && attempt.text?.trim())?.text?.trim() || ""
2634
+ ).filter(Boolean).join("\n\n");
2635
+ }
2636
+ function CopyTranscripts({
2637
+ segments,
2638
+ copy
2639
+ }) {
2640
+ const text = segmentTranscripts(segments);
2641
+ const ref = import_react25.default.useRef(null);
2642
+ const mounted = import_react25.default.useRef(true);
2643
+ const [busy, setBusy] = import_react25.default.useState(false);
2644
+ const [feedback, setFeedback] = import_react25.default.useState(null);
2645
+ import_react25.default.useEffect(() => {
2646
+ mounted.current = true;
2647
+ return () => {
2648
+ mounted.current = false;
2649
+ };
2650
+ }, []);
2651
+ import_react25.default.useEffect(() => setFeedback(null), [text]);
2652
+ return /* @__PURE__ */ import_react25.default.createElement("span", { className: "dlvd-copy-transcripts" }, /* @__PURE__ */ import_react25.default.createElement(
2653
+ "button",
2654
+ {
2655
+ ref,
2656
+ type: "button",
2657
+ disabled: !text || busy,
2658
+ title: copy.copyTranscriptsHelp,
2659
+ onClick: async () => {
2660
+ setBusy(true);
2661
+ setFeedback(null);
2662
+ try {
2663
+ const clipboard = ref.current?.ownerDocument.defaultView?.navigator.clipboard;
2664
+ if (!clipboard?.writeText) throw Error("clipboard-unavailable");
2665
+ await clipboard.writeText(text);
2666
+ if (mounted.current) setFeedback("success");
2667
+ } catch {
2668
+ if (mounted.current) setFeedback("error");
2669
+ } finally {
2670
+ if (mounted.current) setBusy(false);
2671
+ }
2672
+ }
2673
+ },
2674
+ copy.copyTranscripts
2675
+ ), feedback && /* @__PURE__ */ import_react25.default.createElement("span", { role: feedback === "error" ? "alert" : "status" }, feedback === "error" ? copy.copyTranscriptsError : copy.copyTranscriptsSuccess));
2676
+ }
2677
+
2535
2678
  // src/segmentSearch.ts
2536
2679
  var normalize = (text) => text.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
2537
2680
  function distance(a, b) {
@@ -2695,7 +2838,7 @@ function createRecordingInspector(target, changed) {
2695
2838
 
2696
2839
  // src/RecordingPanel.tsx
2697
2840
  function SegmentIcon({ kind }) {
2698
- return /* @__PURE__ */ import_react25.default.createElement(
2841
+ return /* @__PURE__ */ import_react26.default.createElement(
2699
2842
  "svg",
2700
2843
  {
2701
2844
  width: "18",
@@ -2709,7 +2852,7 @@ function SegmentIcon({ kind }) {
2709
2852
  "aria-hidden": "true",
2710
2853
  focusable: "false"
2711
2854
  },
2712
- kind === "play" ? /* @__PURE__ */ import_react25.default.createElement("path", { d: "m8 5 11 7-11 7z", fill: "currentColor" }) : kind === "download" ? /* @__PURE__ */ import_react25.default.createElement("path", { d: "M12 3v12m-5-5 5 5 5-5M5 16v5h14v-5" }) : /* @__PURE__ */ import_react25.default.createElement("path", { d: "M20 7v5h-5M20 12a8 8 0 1 0-2 6M20 7v5" })
2855
+ kind === "play" ? /* @__PURE__ */ import_react26.default.createElement("path", { d: "m8 5 11 7-11 7z", fill: "currentColor" }) : kind === "download" ? /* @__PURE__ */ import_react26.default.createElement("path", { d: "M12 3v12m-5-5 5 5 5-5M5 16v5h14v-5" }) : /* @__PURE__ */ import_react26.default.createElement("path", { d: "M20 7v5h-5M20 12a8 8 0 1 0-2 6M20 7v5" })
2713
2856
  );
2714
2857
  }
2715
2858
  function duration(samples, rate) {
@@ -2730,9 +2873,13 @@ function stateName(session, copy) {
2730
2873
  function RecordingPanel({
2731
2874
  copy,
2732
2875
  target,
2733
- locale = "en"
2876
+ locale = "en",
2877
+ toolbarTarget,
2878
+ storageTarget,
2879
+ onStorageTotal,
2880
+ recordingRequest
2734
2881
  }) {
2735
- const [view, setView] = import_react25.default.useState({
2882
+ const [view, setView] = import_react26.default.useState({
2736
2883
  service: null,
2737
2884
  sessions: [],
2738
2885
  selected: null,
@@ -2740,10 +2887,25 @@ function RecordingPanel({
2740
2887
  loading: true,
2741
2888
  failed: false
2742
2889
  });
2743
- const inspector = import_react25.default.useRef(null);
2744
- const [filter, setFilter] = import_react25.default.useState("");
2745
- const [clearing, setClearing] = import_react25.default.useState(false);
2746
- import_react25.default.useEffect(() => {
2890
+ const total = recordingStorage(view.sessions);
2891
+ const totalLabel = view.failed || view.loading || !view.service ? "\u2014" : (total.unknown ? "\u2265 " : "") + formatBytes(total.bytes, locale);
2892
+ import_react26.default.useEffect(() => {
2893
+ onStorageTotal?.(totalLabel);
2894
+ }, [onStorageTotal, totalLabel]);
2895
+ const inspector = import_react26.default.useRef(null);
2896
+ const [filter, setFilter] = import_react26.default.useState("");
2897
+ const [layout, setLayout] = import_react26.default.useState("split");
2898
+ const [clearing, setClearing] = import_react26.default.useState(false);
2899
+ const appliedRequest = import_react26.default.useRef(null);
2900
+ import_react26.default.useEffect(() => {
2901
+ if (!recordingRequest?.id || appliedRequest.current === recordingRequest || !view.sessions.some((session) => session.id === recordingRequest.id))
2902
+ return;
2903
+ appliedRequest.current = recordingRequest;
2904
+ setFilter("");
2905
+ setLayout("session");
2906
+ inspector.current?.select(recordingRequest.id);
2907
+ }, [recordingRequest, view.sessions]);
2908
+ import_react26.default.useEffect(() => {
2747
2909
  const current = createRecordingInspector(target, setView);
2748
2910
  inspector.current = current;
2749
2911
  current.discover();
@@ -2760,16 +2922,40 @@ function RecordingPanel({
2760
2922
  ...new Map(view.sessions.map((session) => [sourceKey(session), session])).values()
2761
2923
  ];
2762
2924
  const visible = view.sessions.filter((session) => !filter || sourceKey(session) === filter).slice().sort((a, b) => b.startedAt.localeCompare(a.startedAt));
2763
- import_react25.default.useEffect(() => {
2925
+ import_react26.default.useEffect(() => {
2764
2926
  if (!view.loading && !view.failed && filter && !view.sessions.some((session) => sourceKey(session) === filter))
2765
2927
  setFilter("");
2766
2928
  }, [filter, view.sessions, view.loading, view.failed]);
2767
- import_react25.default.useEffect(() => {
2929
+ import_react26.default.useEffect(() => {
2768
2930
  if (!view.loading && filter && visible.length && !visible.some((s) => s.id === view.selected?.id))
2769
2931
  inspector.current?.select(visible[0].id);
2770
2932
  }, [filter, view.selected?.id, view.loading, visible[0]?.id]);
2771
2933
  const selected = view.selected && visible.some((s) => s.id === view.selected?.id) ? view.selected : null;
2772
- return /* @__PURE__ */ import_react25.default.createElement("section", { className: "dlvd-recordings", "aria-label": copy.recordings.title }, /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.help), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react25.default.createElement("label", null, copy.recordings.sourceFilter, " ", /* @__PURE__ */ import_react25.default.createElement("select", { value: filter, onChange: (event) => setFilter(event.target.value) }, /* @__PURE__ */ import_react25.default.createElement("option", { value: "" }, copy.recordings.allSources), sources.map((session) => /* @__PURE__ */ import_react25.default.createElement("option", { key: sourceKey(session), value: sourceKey(session) }, sourceName(session, copy), session.participant ? " \xB7 " + session.participant.participantId : "")))), /* @__PURE__ */ import_react25.default.createElement(
2934
+ const effectiveLayout = selected ? layout : "list";
2935
+ const layoutControl = /* @__PURE__ */ import_react26.default.createElement("label", { className: "dlvd-layout-control" }, copy.recordings.viewLayout, " ", /* @__PURE__ */ import_react26.default.createElement(
2936
+ "select",
2937
+ {
2938
+ value: effectiveLayout,
2939
+ onChange: (event) => setLayout(event.target.value)
2940
+ },
2941
+ /* @__PURE__ */ import_react26.default.createElement("option", { value: "list" }, copy.recordings.viewList),
2942
+ /* @__PURE__ */ import_react26.default.createElement("option", { value: "split", disabled: !selected }, copy.recordings.viewSplit),
2943
+ /* @__PURE__ */ import_react26.default.createElement("option", { value: "session", disabled: !selected }, copy.recordings.viewSession)
2944
+ ));
2945
+ return /* @__PURE__ */ import_react26.default.createElement("section", { className: "dlvd-recordings", "aria-label": copy.recordings.title }, /* @__PURE__ */ import_react26.default.createElement("p", null, copy.recordings.help), toolbarTarget ? (0, import_react_dom.createPortal)(
2946
+ /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-toolbar" }, layoutControl, /* @__PURE__ */ import_react26.default.createElement("label", null, copy.recordings.sourceFilter, " ", /* @__PURE__ */ import_react26.default.createElement("select", { value: filter, onChange: (event) => setFilter(event.target.value) }, /* @__PURE__ */ import_react26.default.createElement("option", { value: "" }, copy.recordings.allSources), sources.map((session) => /* @__PURE__ */ import_react26.default.createElement("option", { key: sourceKey(session), value: sourceKey(session) }, sourceName(session, copy), session.participant ? " \xB7 " + session.participant.participantId : "")))), /* @__PURE__ */ import_react26.default.createElement(
2947
+ "button",
2948
+ {
2949
+ type: "button",
2950
+ onClick: () => {
2951
+ inspector.current?.discover();
2952
+ inspector.current?.refresh();
2953
+ }
2954
+ },
2955
+ copy.recordings.refresh
2956
+ )),
2957
+ toolbarTarget
2958
+ ) : /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-toolbar" }, layoutControl, /* @__PURE__ */ import_react26.default.createElement("label", null, copy.recordings.sourceFilter, " ", /* @__PURE__ */ import_react26.default.createElement("select", { value: filter, onChange: (event) => setFilter(event.target.value) }, /* @__PURE__ */ import_react26.default.createElement("option", { value: "" }, copy.recordings.allSources), sources.map((session) => /* @__PURE__ */ import_react26.default.createElement("option", { key: sourceKey(session), value: sourceKey(session) }, sourceName(session, copy), session.participant ? " \xB7 " + session.participant.participantId : "")))), /* @__PURE__ */ import_react26.default.createElement(
2773
2959
  "button",
2774
2960
  {
2775
2961
  type: "button",
@@ -2779,7 +2965,22 @@ function RecordingPanel({
2779
2965
  }
2780
2966
  },
2781
2967
  copy.recordings.refresh
2782
- )), view.failed && /* @__PURE__ */ import_react25.default.createElement("p", { role: "alert" }, copy.recordings.loadError), !view.service ? /* @__PURE__ */ import_react25.default.createElement("p", { role: "status" }, copy.recordings.unavailable) : /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, view.loading && /* @__PURE__ */ import_react25.default.createElement("p", { role: "status" }, copy.recordings.loading), /* @__PURE__ */ import_react25.default.createElement(
2968
+ )), view.failed && /* @__PURE__ */ import_react26.default.createElement("p", { role: "alert" }, copy.recordings.loadError), !view.service ? /* @__PURE__ */ import_react26.default.createElement("p", { role: "status" }, copy.recordings.unavailable) : /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, null, view.loading && /* @__PURE__ */ import_react26.default.createElement("p", { role: "status" }, copy.recordings.loading), storageTarget ? (0, import_react_dom.createPortal)(
2969
+ /* @__PURE__ */ import_react26.default.createElement(
2970
+ RecordingStorage,
2971
+ {
2972
+ locale,
2973
+ service: view.service,
2974
+ sessions: view.sessions,
2975
+ loading: view.loading,
2976
+ failed: view.failed,
2977
+ copy: copy.recordings,
2978
+ refresh: () => inspector.current?.refresh(),
2979
+ onBusy: setClearing
2980
+ }
2981
+ ),
2982
+ storageTarget
2983
+ ) : /* @__PURE__ */ import_react26.default.createElement(
2783
2984
  RecordingStorage,
2784
2985
  {
2785
2986
  locale,
@@ -2791,22 +2992,26 @@ function RecordingPanel({
2791
2992
  refresh: () => inspector.current?.refresh(),
2792
2993
  onBusy: setClearing
2793
2994
  }
2794
- ), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-recording-columns" }, /* @__PURE__ */ import_react25.default.createElement(
2995
+ ), /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-recording-columns", "data-layout": effectiveLayout }, /* @__PURE__ */ import_react26.default.createElement(
2795
2996
  "div",
2796
2997
  {
2998
+ hidden: effectiveLayout === "session",
2797
2999
  className: "dlvd-session-list dlvd-scroll",
2798
3000
  tabIndex: 0,
2799
3001
  "aria-label": copy.recordings.title
2800
3002
  },
2801
- !visible.length && !view.loading && /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.empty),
2802
- sources.filter((source) => !filter || sourceKey(source) === filter).map((source) => /* @__PURE__ */ import_react25.default.createElement("section", { key: sourceKey(source), "aria-label": sourceName(source, copy) }, /* @__PURE__ */ import_react25.default.createElement("h3", null, sourceName(source, copy)), /* @__PURE__ */ import_react25.default.createElement("table", null, /* @__PURE__ */ import_react25.default.createElement("thead", null, /* @__PURE__ */ import_react25.default.createElement("tr", null, /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.date), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.duration), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.saved))), /* @__PURE__ */ import_react25.default.createElement("tbody", null, visible.filter((session) => sourceKey(session) === sourceKey(source)).map((session) => /* @__PURE__ */ import_react25.default.createElement("tr", { key: session.id, "data-selected": session.id === selected?.id }, /* @__PURE__ */ import_react25.default.createElement("td", null, /* @__PURE__ */ import_react25.default.createElement(
3003
+ !visible.length && !view.loading && /* @__PURE__ */ import_react26.default.createElement("p", null, copy.recordings.empty),
3004
+ sources.filter((source) => !filter || sourceKey(source) === filter).map((source) => /* @__PURE__ */ import_react26.default.createElement("section", { key: sourceKey(source), "aria-label": sourceName(source, copy) }, /* @__PURE__ */ import_react26.default.createElement("h3", null, sourceName(source, copy)), /* @__PURE__ */ import_react26.default.createElement("table", null, /* @__PURE__ */ import_react26.default.createElement("thead", null, /* @__PURE__ */ import_react26.default.createElement("tr", null, /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.date), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.duration), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.saved))), /* @__PURE__ */ import_react26.default.createElement("tbody", null, visible.filter((session) => sourceKey(session) === sourceKey(source)).map((session) => /* @__PURE__ */ import_react26.default.createElement("tr", { key: session.id, "data-selected": session.id === selected?.id }, /* @__PURE__ */ import_react26.default.createElement("td", null, /* @__PURE__ */ import_react26.default.createElement(
2803
3005
  "button",
2804
3006
  {
2805
3007
  type: "button",
2806
3008
  "aria-pressed": session.id === selected?.id,
2807
- onClick: () => inspector.current?.select(session.id)
3009
+ onClick: () => {
3010
+ setLayout("split");
3011
+ inspector.current?.select(session.id);
3012
+ }
2808
3013
  },
2809
- session.starred && /* @__PURE__ */ import_react25.default.createElement(
3014
+ session.starred && /* @__PURE__ */ import_react26.default.createElement(
2810
3015
  "span",
2811
3016
  {
2812
3017
  role: "img",
@@ -2816,23 +3021,31 @@ function RecordingPanel({
2816
3021
  "\u2605"
2817
3022
  ),
2818
3023
  " ",
2819
- /* @__PURE__ */ import_react25.default.createElement("span", null, sourceName(session, copy)),
2820
- /* @__PURE__ */ import_react25.default.createElement("br", null),
2821
- session.name && /* @__PURE__ */ import_react25.default.createElement("strong", null, session.name, /* @__PURE__ */ import_react25.default.createElement("br", null)),
2822
- /* @__PURE__ */ import_react25.default.createElement("time", { dateTime: session.startedAt }, new Date(session.startedAt).toLocaleString())
2823
- )), /* @__PURE__ */ import_react25.default.createElement("td", null, stateName(session, copy)), /* @__PURE__ */ import_react25.default.createElement("td", null, duration(session.capturedSamples, session.sampleRate)), /* @__PURE__ */ import_react25.default.createElement("td", null, duration(session.savedSamples, session.sampleRate), /* @__PURE__ */ import_react25.default.createElement("br", null), session.storage === "indexeddb" || session.persistent ? copy.recordings.persistent : copy.recordings.memory)))))))
2824
- ), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-recording-detail dlvd-scroll", tabIndex: 0 }, selected && !clearing ? /* @__PURE__ */ import_react25.default.createElement(
2825
- RecordingDetail,
3024
+ /* @__PURE__ */ import_react26.default.createElement("span", null, sourceName(session, copy)),
3025
+ /* @__PURE__ */ import_react26.default.createElement("br", null),
3026
+ session.name && /* @__PURE__ */ import_react26.default.createElement("strong", null, session.name, /* @__PURE__ */ import_react26.default.createElement("br", null)),
3027
+ /* @__PURE__ */ import_react26.default.createElement("time", { dateTime: session.startedAt }, new Date(session.startedAt).toLocaleString())
3028
+ )), /* @__PURE__ */ import_react26.default.createElement("td", null, stateName(session, copy)), /* @__PURE__ */ import_react26.default.createElement("td", null, duration(session.capturedSamples, session.sampleRate)), /* @__PURE__ */ import_react26.default.createElement("td", null, duration(session.savedSamples, session.sampleRate), /* @__PURE__ */ import_react26.default.createElement("br", null), session.storage === "indexeddb" || session.persistent ? copy.recordings.persistent : copy.recordings.memory)))))))
3029
+ ), /* @__PURE__ */ import_react26.default.createElement(
3030
+ "div",
2826
3031
  {
2827
- locale,
2828
- key: selected.id,
2829
- copy,
2830
- session: selected,
2831
- service: view.service,
2832
- segments: view.segments,
2833
- refresh: () => inspector.current?.refresh()
2834
- }
2835
- ) : /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.selectSession)))));
3032
+ className: "dlvd-recording-detail dlvd-scroll",
3033
+ hidden: effectiveLayout === "list",
3034
+ tabIndex: 0
3035
+ },
3036
+ selected && !clearing ? /* @__PURE__ */ import_react26.default.createElement(
3037
+ RecordingDetail,
3038
+ {
3039
+ locale,
3040
+ key: selected.id,
3041
+ copy,
3042
+ session: selected,
3043
+ service: view.service,
3044
+ segments: view.segments,
3045
+ refresh: () => inspector.current?.refresh()
3046
+ }
3047
+ ) : /* @__PURE__ */ import_react26.default.createElement("p", null, copy.recordings.selectSession)
3048
+ ))));
2836
3049
  }
2837
3050
  function RecordingDetail({
2838
3051
  locale,
@@ -2842,28 +3055,30 @@ function RecordingDetail({
2842
3055
  segments,
2843
3056
  refresh
2844
3057
  }) {
2845
- const container = import_react25.default.useRef(null);
2846
- const audio = import_react25.default.useRef(null);
2847
- const playback = import_react25.default.useRef(null);
2848
- const download = import_react25.default.useRef(null);
2849
- const generation = import_react25.default.useRef(0);
2850
- const playGeneration = import_react25.default.useRef(0);
2851
- const [error, setError] = import_react25.default.useState(null);
2852
- const [playing, setPlaying] = import_react25.default.useState(false);
2853
- const [downloading, setDownloading] = import_react25.default.useState(false);
2854
- const [retrying, setRetrying] = import_react25.default.useState("");
2855
- const retry = import_react25.default.useRef(null);
2856
- const [exporting, setExporting] = import_react25.default.useState(false);
2857
- const [deleting, setDeleting] = import_react25.default.useState(false);
2858
- const [starring, setStarring] = import_react25.default.useState(false);
2859
- const [confirmDelete, setConfirmDelete] = import_react25.default.useState(false);
2860
- const [name, setName] = import_react25.default.useState(session.name ?? "");
2861
- const [naming, setNaming] = import_react25.default.useState(false);
2862
- import_react25.default.useEffect(() => {
3058
+ const container = import_react26.default.useRef(null);
3059
+ const audio = import_react26.default.useRef(null);
3060
+ const playback = import_react26.default.useRef(null);
3061
+ const download = import_react26.default.useRef(null);
3062
+ const generation = import_react26.default.useRef(0);
3063
+ const playGeneration = import_react26.default.useRef(0);
3064
+ const [error, setError] = import_react26.default.useState(null);
3065
+ const [playing, setPlaying] = import_react26.default.useState(false);
3066
+ const [downloading, setDownloading] = import_react26.default.useState(false);
3067
+ const [retrying, setRetrying] = import_react26.default.useState("");
3068
+ const retry = import_react26.default.useRef(null);
3069
+ const [exporting, setExporting] = import_react26.default.useState(false);
3070
+ const [deleting, setDeleting] = import_react26.default.useState(false);
3071
+ const [starring, setStarring] = import_react26.default.useState(false);
3072
+ const [detailTab, setDetailTab] = import_react26.default.useState("segments");
3073
+ const detailTabsId = import_react26.default.useId();
3074
+ const [confirmDelete, setConfirmDelete] = import_react26.default.useState(false);
3075
+ const [name, setName] = import_react26.default.useState(session.name ?? "");
3076
+ const [naming, setNaming] = import_react26.default.useState(false);
3077
+ import_react26.default.useEffect(() => {
2863
3078
  setName(session.name ?? "");
2864
3079
  }, [session.id, session.name, service]);
2865
- const [selectedSegment, setSelectedSegment] = import_react25.default.useState("");
2866
- const [selectedEvents, setSelectedEvents] = import_react25.default.useState([]);
3080
+ const [selectedSegment, setSelectedSegment] = import_react26.default.useState("");
3081
+ const [selectedEvents, setSelectedEvents] = import_react26.default.useState([]);
2867
3082
  const clearPlayback = (player = audio.current) => {
2868
3083
  ++playGeneration.current;
2869
3084
  if (player?.hasAttribute("src")) {
@@ -2878,7 +3093,7 @@ function RecordingDetail({
2878
3093
  if (download.current) download.current.api.revokeObjectURL(download.current.url);
2879
3094
  download.current = null;
2880
3095
  };
2881
- import_react25.default.useEffect(() => {
3096
+ import_react26.default.useEffect(() => {
2882
3097
  setStarring(false);
2883
3098
  const owner = container.current?.ownerDocument.defaultView;
2884
3099
  const player = audio.current;
@@ -3023,6 +3238,7 @@ function RecordingDetail({
3023
3238
  }
3024
3239
  };
3025
3240
  const eventName = (kind) => ({
3241
+ "transcription-ignored-muted": copy.recordings.ignoredMuted,
3026
3242
  "voice-command-recognized": copy.recordings.voiceCommandRecognized,
3027
3243
  "assistant-wait-start": copy.recordings.assistantWaitStart,
3028
3244
  "assistant-wait-end": copy.recordings.assistantWaitEnd,
@@ -3049,6 +3265,9 @@ function RecordingDetail({
3049
3265
  stopSpeaking: copy.recordings.commandStopSpeaking,
3050
3266
  clear: copy.recordings.commandClear
3051
3267
  })[command] ?? command;
3268
+ const ignoredFor = (segment) => (session.events ?? []).filter(
3269
+ (event) => event.kind === "transcription-ignored-muted" && event.details?.segmentId === segment.id
3270
+ );
3052
3271
  const commandsFor = (segment) => (session.events ?? []).filter(
3053
3272
  (event) => event.kind === "voice-command-recognized" && event.details?.segmentId === segment.id
3054
3273
  );
@@ -3100,56 +3319,31 @@ function RecordingDetail({
3100
3319
  if (token === generation.current) setDeleting(false);
3101
3320
  }
3102
3321
  };
3103
- const [search, setSearch] = import_react25.default.useState("");
3104
- import_react25.default.useEffect(() => setSearch(""), [session.id, service]);
3322
+ const [search, setSearch] = import_react26.default.useState("");
3323
+ import_react26.default.useEffect(() => setSearch(""), [session.id, service]);
3105
3324
  const length = Math.max(1, session.savedSamples);
3106
- const [windowStart, setWindowStart] = import_react25.default.useState(0);
3107
- const [windowEnd, setWindowEnd] = import_react25.default.useState(null);
3108
- import_react25.default.useEffect(() => {
3325
+ const [windowStart, setWindowStart] = import_react26.default.useState(0);
3326
+ const [windowEnd, setWindowEnd] = import_react26.default.useState(null);
3327
+ import_react26.default.useEffect(() => {
3109
3328
  setWindowStart(0);
3110
3329
  setWindowEnd(null);
3111
3330
  }, [session.id, service]);
3112
3331
  const viewEnd = Math.max(1, Math.min(length, windowEnd ?? length));
3113
3332
  const viewStart = Math.max(0, Math.min(windowStart, viewEnd - 1));
3114
3333
  const viewLength = viewEnd - viewStart;
3115
- const visibleSegments = import_react25.default.useMemo(
3334
+ const visibleSegments = import_react26.default.useMemo(
3116
3335
  () => segments.filter((segment) => segment.endSample > viewStart && segment.startSample < viewEnd),
3117
3336
  [segments, viewStart, viewEnd]
3118
3337
  );
3119
- const matchedSegments = import_react25.default.useMemo(
3338
+ const matchedSegments = import_react26.default.useMemo(
3120
3339
  () => searchSegments(visibleSegments, search),
3121
3340
  [visibleSegments, search]
3122
3341
  );
3123
3342
  const inWindow = (sample) => sample >= viewStart && sample <= viewEnd;
3343
+ const visibleEvents = (session.events ?? []).filter((event) => inWindow(event.atSample));
3124
3344
  const position = (sample) => (sample - viewStart) / viewLength * 100 + "%";
3125
- const segmentName = (segment) => (commandsFor(segment).length ? copy.recordings.eventCommand : segment.kind === "speech" ? copy.recordings.speech : copy.recordings.silence) + " \xB7 " + duration(segment.startSample, session.sampleRate) + " \u2014 " + duration(segment.endSample, session.sampleRate);
3126
- return /* @__PURE__ */ import_react25.default.createElement("section", { ref: container, "aria-label": sourceName(session, copy) }, /* @__PURE__ */ import_react25.default.createElement("h3", null, sourceName(session, copy)), session.name && /* @__PURE__ */ import_react25.default.createElement("p", null, /* @__PURE__ */ import_react25.default.createElement("strong", null, copy.recordings.name, ": ", session.name)), session.participant && /* @__PURE__ */ import_react25.default.createElement("details", null, /* @__PURE__ */ import_react25.default.createElement("summary", null, copy.recordings.participantDetails), /* @__PURE__ */ import_react25.default.createElement("dl", null, /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.originalName), /* @__PURE__ */ import_react25.default.createElement("dd", null, session.participant.originalName), session.participant.alias && /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.aliasAtCapture), /* @__PURE__ */ import_react25.default.createElement("dd", null, session.participant.alias)), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.participantId), /* @__PURE__ */ import_react25.default.createElement("dd", null, session.participant.participantId), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.meetingId), /* @__PURE__ */ import_react25.default.createElement("dd", null, session.participant.groupId))), session.starred && service.setName && /* @__PURE__ */ import_react25.default.createElement(
3127
- "form",
3128
- {
3129
- className: "dlvd-toolbar",
3130
- onSubmit: (event) => {
3131
- event.preventDefault();
3132
- void saveName();
3133
- }
3134
- },
3135
- /* @__PURE__ */ import_react25.default.createElement("label", null, copy.recordings.name, " ", /* @__PURE__ */ import_react25.default.createElement(
3136
- "input",
3137
- {
3138
- value: name,
3139
- maxLength: 120,
3140
- disabled: naming || deleting,
3141
- onInput: (event) => setName(event.currentTarget.value)
3142
- }
3143
- )),
3144
- /* @__PURE__ */ import_react25.default.createElement(
3145
- "button",
3146
- {
3147
- type: "submit",
3148
- disabled: naming || deleting || starring || name === (session.name ?? "")
3149
- },
3150
- copy.recordings.saveName
3151
- )
3152
- ), /* @__PURE__ */ import_react25.default.createElement("dl", { className: "dlvd-recording-meta" }, /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.date), /* @__PURE__ */ import_react25.default.createElement("dd", null, new Date(session.startedAt).toLocaleString()), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.state), /* @__PURE__ */ import_react25.default.createElement("dd", null, stateName(session, copy)), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.storageAudio), /* @__PURE__ */ import_react25.default.createElement("dd", null, session.audioBytes === void 0 ? copy.recordings.storageUnknown : formatBytes(session.audioBytes, locale)), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.captured), /* @__PURE__ */ import_react25.default.createElement("dd", null, duration(session.capturedSamples, session.sampleRate)), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.saved), /* @__PURE__ */ import_react25.default.createElement("dd", null, duration(session.savedSamples, session.sampleRate), " \xB7", session.storage === "indexeddb" || session.persistent ? copy.recordings.persistent : copy.recordings.memory), session.endedAt && /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.end), /* @__PURE__ */ import_react25.default.createElement("dd", null, new Date(session.endedAt).toLocaleString()))), session.error && /* @__PURE__ */ import_react25.default.createElement("p", { role: "alert" }, copy.recordings.error, ": ", session.error), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react25.default.createElement(
3345
+ const segmentName = (segment) => (commandsFor(segment).length ? copy.recordings.eventCommand : ignoredFor(segment).length ? copy.recordings.ignoredMuted : segment.kind === "speech" ? copy.recordings.speech : copy.recordings.silence) + " \xB7 " + duration(segment.startSample, session.sampleRate) + " \u2014 " + duration(segment.endSample, session.sampleRate);
3346
+ return /* @__PURE__ */ import_react26.default.createElement("section", { ref: container, "aria-label": sourceName(session, copy) }, /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-detail-heading" }, /* @__PURE__ */ import_react26.default.createElement("h3", null, sourceName(session, copy)), /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-toolbar dlvd-detail-actions" }, /* @__PURE__ */ import_react26.default.createElement(
3153
3347
  "button",
3154
3348
  {
3155
3349
  type: "button",
@@ -3160,7 +3354,7 @@ function RecordingDetail({
3160
3354
  onClick: () => void toggleStarred()
3161
3355
  },
3162
3356
  session.starred ? "\u2605" : "\u2606"
3163
- ), /* @__PURE__ */ import_react25.default.createElement(
3357
+ ), /* @__PURE__ */ import_react26.default.createElement(
3164
3358
  "button",
3165
3359
  {
3166
3360
  type: "button",
@@ -3168,7 +3362,7 @@ function RecordingDetail({
3168
3362
  onClick: () => void play()
3169
3363
  },
3170
3364
  copy.recordings.playSession
3171
- ), /* @__PURE__ */ import_react25.default.createElement(
3365
+ ), /* @__PURE__ */ import_react26.default.createElement(
3172
3366
  "button",
3173
3367
  {
3174
3368
  type: "button",
@@ -3176,7 +3370,7 @@ function RecordingDetail({
3176
3370
  onClick: () => void exportSession()
3177
3371
  },
3178
3372
  copy.recordings.export
3179
- ), /* @__PURE__ */ import_react25.default.createElement(
3373
+ ), /* @__PURE__ */ import_react26.default.createElement(
3180
3374
  "button",
3181
3375
  {
3182
3376
  type: "button",
@@ -3184,15 +3378,41 @@ function RecordingDetail({
3184
3378
  onClick: () => setConfirmDelete(true)
3185
3379
  },
3186
3380
  copy.recordings.delete
3187
- )), confirmDelete && /* @__PURE__ */ import_react25.default.createElement(
3381
+ ))), session.name && /* @__PURE__ */ import_react26.default.createElement("p", null, /* @__PURE__ */ import_react26.default.createElement("strong", null, copy.recordings.name, ": ", session.name)), session.participant && /* @__PURE__ */ import_react26.default.createElement("details", null, /* @__PURE__ */ import_react26.default.createElement("summary", null, copy.recordings.participantDetails), /* @__PURE__ */ import_react26.default.createElement("dl", null, /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.originalName), /* @__PURE__ */ import_react26.default.createElement("dd", null, session.participant.originalName), session.participant.alias && /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, null, /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.aliasAtCapture), /* @__PURE__ */ import_react26.default.createElement("dd", null, session.participant.alias)), /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.participantId), /* @__PURE__ */ import_react26.default.createElement("dd", null, session.participant.participantId), /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.meetingId), /* @__PURE__ */ import_react26.default.createElement("dd", null, session.participant.groupId))), session.starred && service.setName && /* @__PURE__ */ import_react26.default.createElement(
3382
+ "form",
3383
+ {
3384
+ className: "dlvd-toolbar",
3385
+ onSubmit: (event) => {
3386
+ event.preventDefault();
3387
+ void saveName();
3388
+ }
3389
+ },
3390
+ /* @__PURE__ */ import_react26.default.createElement("label", null, copy.recordings.name, " ", /* @__PURE__ */ import_react26.default.createElement(
3391
+ "input",
3392
+ {
3393
+ value: name,
3394
+ maxLength: 120,
3395
+ disabled: naming || deleting,
3396
+ onInput: (event) => setName(event.currentTarget.value)
3397
+ }
3398
+ )),
3399
+ /* @__PURE__ */ import_react26.default.createElement(
3400
+ "button",
3401
+ {
3402
+ type: "submit",
3403
+ disabled: naming || deleting || starring || name === (session.name ?? "")
3404
+ },
3405
+ copy.recordings.saveName
3406
+ )
3407
+ ), /* @__PURE__ */ import_react26.default.createElement("dl", { className: "dlvd-recording-meta dlvd-session-meta" }, /* @__PURE__ */ import_react26.default.createElement("div", null, /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.date), /* @__PURE__ */ import_react26.default.createElement("dd", null, new Date(session.startedAt).toLocaleString())), /* @__PURE__ */ import_react26.default.createElement("div", null, /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.state), /* @__PURE__ */ import_react26.default.createElement("dd", null, stateName(session, copy))), /* @__PURE__ */ import_react26.default.createElement("div", null, /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.storageAudio), /* @__PURE__ */ import_react26.default.createElement("dd", null, session.audioBytes === void 0 ? copy.recordings.storageUnknown : formatBytes(session.audioBytes, locale))), /* @__PURE__ */ import_react26.default.createElement("div", null, /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.captured), /* @__PURE__ */ import_react26.default.createElement("dd", null, duration(session.capturedSamples, session.sampleRate))), /* @__PURE__ */ import_react26.default.createElement("div", null, /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.saved), /* @__PURE__ */ import_react26.default.createElement("dd", null, duration(session.savedSamples, session.sampleRate), " \xB7", session.storage === "indexeddb" || session.persistent ? copy.recordings.persistent : copy.recordings.memory)), session.endedAt && /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, null, /* @__PURE__ */ import_react26.default.createElement("div", null, /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.end), /* @__PURE__ */ import_react26.default.createElement("dd", null, new Date(session.endedAt).toLocaleString())))), session.error && /* @__PURE__ */ import_react26.default.createElement("p", { role: "alert" }, copy.recordings.error, ": ", session.error), confirmDelete && /* @__PURE__ */ import_react26.default.createElement(
3188
3408
  "div",
3189
3409
  {
3190
3410
  className: "dlvd-delete-confirm",
3191
3411
  role: "group",
3192
3412
  "aria-label": copy.recordings.confirmDelete
3193
3413
  },
3194
- /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.confirmDelete),
3195
- /* @__PURE__ */ import_react25.default.createElement(
3414
+ /* @__PURE__ */ import_react26.default.createElement("p", null, copy.recordings.confirmDelete),
3415
+ /* @__PURE__ */ import_react26.default.createElement(
3196
3416
  "button",
3197
3417
  {
3198
3418
  type: "button",
@@ -3202,20 +3422,8 @@ function RecordingDetail({
3202
3422
  copy.recordings.delete
3203
3423
  ),
3204
3424
  " ",
3205
- /* @__PURE__ */ import_react25.default.createElement("button", { type: "button", disabled: deleting, onClick: () => setConfirmDelete(false) }, copy.recordings.cancel)
3206
- ), error && /* @__PURE__ */ import_react25.default.createElement("p", { role: "alert" }, copy.recordings[error]), retrying && /* @__PURE__ */ import_react25.default.createElement("p", { role: "status" }, copy.recordings.retranscribing), /* @__PURE__ */ import_react25.default.createElement(
3207
- "audio",
3208
- {
3209
- ref: audio,
3210
- controls: true,
3211
- preload: "metadata",
3212
- "aria-label": copy.recordings.audio,
3213
- onError: () => {
3214
- clearPlayback();
3215
- setError("audioError");
3216
- }
3217
- }
3218
- ), /* @__PURE__ */ import_react25.default.createElement("h4", null, copy.recordings.timeline), /* @__PURE__ */ import_react25.default.createElement(
3425
+ /* @__PURE__ */ import_react26.default.createElement("button", { type: "button", disabled: deleting, onClick: () => setConfirmDelete(false) }, copy.recordings.cancel)
3426
+ ), error && /* @__PURE__ */ import_react26.default.createElement("p", { role: "alert" }, copy.recordings[error]), retrying && /* @__PURE__ */ import_react26.default.createElement("p", { role: "status" }, copy.recordings.retranscribing), /* @__PURE__ */ import_react26.default.createElement(
3219
3427
  TimelineWindow,
3220
3428
  {
3221
3429
  key: session.id,
@@ -3233,12 +3441,12 @@ function RecordingDetail({
3233
3441
  setWindowEnd(null);
3234
3442
  }
3235
3443
  }
3236
- ), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-timeline", role: "group", "aria-label": copy.recordings.timeline }, visibleSegments.map((segment) => /* @__PURE__ */ import_react25.default.createElement(
3444
+ ), /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-timeline", role: "group", "aria-label": copy.recordings.timeline }, visibleSegments.map((segment) => /* @__PURE__ */ import_react26.default.createElement(
3237
3445
  "button",
3238
3446
  {
3239
3447
  key: segment.id,
3240
3448
  type: "button",
3241
- "data-kind": commandsFor(segment).length ? "command" : segment.kind,
3449
+ "data-kind": commandsFor(segment).length ? "command" : ignoredFor(segment).length ? "ignored" : segment.kind,
3242
3450
  "aria-label": segmentName(segment),
3243
3451
  title: segmentName(segment),
3244
3452
  "aria-pressed": selectedSegment === segment.id,
@@ -3249,7 +3457,21 @@ function RecordingDetail({
3249
3457
  disabled: deleting || segment.endSample <= segment.startSample,
3250
3458
  onClick: () => void play(segment, false)
3251
3459
  }
3252
- )), session.pins.filter((pin) => inWindow(pin.atSample)).map((pin) => /* @__PURE__ */ import_react25.default.createElement(
3460
+ )), eventIntervals(session.events ?? [], session.savedSamples).filter((range) => range.end > viewStart && range.start < viewEnd).map((range, index) => /* @__PURE__ */ import_react26.default.createElement(
3461
+ "span",
3462
+ {
3463
+ key: range.category + index,
3464
+ className: "dlvd-summary-interval",
3465
+ "data-category": range.category,
3466
+ role: "img",
3467
+ "aria-label": (range.category === "waiting" ? copy.recordings.eventWaiting : copy.recordings.eventPlayback) + " \xB7 " + duration(range.start, session.sampleRate) + " \u2013 " + duration(range.end, session.sampleRate),
3468
+ title: (range.category === "waiting" ? copy.recordings.eventWaiting : copy.recordings.eventPlayback) + " \xB7 " + duration(range.start, session.sampleRate) + " \u2013 " + duration(range.end, session.sampleRate),
3469
+ style: {
3470
+ left: position(Math.max(viewStart, range.start)),
3471
+ width: (Math.min(viewEnd, range.end) - Math.max(viewStart, range.start)) / viewLength * 100 + "%"
3472
+ }
3473
+ }
3474
+ )), session.pins.filter((pin) => inWindow(pin.atSample)).map((pin) => /* @__PURE__ */ import_react26.default.createElement(
3253
3475
  "span",
3254
3476
  {
3255
3477
  key: pin.id,
@@ -3258,7 +3480,7 @@ function RecordingDetail({
3258
3480
  style: { left: position(pin.atSample) },
3259
3481
  title: (pin.kind === "speech-start" ? copy.recordings.speech : copy.recordings.silence) + " \xB7 " + duration(pin.atSample, session.sampleRate)
3260
3482
  }
3261
- ))), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-timeline-legend" }, /* @__PURE__ */ import_react25.default.createElement("span", { "data-kind": "speech" }, copy.recordings.speech), " \xB7 ", /* @__PURE__ */ import_react25.default.createElement("span", { "data-kind": "silence" }, copy.recordings.silence), " \xB7", " ", /* @__PURE__ */ import_react25.default.createElement("span", { "data-kind": "command" }, copy.recordings.eventCommand), " \xB7 ", duration(viewStart, session.sampleRate), " \u2013", " ", duration(Math.min(viewEnd, session.savedSamples), session.sampleRate)), /* @__PURE__ */ import_react25.default.createElement(
3483
+ ))), /* @__PURE__ */ import_react26.default.createElement(
3262
3484
  EventTimeline,
3263
3485
  {
3264
3486
  events: session.events ?? [],
@@ -3269,79 +3491,164 @@ function RecordingDetail({
3269
3491
  name: eventName,
3270
3492
  details: eventDetails,
3271
3493
  onSelect: (events) => {
3494
+ setDetailTab("events");
3272
3495
  setSelectedEvents(events.map((event) => event.id));
3273
3496
  const row = Array.from(
3274
3497
  container.current?.querySelectorAll("[data-recording-event]") ?? []
3275
3498
  ).find((node) => node.dataset.recordingEvent === events[0].id);
3276
- row?.scrollIntoView?.({ block: "nearest" });
3277
- row?.focus();
3499
+ const focusRow = () => {
3500
+ row?.scrollIntoView?.({ block: "nearest" });
3501
+ row?.focus();
3502
+ };
3503
+ const win = row?.ownerDocument.defaultView;
3504
+ if (win?.requestAnimationFrame) win.requestAnimationFrame(focusRow);
3505
+ else setTimeout(focusRow, 0);
3278
3506
  }
3279
3507
  }
3280
- ), /* @__PURE__ */ import_react25.default.createElement("h4", null, copy.recordings.events), /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.eventsHelp), /* @__PURE__ */ import_react25.default.createElement("table", { className: "dlvd-recording-events" }, /* @__PURE__ */ import_react25.default.createElement("thead", null, /* @__PURE__ */ import_react25.default.createElement("tr", null, /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.eventTime), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.captured))), /* @__PURE__ */ import_react25.default.createElement("tbody", null, (session.events ?? []).map((event) => /* @__PURE__ */ import_react25.default.createElement(
3281
- "tr",
3282
- {
3283
- key: event.id,
3284
- "data-recording-event": event.id,
3285
- "data-selected": selectedEvents.includes(event.id),
3286
- tabIndex: -1
3287
- },
3288
- /* @__PURE__ */ import_react25.default.createElement("td", null, eventName(event.kind), event.details && /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-event-details" }, eventDetails(event))),
3289
- /* @__PURE__ */ import_react25.default.createElement("td", null, /* @__PURE__ */ import_react25.default.createElement("time", { dateTime: event.occurredAt }, new Date(event.occurredAt).toLocaleString(), ".", new Date(event.occurredAt).getMilliseconds().toString().padStart(3, "0"))),
3290
- /* @__PURE__ */ import_react25.default.createElement("td", null, duration(event.atSample, session.sampleRate))
3291
- )))), /* @__PURE__ */ import_react25.default.createElement("h4", null, copy.recordings.segments), /* @__PURE__ */ import_react25.default.createElement("label", null, copy.recordings.search, /* @__PURE__ */ import_react25.default.createElement(
3292
- "input",
3508
+ ), /* @__PURE__ */ import_react26.default.createElement(
3509
+ "audio",
3293
3510
  {
3294
- type: "search",
3295
- value: search,
3296
- maxLength: 160,
3297
- "aria-label": copy.recordings.search,
3298
- onInput: (event) => setSearch(event.currentTarget.value)
3511
+ ref: audio,
3512
+ controls: true,
3513
+ preload: "metadata",
3514
+ "aria-label": copy.recordings.audio,
3515
+ onError: () => {
3516
+ clearPlayback();
3517
+ setError("audioError");
3518
+ }
3299
3519
  }
3300
- )), search.trim() && matchedSegments.length === 0 && /* @__PURE__ */ import_react25.default.createElement("p", { role: "status" }, copy.recordings.searchEmpty), /* @__PURE__ */ import_react25.default.createElement("table", { className: "dlvd-segments" }, /* @__PURE__ */ import_react25.default.createElement("thead", null, /* @__PURE__ */ import_react25.default.createElement("tr", null, /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.start), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.end), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.duration), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.audio))), /* @__PURE__ */ import_react25.default.createElement("tbody", null, matchedSegments.map((segment) => /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, { key: segment.id }, /* @__PURE__ */ import_react25.default.createElement("tr", { "data-selected": selectedSegment === segment.id }, /* @__PURE__ */ import_react25.default.createElement("td", null, /* @__PURE__ */ import_react25.default.createElement("span", null, sourceName(session, copy)), /* @__PURE__ */ import_react25.default.createElement("br", null), commandsFor(segment).length ? /* @__PURE__ */ import_react25.default.createElement("span", { className: "dlvd-command-label" }, "\u03DF ", copy.recordings.eventCommand, " \xB7", " ", commandsFor(segment).map((event) => commandName(event.details.command)).join(", ")) : segment.kind === "speech" ? copy.recordings.speech : copy.recordings.silence, segment.open && /* @__PURE__ */ import_react25.default.createElement("small", null, copy.recordings.open)), /* @__PURE__ */ import_react25.default.createElement("td", null, duration(segment.startSample, session.sampleRate)), /* @__PURE__ */ import_react25.default.createElement("td", null, duration(segment.endSample, session.sampleRate)), /* @__PURE__ */ import_react25.default.createElement("td", null, duration(segment.endSample - segment.startSample, session.sampleRate)), /* @__PURE__ */ import_react25.default.createElement("td", null, /* @__PURE__ */ import_react25.default.createElement(
3520
+ ), /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-tabs dlvd-detail-tabs", role: "tablist", "aria-label": copy.recordings.title }, ["segments", "events", "pins"].map((name2) => /* @__PURE__ */ import_react26.default.createElement(
3301
3521
  "button",
3302
3522
  {
3303
3523
  type: "button",
3304
- className: "dlvd-segment-action",
3305
- title: copy.recordings.play,
3306
- disabled: deleting || segment.endSample <= segment.startSample,
3307
- "aria-label": copy.recordings.play + " \xB7 " + sourceName(session, copy) + " \xB7 " + segmentName(segment),
3308
- onClick: () => void play(segment)
3524
+ role: "tab",
3525
+ key: name2,
3526
+ id: detailTabsId + "-" + name2 + "-tab",
3527
+ "aria-controls": detailTabsId + "-" + name2,
3528
+ "aria-selected": detailTab === name2,
3529
+ tabIndex: detailTab === name2 ? 0 : -1,
3530
+ onClick: () => setDetailTab(name2),
3531
+ onKeyDown: (event) => {
3532
+ if (["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) {
3533
+ event.preventDefault();
3534
+ const names = ["segments", "events", "pins"];
3535
+ const index = names.indexOf(name2);
3536
+ const next = event.key === "Home" ? names[0] : event.key === "End" ? names[2] : names[(index + (event.key === "ArrowRight" ? 1 : 2)) % names.length];
3537
+ setDetailTab(next);
3538
+ event.currentTarget.parentElement?.querySelector('[id="' + detailTabsId + "-" + next + '-tab"]')?.focus();
3539
+ }
3540
+ }
3309
3541
  },
3310
- /* @__PURE__ */ import_react25.default.createElement(SegmentIcon, { kind: "play" })
3311
- ), /* @__PURE__ */ import_react25.default.createElement(
3312
- "button",
3542
+ name2 === "segments" ? copy.recordings.segments : name2 === "pins" ? copy.recordings.pins : copy.recordings.events
3543
+ ))), /* @__PURE__ */ import_react26.default.createElement(
3544
+ "div",
3313
3545
  {
3314
- type: "button",
3315
- className: "dlvd-segment-action",
3316
- disabled: downloading || exporting || deleting || segment.endSample <= segment.startSample,
3317
- title: copy.recordings.download,
3318
- "aria-label": copy.recordings.download + " \xB7 " + segmentName(segment),
3319
- onClick: () => void downloadSegment(segment)
3546
+ className: "dlvd-detail-list",
3547
+ role: "tabpanel",
3548
+ id: detailTabsId + "-events",
3549
+ "aria-labelledby": detailTabsId + "-events-tab",
3550
+ hidden: detailTab !== "events"
3320
3551
  },
3321
- /* @__PURE__ */ import_react25.default.createElement(SegmentIcon, { kind: "download" })
3322
- ), segment.kind === "speech" && service.retranscribe && /* @__PURE__ */ import_react25.default.createElement(
3323
- "button",
3552
+ /* @__PURE__ */ import_react26.default.createElement("h4", null, copy.recordings.events),
3553
+ /* @__PURE__ */ import_react26.default.createElement("p", null, copy.recordings.eventsHelp),
3554
+ /* @__PURE__ */ import_react26.default.createElement("table", { className: "dlvd-recording-events" }, /* @__PURE__ */ import_react26.default.createElement("thead", null, /* @__PURE__ */ import_react26.default.createElement("tr", null, /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.eventTime), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.captured))), /* @__PURE__ */ import_react26.default.createElement("tbody", null, visibleEvents.map((event) => /* @__PURE__ */ import_react26.default.createElement(
3555
+ "tr",
3556
+ {
3557
+ key: event.id,
3558
+ "data-recording-event": event.id,
3559
+ "data-selected": selectedEvents.includes(event.id),
3560
+ tabIndex: -1
3561
+ },
3562
+ /* @__PURE__ */ import_react26.default.createElement("td", null, eventName(event.kind), event.details && /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-event-details" }, eventDetails(event))),
3563
+ /* @__PURE__ */ import_react26.default.createElement("td", null, /* @__PURE__ */ import_react26.default.createElement("time", { dateTime: event.occurredAt }, new Date(event.occurredAt).toLocaleString(), ".", new Date(event.occurredAt).getMilliseconds().toString().padStart(3, "0"))),
3564
+ /* @__PURE__ */ import_react26.default.createElement("td", null, duration(event.atSample, session.sampleRate))
3565
+ ))))
3566
+ ), /* @__PURE__ */ import_react26.default.createElement(
3567
+ "div",
3324
3568
  {
3325
- type: "button",
3326
- className: "dlvd-segment-action",
3327
- disabled: deleting || !!retrying || session.state === "recording" || segment.open || segment.endSample <= segment.startSample || segment.attempts.some((a) => a.status === "pending"),
3328
- title: copy.recordings.retranscribeHelp,
3329
- "aria-label": copy.recordings.retranscribe + " \xB7 " + segmentName(segment),
3330
- onClick: () => void retranscribe(segment)
3569
+ className: "dlvd-detail-list",
3570
+ role: "tabpanel",
3571
+ id: detailTabsId + "-segments",
3572
+ "aria-labelledby": detailTabsId + "-segments-tab",
3573
+ hidden: detailTab !== "segments"
3331
3574
  },
3332
- /* @__PURE__ */ import_react25.default.createElement(SegmentIcon, { kind: "retry" })
3333
- ))), segment.kind === "speech" && /* @__PURE__ */ import_react25.default.createElement("tr", null, /* @__PURE__ */ import_react25.default.createElement("td", { colSpan: 5 }, /* @__PURE__ */ import_react25.default.createElement("details", { open: !!search.trim() || selectedSegment === segment.id || void 0 }, /* @__PURE__ */ import_react25.default.createElement("summary", null, copy.recordings.attempts, " \xB7 ", segment.attempts.length), !segment.attempts.length && /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.noAttempts), segment.attempts.map((attempt) => /* @__PURE__ */ import_react25.default.createElement("article", { className: "dlvd-attempt", key: attempt.id }, /* @__PURE__ */ import_react25.default.createElement("div", null, copy.recordings.engine, ": ", attempt.engine, " \xB7", {
3334
- pending: copy.recordings.pending,
3335
- success: copy.recordings.success,
3336
- error: copy.recordings.error
3337
- }[attempt.status]), /* @__PURE__ */ import_react25.default.createElement("time", { dateTime: attempt.createdAt }, new Date(attempt.createdAt).toLocaleString()), attempt.finishedAt && /* @__PURE__ */ import_react25.default.createElement(import_react25.default.Fragment, null, " ", "\u2014", " ", /* @__PURE__ */ import_react25.default.createElement("time", { dateTime: attempt.finishedAt }, new Date(attempt.finishedAt).toLocaleString())), /* @__PURE__ */ import_react25.default.createElement("div", null, duration(attempt.startSample, session.sampleRate), " \u2014", " ", duration(attempt.endSample, session.sampleRate)), attempt.request ? /* @__PURE__ */ import_react25.default.createElement("details", { className: "dlvd-request" }, /* @__PURE__ */ import_react25.default.createElement("summary", null, copy.recordings.requestMetadata, " \xB7", " ", attempt.request.payloadBytes.toLocaleString(), " B \xB7", " ", (attempt.request.durationMs / 1e3).toFixed(3), " s"), /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.requestHelp), /* @__PURE__ */ import_react25.default.createElement("dl", { className: "dlvd-recording-meta" }, /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.requestBytes), /* @__PURE__ */ import_react25.default.createElement("dd", null, attempt.request.payloadBytes.toLocaleString(), " B"), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.requestInput), /* @__PURE__ */ import_react25.default.createElement("dd", null, attempt.request.inputSamples.toLocaleString(), " /", " ", attempt.request.inputSampleRate.toLocaleString(), " Hz"), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.requestSamples), /* @__PURE__ */ import_react25.default.createElement("dd", null, attempt.request.sampleCount.toLocaleString(), " /", " ", attempt.request.sampleRate.toLocaleString(), " Hz"), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.duration), /* @__PURE__ */ import_react25.default.createElement("dd", null, (attempt.request.durationMs / 1e3).toFixed(3), " s"), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.requestExpected), /* @__PURE__ */ import_react25.default.createElement("dd", null, duration(
3338
- attempt.endSample - attempt.startSample,
3339
- session.sampleRate
3340
- )), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.requestLanguage), /* @__PURE__ */ import_react25.default.createElement("dd", null, attempt.request.language), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.requestPrepared), /* @__PURE__ */ import_react25.default.createElement("dd", null, /* @__PURE__ */ import_react25.default.createElement("time", { dateTime: attempt.request.preparedAt }, new Date(attempt.request.preparedAt).toLocaleString())), /* @__PURE__ */ import_react25.default.createElement("dt", null, copy.recordings.requestStatus), /* @__PURE__ */ import_react25.default.createElement("dd", null, attempt.request.responseStatus ?? copy.recordings.requestUnknown))) : /* @__PURE__ */ import_react25.default.createElement("small", null, copy.recordings.requestUnknown), attempt.text != null && /* @__PURE__ */ import_react25.default.createElement("p", { className: "dlvd-transcript" }, attempt.text), attempt.error && /* @__PURE__ */ import_react25.default.createElement("p", { className: "dlvd-attempt-error" }, copy.recordings.error, ": ", attempt.error)))))))))), /* @__PURE__ */ import_react25.default.createElement("details", null, /* @__PURE__ */ import_react25.default.createElement("summary", null, copy.recordings.pins, " \xB7 ", session.pins.length), /* @__PURE__ */ import_react25.default.createElement("table", null, /* @__PURE__ */ import_react25.default.createElement("thead", null, /* @__PURE__ */ import_react25.default.createElement("tr", null, /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.start), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.pinConfirmed), /* @__PURE__ */ import_react25.default.createElement("th", { scope: "col" }, copy.recordings.pinDelay))), /* @__PURE__ */ import_react25.default.createElement("tbody", null, session.pins.map((pin) => /* @__PURE__ */ import_react25.default.createElement("tr", { key: pin.id }, /* @__PURE__ */ import_react25.default.createElement("td", null, pin.kind === "speech-start" ? copy.recordings.speech : copy.recordings.silence), /* @__PURE__ */ import_react25.default.createElement("td", null, duration(pin.atSample, session.sampleRate)), /* @__PURE__ */ import_react25.default.createElement("td", null, duration(pin.confirmedAtSample, session.sampleRate)), /* @__PURE__ */ import_react25.default.createElement("td", null, duration(pin.delaySamples, session.sampleRate))))))));
3575
+ /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-segment-heading" }, /* @__PURE__ */ import_react26.default.createElement("h4", null, copy.recordings.segments), /* @__PURE__ */ import_react26.default.createElement(CopyTranscripts, { segments: matchedSegments, copy: copy.recordings })),
3576
+ /* @__PURE__ */ import_react26.default.createElement("label", null, copy.recordings.search, /* @__PURE__ */ import_react26.default.createElement(
3577
+ "input",
3578
+ {
3579
+ type: "search",
3580
+ value: search,
3581
+ maxLength: 160,
3582
+ "aria-label": copy.recordings.search,
3583
+ onInput: (event) => setSearch(event.currentTarget.value)
3584
+ }
3585
+ )),
3586
+ search.trim() && matchedSegments.length === 0 && /* @__PURE__ */ import_react26.default.createElement("p", { role: "status" }, copy.recordings.searchEmpty),
3587
+ /* @__PURE__ */ import_react26.default.createElement("table", { className: "dlvd-segments" }, /* @__PURE__ */ import_react26.default.createElement("thead", null, /* @__PURE__ */ import_react26.default.createElement("tr", null, /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.start), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.end), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.duration), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.audio))), /* @__PURE__ */ import_react26.default.createElement("tbody", null, matchedSegments.map((segment) => /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, { key: segment.id }, /* @__PURE__ */ import_react26.default.createElement("tr", { "data-selected": selectedSegment === segment.id }, /* @__PURE__ */ import_react26.default.createElement("td", null, /* @__PURE__ */ import_react26.default.createElement("span", null, sourceName(session, copy)), /* @__PURE__ */ import_react26.default.createElement("br", null), commandsFor(segment).length ? /* @__PURE__ */ import_react26.default.createElement("span", { className: "dlvd-command-label" }, "\u03DF ", copy.recordings.eventCommand, " \xB7", " ", commandsFor(segment).map((event) => commandName(event.details.command)).join(", ")) : ignoredFor(segment).length ? /* @__PURE__ */ import_react26.default.createElement("span", { className: "dlvd-ignored-label" }, "\u2298 ", copy.recordings.ignoredMuted) : segment.kind === "speech" ? /* @__PURE__ */ import_react26.default.createElement("span", { className: "dlvd-speech-label" }, "\u25CF ", copy.recordings.speech) : copy.recordings.silence, segment.open && /* @__PURE__ */ import_react26.default.createElement("small", null, copy.recordings.open)), /* @__PURE__ */ import_react26.default.createElement("td", null, duration(segment.startSample, session.sampleRate)), /* @__PURE__ */ import_react26.default.createElement("td", null, duration(segment.endSample, session.sampleRate)), /* @__PURE__ */ import_react26.default.createElement("td", null, duration(segment.endSample - segment.startSample, session.sampleRate)), /* @__PURE__ */ import_react26.default.createElement("td", null, /* @__PURE__ */ import_react26.default.createElement(
3588
+ "button",
3589
+ {
3590
+ type: "button",
3591
+ className: "dlvd-segment-action",
3592
+ title: copy.recordings.play,
3593
+ disabled: deleting || segment.endSample <= segment.startSample,
3594
+ "aria-label": copy.recordings.play + " \xB7 " + sourceName(session, copy) + " \xB7 " + segmentName(segment),
3595
+ onClick: () => void play(segment)
3596
+ },
3597
+ /* @__PURE__ */ import_react26.default.createElement(SegmentIcon, { kind: "play" })
3598
+ ), /* @__PURE__ */ import_react26.default.createElement(
3599
+ "button",
3600
+ {
3601
+ type: "button",
3602
+ className: "dlvd-segment-action",
3603
+ disabled: downloading || exporting || deleting || segment.endSample <= segment.startSample,
3604
+ title: copy.recordings.download,
3605
+ "aria-label": copy.recordings.download + " \xB7 " + segmentName(segment),
3606
+ onClick: () => void downloadSegment(segment)
3607
+ },
3608
+ /* @__PURE__ */ import_react26.default.createElement(SegmentIcon, { kind: "download" })
3609
+ ), segment.kind === "speech" && service.retranscribe && /* @__PURE__ */ import_react26.default.createElement(
3610
+ "button",
3611
+ {
3612
+ type: "button",
3613
+ className: "dlvd-segment-action",
3614
+ disabled: deleting || !!retrying || session.state === "recording" || segment.open || segment.endSample <= segment.startSample || segment.attempts.some((a) => a.status === "pending"),
3615
+ title: copy.recordings.retranscribeHelp,
3616
+ "aria-label": copy.recordings.retranscribe + " \xB7 " + segmentName(segment),
3617
+ onClick: () => void retranscribe(segment)
3618
+ },
3619
+ /* @__PURE__ */ import_react26.default.createElement(SegmentIcon, { kind: "retry" })
3620
+ ))), segment.kind === "speech" && /* @__PURE__ */ import_react26.default.createElement("tr", null, /* @__PURE__ */ import_react26.default.createElement("td", { colSpan: 5 }, /* @__PURE__ */ import_react26.default.createElement(
3621
+ "details",
3622
+ {
3623
+ open: !!search.trim() || selectedSegment === segment.id || void 0
3624
+ },
3625
+ /* @__PURE__ */ import_react26.default.createElement("summary", null, copy.recordings.attempts, " \xB7 ", segment.attempts.length),
3626
+ !segment.attempts.length && /* @__PURE__ */ import_react26.default.createElement("p", null, copy.recordings.noAttempts),
3627
+ segment.attempts.map((attempt) => /* @__PURE__ */ import_react26.default.createElement("article", { className: "dlvd-attempt", key: attempt.id }, /* @__PURE__ */ import_react26.default.createElement("div", null, copy.recordings.engine, ": ", attempt.engine, " \xB7", {
3628
+ pending: copy.recordings.pending,
3629
+ success: copy.recordings.success,
3630
+ error: copy.recordings.error
3631
+ }[attempt.status]), /* @__PURE__ */ import_react26.default.createElement("time", { dateTime: attempt.createdAt }, new Date(attempt.createdAt).toLocaleString()), attempt.finishedAt && /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, null, " ", "\u2014", " ", /* @__PURE__ */ import_react26.default.createElement("time", { dateTime: attempt.finishedAt }, new Date(attempt.finishedAt).toLocaleString())), /* @__PURE__ */ import_react26.default.createElement("div", null, duration(attempt.startSample, session.sampleRate), " \u2014", " ", duration(attempt.endSample, session.sampleRate)), attempt.request ? /* @__PURE__ */ import_react26.default.createElement("details", { className: "dlvd-request" }, /* @__PURE__ */ import_react26.default.createElement("summary", null, copy.recordings.requestMetadata, " \xB7", " ", attempt.request.payloadBytes.toLocaleString(), " B \xB7", " ", (attempt.request.durationMs / 1e3).toFixed(3), " s"), /* @__PURE__ */ import_react26.default.createElement("p", null, copy.recordings.requestHelp), /* @__PURE__ */ import_react26.default.createElement("dl", { className: "dlvd-recording-meta" }, /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.requestBytes), /* @__PURE__ */ import_react26.default.createElement("dd", null, attempt.request.payloadBytes.toLocaleString(), " B"), /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.requestInput), /* @__PURE__ */ import_react26.default.createElement("dd", null, attempt.request.inputSamples.toLocaleString(), " /", " ", attempt.request.inputSampleRate.toLocaleString(), " Hz"), /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.requestSamples), /* @__PURE__ */ import_react26.default.createElement("dd", null, attempt.request.sampleCount.toLocaleString(), " /", " ", attempt.request.sampleRate.toLocaleString(), " Hz"), /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.duration), /* @__PURE__ */ import_react26.default.createElement("dd", null, (attempt.request.durationMs / 1e3).toFixed(3), " s"), /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.requestExpected), /* @__PURE__ */ import_react26.default.createElement("dd", null, duration(
3632
+ attempt.endSample - attempt.startSample,
3633
+ session.sampleRate
3634
+ )), /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.requestLanguage), /* @__PURE__ */ import_react26.default.createElement("dd", null, attempt.request.language), /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.requestPrepared), /* @__PURE__ */ import_react26.default.createElement("dd", null, /* @__PURE__ */ import_react26.default.createElement("time", { dateTime: attempt.request.preparedAt }, new Date(attempt.request.preparedAt).toLocaleString())), /* @__PURE__ */ import_react26.default.createElement("dt", null, copy.recordings.requestStatus), /* @__PURE__ */ import_react26.default.createElement("dd", null, attempt.request.responseStatus ?? copy.recordings.requestUnknown))) : /* @__PURE__ */ import_react26.default.createElement("small", null, copy.recordings.requestUnknown), attempt.text != null && /* @__PURE__ */ import_react26.default.createElement("p", { className: "dlvd-transcript" }, attempt.text), attempt.error && /* @__PURE__ */ import_react26.default.createElement("p", { className: "dlvd-attempt-error" }, copy.recordings.error, ": ", attempt.error)))
3635
+ )))))))
3636
+ ), /* @__PURE__ */ import_react26.default.createElement(
3637
+ "div",
3638
+ {
3639
+ className: "dlvd-detail-list dlvd-boundary-pins",
3640
+ role: "tabpanel",
3641
+ id: detailTabsId + "-pins",
3642
+ "aria-labelledby": detailTabsId + "-pins-tab",
3643
+ hidden: detailTab !== "pins"
3644
+ },
3645
+ /* @__PURE__ */ import_react26.default.createElement("h4", null, copy.recordings.pins, " \xB7 ", session.pins.length),
3646
+ /* @__PURE__ */ import_react26.default.createElement("table", null, /* @__PURE__ */ import_react26.default.createElement("thead", null, /* @__PURE__ */ import_react26.default.createElement("tr", null, /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.start), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.pinConfirmed), /* @__PURE__ */ import_react26.default.createElement("th", { scope: "col" }, copy.recordings.pinDelay))), /* @__PURE__ */ import_react26.default.createElement("tbody", null, session.pins.map((pin) => /* @__PURE__ */ import_react26.default.createElement("tr", { key: pin.id }, /* @__PURE__ */ import_react26.default.createElement("td", null, pin.kind === "speech-start" ? copy.recordings.speech : copy.recordings.silence), /* @__PURE__ */ import_react26.default.createElement("td", null, duration(pin.atSample, session.sampleRate)), /* @__PURE__ */ import_react26.default.createElement("td", null, duration(pin.confirmedAtSample, session.sampleRate)), /* @__PURE__ */ import_react26.default.createElement("td", null, duration(pin.delaySamples, session.sampleRate))))))
3647
+ ));
3341
3648
  }
3342
3649
 
3343
3650
  // src/RuntimePanel.tsx
3344
- var import_react26 = __toESM(require("react"), 1);
3651
+ var import_react27 = __toESM(require("react"), 1);
3345
3652
  function RuntimePanel({ data, copy }) {
3346
3653
  const unknown = copy.runtimes.unavailable;
3347
3654
  const bytes = (n) => typeof n === "number" && Number.isFinite(n) ? (n / 1073741824).toFixed(3) + " GiB (" + Math.round(n).toLocaleString() + " B)" : unknown;
@@ -3357,20 +3664,20 @@ function RuntimePanel({ data, copy }) {
3357
3664
  released: copy.runtimes.released,
3358
3665
  cancelled: copy.runtimes.cancelled
3359
3666
  })[v] ?? unknown;
3360
- const row = (label, value) => /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, { key: label }, /* @__PURE__ */ import_react26.default.createElement("dt", null, label), /* @__PURE__ */ import_react26.default.createElement("dd", null, value));
3361
- return /* @__PURE__ */ import_react26.default.createElement("section", { className: "dlvd-runtimes dlvd-scroll", tabIndex: 0, "aria-label": copy.runtimes.title }, /* @__PURE__ */ import_react26.default.createElement("h3", null, copy.runtimes.title, " ", /* @__PURE__ */ import_react26.default.createElement(Hint, { label: copy.runtimes.title }, copy.runtimes.help)), /* @__PURE__ */ import_react26.default.createElement("dl", { className: "dlvd-recording-meta" }, row(
3667
+ const row = (label, value) => /* @__PURE__ */ import_react27.default.createElement(import_react27.default.Fragment, { key: label }, /* @__PURE__ */ import_react27.default.createElement("dt", null, label), /* @__PURE__ */ import_react27.default.createElement("dd", null, value));
3668
+ return /* @__PURE__ */ import_react27.default.createElement("section", { className: "dlvd-runtimes dlvd-scroll", tabIndex: 0, "aria-label": copy.runtimes.title }, /* @__PURE__ */ import_react27.default.createElement("h3", null, copy.runtimes.title, " ", /* @__PURE__ */ import_react27.default.createElement(Hint, { label: copy.runtimes.title }, copy.runtimes.help)), /* @__PURE__ */ import_react27.default.createElement("dl", { className: "dlvd-recording-meta" }, row(
3362
3669
  copy.runtimes.versions,
3363
3670
  (data.versions?.onnx ?? unknown) + " / " + (data.versions?.transformers ?? unknown)
3364
3671
  ), row(copy.runtimes.id, data.runtimeId ?? unknown), row(
3365
3672
  copy.runtimes.gpu,
3366
3673
  data.gpu === "available" ? copy.runtimes.yes : data.gpu === "unavailable" ? copy.runtimes.no : unknown
3367
- ), row(copy.runtimes.storage, bytes(data.storageAvailableBytes)), row(copy.runtimes.heap, bytes(data.pageJsHeapBytes)), row(copy.runtimes.ram, unknown), row(copy.runtimes.vram, unknown)), (data.models ?? []).map((model) => /* @__PURE__ */ import_react26.default.createElement("article", { className: "dlvd-runtime-card", key: model.key }, /* @__PURE__ */ import_react26.default.createElement("h3", null, model.name), /* @__PURE__ */ import_react26.default.createElement("dl", { className: "dlvd-recording-meta" }, row(copy.runtimes.status, status(model.phase)), row(
3674
+ ), row(copy.runtimes.storage, bytes(data.storageAvailableBytes)), row(copy.runtimes.heap, bytes(data.pageJsHeapBytes)), row(copy.runtimes.ram, unknown), row(copy.runtimes.vram, unknown)), (data.models ?? []).map((model) => /* @__PURE__ */ import_react27.default.createElement("article", { className: "dlvd-runtime-card", key: model.key }, /* @__PURE__ */ import_react27.default.createElement("h3", null, model.name), /* @__PURE__ */ import_react27.default.createElement("dl", { className: "dlvd-recording-meta" }, row(copy.runtimes.status, status(model.phase)), row(
3368
3675
  copy.runtimes.device,
3369
3676
  model.device === "webgpu" ? "WebGPU" : model.device === "wasm" ? "WebAssembly CPU" : unknown
3370
3677
  ), row(copy.runtimes.worker, model.workerActive ? copy.runtimes.yes : copy.runtimes.no), row(copy.runtimes.revision, model.repository + " @ " + model.revision), row(copy.runtimes.quantization, model.encoderDtype + " / " + model.decoderDtype), row(copy.runtimes.modelBytes, bytes(model.modelBytes)), row(copy.runtimes.cache, bytes(model.cachedBytes)), row(copy.runtimes.pcm, bytes(model.pcmBytes)), row(copy.runtimes.load, ms(model.loadMs) + " / " + ms(model.workerLoadMs)), row(copy.runtimes.inference, ms(model.workerInferenceMs)), row(copy.runtimes.audio, ms(model.audioMs)), row(
3371
3678
  copy.runtimes.ratio,
3372
3679
  model.workerInferenceMs != null && model.audioMs > 0 ? (model.workerInferenceMs / model.audioMs).toFixed(2) + "\xD7" : unknown
3373
- ), row(copy.runtimes.queue, model.queued), row(copy.runtimes.active, model.active), row(copy.runtimes.pending, model.pendingRequests), row(copy.runtimes.completed, model.completed), row(copy.runtimes.failed, model.failed)), model.error && /* @__PURE__ */ import_react26.default.createElement("p", { role: "alert" }, copy.runtimes.error, ": ", /* @__PURE__ */ import_react26.default.createElement("code", null, model.error)))), /* @__PURE__ */ import_react26.default.createElement("h3", null, copy.runtimes.events), /* @__PURE__ */ import_react26.default.createElement("div", { className: "dlvd-history" }, !data.events?.length && /* @__PURE__ */ import_react26.default.createElement("p", null, copy.commons.empty), [...data.events ?? []].reverse().map((event) => /* @__PURE__ */ import_react26.default.createElement("div", { key: event.sequence, "data-level": event.status === "error" ? "error" : "debug" }, /* @__PURE__ */ import_react26.default.createElement("time", null, new Date(event.timeMs).toLocaleTimeString()), " \xB7 ", event.model, " \xB7", " ", event.operation === "load" ? copy.runtimes.load : event.operation === "worker" ? copy.runtimes.worker : copy.runtimes.inference, " ", "\xB7 ", status(event.status), event.durationMs != null ? " \xB7 " + ms(event.durationMs) : "", event.code && /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, null, " ", "\xB7 ", /* @__PURE__ */ import_react26.default.createElement("code", null, event.code))))));
3680
+ ), row(copy.runtimes.queue, model.queued), row(copy.runtimes.active, model.active), row(copy.runtimes.pending, model.pendingRequests), row(copy.runtimes.completed, model.completed), row(copy.runtimes.failed, model.failed)), model.error && /* @__PURE__ */ import_react27.default.createElement("p", { role: "alert" }, copy.runtimes.error, ": ", /* @__PURE__ */ import_react27.default.createElement("code", null, model.error)))), /* @__PURE__ */ import_react27.default.createElement("h3", null, copy.runtimes.events), /* @__PURE__ */ import_react27.default.createElement("div", { className: "dlvd-history" }, !data.events?.length && /* @__PURE__ */ import_react27.default.createElement("p", null, copy.commons.empty), [...data.events ?? []].reverse().map((event) => /* @__PURE__ */ import_react27.default.createElement("div", { key: event.sequence, "data-level": event.status === "error" ? "error" : "debug" }, /* @__PURE__ */ import_react27.default.createElement("time", null, new Date(event.timeMs).toLocaleTimeString()), " \xB7 ", event.model, " \xB7", " ", event.operation === "load" ? copy.runtimes.load : event.operation === "worker" ? copy.runtimes.worker : copy.runtimes.inference, " ", "\xB7 ", status(event.status), event.durationMs != null ? " \xB7 " + ms(event.durationMs) : "", event.code && /* @__PURE__ */ import_react27.default.createElement(import_react27.default.Fragment, null, " ", "\xB7 ", /* @__PURE__ */ import_react27.default.createElement("code", null, event.code))))));
3374
3681
  }
3375
3682
 
3376
3683
  // src/DebuggerPanel.tsx
@@ -3378,21 +3685,30 @@ function DebuggerPanel({
3378
3685
  copy,
3379
3686
  locale = "en",
3380
3687
  onClose,
3381
- target = window
3688
+ target = window,
3689
+ recordingRequest
3382
3690
  }) {
3383
- const [tab, setTab] = import_react27.default.useState("recordings");
3384
- const tabsId = import_react27.default.useId();
3385
- const [frozen, setFrozen] = import_react27.default.useState(false);
3386
- const [refreshHz, setRefreshHz] = import_react27.default.useState(10);
3387
- const updateRef = import_react27.default.useRef(null);
3388
- const [content, setContent] = import_react27.default.useState(false);
3389
- const [module2, setModule] = import_react27.default.useState("");
3390
- const [view, setView] = import_react27.default.useState(null);
3391
- const [failed, setFailed] = import_react27.default.useState(false);
3392
- const [eventsPaused, setEventsPaused] = import_react27.default.useState(false);
3393
- const pausedRef = import_react27.default.useRef(false);
3394
- const inspectorRef = import_react27.default.useRef(null);
3395
- import_react27.default.useEffect(() => {
3691
+ const [tab, setTab] = import_react28.default.useState(
3692
+ "recordings"
3693
+ );
3694
+ import_react28.default.useEffect(() => {
3695
+ if (recordingRequest?.id) setTab("recordings");
3696
+ }, [recordingRequest]);
3697
+ const tabsId = import_react28.default.useId();
3698
+ const [recordingToolbar, setRecordingToolbar] = import_react28.default.useState(null);
3699
+ const [storagePanel, setStoragePanel] = import_react28.default.useState(null);
3700
+ const [storageTotal, setStorageTotal] = import_react28.default.useState("\u2014");
3701
+ const [frozen, setFrozen] = import_react28.default.useState(false);
3702
+ const [refreshHz, setRefreshHz] = import_react28.default.useState(10);
3703
+ const updateRef = import_react28.default.useRef(null);
3704
+ const [content, setContent] = import_react28.default.useState(false);
3705
+ const [module2, setModule] = import_react28.default.useState("");
3706
+ const [view, setView] = import_react28.default.useState(null);
3707
+ const [failed, setFailed] = import_react28.default.useState(false);
3708
+ const [eventsPaused, setEventsPaused] = import_react28.default.useState(false);
3709
+ const pausedRef = import_react28.default.useRef(false);
3710
+ const inspectorRef = import_react28.default.useRef(null);
3711
+ import_react28.default.useEffect(() => {
3396
3712
  if (frozen) return;
3397
3713
  let inspector = null;
3398
3714
  let source = null;
@@ -3425,7 +3741,7 @@ function DebuggerPanel({
3425
3741
  inspectorRef.current = null;
3426
3742
  };
3427
3743
  }, [frozen, content, target]);
3428
- import_react27.default.useEffect(() => {
3744
+ import_react28.default.useEffect(() => {
3429
3745
  if (frozen) return;
3430
3746
  const timer = setInterval(() => updateRef.current?.(), 1e3 / refreshHz);
3431
3747
  return () => clearInterval(timer);
@@ -3433,11 +3749,11 @@ function DebuggerPanel({
3433
3749
  const modules = [...new Set(view?.transitions.map((entry) => entry.module) ?? [])].sort();
3434
3750
  const runtimeData = view?.state?.runtimes;
3435
3751
  const hasRuntimes = !!runtimeData?.models?.length;
3436
- const tabs = hasRuntimes ? ["recordings", "states", "runtimes"] : ["recordings", "states"];
3437
- import_react27.default.useEffect(() => {
3752
+ const tabs = hasRuntimes ? ["recordings", "states", "storage", "runtimes"] : ["recordings", "states", "storage"];
3753
+ import_react28.default.useEffect(() => {
3438
3754
  if (tab === "runtimes" && !hasRuntimes) setTab("states");
3439
3755
  }, [tab, hasRuntimes]);
3440
- return /* @__PURE__ */ import_react27.default.createElement("aside", { className: "dlvd-panel", "aria-label": copy.commons.title }, /* @__PURE__ */ import_react27.default.createElement("header", null, /* @__PURE__ */ import_react27.default.createElement("strong", null, copy.commons.title), /* @__PURE__ */ import_react27.default.createElement("span", null, refreshHz, " Hz"), /* @__PURE__ */ import_react27.default.createElement("button", { type: "button", onClick: onClose, "aria-label": copy.commons.close }, "\xD7")), /* @__PURE__ */ import_react27.default.createElement("div", { className: "dlvd-tabs", role: "tablist", "aria-label": copy.commons.title }, tabs.map((name) => /* @__PURE__ */ import_react27.default.createElement(
3756
+ return /* @__PURE__ */ import_react28.default.createElement("aside", { className: "dlvd-panel", "aria-label": copy.commons.title }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlvd-navigation" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlvd-tabs", role: "tablist", "aria-label": copy.commons.title }, tabs.map((name) => /* @__PURE__ */ import_react28.default.createElement(
3441
3757
  "button",
3442
3758
  {
3443
3759
  type: "button",
@@ -3458,8 +3774,8 @@ function DebuggerPanel({
3458
3774
  }
3459
3775
  }
3460
3776
  },
3461
- name === "recordings" ? copy.recordings.title : name === "runtimes" ? copy.runtimes.title : copy.commons.states
3462
- ))), /* @__PURE__ */ import_react27.default.createElement(
3777
+ name === "storage" ? copy.recordings.storageTab + ": " + storageTotal : name === "recordings" ? copy.recordings.title : name === "runtimes" ? copy.runtimes.title : copy.commons.states
3778
+ ))), /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlvd-navigation-actions" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlvd-recording-toolbar-slot", ref: setRecordingToolbar }), /* @__PURE__ */ import_react28.default.createElement("span", { className: "dlvd-refresh-frequency" }, refreshHz, " Hz"), /* @__PURE__ */ import_react28.default.createElement("button", { type: "button", onClick: onClose, "aria-label": copy.commons.close }, "\xD7"))), /* @__PURE__ */ import_react28.default.createElement(
3463
3779
  "div",
3464
3780
  {
3465
3781
  className: "dlvd-tab-panel",
@@ -3468,8 +3784,29 @@ function DebuggerPanel({
3468
3784
  "aria-labelledby": tabsId + "-recordings-tab",
3469
3785
  hidden: tab !== "recordings"
3470
3786
  },
3471
- tab === "recordings" && /* @__PURE__ */ import_react27.default.createElement(RecordingPanel, { copy, locale, target })
3472
- ), hasRuntimes && /* @__PURE__ */ import_react27.default.createElement(
3787
+ /* @__PURE__ */ import_react28.default.createElement(
3788
+ RecordingPanel,
3789
+ {
3790
+ copy,
3791
+ locale,
3792
+ target,
3793
+ toolbarTarget: recordingToolbar,
3794
+ storageTarget: storagePanel,
3795
+ onStorageTotal: setStorageTotal,
3796
+ recordingRequest
3797
+ }
3798
+ )
3799
+ ), /* @__PURE__ */ import_react28.default.createElement(
3800
+ "div",
3801
+ {
3802
+ className: "dlvd-tab-panel dlvd-scroll",
3803
+ role: "tabpanel",
3804
+ id: tabsId + "-storage",
3805
+ "aria-labelledby": tabsId + "-storage-tab",
3806
+ hidden: tab !== "storage",
3807
+ ref: setStoragePanel
3808
+ }
3809
+ ), hasRuntimes && /* @__PURE__ */ import_react28.default.createElement(
3473
3810
  "div",
3474
3811
  {
3475
3812
  className: "dlvd-tab-panel",
@@ -3478,8 +3815,8 @@ function DebuggerPanel({
3478
3815
  "aria-labelledby": tabsId + "-runtimes-tab",
3479
3816
  hidden: tab !== "runtimes"
3480
3817
  },
3481
- tab === "runtimes" && /* @__PURE__ */ import_react27.default.createElement(RuntimePanel, { data: runtimeData, copy })
3482
- ), /* @__PURE__ */ import_react27.default.createElement(
3818
+ tab === "runtimes" && /* @__PURE__ */ import_react28.default.createElement(RuntimePanel, { data: runtimeData, copy })
3819
+ ), /* @__PURE__ */ import_react28.default.createElement(
3483
3820
  "div",
3484
3821
  {
3485
3822
  className: "dlvd-tab-panel",
@@ -3488,15 +3825,15 @@ function DebuggerPanel({
3488
3825
  "aria-labelledby": tabsId + "-states-tab",
3489
3826
  hidden: tab !== "states"
3490
3827
  },
3491
- /* @__PURE__ */ import_react27.default.createElement("p", null, copy.commons.help),
3492
- /* @__PURE__ */ import_react27.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react27.default.createElement("label", null, copy.commons.refreshRate, /* @__PURE__ */ import_react27.default.createElement(
3828
+ /* @__PURE__ */ import_react28.default.createElement("p", null, copy.commons.help),
3829
+ /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react28.default.createElement("label", null, copy.commons.refreshRate, /* @__PURE__ */ import_react28.default.createElement(
3493
3830
  "select",
3494
3831
  {
3495
3832
  value: refreshHz,
3496
3833
  onChange: (event) => setRefreshHz(Number(event.target.value))
3497
3834
  },
3498
- [1, 2, 5, 10, 20].map((hz) => /* @__PURE__ */ import_react27.default.createElement("option", { key: hz, value: hz }, hz, " Hz"))
3499
- )), /* @__PURE__ */ import_react27.default.createElement("button", { type: "button", onClick: () => setFrozen(!frozen) }, frozen ? copy.commons.resume : copy.commons.freeze), /* @__PURE__ */ import_react27.default.createElement("label", null, /* @__PURE__ */ import_react27.default.createElement(
3835
+ [1, 2, 5, 10, 20].map((hz) => /* @__PURE__ */ import_react28.default.createElement("option", { key: hz, value: hz }, hz, " Hz"))
3836
+ )), /* @__PURE__ */ import_react28.default.createElement("button", { type: "button", onClick: () => setFrozen(!frozen) }, frozen ? copy.commons.resume : copy.commons.freeze), /* @__PURE__ */ import_react28.default.createElement("label", null, /* @__PURE__ */ import_react28.default.createElement(
3500
3837
  "input",
3501
3838
  {
3502
3839
  type: "checkbox",
@@ -3508,7 +3845,7 @@ function DebuggerPanel({
3508
3845
  }
3509
3846
  }
3510
3847
  ), copy.commons.content)),
3511
- failed ? /* @__PURE__ */ import_react27.default.createElement("p", { role: "alert" }, copy.commons.error) : !view ? /* @__PURE__ */ import_react27.default.createElement("p", { role: "status" }, copy.commons.waiting) : /* @__PURE__ */ import_react27.default.createElement("div", { className: "dlvd-columns" }, /* @__PURE__ */ import_react27.default.createElement("section", { className: "dlvd-state-pane", "aria-label": copy.commons.states }, /* @__PURE__ */ import_react27.default.createElement("h3", null, copy.commons.states), /* @__PURE__ */ import_react27.default.createElement("div", { className: "dlvd-scroll", tabIndex: 0 }, /* @__PURE__ */ import_react27.default.createElement(StateTree, { name: "context", value: view.state, omitted: copy.commons.omitted }))), /* @__PURE__ */ import_react27.default.createElement("section", { className: "dlvd-event-pane", "aria-label": copy.commons.transitions }, /* @__PURE__ */ import_react27.default.createElement("h3", null, copy.commons.transitions), /* @__PURE__ */ import_react27.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react27.default.createElement(
3848
+ failed ? /* @__PURE__ */ import_react28.default.createElement("p", { role: "alert" }, copy.commons.error) : !view ? /* @__PURE__ */ import_react28.default.createElement("p", { role: "status" }, copy.commons.waiting) : /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlvd-columns" }, /* @__PURE__ */ import_react28.default.createElement("section", { className: "dlvd-state-pane", "aria-label": copy.commons.states }, /* @__PURE__ */ import_react28.default.createElement("h3", null, copy.commons.states), /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlvd-scroll", tabIndex: 0 }, /* @__PURE__ */ import_react28.default.createElement(StateTree, { name: "context", value: view.state, omitted: copy.commons.omitted }))), /* @__PURE__ */ import_react28.default.createElement("section", { className: "dlvd-event-pane", "aria-label": copy.commons.transitions }, /* @__PURE__ */ import_react28.default.createElement("h3", null, copy.commons.transitions), /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react28.default.createElement(
3512
3849
  "button",
3513
3850
  {
3514
3851
  type: "button",
@@ -3521,7 +3858,7 @@ function DebuggerPanel({
3521
3858
  }
3522
3859
  },
3523
3860
  eventsPaused ? copy.commons.eventsResume : copy.commons.eventsPause
3524
- ), /* @__PURE__ */ import_react27.default.createElement(
3861
+ ), /* @__PURE__ */ import_react28.default.createElement(
3525
3862
  "button",
3526
3863
  {
3527
3864
  type: "button",
@@ -3531,14 +3868,14 @@ function DebuggerPanel({
3531
3868
  }
3532
3869
  },
3533
3870
  copy.commons.eventsClear
3534
- )), /* @__PURE__ */ import_react27.default.createElement("label", null, copy.commons.filter, /* @__PURE__ */ import_react27.default.createElement("select", { value: module2, onChange: (event) => setModule(event.target.value) }, /* @__PURE__ */ import_react27.default.createElement("option", { value: "" }, copy.commons.all), modules.map((name) => /* @__PURE__ */ import_react27.default.createElement("option", { key: name }, name)))), /* @__PURE__ */ import_react27.default.createElement("div", { className: "dlvd-history dlvd-scroll", tabIndex: 0 }, view.transitions.filter((entry) => !module2 || entry.module === module2).slice().reverse().map((entry) => /* @__PURE__ */ import_react27.default.createElement("details", { key: entry.sequence, "data-level": entry.level }, /* @__PURE__ */ import_react27.default.createElement("summary", null, /* @__PURE__ */ import_react27.default.createElement("small", null, new Date(entry.timeMs).toLocaleTimeString()), " ", /* @__PURE__ */ import_react27.default.createElement("code", null, entry.sessionId, " / ", entry.module)), /* @__PURE__ */ import_react27.default.createElement(
3871
+ )), /* @__PURE__ */ import_react28.default.createElement("label", null, copy.commons.filter, /* @__PURE__ */ import_react28.default.createElement("select", { value: module2, onChange: (event) => setModule(event.target.value) }, /* @__PURE__ */ import_react28.default.createElement("option", { value: "" }, copy.commons.all), modules.map((name) => /* @__PURE__ */ import_react28.default.createElement("option", { key: name }, name)))), /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlvd-history dlvd-scroll", tabIndex: 0 }, view.transitions.filter((entry) => !module2 || entry.module === module2).slice().reverse().map((entry) => /* @__PURE__ */ import_react28.default.createElement("details", { key: entry.sequence, "data-level": entry.level }, /* @__PURE__ */ import_react28.default.createElement("summary", null, /* @__PURE__ */ import_react28.default.createElement("small", null, new Date(entry.timeMs).toLocaleTimeString()), " ", /* @__PURE__ */ import_react28.default.createElement("code", null, entry.sessionId, " / ", entry.module)), /* @__PURE__ */ import_react28.default.createElement(
3535
3872
  StateTree,
3536
3873
  {
3537
3874
  name: "before",
3538
3875
  value: entry.before,
3539
3876
  omitted: copy.commons.omitted
3540
3877
  }
3541
- ), /* @__PURE__ */ import_react27.default.createElement(StateTree, { name: "after", value: entry.after, omitted: copy.commons.omitted }))), !view.transitions.length && /* @__PURE__ */ import_react27.default.createElement("p", null, copy.commons.empty))))
3878
+ ), /* @__PURE__ */ import_react28.default.createElement(StateTree, { name: "after", value: entry.after, omitted: copy.commons.omitted }))), !view.transitions.length && /* @__PURE__ */ import_react28.default.createElement("p", null, copy.commons.empty))))
3542
3879
  ));
3543
3880
  }
3544
3881
 
@@ -3570,7 +3907,7 @@ function normalizeDebuggerLocale(locale) {
3570
3907
  }
3571
3908
 
3572
3909
  // node_modules/.pnpm/@wads.dev+i18n-react@0.0.1-alpha.0_react@19.3.0_typescript@7.0.2/node_modules/@wads.dev/i18n-react/dist/index.js
3573
- var import_react28 = require("react");
3910
+ var import_react29 = require("react");
3574
3911
 
3575
3912
  // node_modules/.pnpm/@wads.dev+i18n-ts@0.0.1-alpha.0_typescript@7.0.2/node_modules/@wads.dev/i18n-ts/dist/runtime/language.js
3576
3913
  function detectLanguage() {
@@ -3603,21 +3940,21 @@ function deepFreeze(value) {
3603
3940
 
3604
3941
  // node_modules/.pnpm/@wads.dev+i18n-react@0.0.1-alpha.0_react@19.3.0_typescript@7.0.2/node_modules/@wads.dev/i18n-react/dist/index.js
3605
3942
  function createTranslationRuntime({ availableLangs, defaultLang, onLanguageLoaded }) {
3606
- const context = (0, import_react28.createContext)({});
3943
+ const context = (0, import_react29.createContext)({});
3607
3944
  function useTranslation(selector) {
3608
- const value = (0, import_react28.useContext)(context);
3945
+ const value = (0, import_react29.useContext)(context);
3609
3946
  if (!value.current)
3610
3947
  throw new Error("useTranslation must be used within TranslationProvider.");
3611
3948
  return { ...value, scoped: selector ? selector(value.current.lang) : value.current.lang };
3612
3949
  }
3613
3950
  function TranslationProvider({ children }) {
3614
- const [saved, setSaved] = (0, import_react28.useState)();
3615
- const [language, setCurrentLanguage] = (0, import_react28.useState)();
3951
+ const [saved, setSaved] = (0, import_react29.useState)();
3952
+ const [language, setCurrentLanguage] = (0, import_react29.useState)();
3616
3953
  function setLanguage(nextLanguage, save) {
3617
3954
  if (save)
3618
3955
  setSaved(nextLanguage);
3619
3956
  }
3620
- (0, import_react28.useEffect)(() => {
3957
+ (0, import_react29.useEffect)(() => {
3621
3958
  void loadLanguage(availableLangs, defaultLang, saved).then((loadedLanguage) => {
3622
3959
  setCurrentLanguage(loadedLanguage);
3623
3960
  onLanguageLoaded?.(loadedLanguage);
@@ -3625,7 +3962,7 @@ function createTranslationRuntime({ availableLangs, defaultLang, onLanguageLoade
3625
3962
  }, [saved]);
3626
3963
  if (!language)
3627
3964
  return null;
3628
- return (0, import_react28.createElement)(context.Provider, { value: { setLanguage, available: availableLangs, current: language, saved } }, children);
3965
+ return (0, import_react29.createElement)(context.Provider, { value: { setLanguage, available: availableLangs, current: language, saved } }, children);
3629
3966
  }
3630
3967
  return { TranslationProvider, useTranslation };
3631
3968
  }
@@ -3640,28 +3977,28 @@ var {
3640
3977
  });
3641
3978
 
3642
3979
  // src/i18n/DshLanguageBoundary.tsx
3643
- var import_react29 = __toESM(require("react"), 1);
3980
+ var import_react30 = __toESM(require("react"), 1);
3644
3981
  var fallback = Object.freeze({ active: "en", revision: 0 });
3645
3982
  function DshLanguageBoundary({ locale, children }) {
3646
3983
  const { current, setLanguage } = useDebuggerLanguage();
3647
- const subscribe = import_react29.default.useCallback(
3984
+ const subscribe = import_react30.default.useCallback(
3648
3985
  (listener) => locale?.subscribe?.(listener) ?? (() => {
3649
3986
  }),
3650
3987
  [locale]
3651
3988
  );
3652
- const snapshot = import_react29.default.useCallback(() => locale?.getSnapshot?.() ?? fallback, [locale]);
3653
- const state = import_react29.default.useSyncExternalStore(subscribe, snapshot, snapshot);
3989
+ const snapshot = import_react30.default.useCallback(() => locale?.getSnapshot?.() ?? fallback, [locale]);
3990
+ const state = import_react30.default.useSyncExternalStore(subscribe, snapshot, snapshot);
3654
3991
  const expected = normalizeDebuggerLocale(state.active);
3655
- import_react29.default.useEffect(() => {
3992
+ import_react30.default.useEffect(() => {
3656
3993
  if (current.locale !== expected) setLanguage(expected, true);
3657
3994
  }, [current.locale, expected, setLanguage]);
3658
- const initialized = import_react29.default.useRef(false);
3995
+ const initialized = import_react30.default.useRef(false);
3659
3996
  if (current.locale === expected) initialized.current = true;
3660
3997
  return initialized.current ? children : null;
3661
3998
  }
3662
3999
  function withDebuggerLanguage(Component, locale) {
3663
4000
  function Localized(props) {
3664
- return /* @__PURE__ */ import_react29.default.createElement(DebuggerTranslationProvider, null, /* @__PURE__ */ import_react29.default.createElement(DshLanguageBoundary, { locale }, /* @__PURE__ */ import_react29.default.createElement(Component, { ...props })));
4001
+ return /* @__PURE__ */ import_react30.default.createElement(DebuggerTranslationProvider, null, /* @__PURE__ */ import_react30.default.createElement(DshLanguageBoundary, { locale }, /* @__PURE__ */ import_react30.default.createElement(Component, { ...props })));
3665
4002
  }
3666
4003
  Localized.displayName = "DebuggerLanguage(" + (Component.displayName || Component.name || "Component") + ")";
3667
4004
  return Localized;
@@ -3697,9 +4034,16 @@ function registerDshLocales(ctx) {
3697
4034
 
3698
4035
  // src/styles.ts
3699
4036
  var styles = `
4037
+ .dlvd-recording-columns>[hidden]{display:none!important}.dlvd-recording-columns[data-layout=list],.dlvd-recording-columns[data-layout=session]{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr)}.dlvd-recording-columns[data-layout=session]>.dlvd-recording-detail{border:0;padding:0}.dlvd-layout-control{white-space:nowrap}
4038
+ .dlvd-navigation-actions{display:flex;align-items:center;justify-content:flex-end;gap:10px;flex-wrap:wrap;margin-left:auto;min-width:0}.dlvd-refresh-frequency{white-space:nowrap;font-size:12px;color:var(--dsw-alias-text-secondary,#b8bdc5)}
4039
+ .dlvd-timeline button[data-kind=speech]{background:#82d9bd!important}.dlvd-event-timeline [data-category=speech]{--event-color:#82d9bd}.dlvd-event-timeline [data-category=silence]{--event-color:#566578}.dlvd-timeline button[data-kind=command]{background:#ffba72!important}.dlvd-summary-interval{position:absolute;pointer-events:none;height:5px;z-index:1;min-width:1px}.dlvd-summary-interval[data-category=waiting]{top:1px;background:#d9beff}.dlvd-summary-interval[data-category=playback]{bottom:1px;background:#90c4ff}
4040
+ .dlvd-detail-list[hidden]{display:none!important}.dlvd-detail-tabs{flex-wrap:wrap}.dlvd-event-track:empty{display:none}
4041
+ .dlvd-detail-heading{display:flex;align-items:center;justify-content:space-between;gap:10px 20px;flex-wrap:wrap;margin:4px 0 10px}.dlvd-detail-heading h3{margin:0;min-width:0}.dlvd-detail-heading .dlvd-detail-actions{margin:0;flex-wrap:wrap}.dlvd-session-meta{display:flex!important;flex-wrap:wrap;gap:8px 20px!important;margin:8px 0 12px}.dlvd-session-meta>div{display:inline-flex;align-items:baseline;gap:6px;min-width:0;max-width:100%}.dlvd-session-meta dt{font-weight:600}.dlvd-session-meta dt:after{content:":"}.dlvd-session-meta dd{margin:0;overflow-wrap:anywhere}
4042
+ .dlvd-navigation{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;flex:none;margin:8px 0 12px}.dlvd-navigation .dlvd-tabs{margin:0;flex-wrap:wrap}.dlvd-recording-toolbar-slot{margin-left:auto}.dlvd-recording-toolbar-slot .dlvd-toolbar{margin:0;justify-content:flex-end;flex-wrap:wrap}.dlvd-recording-toolbar-slot select{max-width:min(260px,55vw)}@media(max-width:650px){.dlvd-recording-toolbar-slot{width:100%;margin:0}.dlvd-recording-toolbar-slot .dlvd-toolbar{justify-content:flex-start}}
4043
+ .dlvd-segment-heading{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-top:16px}.dlvd-segment-heading h4{margin:0}.dlvd-copy-transcripts{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px}.dlvd-copy-transcripts [role=alert]{color:var(--dsw-alias-state-error-primary,#f25a5a)}
3700
4044
  .dlvd-recording-storage{margin:12px 0 16px;padding:14px 16px;border:1px solid var(--dsw-alias-border-l2,#ffffff28);border-radius:10px;background:var(--dsw-alias-bg-layer-1,#202126)}.dlvd-storage-overview{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.dlvd-storage-overview h3{margin:0 0 8px}.dlvd-storage-overview .dlvd-audio-total{margin:0;font-size:15px}.dlvd-storage-overview .dlvd-audio-total strong{font-size:20px;font-variant-numeric:tabular-nums;color:var(--dsw-alias-text-primary,#f3f4f6)}.dlvd-storage-count{margin:5px 0 0;font-size:12px}.dlvd-storage-scope{font-size:12px;color:var(--dsw-alias-text-secondary,#b8bdc5);margin:10px 0 0}.dlvd-panel button.dlvd-delete-all{border-color:#e28f8f;color:#ffbaba}.dlvd-delete-all-confirm{margin-top:14px;padding:12px;border:1px solid #e28f8f;border-radius:8px}.dlvd-delete-all-confirm h4{margin:0 0 10px}.dlvd-delete-all-confirm p{margin:8px 0}.dlvd-storage-result:empty{display:none}.dlvd-storage-result:focus{outline:2px solid #90c4ff;outline-offset:4px}
3701
4045
 
3702
- .dlvd-event-timeline{margin:14px 0}.dlvd-event-legend{display:flex;gap:10px 16px;flex-wrap:wrap;font-size:12px}.dlvd-event-legend span{display:inline-flex;gap:5px;align-items:center}.dlvd-event-timeline [data-category=waiting],.dlvd-event-tooltip[data-category=waiting]{--event-color:#d9beff}.dlvd-event-timeline [data-category=playback],.dlvd-event-tooltip[data-category=playback]{--event-color:#90c4ff}.dlvd-event-timeline [data-category=capture],.dlvd-event-tooltip[data-category=capture]{--event-color:#82d9bd}.dlvd-event-timeline [data-category=command],.dlvd-event-tooltip[data-category=command]{--event-color:#ffba72}.dlvd-event-timeline [data-category=attention],.dlvd-event-tooltip[data-category=attention]{--event-color:#ff9eac}.dlvd-event-legend span{color:var(--event-color)}.dlvd-event-timeline .dlvd-event-track{height:auto;margin:8px 0;background:var(--dsw-alias-bg-layer-1,#1b1c20);overflow:visible}.dlvd-event-lane{height:34px;position:relative;border-bottom:1px solid var(--dsw-alias-border-l2,#ffffff12)}.dlvd-event-lane:last-child{border-bottom:0}.dlvd-recordings .dlvd-event-marker{position:absolute;top:2px;transform:translateX(-50%);height:30px;width:34px;padding:3px;display:inline-flex;align-items:center;justify-content:center;gap:1px;color:var(--event-color);background:color-mix(in srgb,var(--event-color) 12%,#1c1c1f);border:1px solid color-mix(in srgb,var(--event-color) 45%,transparent);border-radius:5px;font-size:10px}.dlvd-event-marker:hover,.dlvd-event-marker:focus-visible{outline:2px solid var(--event-color);outline-offset:1px;z-index:2}.dlvd-event-count{font-variant-numeric:tabular-nums;font-weight:700}.dlvd-event-tooltip{position:fixed;inset:auto;margin:0;box-sizing:border-box;width:min(390px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:hidden;padding:12px 14px;border:1px solid var(--event-color,#b7c0cf);border-radius:10px;background:#202126;color:#f3f4f6;font:12px/1.45 system-ui,sans-serif;box-shadow:0 12px 35px #0008;z-index:2147483647;pointer-events:none!important}.dlvd-event-tooltip *{pointer-events:none!important}.dlvd-event-tooltip::backdrop{pointer-events:none}.dlvd-event-tooltip strong{color:var(--event-color);display:block;margin-bottom:8px}.dlvd-event-tooltip-list>div{display:flex;flex-direction:column;gap:2px;padding:6px 0;border-top:1px solid #ffffff18}.dlvd-event-tooltip time,.dlvd-event-tooltip span{color:#c6cbd4}.dlvd-event-tooltip footer{margin-top:8px;color:#c6cbd4;font-size:11px}.dlvd-recording-events tr:focus{outline:2px solid #90c4ff;outline-offset:-2px}.dlvd-timeline button[data-kind=command]{background:#a8611d!important}.dlvd-timeline-legend [data-kind=command]{border-color:#ffba72!important}.dlvd-command-label{color:#ffba72}
4046
+ .dlvd-event-timeline{margin:14px 0}.dlvd-event-legend{display:flex;gap:10px 16px;flex-wrap:wrap;font-size:12px}.dlvd-event-legend span{display:inline-flex;gap:5px;align-items:center}.dlvd-event-timeline [data-category=waiting],.dlvd-event-tooltip[data-category=waiting]{--event-color:#d9beff}.dlvd-event-timeline [data-category=playback],.dlvd-event-tooltip[data-category=playback]{--event-color:#90c4ff}.dlvd-event-timeline [data-category=capture],.dlvd-event-tooltip[data-category=capture]{--event-color:#82d9bd}.dlvd-event-timeline [data-category=command],.dlvd-event-tooltip[data-category=command]{--event-color:#ffba72}.dlvd-event-timeline [data-category=attention],.dlvd-event-tooltip[data-category=attention]{--event-color:#ff9eac}.dlvd-event-legend span{color:var(--event-color)}.dlvd-event-timeline .dlvd-event-track{height:auto;margin:8px 0;background:var(--dsw-alias-bg-layer-1,#1b1c20);overflow:visible}.dlvd-event-lane{height:34px;position:relative;border-bottom:1px solid var(--dsw-alias-border-l2,#ffffff12)}.dlvd-event-lane:last-child{border-bottom:0}.dlvd-recordings .dlvd-event-marker{position:absolute;top:2px;transform:translateX(-50%);height:30px;width:34px;padding:3px;display:inline-flex;align-items:center;justify-content:center;gap:1px;color:var(--event-color);background:color-mix(in srgb,var(--event-color) 12%,#1c1c1f);border:1px solid color-mix(in srgb,var(--event-color) 45%,transparent);border-radius:5px;font-size:10px}.dlvd-event-marker:hover,.dlvd-event-marker:focus-visible{outline:2px solid var(--event-color);outline-offset:1px;z-index:2}.dlvd-event-count{font-variant-numeric:tabular-nums;font-weight:700}.dlvd-event-tooltip{position:fixed;inset:auto;margin:0;box-sizing:border-box;width:min(390px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:hidden;padding:12px 14px;border:1px solid var(--event-color,#b7c0cf);border-radius:10px;background:#202126;color:#f3f4f6;font:12px/1.45 system-ui,sans-serif;box-shadow:0 12px 35px #0008;z-index:2147483647;pointer-events:none!important}.dlvd-event-tooltip *{pointer-events:none!important}.dlvd-event-tooltip::backdrop{pointer-events:none}.dlvd-event-tooltip strong{color:var(--event-color);display:block;margin-bottom:8px}.dlvd-event-tooltip-list>div{display:flex;flex-direction:column;gap:2px;padding:6px 0;border-top:1px solid #ffffff18}.dlvd-event-tooltip time,.dlvd-event-tooltip span{color:#c6cbd4}.dlvd-event-tooltip footer{margin-top:8px;color:#c6cbd4;font-size:11px}.dlvd-recording-events tr:focus{outline:2px solid #90c4ff;outline-offset:-2px}.dlvd-timeline button[data-kind=command]{background:#a8611d!important}.dlvd-timeline-legend [data-kind=command]{border-color:#ffba72!important}.dlvd-command-label{color:#ffba72}.dlvd-ignored-label{color:#ff9cc7}.dlvd-speech-label{color:#82d9bd}.dlvd-timeline button[data-kind=ignored]{background:repeating-linear-gradient(135deg,#a33f72,#a33f72 5px,#632448 5px,#632448 9px)!important}.dlvd-timeline-legend [data-kind=ignored]{border-bottom:3px solid #ff9cc7}.dlvd-event-timeline [data-category=ignored],.dlvd-event-tooltip[data-category=ignored]{--event-color:#ff9cc7}
3703
4047
 
3704
4048
  .dlvd-event-track{position:relative;height:44px;margin:6px 0;border:1px solid var(--dsw-alias-border-l2,#ffffff1f);border-radius:6px}.dlvd-event-track .dlvd-event-pin{pointer-events:auto}.dlvd-event-details{color:var(--dsw-alias-text-secondary,#b8bdc5);margin-top:3px;font-size:12px}
3705
4049
  .dlvd-panel .dlvd-segment-action{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:5px;margin:0 4px 4px 0}.dlvd-segments td:last-child{min-width:112px}
@@ -3748,6 +4092,7 @@ var inject = ["locale"];
3748
4092
  function apply(ctx) {
3749
4093
  let enabled = false;
3750
4094
  let blocked = false;
4095
+ let requestedRecording = { revision: 0 };
3751
4096
  let popup = null;
3752
4097
  let root = null;
3753
4098
  let closedTimer = null;
@@ -3775,24 +4120,28 @@ function apply(ctx) {
3775
4120
  notify();
3776
4121
  };
3777
4122
  function Host() {
4123
+ const request = import_react31.default.useSyncExternalStore(subscribe, () => requestedRecording);
3778
4124
  const { scoped: labels, current } = useDebuggerLanguage();
3779
- import_react30.default.useEffect(() => {
4125
+ import_react31.default.useEffect(() => {
3780
4126
  if (popup && !popup.closed) popup.document.title = labels.commons.title;
3781
4127
  }, [labels.commons.title]);
3782
- return /* @__PURE__ */ import_react30.default.createElement(
4128
+ return /* @__PURE__ */ import_react31.default.createElement(
3783
4129
  DebuggerPanel,
3784
4130
  {
3785
4131
  copy: labels,
3786
4132
  locale: current.locale,
3787
4133
  target: window,
4134
+ recordingRequest: request,
3788
4135
  onClose: () => queueMicrotask(close)
3789
4136
  }
3790
4137
  );
3791
4138
  }
3792
- const open = () => {
4139
+ const open = (request) => {
4140
+ requestedRecording = { id: request?.recordingId, revision: requestedRecording.revision + 1 };
3793
4141
  if (popup && !popup.closed) {
3794
4142
  popup.focus();
3795
- return;
4143
+ notify();
4144
+ return true;
3796
4145
  }
3797
4146
  close();
3798
4147
  blocked = false;
@@ -3806,7 +4155,7 @@ function apply(ctx) {
3806
4155
  if (!candidate) {
3807
4156
  blocked = true;
3808
4157
  notify();
3809
- return;
4158
+ return false;
3810
4159
  }
3811
4160
  popup = candidate;
3812
4161
  const doc = candidate.document;
@@ -3821,24 +4170,26 @@ function apply(ctx) {
3821
4170
  container.dataset.plugin = namespace;
3822
4171
  doc.body.appendChild(container);
3823
4172
  root = (0, import_client.createRoot)(container);
3824
- root.render(/* @__PURE__ */ import_react30.default.createElement(LocalizedHost, null));
4173
+ root.render(/* @__PURE__ */ import_react31.default.createElement(LocalizedHost, null));
3825
4174
  enabled = true;
3826
4175
  closedTimer = setInterval(() => {
3827
4176
  if (popup?.closed) close();
3828
4177
  else if (popup) syncDebuggerTheme(window, popup.document);
3829
4178
  }, 250);
3830
4179
  notify();
4180
+ return true;
3831
4181
  } catch {
3832
4182
  close();
3833
4183
  if (candidate && !candidate.closed) candidate.close();
3834
4184
  blocked = true;
3835
4185
  notify();
4186
+ return false;
3836
4187
  }
3837
4188
  };
3838
4189
  function Settings() {
3839
- const failed = import_react30.default.useSyncExternalStore(subscribe, () => blocked);
4190
+ const failed = import_react31.default.useSyncExternalStore(subscribe, () => blocked);
3840
4191
  const { scoped: labels } = useDebuggerLanguage();
3841
- return /* @__PURE__ */ import_react30.default.createElement("div", { className: "dlv-diagnostics-launcher" }, /* @__PURE__ */ import_react30.default.createElement("button", { type: "button", className: "dlv-diagnostics-button", onClick: open }, /* @__PURE__ */ import_react30.default.createElement(Icon, { name: "bug" }), labels.commons.diagnostics), /* @__PURE__ */ import_react30.default.createElement(Hint, { label: labels.commons.diagnostics }, labels.commons.activate), failed && /* @__PURE__ */ import_react30.default.createElement("p", { role: "alert" }, labels.commons.popupBlocked));
4192
+ return /* @__PURE__ */ import_react31.default.createElement("div", { className: "dlv-diagnostics-launcher" }, /* @__PURE__ */ import_react31.default.createElement("button", { type: "button", className: "dlv-diagnostics-button", onClick: () => open() }, /* @__PURE__ */ import_react31.default.createElement(Icon, { name: "bug" }), labels.commons.diagnostics), /* @__PURE__ */ import_react31.default.createElement(Hint, { label: labels.commons.diagnostics }, labels.commons.activate), failed && /* @__PURE__ */ import_react31.default.createElement("p", { role: "alert" }, labels.commons.popupBlocked));
3842
4193
  }
3843
4194
  const LocalizedHost = withDebuggerLanguage(Host, ctx.locale);
3844
4195
  const LocalizedSettings = withDebuggerLanguage(Settings, ctx.locale);
@@ -3846,7 +4197,8 @@ function apply(ctx) {
3846
4197
  () => publishDeveloperExtension(window, {
3847
4198
  version: 1,
3848
4199
  label: () => t("commons.developer"),
3849
- component: LocalizedSettings
4200
+ component: LocalizedSettings,
4201
+ open
3850
4202
  }),
3851
4203
  namespace + ": Developer tab"
3852
4204
  );