dsh-live-voice-debugger 0.6.2 → 0.7.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.
Files changed (3) hide show
  1. package/ARCHITECTURE.md +13 -1
  2. package/lib/client.js +1235 -180
  3. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -73,45 +73,87 @@ var init_en2 = __esm({
73
73
  "src/i18n/recordings/en.ts"() {
74
74
  translation2 = {
75
75
  active: "Recording",
76
+ aliasAtCapture: "Alias at capture",
76
77
  allSources: "All sources",
78
+ assistantCancel: "Assistant speech cancelled",
77
79
  assistantEnd: "Assistant playback ended",
80
+ assistantError: "Assistant speech failed",
81
+ assistantInterruptionRequest: "Pause requested after confirmed user speech",
78
82
  assistantPause: "Assistant playback paused",
83
+ assistantPlayRequest: "Assistant playback requested",
79
84
  assistantResume: "Assistant playback resumed",
80
85
  assistantStart: "Assistant playback started",
86
+ assistantWaitEnd: "Assistant wait ended",
87
+ assistantWaitStart: "Assistant began waiting",
81
88
  attempts: "Transcription history",
82
89
  audio: "Recording playback",
83
90
  audioError: "Unable to load or play recording audio.",
91
+ automaticAction: "Automatic",
84
92
  cancel: "Cancel",
85
93
  captureEnd: "Capture ended",
94
+ captureGateFailed: "Input stop failed before playback",
86
95
  captureGated: "Microphone suspension requested for speaker playback",
87
96
  captureStart: "Capture started",
88
97
  captured: "Captured audio",
98
+ commandClear: "Clear draft",
99
+ commandEnd: "End conversation",
100
+ commandMute: "Mute microphone",
101
+ commandQueue: "Queue message",
102
+ commandResume: "Resume microphone",
103
+ commandSend: "Send message",
104
+ commandStopSpeaking: "Stop speaking",
89
105
  completed: "Completed",
90
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.",
91
107
  date: "Started",
92
108
  delete: "Delete entire session",
109
+ deleteAll: "Delete all recordings",
110
+ deleteAllActive: "All sessions are recording or have pending transcription. Stop capture and wait for processing before clearing them.",
111
+ deleteAllConfirm: "Delete saved recordings from all sources?",
112
+ deleteAllConfirmAction: "Yes, delete these recordings",
113
+ deleteAllPreview: "{count} session(s) selected, including {stars} starred. {busy} recording or processing session(s) will be kept.",
114
+ deleteAllProgress: "Deleting {done} of {count}\u2026",
115
+ deleteAllResult: "Deleted: {removed} \xB7 Kept or already absent: {skipped} \xB7 Failed: {failed}",
116
+ deleteAllWarning: "This permanently removes their audio, transcripts and events from this browser and site. Includes starred sessions, regardless of the source filter. New sessions created after this confirmation are not included. DSH conversations, exported files and future recording remain unchanged.",
93
117
  deleteError: "Unable to delete the recording.",
118
+ download: "Download segment WAV",
119
+ downloadError: "Unable to download this segment.",
94
120
  duration: "Duration",
95
121
  empty: "No recording sessions match this filter.",
96
122
  end: "End",
97
123
  engine: "Engine",
98
124
  error: "Error",
125
+ eventAttention: "Interruptions / issues",
126
+ eventCapture: "Capture / listening",
127
+ eventCommand: "Voice command",
128
+ eventCount: "{count} event(s)",
129
+ eventInspect: "Click or press Enter to inspect all events",
130
+ eventMore: "{count} more \u2014 inspect the list",
131
+ eventPlayback: "Playback",
99
132
  eventTime: "Event time",
133
+ eventTrackHelp: "Markers are grouped by category and nearby captured-audio position, not by wall time. Hover or focus for instant details; activate to inspect the complete list.",
134
+ eventWaiting: "Waiting",
100
135
  events: "Assistant and capture events",
101
136
  eventsHelp: "Markers show the captured audio boundary and the actual event time. Events after capture stops may share its final boundary; no assistant audio is added to the recording.",
102
137
  export: "Export self-contained JSON",
103
138
  exportError: "Unable to export the recording.",
104
139
  failed: "Failed",
105
140
  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.",
141
+ inputStopComplete: "Recognition/meter stop completed",
106
142
  interrupted: "Interrupted",
107
143
  loadError: "Unable to load recording sessions. Try refreshing.",
108
144
  loading: "Loading recordings\u2026",
145
+ manualAction: "Manual",
146
+ meetingId: "Meeting ID",
109
147
  memory: "Memory only",
110
148
  microphone: "Microphone",
111
149
  name: "Session name",
112
150
  nameError: "Unable to save the name. Star the session and use a single line of at most 120 characters.",
113
151
  noAttempts: "No transcription attempts yet.",
114
152
  open: "Open segment",
153
+ originalName: "Original name at capture",
154
+ participant: "Participant",
155
+ participantDetails: "Participant identity at capture",
156
+ participantId: "Participant ID",
115
157
  pending: "Pending",
116
158
  persistent: "Saved locally",
117
159
  pinConfirmed: "Confirmed at",
@@ -119,7 +161,22 @@ var init_en2 = __esm({
119
161
  pins: "Boundary pins",
120
162
  play: "Play segment",
121
163
  playSession: "Play saved session",
164
+ queuedSegments: "Queued segments",
122
165
  refresh: "Refresh recordings",
166
+ requestBytes: "WAV body size",
167
+ requestExpected: "Expected range duration",
168
+ requestHelp: "These measurements describe the WAV body prepared by the browser for DSH, not receipt by the server or consumption by the model. Host repackaging and HTTP headers are excluded. No duplicate audio is stored.",
169
+ requestInput: "Input PCM samples / rate",
170
+ requestLanguage: "Recognition language",
171
+ requestMetadata: "HTTP request metadata",
172
+ requestPrepared: "Body prepared at",
173
+ requestSamples: "WAV samples / rate",
174
+ requestStatus: "HTTP response status",
175
+ requestUnknown: "Not recorded / unavailable",
176
+ retranscribe: "Transcribe again",
177
+ retranscribeError: "Unable to transcribe again. Finish capture, then check the selected recognition engine, downloaded model and browser support.",
178
+ retranscribeHelp: "Retry saved speech with current Qwen/Whisper HTTP, Whisper \xB7 Browser or Qwen \xB7 Browser settings and at least 800 ms of context. Download browser-local models in Settings first; retries never download models or fall back to HTTP. Native Browser requires verified safe external-audio-track support; never falls back to the microphone. Adds history only; never changes the composer or runs commands.",
179
+ retranscribing: "Transcribing segment again\u2026",
123
180
  saveName: "Save name",
124
181
  saved: "Saved audio",
125
182
  search: "Search segment transcripts",
@@ -137,8 +194,12 @@ var init_en2 = __esm({
137
194
  starred: "Protected from automatic cleanup",
138
195
  start: "Start",
139
196
  state: "State",
140
- storageAudio: "Stored audio (MB)",
141
- storageTotal: "Total stored audio (MB)",
197
+ storageAudio: "Stored audio",
198
+ storageOverview: "Recording storage",
199
+ storagePartial: "At least {size} \xB7 {count} session(s) unmeasured",
200
+ 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
+ storageSessions: "{count} session(s) across all sources",
202
+ storageTotal: "Total stored audio",
142
203
  storageUnknown: "Unavailable",
143
204
  success: "Success",
144
205
  timeline: "Speech / silence timeline",
@@ -146,6 +207,20 @@ var init_en2 = __esm({
146
207
  unavailable: "Recording service unavailable. Enable recording in Live Voice settings with a compatible runtime.",
147
208
  unstar: "Remove protection",
148
209
  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.",
210
+ voiceCommandRecognized: "Voice command recognized",
211
+ waitCancelled: "Wait cancelled",
212
+ waitChanged: "Wait reasons changed",
213
+ waitDelay: "Delay",
214
+ waitDelivery: "Pending delivery",
215
+ waitExternalSpeech: "Shared-source speech",
216
+ waitInputStarting: "Input starting",
217
+ waitPaused: "Paused",
218
+ waitReady: "Ready to speak",
219
+ waitSegmentGap: "Segment gap",
220
+ waitSilence: "Quiet-period delay",
221
+ waitSuspended: "Speech suspended",
222
+ waitTranscription: "Pending transcription",
223
+ waitUserSpeech: "Microphone speech",
149
224
  window: "Timeline window",
150
225
  windowEnd: "Window end",
151
226
  windowReset: "Show full session",
@@ -257,45 +332,87 @@ var init_es2 = __esm({
257
332
  "src/i18n/recordings/es.ts"() {
258
333
  translation6 = {
259
334
  active: "Grabando",
335
+ aliasAtCapture: "Alias al capturar",
260
336
  allSources: "Todas las fuentes",
337
+ assistantCancel: "Voz del asistente cancelada",
261
338
  assistantEnd: "Reproducci\xF3n del asistente finalizada",
339
+ assistantError: "Fallo de voz del asistente",
340
+ assistantInterruptionRequest: "Pausa solicitada tras voz confirmada del usuario",
262
341
  assistantPause: "Reproducci\xF3n del asistente pausada",
342
+ assistantPlayRequest: "Reproducci\xF3n del asistente solicitada",
263
343
  assistantResume: "Reproducci\xF3n del asistente reanudada",
264
344
  assistantStart: "Reproducci\xF3n del asistente iniciada",
345
+ assistantWaitEnd: "Espera del asistente finalizada",
346
+ assistantWaitStart: "El asistente comenz\xF3 a esperar",
265
347
  attempts: "Historial de transcripci\xF3n",
266
348
  audio: "Reproducci\xF3n de la grabaci\xF3n",
267
349
  audioError: "No se pudo cargar o reproducir el audio.",
350
+ automaticAction: "Autom\xE1tico",
268
351
  cancel: "Cancelar",
269
352
  captureEnd: "Captura finalizada",
353
+ captureGateFailed: "Error al detener la escucha antes de reproducir",
270
354
  captureGated: "Suspensi\xF3n del micr\xF3fono solicitada para reproducir por altavoz",
271
355
  captureStart: "Captura iniciada",
272
356
  captured: "Audio capturado",
357
+ commandClear: "Borrar borrador",
358
+ commandEnd: "Terminar conversaci\xF3n",
359
+ commandMute: "Silenciar micr\xF3fono",
360
+ commandQueue: "Encolar mensaje",
361
+ commandResume: "Reanudar micr\xF3fono",
362
+ commandSend: "Enviar mensaje",
363
+ commandStopSpeaking: "Detener voz",
273
364
  completed: "Completada",
274
365
  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.",
275
366
  date: "Inicio",
276
367
  delete: "Eliminar sesi\xF3n completa",
368
+ deleteAll: "Borrar todas las grabaciones",
369
+ deleteAllActive: "Todas las sesiones est\xE1n grabando o tienen transcripci\xF3n pendiente. Det\xE9n la captura y espera al procesamiento antes de borrarlas.",
370
+ deleteAllConfirm: "\xBFBorrar grabaciones guardadas de todas las fuentes?",
371
+ deleteAllConfirmAction: "S\xED, borrar estas grabaciones",
372
+ deleteAllPreview: "{count} sesi\xF3n(es) seleccionada(s), incluidas {stars} con estrella. Se conservar\xE1n {busy} sesi\xF3n(es) grabando o procesando.",
373
+ deleteAllProgress: "Borrando {done} de {count}\u2026",
374
+ deleteAllResult: "Borradas: {removed} \xB7 Conservadas o ya ausentes: {skipped} \xB7 Fallidas: {failed}",
375
+ deleteAllWarning: "Elimina permanentemente audio, transcripciones y eventos de este navegador y sitio. Incluye sesiones con estrella, sin importar el filtro de fuente. No incluye sesiones creadas despu\xE9s de esta confirmaci\xF3n. No cambia conversaciones de DSH, archivos exportados ni futuras grabaciones.",
277
376
  deleteError: "No se pudo eliminar la grabaci\xF3n.",
377
+ download: "Descargar WAV del segmento",
378
+ downloadError: "No se pudo descargar este segmento.",
278
379
  duration: "Duraci\xF3n",
279
380
  empty: "Ninguna grabaci\xF3n coincide con el filtro.",
280
381
  end: "Fin",
281
382
  engine: "Motor",
282
383
  error: "Error",
384
+ eventAttention: "Interrupciones / fallos",
385
+ eventCapture: "Captura / escucha",
386
+ eventCommand: "Comando de voz",
387
+ eventCount: "{count} evento(s)",
388
+ eventInspect: "Haz clic o pulsa Enter para inspeccionar todos los eventos",
389
+ eventMore: "{count} m\xE1s \u2014 consulta la lista",
390
+ eventPlayback: "Reproducci\xF3n",
283
391
  eventTime: "Hora del evento",
392
+ eventTrackHelp: "Los marcadores se agrupan por categor\xEDa y posici\xF3n cercana del audio capturado, no por hora real. Pasa el rat\xF3n o enfoca para detalles inmediatos; activa para ver la lista completa.",
393
+ eventWaiting: "Espera",
284
394
  events: "Eventos del asistente y la captura",
285
395
  eventsHelp: "Los marcadores muestran el l\xEDmite del audio capturado y la hora real del evento. Tras detener la captura, varios eventos pueden compartir su l\xEDmite final; no se a\xF1ade audio del asistente.",
286
396
  export: "Exportar JSON completo",
287
397
  exportError: "No se pudo exportar la grabaci\xF3n.",
288
398
  failed: "Fallida",
289
399
  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.",
400
+ inputStopComplete: "Parada de reconocimiento/medidor completada",
290
401
  interrupted: "Interrumpida",
291
402
  loadError: "No se pudieron cargar las grabaciones. Intente actualizar.",
292
403
  loading: "Cargando grabaciones\u2026",
404
+ manualAction: "Manual",
405
+ meetingId: "ID de reuni\xF3n",
293
406
  memory: "Solo en memoria",
294
407
  microphone: "Micr\xF3fono",
295
408
  name: "Nombre de la sesi\xF3n",
296
409
  nameError: "No se pudo guardar el nombre. Marca la sesi\xF3n con estrella y usa una l\xEDnea de hasta 120 caracteres.",
297
410
  noAttempts: "Todav\xEDa no hay intentos de transcripci\xF3n.",
298
411
  open: "Segmento abierto",
412
+ originalName: "Nombre original al capturar",
413
+ participant: "Participante",
414
+ participantDetails: "Identidad del participante al capturar",
415
+ participantId: "ID del participante",
299
416
  pending: "Pendiente",
300
417
  persistent: "Guardada localmente",
301
418
  pinConfirmed: "Confirmado en",
@@ -303,7 +420,22 @@ var init_es2 = __esm({
303
420
  pins: "Marcadores de l\xEDmite",
304
421
  play: "Reproducir segmento",
305
422
  playSession: "Reproducir sesi\xF3n guardada",
423
+ queuedSegments: "Segmentos en cola",
306
424
  refresh: "Actualizar grabaciones",
425
+ requestBytes: "Tama\xF1o del cuerpo WAV",
426
+ requestExpected: "Duraci\xF3n del intervalo esperado",
427
+ requestHelp: "Estas medidas describen el cuerpo WAV preparado por el navegador para DSH, no su recepci\xF3n por el servidor ni su procesamiento por el modelo. Excluyen cabeceras HTTP y el reempaquetado del host. No se duplica el audio.",
428
+ requestInput: "Muestras PCM de entrada / frecuencia",
429
+ requestLanguage: "Idioma de reconocimiento",
430
+ requestMetadata: "Metadatos de la solicitud HTTP",
431
+ requestPrepared: "Cuerpo preparado a las",
432
+ requestSamples: "Muestras WAV / frecuencia",
433
+ requestStatus: "Estado de respuesta HTTP",
434
+ requestUnknown: "No registrado / no disponible",
435
+ retranscribe: "Transcribir de nuevo",
436
+ retranscribeError: "No se pudo transcribir de nuevo. Finaliza la captura y comprueba el motor de reconocimiento seleccionado, el modelo descargado y el soporte del navegador.",
437
+ retranscribeHelp: "Reintenta la voz guardada con los ajustes actuales de Qwen/Whisper HTTP, Whisper \xB7 Browser o Qwen \xB7 Browser y al menos 800 ms de contexto. Descarga primero los modelos locales del navegador en Ajustes; los reintentos nunca descargan modelos ni recurren a HTTP. Browser nativo requiere soporte seguro verificado para pistas de audio externas; nunca recurre al micr\xF3fono. Solo a\xF1ade historial; no cambia el editor ni ejecuta comandos.",
438
+ retranscribing: "Transcribiendo el segmento de nuevo\u2026",
307
439
  saveName: "Guardar nombre",
308
440
  saved: "Audio guardado",
309
441
  search: "Buscar transcripciones de segmentos",
@@ -321,8 +453,12 @@ var init_es2 = __esm({
321
453
  starred: "Protegida de la limpieza autom\xE1tica",
322
454
  start: "Inicio",
323
455
  state: "Estado",
324
- storageAudio: "Audio almacenado (MB)",
325
- storageTotal: "Total de audio almacenado (MB)",
456
+ storageAudio: "Audio almacenado",
457
+ storageOverview: "Almacenamiento de grabaciones",
458
+ storagePartial: "Al menos {size} \xB7 {count} sesi\xF3n(es) sin medir",
459
+ 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
+ storageSessions: "{count} sesi\xF3n(es) de todas las fuentes",
461
+ storageTotal: "Total de audio almacenado",
326
462
  storageUnknown: "No disponible",
327
463
  success: "\xC9xito",
328
464
  timeline: "Cronolog\xEDa de voz / silencio",
@@ -330,6 +466,20 @@ var init_es2 = __esm({
330
466
  unavailable: "Servicio de grabaci\xF3n no disponible. Active la grabaci\xF3n en los ajustes de Voz en vivo con un runtime compatible.",
331
467
  unstar: "Quitar protecci\xF3n",
332
468
  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.",
469
+ voiceCommandRecognized: "Comando de voz reconocido",
470
+ waitCancelled: "Espera cancelada",
471
+ waitChanged: "Motivos de espera cambiados",
472
+ waitDelay: "Retardo",
473
+ waitDelivery: "Env\xEDo pendiente",
474
+ waitExternalSpeech: "Voz de fuente compartida",
475
+ waitInputStarting: "Iniciando escucha",
476
+ waitPaused: "En pausa",
477
+ waitReady: "Listo para hablar",
478
+ waitSegmentGap: "Intervalo entre segmentos",
479
+ waitSilence: "Retardo de silencio",
480
+ waitSuspended: "Voz suspendida",
481
+ waitTranscription: "Transcripci\xF3n pendiente",
482
+ waitUserSpeech: "Voz del micr\xF3fono",
333
483
  window: "Intervalo de la l\xEDnea de tiempo",
334
484
  windowEnd: "Fin del intervalo",
335
485
  windowReset: "Mostrar toda la sesi\xF3n",
@@ -441,45 +591,87 @@ var init_fr2 = __esm({
441
591
  "src/i18n/recordings/fr.ts"() {
442
592
  translation10 = {
443
593
  active: "Enregistrement",
594
+ aliasAtCapture: "Alias \xE0 la capture",
444
595
  allSources: "Toutes les sources",
596
+ assistantCancel: "Parole de l\u2019assistant annul\xE9e",
445
597
  assistantEnd: "Lecture de l\u2019assistant termin\xE9e",
598
+ assistantError: "\xC9chec de la parole de l\u2019assistant",
599
+ assistantInterruptionRequest: "Pause demand\xE9e apr\xE8s parole utilisateur confirm\xE9e",
446
600
  assistantPause: "Lecture de l\u2019assistant en pause",
601
+ assistantPlayRequest: "Lecture de l\u2019assistant demand\xE9e",
447
602
  assistantResume: "Lecture de l\u2019assistant reprise",
448
603
  assistantStart: "Lecture de l\u2019assistant d\xE9marr\xE9e",
604
+ assistantWaitEnd: "Attente de l\u2019assistant termin\xE9e",
605
+ assistantWaitStart: "L\u2019assistant commence \xE0 attendre",
449
606
  attempts: "Historique des transcriptions",
450
607
  audio: "Lecture de l\u2019enregistrement",
451
608
  audioError: "Impossible de charger ou lire l\u2019audio.",
609
+ automaticAction: "Automatique",
452
610
  cancel: "Annuler",
453
611
  captureEnd: "Capture termin\xE9e",
612
+ captureGateFailed: "\xC9chec de l\u2019arr\xEAt de l\u2019\xE9coute avant lecture",
454
613
  captureGated: "Suspension du microphone demand\xE9e pour la lecture sur haut-parleur",
455
614
  captureStart: "Capture d\xE9marr\xE9e",
456
615
  captured: "Audio captur\xE9",
616
+ commandClear: "Effacer le brouillon",
617
+ commandEnd: "Terminer la conversation",
618
+ commandMute: "Couper le microphone",
619
+ commandQueue: "Mettre le message en file",
620
+ commandResume: "R\xE9activer le microphone",
621
+ commandSend: "Envoyer le message",
622
+ commandStopSpeaking: "Arr\xEAter la parole",
457
623
  completed: "Termin\xE9",
458
624
  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.",
459
625
  date: "D\xE9but",
460
626
  delete: "Supprimer toute la session",
627
+ deleteAll: "Supprimer tous les enregistrements",
628
+ deleteAllActive: "Toutes les sessions enregistrent ou ont une transcription en attente. Arr\xEAtez la capture et attendez la fin du traitement avant de les supprimer.",
629
+ deleteAllConfirm: "Supprimer les enregistrements de toutes les sources ?",
630
+ deleteAllConfirmAction: "Oui, supprimer ces enregistrements",
631
+ deleteAllPreview: "{count} session(s) s\xE9lectionn\xE9e(s), dont {stars} avec \xE9toile. {busy} session(s) en capture ou traitement seront conserv\xE9es.",
632
+ deleteAllProgress: "Suppression : {done} sur {count}\u2026",
633
+ deleteAllResult: "Supprim\xE9es : {removed} \xB7 Conserv\xE9es ou d\xE9j\xE0 absentes : {skipped} \xB7 \xC9checs : {failed}",
634
+ deleteAllWarning: "Supprime d\xE9finitivement audio, transcriptions et \xE9v\xE9nements de ce navigateur et site. Inclut les sessions \xE9toil\xE9es, quel que soit le filtre. Exclut les sessions cr\xE9\xE9es apr\xE8s cette confirmation. Les conversations DSH, fichiers export\xE9s et futurs enregistrements ne changent pas.",
461
635
  deleteError: "Impossible de supprimer l\u2019enregistrement.",
636
+ download: "T\xE9l\xE9charger le segment WAV",
637
+ downloadError: "Impossible de t\xE9l\xE9charger ce segment.",
462
638
  duration: "Dur\xE9e",
463
639
  empty: "Aucun enregistrement ne correspond au filtre.",
464
640
  end: "Fin",
465
641
  engine: "Moteur",
466
642
  error: "Erreur",
643
+ eventAttention: "Interruptions / erreurs",
644
+ eventCapture: "Capture / \xE9coute",
645
+ eventCommand: "Commande vocale",
646
+ eventCount: "{count} \xE9v\xE9nement(s)",
647
+ eventInspect: "Cliquez ou appuyez sur Entr\xE9e pour inspecter tous les \xE9v\xE9nements",
648
+ eventMore: "{count} autres \u2014 voir la liste",
649
+ eventPlayback: "Lecture",
467
650
  eventTime: "Heure de l\u2019\xE9v\xE9nement",
651
+ eventTrackHelp: "Les marqueurs sont group\xE9s par cat\xE9gorie et position audio proche, pas par heure r\xE9elle. Survolez ou placez le focus pour les d\xE9tails imm\xE9diats ; activez pour la liste compl\xE8te.",
652
+ eventWaiting: "Attente",
468
653
  events: "\xC9v\xE9nements de l\u2019assistant et de la capture",
469
654
  eventsHelp: "Les rep\xE8res indiquent la limite audio captur\xE9e et l\u2019heure r\xE9elle. Apr\xE8s l\u2019arr\xEAt de la capture, plusieurs \xE9v\xE9nements peuvent partager la limite finale ; aucun audio de l\u2019assistant n\u2019est ajout\xE9.",
470
655
  export: "Exporter le JSON autonome",
471
656
  exportError: "Impossible d\u2019exporter l\u2019enregistrement.",
472
657
  failed: "\xC9chec",
473
658
  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.",
659
+ inputStopComplete: "Arr\xEAt reconnaissance/indicateur termin\xE9",
474
660
  interrupted: "Interrompu",
475
661
  loadError: "Impossible de charger les enregistrements. Actualisez la liste.",
476
662
  loading: "Chargement des enregistrements\u2026",
663
+ manualAction: "Manuel",
664
+ meetingId: "ID de r\xE9union",
477
665
  memory: "En m\xE9moire uniquement",
478
666
  microphone: "Microphone",
479
667
  name: "Nom de la session",
480
668
  nameError: "Impossible d\u2019enregistrer le nom. Marquez la session d\u2019une \xE9toile et utilisez une ligne de 120 caract\xE8res maximum.",
481
669
  noAttempts: "Aucune tentative de transcription.",
482
670
  open: "Segment ouvert",
671
+ originalName: "Nom original \xE0 la capture",
672
+ participant: "Participant",
673
+ participantDetails: "Identit\xE9 du participant \xE0 la capture",
674
+ participantId: "ID du participant",
483
675
  pending: "En attente",
484
676
  persistent: "Sauvegard\xE9 localement",
485
677
  pinConfirmed: "Confirm\xE9 \xE0",
@@ -487,7 +679,22 @@ var init_fr2 = __esm({
487
679
  pins: "Marqueurs de limite",
488
680
  play: "Lire le segment",
489
681
  playSession: "Lire la session sauvegard\xE9e",
682
+ queuedSegments: "Segments en attente",
490
683
  refresh: "Actualiser les enregistrements",
684
+ requestBytes: "Taille du corps WAV",
685
+ requestExpected: "Dur\xE9e de la plage attendue",
686
+ requestHelp: "Ces mesures d\xE9crivent le corps WAV pr\xE9par\xE9 par le navigateur pour DSH, et non sa r\xE9ception par le serveur ou son traitement par le mod\xE8le. Les en-t\xEAtes HTTP et le r\xE9encodage c\xF4t\xE9 h\xF4te sont exclus. Aucun audio suppl\xE9mentaire n\u2019est stock\xE9.",
687
+ requestInput: "\xC9chantillons PCM entrants / fr\xE9quence",
688
+ requestLanguage: "Langue de reconnaissance",
689
+ requestMetadata: "M\xE9tadonn\xE9es de requ\xEAte HTTP",
690
+ requestPrepared: "Corps pr\xE9par\xE9 \xE0",
691
+ requestSamples: "\xC9chantillons WAV / fr\xE9quence",
692
+ requestStatus: "Statut de r\xE9ponse HTTP",
693
+ requestUnknown: "Non enregistr\xE9 / indisponible",
694
+ retranscribe: "Retranscrire",
695
+ retranscribeError: "Impossible de retranscrire. Terminez la capture, puis v\xE9rifiez le moteur de reconnaissance s\xE9lectionn\xE9, le mod\xE8le t\xE9l\xE9charg\xE9 et la compatibilit\xE9 du navigateur.",
696
+ retranscribeHelp: "R\xE9essayez la parole sauvegard\xE9e avec les param\xE8tres actuels Qwen/Whisper HTTP, Whisper \xB7 Browser ou Qwen \xB7 Browser et au moins 800 ms de contexte. T\xE9l\xE9chargez les mod\xE8les locaux du navigateur dans les param\xE8tres au pr\xE9alable ; les essais ne t\xE9l\xE9chargent jamais de mod\xE8les et ne basculent pas vers HTTP. Browser natif exige une prise en charge s\xFBre et v\xE9rifi\xE9e des pistes audio externes ; aucun recours au microphone. Ajoute uniquement un essai \xE0 l\u2019historique, sans modifier la saisie ni ex\xE9cuter de commandes.",
697
+ retranscribing: "Retranscription du segment\u2026",
491
698
  saveName: "Enregistrer le nom",
492
699
  saved: "Audio sauvegard\xE9",
493
700
  search: "Rechercher dans les transcriptions",
@@ -505,8 +712,12 @@ var init_fr2 = __esm({
505
712
  starred: "Prot\xE9g\xE9 du nettoyage automatique",
506
713
  start: "D\xE9but",
507
714
  state: "\xC9tat",
508
- storageAudio: "Audio stock\xE9 (MB)",
509
- storageTotal: "Total de l\u2019audio stock\xE9 (MB)",
715
+ storageAudio: "Audio stock\xE9",
716
+ storageOverview: "Stockage des enregistrements",
717
+ storagePartial: "Au moins {size} \xB7 {count} session(s) non mesur\xE9e(s)",
718
+ 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
+ storageSessions: "{count} session(s), toutes sources confondues",
720
+ storageTotal: "Total de l\u2019audio stock\xE9",
510
721
  storageUnknown: "Indisponible",
511
722
  success: "R\xE9ussite",
512
723
  timeline: "Chronologie parole / silence",
@@ -514,6 +725,20 @@ var init_fr2 = __esm({
514
725
  unavailable: "Service d\u2019enregistrement indisponible. Activez l\u2019enregistrement dans les param\xE8tres de Voix en direct avec un runtime compatible.",
515
726
  unstar: "Retirer la protection",
516
727
  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.",
728
+ voiceCommandRecognized: "Commande vocale reconnue",
729
+ waitCancelled: "Attente annul\xE9e",
730
+ waitChanged: "Motifs d\u2019attente modifi\xE9s",
731
+ waitDelay: "D\xE9lai",
732
+ waitDelivery: "Envoi en attente",
733
+ waitExternalSpeech: "Parole de source partag\xE9e",
734
+ waitInputStarting: "D\xE9marrage de l\u2019\xE9coute",
735
+ waitPaused: "En pause",
736
+ waitReady: "Pr\xEAt \xE0 parler",
737
+ waitSegmentGap: "Intervalle entre segments",
738
+ waitSilence: "D\xE9lai de silence",
739
+ waitSuspended: "Parole suspendue",
740
+ waitTranscription: "Transcription en attente",
741
+ waitUserSpeech: "Parole du microphone",
517
742
  window: "Intervalle de la chronologie",
518
743
  windowEnd: "Fin de l\u2019intervalle",
519
744
  windowReset: "Afficher toute la session",
@@ -625,45 +850,87 @@ var init_hi2 = __esm({
625
850
  "src/i18n/recordings/hi.ts"() {
626
851
  translation14 = {
627
852
  active: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u091C\u093E\u0930\u0940",
853
+ aliasAtCapture: "\u0915\u0948\u092A\u094D\u091A\u0930 \u0915\u0947 \u0938\u092E\u092F \u0909\u092A\u0928\u093E\u092E",
628
854
  allSources: "\u0938\u092D\u0940 \u0938\u094D\u0930\u094B\u0924",
855
+ assistantCancel: "\u0938\u0939\u093E\u092F\u0915 \u0915\u0940 \u0935\u093E\u0923\u0940 \u0930\u0926\u094D\u0926",
629
856
  assistantEnd: "\u0938\u0939\u093E\u092F\u0915 \u0915\u093E \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0938\u092E\u093E\u092A\u094D\u0924",
857
+ assistantError: "\u0938\u0939\u093E\u092F\u0915 \u0915\u0940 \u0935\u093E\u0923\u0940 \u0935\u093F\u092B\u0932",
858
+ assistantInterruptionRequest: "\u092A\u0941\u0937\u094D\u091F \u0909\u092A\u092F\u094B\u0917\u0915\u0930\u094D\u0924\u093E \u0935\u093E\u0923\u0940 \u0915\u0947 \u092C\u093E\u0926 \u0935\u093F\u0930\u093E\u092E \u0915\u093E \u0905\u0928\u0941\u0930\u094B\u0927",
630
859
  assistantPause: "\u0938\u0939\u093E\u092F\u0915 \u0915\u093E \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0930\u0941\u0915\u093E",
860
+ assistantPlayRequest: "\u0938\u0939\u093E\u092F\u0915 \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0915\u093E \u0905\u0928\u0941\u0930\u094B\u0927",
631
861
  assistantResume: "\u0938\u0939\u093E\u092F\u0915 \u0915\u093E \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u092B\u093F\u0930 \u0936\u0941\u0930\u0942",
632
862
  assistantStart: "\u0938\u0939\u093E\u092F\u0915 \u0915\u093E \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0936\u0941\u0930\u0942",
863
+ assistantWaitEnd: "\u0938\u0939\u093E\u092F\u0915 \u0915\u0940 \u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0938\u092E\u093E\u092A\u094D\u0924",
864
+ assistantWaitStart: "\u0938\u0939\u093E\u092F\u0915 \u0928\u0947 \u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0936\u0941\u0930\u0942 \u0915\u0940",
633
865
  attempts: "\u091F\u094D\u0930\u093E\u0902\u0938\u0915\u094D\u0930\u093F\u092A\u094D\u0936\u0928 \u0907\u0924\u093F\u0939\u093E\u0938",
634
866
  audio: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u092A\u094D\u0932\u0947\u092C\u0948\u0915",
635
867
  audioError: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0911\u0921\u093F\u092F\u094B \u0932\u094B\u0921 \u092F\u093E \u091A\u0932\u093E\u092F\u093E \u0928\u0939\u0940\u0902 \u091C\u093E \u0938\u0915\u093E\u0964",
868
+ automaticAction: "\u0938\u094D\u0935\u091A\u093E\u0932\u093F\u0924",
636
869
  cancel: "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
637
870
  captureEnd: "\u0915\u0948\u092A\u094D\u091A\u0930 \u0938\u092E\u093E\u092A\u094D\u0924",
871
+ captureGateFailed: "\u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0938\u0947 \u092A\u0939\u0932\u0947 \u0938\u0941\u0928\u0928\u093E \u0930\u094B\u0915\u0928\u0947 \u092E\u0947\u0902 \u0935\u093F\u092B\u0932",
638
872
  captureGated: "\u0938\u094D\u092A\u0940\u0915\u0930 \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0915\u0947 \u0932\u093F\u090F \u092E\u093E\u0907\u0915\u094D\u0930\u094B\u092B\u093C\u094B\u0928 \u0930\u094B\u0915\u0928\u0947 \u0915\u093E \u0905\u0928\u0941\u0930\u094B\u0927",
639
873
  captureStart: "\u0915\u0948\u092A\u094D\u091A\u0930 \u0936\u0941\u0930\u0942",
640
874
  captured: "\u0915\u0948\u092A\u094D\u091A\u0930 \u0915\u093F\u092F\u093E \u0917\u092F\u093E \u0911\u0921\u093F\u092F\u094B",
875
+ commandClear: "\u0921\u094D\u0930\u093E\u092B\u093C\u094D\u091F \u0938\u093E\u092B\u093C \u0915\u0930\u0947\u0902",
876
+ commandEnd: "\u092C\u093E\u0924\u091A\u0940\u0924 \u0938\u092E\u093E\u092A\u094D\u0924 \u0915\u0930\u0947\u0902",
877
+ commandMute: "\u092E\u093E\u0907\u0915\u094D\u0930\u094B\u092B\u093C\u094B\u0928 \u092E\u094D\u092F\u0942\u091F \u0915\u0930\u0947\u0902",
878
+ commandQueue: "\u0938\u0902\u0926\u0947\u0936 \u0915\u0924\u093E\u0930 \u092E\u0947\u0902 \u0930\u0916\u0947\u0902",
879
+ commandResume: "\u092E\u093E\u0907\u0915\u094D\u0930\u094B\u092B\u093C\u094B\u0928 \u092B\u093F\u0930 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902",
880
+ commandSend: "\u0938\u0902\u0926\u0947\u0936 \u092D\u0947\u091C\u0947\u0902",
881
+ commandStopSpeaking: "\u092C\u094B\u0932\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
641
882
  completed: "\u092A\u0942\u0930\u094D\u0923",
642
883
  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",
643
884
  date: "\u0936\u0941\u0930\u0941\u0906\u0924",
644
885
  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
+ deleteAll: "\u0938\u092D\u0940 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0939\u091F\u093E\u090F\u0901",
887
+ deleteAllActive: "\u0938\u092D\u0940 \u0938\u0924\u094D\u0930 \u0930\u093F\u0915\u0949\u0930\u094D\u0921 \u0939\u094B \u0930\u0939\u0947 \u0939\u0948\u0902 \u092F\u093E \u091F\u094D\u0930\u093E\u0902\u0938\u0915\u094D\u0930\u093F\u092A\u094D\u0936\u0928 \u0932\u0902\u092C\u093F\u0924 \u0939\u0948\u0964 \u0939\u091F\u093E\u0928\u0947 \u0938\u0947 \u092A\u0939\u0932\u0947 \u0915\u0948\u092A\u094D\u091A\u0930 \u0930\u094B\u0915\u0947\u0902 \u0914\u0930 \u092A\u094D\u0930\u094B\u0938\u0947\u0938\u093F\u0902\u0917 \u092A\u0942\u0930\u0940 \u0939\u094B\u0928\u0947 \u0926\u0947\u0902\u0964",
888
+ deleteAllConfirm: "\u0938\u092D\u0940 \u0938\u094D\u0930\u094B\u0924\u094B\u0902 \u0915\u0940 \u0938\u0939\u0947\u091C\u0940 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0939\u091F\u093E\u090F\u0901?",
889
+ deleteAllConfirmAction: "\u0939\u093E\u0901, \u092F\u0947 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0939\u091F\u093E\u090F\u0901",
890
+ deleteAllPreview: "{count} \u0938\u0924\u094D\u0930 \u091A\u0941\u0928\u0947 \u0917\u090F, \u091C\u093F\u0928\u092E\u0947\u0902 {stars} \u0924\u093E\u0930\u093E\u0902\u0915\u093F\u0924 \u0939\u0948\u0902\u0964 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u092F\u093E \u092A\u094D\u0930\u094B\u0938\u0947\u0938\u093F\u0902\u0917 \u0935\u093E\u0932\u0947 {busy} \u0938\u0924\u094D\u0930 \u0938\u0941\u0930\u0915\u094D\u0937\u093F\u0924 \u0930\u0939\u0947\u0902\u0917\u0947\u0964",
891
+ deleteAllProgress: "{done} \u0939\u091F\u093E\u090F \u091C\u093E \u0930\u0939\u0947 \u0939\u0948\u0902, \u0915\u0941\u0932 {count}\u2026",
892
+ deleteAllResult: "\u0939\u091F\u093E\u090F \u0917\u090F: {removed} \xB7 \u0938\u0941\u0930\u0915\u094D\u0937\u093F\u0924 \u092F\u093E \u092A\u0939\u0932\u0947 \u0938\u0947 \u0905\u0928\u0941\u092A\u0938\u094D\u0925\u093F\u0924: {skipped} \xB7 \u0935\u093F\u092B\u0932: {failed}",
893
+ deleteAllWarning: "\u0907\u0938 \u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u0914\u0930 \u0938\u093E\u0907\u091F \u0938\u0947 \u0911\u0921\u093F\u092F\u094B, \u091F\u094D\u0930\u093E\u0902\u0938\u0915\u094D\u0930\u093F\u092A\u094D\u091F \u0914\u0930 \u0918\u091F\u0928\u093E\u090F\u0901 \u0938\u094D\u0925\u093E\u092F\u0940 \u0930\u0942\u092A \u0938\u0947 \u0939\u091F\u093E\u0924\u093E \u0939\u0948\u0964 \u0938\u094D\u0930\u094B\u0924 \u092B\u093C\u093F\u0932\u094D\u091F\u0930 \u0915\u0940 \u092A\u0930\u0935\u093E\u0939 \u0915\u093F\u090F \u092C\u093F\u0928\u093E \u0924\u093E\u0930\u093E\u0902\u0915\u093F\u0924 \u0938\u0924\u094D\u0930 \u092D\u0940 \u0936\u093E\u092E\u093F\u0932 \u0939\u0948\u0902\u0964 \u092A\u0941\u0937\u094D\u091F\u093F \u0915\u0947 \u092C\u093E\u0926 \u092C\u0928\u0947 \u0928\u090F \u0938\u0924\u094D\u0930 \u0936\u093E\u092E\u093F\u0932 \u0928\u0939\u0940\u0902 \u0939\u0948\u0902\u0964 DSH \u092C\u093E\u0924\u091A\u0940\u0924, \u0928\u093F\u0930\u094D\u092F\u093E\u0924 \u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u0914\u0930 \u092D\u0935\u093F\u0937\u094D\u092F \u0915\u0940 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0928\u0939\u0940\u0902 \u092C\u0926\u0932\u0947\u0902\u0917\u0940\u0964",
645
894
  deleteError: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0939\u091F\u093E\u0908 \u0928\u0939\u0940\u0902 \u091C\u093E \u0938\u0915\u0940\u0964",
895
+ download: "\u0938\u0947\u0917\u092E\u0947\u0902\u091F WAV \u0921\u093E\u0909\u0928\u0932\u094B\u0921 \u0915\u0930\u0947\u0902",
896
+ downloadError: "\u092F\u0939 \u0938\u0947\u0917\u092E\u0947\u0902\u091F \u0921\u093E\u0909\u0928\u0932\u094B\u0921 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E\u0964",
646
897
  duration: "\u0905\u0935\u0927\u093F",
647
898
  empty: "\u0907\u0938 \u092B\u093C\u093F\u0932\u094D\u091F\u0930 \u0938\u0947 \u092E\u0947\u0932 \u0916\u093E\u0924\u0940 \u0915\u094B\u0908 \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964",
648
899
  end: "\u0905\u0902\u0924",
649
900
  engine: "\u0907\u0902\u091C\u0928",
650
901
  error: "\u0924\u094D\u0930\u0941\u091F\u093F",
902
+ eventAttention: "\u0935\u094D\u092F\u0935\u0927\u093E\u0928 / \u0924\u094D\u0930\u0941\u091F\u093F\u092F\u093E\u0901",
903
+ eventCapture: "\u0915\u0948\u092A\u094D\u091A\u0930 / \u0938\u0941\u0928\u0928\u093E",
904
+ eventCommand: "\u0935\u0949\u0907\u0938 \u0915\u092E\u093E\u0902\u0921",
905
+ eventCount: "{count} \u0918\u091F\u0928\u093E(\u090F\u0901)",
906
+ eventInspect: "\u0938\u092D\u0940 \u0918\u091F\u0928\u093E\u090F\u0901 \u0926\u0947\u0916\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u0915\u094D\u0932\u093F\u0915 \u0915\u0930\u0947\u0902 \u092F\u093E Enter \u0926\u092C\u093E\u090F\u0901",
907
+ eventMore: "{count} \u0914\u0930 \u2014 \u0938\u0942\u091A\u0940 \u0926\u0947\u0916\u0947\u0902",
908
+ eventPlayback: "\u092A\u094D\u0932\u0947\u092C\u0948\u0915",
651
909
  eventTime: "\u0918\u091F\u0928\u093E \u0915\u093E \u0938\u092E\u092F",
910
+ eventTrackHelp: "\u092E\u093E\u0930\u094D\u0915\u0930 \u0936\u094D\u0930\u0947\u0923\u0940 \u0914\u0930 \u0915\u0948\u092A\u094D\u091A\u0930 \u0915\u093F\u090F \u0911\u0921\u093F\u092F\u094B \u0915\u0940 \u0928\u093F\u0915\u091F \u0938\u094D\u0925\u093F\u0924\u093F \u0938\u0947 \u0938\u092E\u0942\u0939\u093F\u0924 \u0939\u0948\u0902, \u0935\u093E\u0938\u094D\u0924\u0935\u093F\u0915 \u0938\u092E\u092F \u0938\u0947 \u0928\u0939\u0940\u0902\u0964 \u0924\u0941\u0930\u0902\u0924 \u0935\u093F\u0935\u0930\u0923 \u0915\u0947 \u0932\u093F\u090F \u0939\u094B\u0935\u0930 \u092F\u093E \u092B\u093C\u094B\u0915\u0938 \u0915\u0930\u0947\u0902; \u092A\u0942\u0930\u0940 \u0938\u0942\u091A\u0940 \u0915\u0947 \u0932\u093F\u090F \u0938\u0915\u094D\u0930\u093F\u092F \u0915\u0930\u0947\u0902\u0964",
911
+ eventWaiting: "\u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E",
652
912
  events: "\u0938\u0939\u093E\u092F\u0915 \u0914\u0930 \u0915\u0948\u092A\u094D\u091A\u0930 \u0915\u0940 \u0918\u091F\u0928\u093E\u090F\u0901",
653
913
  eventsHelp: "\u091A\u093F\u0939\u094D\u0928 \u0915\u0948\u092A\u094D\u091A\u0930 \u0915\u093F\u090F \u0917\u090F \u0911\u0921\u093F\u092F\u094B \u0915\u0940 \u0938\u0940\u092E\u093E \u0914\u0930 \u0918\u091F\u0928\u093E \u0915\u093E \u0935\u093E\u0938\u094D\u0924\u0935\u093F\u0915 \u0938\u092E\u092F \u0926\u093F\u0916\u093E\u0924\u0947 \u0939\u0948\u0902\u0964 \u0915\u0948\u092A\u094D\u091A\u0930 \u0930\u0941\u0915\u0928\u0947 \u0915\u0947 \u092C\u093E\u0926 \u0918\u091F\u0928\u093E\u0913\u0902 \u0915\u0940 \u0905\u0902\u0924\u093F\u092E \u0938\u0940\u092E\u093E \u0938\u092E\u093E\u0928 \u0939\u094B \u0938\u0915\u0924\u0940 \u0939\u0948; \u0938\u0939\u093E\u092F\u0915 \u0915\u093E \u0911\u0921\u093F\u092F\u094B \u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u092E\u0947\u0902 \u0928\u0939\u0940\u0902 \u091C\u094B\u0921\u093C\u093E \u091C\u093E\u0924\u093E\u0964",
654
914
  export: "\u0938\u094D\u0935\u092F\u0902\u092A\u0942\u0930\u094D\u0923 JSON \u0928\u093F\u0930\u094D\u092F\u093E\u0924 \u0915\u0930\u0947\u0902",
655
915
  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",
656
916
  failed: "\u0935\u093F\u092B\u0932",
657
917
  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",
918
+ inputStopComplete: "\u092A\u0939\u091A\u093E\u0928/\u092E\u0940\u091F\u0930 \u0930\u094B\u0915\u0928\u093E \u092A\u0942\u0930\u093E \u0939\u0941\u0906",
658
919
  interrupted: "\u092C\u093E\u0927\u093F\u0924",
659
920
  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",
660
921
  loading: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0932\u094B\u0921 \u0939\u094B \u0930\u0939\u0940 \u0939\u0948\u0902\u2026",
922
+ manualAction: "\u092E\u0948\u0928\u094D\u092F\u0941\u0905\u0932",
923
+ meetingId: "\u092E\u0940\u091F\u093F\u0902\u0917 \u0906\u0908\u0921\u0940",
661
924
  memory: "\u0915\u0947\u0935\u0932 \u092E\u0947\u092E\u094B\u0930\u0940 \u092E\u0947\u0902",
662
925
  microphone: "\u092E\u093E\u0907\u0915\u094D\u0930\u094B\u092B\u093C\u094B\u0928",
663
926
  name: "\u0938\u0924\u094D\u0930 \u0915\u093E \u0928\u093E\u092E",
664
927
  nameError: "\u0928\u093E\u092E \u0938\u0939\u0947\u091C\u093E \u0928\u0939\u0940\u0902 \u091C\u093E \u0938\u0915\u093E\u0964 \u0938\u0924\u094D\u0930 \u092A\u0930 \u0924\u093E\u0930\u093E \u0932\u0917\u093E\u090F\u0901 \u0914\u0930 \u0905\u0927\u093F\u0915\u0924\u092E 120 \u0905\u0915\u094D\u0937\u0930\u094B\u0902 \u0915\u0940 \u090F\u0915 \u092A\u0902\u0915\u094D\u0924\u093F \u0915\u093E \u0909\u092A\u092F\u094B\u0917 \u0915\u0930\u0947\u0902\u0964",
665
928
  noAttempts: "\u0905\u092D\u0940 \u0915\u094B\u0908 \u091F\u094D\u0930\u093E\u0902\u0938\u0915\u094D\u0930\u093F\u092A\u094D\u0936\u0928 \u092A\u094D\u0930\u092F\u093E\u0938 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964",
666
929
  open: "\u0916\u0941\u0932\u093E \u0916\u0902\u0921",
930
+ originalName: "\u0915\u0948\u092A\u094D\u091A\u0930 \u0915\u0947 \u0938\u092E\u092F \u092E\u0942\u0932 \u0928\u093E\u092E",
931
+ participant: "\u092A\u094D\u0930\u0924\u093F\u092D\u093E\u0917\u0940",
932
+ participantDetails: "\u0915\u0948\u092A\u094D\u091A\u0930 \u0915\u0947 \u0938\u092E\u092F \u092A\u094D\u0930\u0924\u093F\u092D\u093E\u0917\u0940 \u0915\u0940 \u092A\u0939\u091A\u093E\u0928",
933
+ participantId: "\u092A\u094D\u0930\u0924\u093F\u092D\u093E\u0917\u0940 \u0906\u0908\u0921\u0940",
667
934
  pending: "\u0932\u0902\u092C\u093F\u0924",
668
935
  persistent: "\u0938\u094D\u0925\u093E\u0928\u0940\u092F \u0930\u0942\u092A \u0938\u0947 \u0938\u0939\u0947\u091C\u093E",
669
936
  pinConfirmed: "\u092A\u0941\u0937\u094D\u091F\u093F \u0938\u092E\u092F",
@@ -671,7 +938,22 @@ var init_hi2 = __esm({
671
938
  pins: "\u0938\u0940\u092E\u093E \u091A\u093F\u0939\u094D\u0928",
672
939
  play: "\u0916\u0902\u0921 \u091A\u0932\u093E\u090F\u0901",
673
940
  playSession: "\u0938\u0939\u0947\u091C\u093E \u0938\u0924\u094D\u0930 \u091A\u0932\u093E\u090F\u0901",
941
+ queuedSegments: "\u0915\u0924\u093E\u0930 \u092E\u0947\u0902 \u0938\u0947\u0917\u092E\u0947\u0902\u091F",
674
942
  refresh: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0930\u0940\u092B\u093C\u094D\u0930\u0947\u0936 \u0915\u0930\u0947\u0902",
943
+ requestBytes: "WAV \u092C\u0949\u0921\u0940 \u0915\u093E \u0906\u0915\u093E\u0930",
944
+ requestExpected: "\u0905\u092A\u0947\u0915\u094D\u0937\u093F\u0924 \u0905\u0902\u0924\u0930\u093E\u0932 \u0915\u0940 \u0905\u0935\u0927\u093F",
945
+ requestHelp: "\u092F\u0947 \u092E\u093E\u092A \u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u0926\u094D\u0935\u093E\u0930\u093E DSH \u0915\u0947 \u0932\u093F\u090F \u0924\u0948\u092F\u093E\u0930 WAV \u092C\u0949\u0921\u0940 \u0915\u0947 \u0939\u0948\u0902, \u0938\u0930\u094D\u0935\u0930 \u0915\u0940 \u092A\u094D\u0930\u093E\u092A\u094D\u0924\u093F \u092F\u093E \u092E\u0949\u0921\u0932 \u0915\u0947 \u092A\u094D\u0930\u0938\u0902\u0938\u094D\u0915\u0930\u0923 \u0915\u093E \u092A\u094D\u0930\u092E\u093E\u0923 \u0928\u0939\u0940\u0902 \u0939\u0948\u0902\u0964 HTTP \u0939\u0947\u0921\u0930 \u0914\u0930 \u0939\u094B\u0938\u094D\u091F \u0915\u0940 \u0930\u0940\u092A\u0948\u0915\u0947\u091C\u093F\u0902\u0917 \u0936\u093E\u092E\u093F\u0932 \u0928\u0939\u0940\u0902 \u0939\u0948\u0902\u0964 \u0911\u0921\u093F\u092F\u094B \u0915\u0940 \u0905\u0924\u093F\u0930\u093F\u0915\u094D\u0924 \u092A\u094D\u0930\u0924\u093F \u0928\u0939\u0940\u0902 \u0930\u0916\u0940 \u091C\u093E\u0924\u0940\u0964",
946
+ requestInput: "\u0907\u0928\u092A\u0941\u091F PCM \u0928\u092E\u0942\u0928\u0947 / \u0926\u0930",
947
+ requestLanguage: "\u092A\u0939\u091A\u093E\u0928 \u0915\u0940 \u092D\u093E\u0937\u093E",
948
+ requestMetadata: "HTTP \u0905\u0928\u0941\u0930\u094B\u0927 \u092E\u0947\u091F\u093E\u0921\u0947\u091F\u093E",
949
+ requestPrepared: "\u092C\u0949\u0921\u0940 \u0924\u0948\u092F\u093E\u0930 \u0939\u094B\u0928\u0947 \u0915\u093E \u0938\u092E\u092F",
950
+ requestSamples: "WAV \u0928\u092E\u0942\u0928\u0947 / \u0926\u0930",
951
+ requestStatus: "HTTP \u092A\u094D\u0930\u0924\u093F\u0915\u094D\u0930\u093F\u092F\u093E \u0938\u094D\u0925\u093F\u0924\u093F",
952
+ requestUnknown: "\u0926\u0930\u094D\u091C \u0928\u0939\u0940\u0902 / \u0905\u0928\u0941\u092A\u0932\u092C\u094D\u0927",
953
+ retranscribe: "\u092B\u093F\u0930 \u0938\u0947 \u0932\u093F\u092A\u094D\u092F\u0902\u0924\u0930\u0923 \u0915\u0930\u0947\u0902",
954
+ retranscribeError: "\u092B\u093F\u0930 \u0938\u0947 \u0932\u093F\u092A\u094D\u092F\u0902\u0924\u0930\u0923 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E\u0964 \u0915\u0948\u092A\u094D\u091A\u0930 \u0938\u092E\u093E\u092A\u094D\u0924 \u0915\u0930\u0947\u0902, \u092B\u093F\u0930 \u091A\u0941\u0928\u0947 \u0917\u090F \u092A\u0939\u091A\u093E\u0928 \u0907\u0902\u091C\u0928, \u0921\u093E\u0909\u0928\u0932\u094B\u0921 \u0915\u093F\u090F \u0917\u090F \u092E\u0949\u0921\u0932 \u0914\u0930 \u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u0938\u092E\u0930\u094D\u0925\u0928 \u0915\u0940 \u091C\u093E\u0901\u091A \u0915\u0930\u0947\u0902\u0964",
955
+ retranscribeHelp: "\u0938\u0939\u0947\u091C\u0940 \u0935\u093E\u0923\u0940 \u0915\u094B \u092E\u094C\u091C\u0942\u0926\u093E Qwen/Whisper HTTP, Whisper \xB7 Browser \u092F\u093E Qwen \xB7 Browser \u0938\u0947\u091F\u093F\u0902\u0917 \u0914\u0930 \u0915\u092E \u0938\u0947 \u0915\u092E 800 ms \u0938\u0902\u0926\u0930\u094D\u092D \u0915\u0947 \u0938\u093E\u0925 \u092B\u093F\u0930 \u0932\u093F\u092A\u094D\u092F\u0902\u0924\u0930\u093F\u0924 \u0915\u0930\u0947\u0902\u0964 \u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u092E\u0947\u0902 \u0938\u094D\u0925\u093E\u0928\u0940\u092F \u092E\u0949\u0921\u0932 \u092A\u0939\u0932\u0947 \u0938\u0947\u091F\u093F\u0902\u0917 \u0938\u0947 \u0921\u093E\u0909\u0928\u0932\u094B\u0921 \u0915\u0930\u0947\u0902; \u092A\u0941\u0928\u0903 \u092A\u094D\u0930\u092F\u093E\u0938 \u092E\u0949\u0921\u0932 \u0921\u093E\u0909\u0928\u0932\u094B\u0921 \u0928\u0939\u0940\u0902 \u0915\u0930\u0924\u0947 \u0914\u0930 HTTP \u092A\u0930 \u0928\u0939\u0940\u0902 \u091C\u093E\u0924\u0947\u0964 \u092E\u0942\u0932 Browser \u0915\u0947 \u0932\u093F\u090F \u092C\u093E\u0939\u0930\u0940 \u0911\u0921\u093F\u092F\u094B \u091F\u094D\u0930\u0948\u0915 \u0915\u093E \u0938\u0941\u0930\u0915\u094D\u0937\u093F\u0924, \u0938\u0924\u094D\u092F\u093E\u092A\u093F\u0924 \u0938\u092E\u0930\u094D\u0925\u0928 \u091A\u093E\u0939\u093F\u090F; \u092E\u093E\u0907\u0915\u094D\u0930\u094B\u092B\u093C\u094B\u0928 \u0915\u093E \u0935\u093F\u0915\u0932\u094D\u092A \u0915\u092D\u0940 \u0928\u0939\u0940\u0902 \u0932\u093F\u092F\u093E \u091C\u093E\u0924\u093E\u0964 \u0915\u0947\u0935\u0932 \u0907\u0924\u093F\u0939\u093E\u0938 \u092E\u0947\u0902 \u092A\u094D\u0930\u092F\u093E\u0938 \u091C\u094B\u0921\u093C\u0924\u093E \u0939\u0948; \u0915\u0902\u092A\u094B\u091C\u093C\u0930 \u0928\u0939\u0940\u0902 \u092C\u0926\u0932\u0924\u093E \u092F\u093E \u0915\u092E\u093E\u0902\u0921 \u0928\u0939\u0940\u0902 \u091A\u0932\u093E\u0924\u093E\u0964",
956
+ retranscribing: "\u0938\u0947\u0917\u092E\u0947\u0902\u091F \u0915\u093E \u092B\u093F\u0930 \u0938\u0947 \u0932\u093F\u092A\u094D\u092F\u0902\u0924\u0930\u0923 \u0939\u094B \u0930\u0939\u093E \u0939\u0948\u2026",
675
957
  saveName: "\u0928\u093E\u092E \u0938\u0939\u0947\u091C\u0947\u0902",
676
958
  saved: "\u0938\u0939\u0947\u091C\u093E \u0911\u0921\u093F\u092F\u094B",
677
959
  search: "\u0916\u0902\u0921\u094B\u0902 \u0915\u0947 \u092A\u094D\u0930\u0924\u093F\u0932\u0947\u0916 \u0916\u094B\u091C\u0947\u0902",
@@ -689,8 +971,12 @@ var init_hi2 = __esm({
689
971
  starred: "\u0938\u094D\u0935\u091A\u093E\u0932\u093F\u0924 \u0938\u092B\u093C\u093E\u0908 \u0938\u0947 \u0938\u0941\u0930\u0915\u094D\u0937\u093F\u0924",
690
972
  start: "\u0936\u0941\u0930\u0941\u0906\u0924",
691
973
  state: "\u0938\u094D\u0925\u093F\u0924\u093F",
692
- storageAudio: "\u0938\u0939\u0947\u091C\u093E \u0917\u092F\u093E \u0911\u0921\u093F\u092F\u094B (MB)",
693
- storageTotal: "\u0915\u0941\u0932 \u0938\u0939\u0947\u091C\u093E \u0917\u092F\u093E \u0911\u0921\u093F\u092F\u094B (MB)",
974
+ storageAudio: "\u0938\u0939\u0947\u091C\u093E \u0917\u092F\u093E \u0911\u0921\u093F\u092F\u094B",
975
+ storageOverview: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0938\u0902\u0917\u094D\u0930\u0939\u0923",
976
+ 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
+ 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
+ storageSessions: "\u0938\u092D\u0940 \u0938\u094D\u0930\u094B\u0924\u094B\u0902 \u092E\u0947\u0902 {count} \u0938\u0924\u094D\u0930",
979
+ storageTotal: "\u0915\u0941\u0932 \u0938\u0939\u0947\u091C\u093E \u0917\u092F\u093E \u0911\u0921\u093F\u092F\u094B",
694
980
  storageUnknown: "\u0905\u0928\u0941\u092A\u0932\u092C\u094D\u0927",
695
981
  success: "\u0938\u092B\u0932",
696
982
  timeline: "\u0906\u0935\u093E\u091C\u093C / \u092E\u094C\u0928 \u0938\u092E\u092F\u0930\u0947\u0916\u093E",
@@ -698,6 +984,20 @@ var init_hi2 = __esm({
698
984
  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",
699
985
  unstar: "\u0938\u0941\u0930\u0915\u094D\u0937\u093E \u0939\u091F\u093E\u090F\u0901",
700
986
  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",
987
+ voiceCommandRecognized: "\u0935\u0949\u0907\u0938 \u0915\u092E\u093E\u0902\u0921 \u092A\u0939\u091A\u093E\u0928\u093E \u0917\u092F\u093E",
988
+ waitCancelled: "\u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0930\u0926\u094D\u0926",
989
+ waitChanged: "\u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0915\u0947 \u0915\u093E\u0930\u0923 \u092C\u0926\u0932\u0947",
990
+ waitDelay: "\u0935\u093F\u0932\u0902\u092C",
991
+ waitDelivery: "\u092D\u0947\u091C\u0928\u093E \u0932\u0902\u092C\u093F\u0924",
992
+ waitExternalSpeech: "\u0938\u093E\u091D\u093E \u0938\u094D\u0930\u094B\u0924 \u0915\u0940 \u0935\u093E\u0923\u0940",
993
+ waitInputStarting: "\u0938\u0941\u0928\u0928\u093E \u0936\u0941\u0930\u0942 \u0939\u094B \u0930\u0939\u093E \u0939\u0948",
994
+ waitPaused: "\u0930\u0941\u0915\u093E \u0939\u0941\u0906",
995
+ waitReady: "\u092C\u094B\u0932\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u0924\u0948\u092F\u093E\u0930",
996
+ waitSegmentGap: "\u0938\u0947\u0917\u092E\u0947\u0902\u091F \u0915\u093E \u0905\u0902\u0924\u0930\u093E\u0932",
997
+ waitSilence: "\u092E\u094C\u0928 \u0905\u0935\u0927\u093F \u0915\u093E \u0935\u093F\u0932\u0902\u092C",
998
+ waitSuspended: "\u0935\u093E\u0923\u0940 \u0928\u093F\u0932\u0902\u092C\u093F\u0924",
999
+ waitTranscription: "\u0932\u093F\u092A\u094D\u092F\u0902\u0924\u0930\u0923 \u0932\u0902\u092C\u093F\u0924",
1000
+ waitUserSpeech: "\u092E\u093E\u0907\u0915\u094D\u0930\u094B\u092B\u093C\u094B\u0928 \u0915\u0940 \u0935\u093E\u0923\u0940",
701
1001
  window: "\u091F\u093E\u0907\u092E\u0932\u093E\u0907\u0928 \u0915\u093E \u0939\u093F\u0938\u094D\u0938\u093E",
702
1002
  windowEnd: "\u0939\u093F\u0938\u094D\u0938\u0947 \u0915\u093E \u0905\u0902\u0924",
703
1003
  windowReset: "\u092A\u0942\u0930\u093E \u0938\u0924\u094D\u0930 \u0926\u093F\u0916\u093E\u090F\u0901",
@@ -809,45 +1109,87 @@ var init_pt_BR2 = __esm({
809
1109
  "src/i18n/recordings/pt-BR.ts"() {
810
1110
  translation18 = {
811
1111
  active: "Gravando",
1112
+ aliasAtCapture: "Apelido na captura",
812
1113
  allSources: "Todas as fontes",
1114
+ assistantCancel: "Fala do assistente cancelada",
813
1115
  assistantEnd: "Reprodu\xE7\xE3o do assistente encerrada",
1116
+ assistantError: "Falha na fala do assistente",
1117
+ assistantInterruptionRequest: "Pausa solicitada ap\xF3s fala confirmada do usu\xE1rio",
814
1118
  assistantPause: "Reprodu\xE7\xE3o do assistente pausada",
1119
+ assistantPlayRequest: "Reprodu\xE7\xE3o do assistente solicitada",
815
1120
  assistantResume: "Reprodu\xE7\xE3o do assistente retomada",
816
1121
  assistantStart: "Reprodu\xE7\xE3o do assistente iniciada",
1122
+ assistantWaitEnd: "Espera do assistente encerrada",
1123
+ assistantWaitStart: "Assistente come\xE7ou a esperar",
817
1124
  attempts: "Hist\xF3rico de transcri\xE7\xE3o",
818
1125
  audio: "Reprodu\xE7\xE3o da grava\xE7\xE3o",
819
1126
  audioError: "N\xE3o foi poss\xEDvel carregar ou reproduzir o \xE1udio.",
1127
+ automaticAction: "Autom\xE1tico",
820
1128
  cancel: "Cancelar",
821
1129
  captureEnd: "Captura encerrada",
1130
+ captureGateFailed: "Falha ao parar a escuta antes da reprodu\xE7\xE3o",
822
1131
  captureGated: "Suspens\xE3o do microfone solicitada para reprodu\xE7\xE3o no alto-falante",
823
1132
  captureStart: "Captura iniciada",
824
1133
  captured: "\xC1udio capturado",
1134
+ commandClear: "Limpar rascunho",
1135
+ commandEnd: "Encerrar conversa",
1136
+ commandMute: "Silenciar microfone",
1137
+ commandQueue: "Enfileirar mensagem",
1138
+ commandResume: "Retomar microfone",
1139
+ commandSend: "Enviar mensagem",
1140
+ commandStopSpeaking: "Parar fala",
825
1141
  completed: "Conclu\xEDda",
826
1142
  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.",
827
1143
  date: "In\xEDcio",
828
1144
  delete: "Excluir sess\xE3o inteira",
1145
+ deleteAll: "Apagar todas as grava\xE7\xF5es",
1146
+ deleteAllActive: "Todas as sess\xF5es est\xE3o gravando ou com transcri\xE7\xE3o pendente. Pare a captura e aguarde o processamento antes de apag\xE1-las.",
1147
+ deleteAllConfirm: "Apagar grava\xE7\xF5es salvas de todas as fontes?",
1148
+ deleteAllConfirmAction: "Sim, apagar estas grava\xE7\xF5es",
1149
+ deleteAllPreview: "{count} sess\xE3o(\xF5es) selecionada(s), incluindo {stars} com estrela. {busy} sess\xE3o(\xF5es) gravando ou processando ser\xE3o preservadas.",
1150
+ deleteAllProgress: "Apagando {done} de {count}\u2026",
1151
+ deleteAllResult: "Apagadas: {removed} \xB7 Preservadas ou j\xE1 ausentes: {skipped} \xB7 Falhas: {failed}",
1152
+ deleteAllWarning: "Remove definitivamente \xE1udio, transcri\xE7\xF5es e eventos deste navegador e site. Inclui sess\xF5es com estrela, independentemente do filtro de fonte. Novas sess\xF5es criadas ap\xF3s esta confirma\xE7\xE3o n\xE3o entram na limpeza. Conversas do DSH, arquivos exportados e grava\xE7\xF5es futuras n\xE3o s\xE3o alterados.",
829
1153
  deleteError: "N\xE3o foi poss\xEDvel excluir a grava\xE7\xE3o.",
1154
+ download: "Baixar WAV do segmento",
1155
+ downloadError: "N\xE3o foi poss\xEDvel baixar este segmento.",
830
1156
  duration: "Dura\xE7\xE3o",
831
1157
  empty: "Nenhuma grava\xE7\xE3o corresponde ao filtro.",
832
1158
  end: "Fim",
833
1159
  engine: "Motor",
834
1160
  error: "Erro",
1161
+ eventAttention: "Interrup\xE7\xF5es / falhas",
1162
+ eventCapture: "Captura / escuta",
1163
+ eventCommand: "Comando de voz",
1164
+ eventCount: "{count} evento(s)",
1165
+ eventInspect: "Clique ou pressione Enter para inspecionar todos os eventos",
1166
+ eventMore: "Mais {count} \u2014 consulte a lista",
1167
+ eventPlayback: "Reprodu\xE7\xE3o",
835
1168
  eventTime: "Hor\xE1rio do evento",
1169
+ eventTrackHelp: "Marcadores s\xE3o agrupados por categoria e posi\xE7\xE3o pr\xF3xima no \xE1udio capturado, n\xE3o pelo hor\xE1rio real. Passe o mouse ou use o foco para detalhes imediatos; ative para consultar a lista completa.",
1170
+ eventWaiting: "Espera",
836
1171
  events: "Eventos do assistente e da captura",
837
1172
  eventsHelp: "Os marcadores mostram o limite do \xE1udio capturado e o hor\xE1rio real do evento. Eventos ap\xF3s o fim da captura podem compartilhar seu limite final; nenhum \xE1udio do assistente \xE9 adicionado \xE0 grava\xE7\xE3o.",
838
1173
  export: "Exportar JSON completo",
839
1174
  exportError: "N\xE3o foi poss\xEDvel exportar a grava\xE7\xE3o.",
840
1175
  failed: "Falhou",
841
1176
  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.",
1177
+ inputStopComplete: "Parada do reconhecimento/medidor conclu\xEDda",
842
1178
  interrupted: "Interrompida",
843
1179
  loadError: "N\xE3o foi poss\xEDvel carregar as grava\xE7\xF5es. Tente atualizar.",
844
1180
  loading: "Carregando grava\xE7\xF5es\u2026",
1181
+ manualAction: "Manual",
1182
+ meetingId: "ID da reuni\xE3o",
845
1183
  memory: "Somente na mem\xF3ria",
846
1184
  microphone: "Microfone",
847
1185
  name: "Nome da sess\xE3o",
848
1186
  nameError: "N\xE3o foi poss\xEDvel salvar o nome. Marque a sess\xE3o com estrela e use uma \xFAnica linha de at\xE9 120 caracteres.",
849
1187
  noAttempts: "Nenhuma tentativa de transcri\xE7\xE3o ainda.",
850
1188
  open: "Segmento aberto",
1189
+ originalName: "Nome original na captura",
1190
+ participant: "Participante",
1191
+ participantDetails: "Identidade do participante na captura",
1192
+ participantId: "ID do participante",
851
1193
  pending: "Pendente",
852
1194
  persistent: "Salva localmente",
853
1195
  pinConfirmed: "Confirmado em",
@@ -855,7 +1197,22 @@ var init_pt_BR2 = __esm({
855
1197
  pins: "Marcadores de limite",
856
1198
  play: "Reproduzir segmento",
857
1199
  playSession: "Reproduzir sess\xE3o salva",
1200
+ queuedSegments: "Segmentos na fila",
858
1201
  refresh: "Atualizar grava\xE7\xF5es",
1202
+ requestBytes: "Tamanho do corpo WAV",
1203
+ requestExpected: "Dura\xE7\xE3o do intervalo esperado",
1204
+ requestHelp: "Estas medidas descrevem o corpo WAV preparado pelo navegador para o DSH, n\xE3o o recebimento pelo servidor nem o processamento pelo modelo. N\xE3o incluem cabe\xE7alhos HTTP ou reempacotamento no host. N\xE3o h\xE1 c\xF3pia adicional do \xE1udio.",
1205
+ requestInput: "Amostras PCM de entrada / taxa",
1206
+ requestLanguage: "Idioma do reconhecimento",
1207
+ requestMetadata: "Metadados do pedido HTTP",
1208
+ requestPrepared: "Corpo preparado em",
1209
+ requestSamples: "Amostras WAV / taxa",
1210
+ requestStatus: "Status da resposta HTTP",
1211
+ requestUnknown: "N\xE3o registrado / indispon\xEDvel",
1212
+ retranscribe: "Transcrever novamente",
1213
+ retranscribeError: "N\xE3o foi poss\xEDvel transcrever novamente. Encerre a captura e verifique o mecanismo de reconhecimento selecionado, o modelo baixado e o suporte do navegador.",
1214
+ retranscribeHelp: "Tenta transcrever a fala salva com as configura\xE7\xF5es atuais de Qwen/Whisper HTTP, Whisper \xB7 Browser ou Qwen \xB7 Browser e contexto m\xEDnimo de 800 ms. Baixe primeiro os modelos locais do navegador nas Configura\xE7\xF5es; novas tentativas nunca baixam modelos nem recorrem ao HTTP. Browser nativo exige suporte seguro verificado a faixas de \xE1udio externas; nunca recorre ao microfone. Apenas adiciona ao hist\xF3rico; n\xE3o altera o compositor nem executa comandos.",
1215
+ retranscribing: "Transcrevendo segmento novamente\u2026",
859
1216
  saveName: "Salvar nome",
860
1217
  saved: "\xC1udio salvo",
861
1218
  search: "Pesquisar transcri\xE7\xF5es dos segmentos",
@@ -873,8 +1230,12 @@ var init_pt_BR2 = __esm({
873
1230
  starred: "Protegida da limpeza autom\xE1tica",
874
1231
  start: "In\xEDcio",
875
1232
  state: "Estado",
876
- storageAudio: "\xC1udio armazenado (MB)",
877
- storageTotal: "Total de \xE1udio armazenado (MB)",
1233
+ storageAudio: "\xC1udio armazenado",
1234
+ storageOverview: "Armazenamento das grava\xE7\xF5es",
1235
+ storagePartial: "Pelo menos {size} \xB7 {count} sess\xE3o(\xF5es) sem medi\xE7\xE3o",
1236
+ 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
+ storageSessions: "{count} sess\xE3o(\xF5es) de todas as fontes",
1238
+ storageTotal: "Total de \xE1udio armazenado",
878
1239
  storageUnknown: "Indispon\xEDvel",
879
1240
  success: "Sucesso",
880
1241
  timeline: "Linha do tempo de fala / sil\xEAncio",
@@ -882,6 +1243,20 @@ var init_pt_BR2 = __esm({
882
1243
  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.",
883
1244
  unstar: "Remover prote\xE7\xE3o",
884
1245
  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.",
1246
+ voiceCommandRecognized: "Comando de voz reconhecido",
1247
+ waitCancelled: "Espera cancelada",
1248
+ waitChanged: "Motivos da espera alterados",
1249
+ waitDelay: "Atraso",
1250
+ waitDelivery: "Envio pendente",
1251
+ waitExternalSpeech: "Fala de fonte compartilhada",
1252
+ waitInputStarting: "Escuta iniciando",
1253
+ waitPaused: "Pausado",
1254
+ waitReady: "Pronto para falar",
1255
+ waitSegmentGap: "Intervalo entre segmentos",
1256
+ waitSilence: "Atraso de sil\xEAncio",
1257
+ waitSuspended: "Fala suspensa",
1258
+ waitTranscription: "Transcri\xE7\xE3o pendente",
1259
+ waitUserSpeech: "Fala do microfone",
885
1260
  window: "Recorte da linha do tempo",
886
1261
  windowEnd: "Fim do recorte",
887
1262
  windowReset: "Mostrar sess\xE3o inteira",
@@ -993,45 +1368,87 @@ var init_zh2 = __esm({
993
1368
  "src/i18n/recordings/zh.ts"() {
994
1369
  translation22 = {
995
1370
  active: "\u6B63\u5728\u5F55\u5236",
1371
+ aliasAtCapture: "\u91C7\u96C6\u65F6\u7684\u522B\u540D",
996
1372
  allSources: "\u6240\u6709\u6765\u6E90",
1373
+ assistantCancel: "\u52A9\u624B\u8BED\u97F3\u5DF2\u53D6\u6D88",
997
1374
  assistantEnd: "\u52A9\u624B\u64AD\u653E\u7ED3\u675F",
1375
+ assistantError: "\u52A9\u624B\u8BED\u97F3\u5931\u8D25",
1376
+ assistantInterruptionRequest: "\u786E\u8BA4\u7528\u6237\u8BED\u97F3\u540E\u8BF7\u6C42\u6682\u505C",
998
1377
  assistantPause: "\u52A9\u624B\u64AD\u653E\u6682\u505C",
1378
+ assistantPlayRequest: "\u5DF2\u8BF7\u6C42\u52A9\u624B\u64AD\u653E",
999
1379
  assistantResume: "\u52A9\u624B\u64AD\u653E\u6062\u590D",
1000
1380
  assistantStart: "\u52A9\u624B\u5F00\u59CB\u64AD\u653E",
1381
+ assistantWaitEnd: "\u52A9\u624B\u7B49\u5F85\u7ED3\u675F",
1382
+ assistantWaitStart: "\u52A9\u624B\u5F00\u59CB\u7B49\u5F85",
1001
1383
  attempts: "\u8F6C\u5F55\u5386\u53F2",
1002
1384
  audio: "\u5F55\u97F3\u64AD\u653E",
1003
1385
  audioError: "\u65E0\u6CD5\u52A0\u8F7D\u6216\u64AD\u653E\u5F55\u97F3\u97F3\u9891\u3002",
1386
+ automaticAction: "\u81EA\u52A8",
1004
1387
  cancel: "\u53D6\u6D88",
1005
1388
  captureEnd: "\u91C7\u96C6\u7ED3\u675F",
1389
+ captureGateFailed: "\u64AD\u653E\u524D\u505C\u6B62\u6536\u542C\u5931\u8D25",
1006
1390
  captureGated: "\u5DF2\u8BF7\u6C42\u6682\u505C\u9EA6\u514B\u98CE\u4EE5\u901A\u8FC7\u626C\u58F0\u5668\u64AD\u653E",
1007
1391
  captureStart: "\u91C7\u96C6\u5F00\u59CB",
1008
1392
  captured: "\u5DF2\u6355\u83B7\u97F3\u9891",
1393
+ commandClear: "\u6E05\u7A7A\u8349\u7A3F",
1394
+ commandEnd: "\u7ED3\u675F\u5BF9\u8BDD",
1395
+ commandMute: "\u9759\u97F3\u9EA6\u514B\u98CE",
1396
+ commandQueue: "\u6D88\u606F\u5165\u961F",
1397
+ commandResume: "\u6062\u590D\u9EA6\u514B\u98CE",
1398
+ commandSend: "\u53D1\u9001\u6D88\u606F",
1399
+ commandStopSpeaking: "\u505C\u6B62\u8BB2\u8BDD",
1009
1400
  completed: "\u5DF2\u5B8C\u6210",
1010
1401
  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",
1011
1402
  date: "\u5F00\u59CB\u65F6\u95F4",
1012
1403
  delete: "\u5220\u9664\u6574\u4E2A\u4F1A\u8BDD",
1404
+ deleteAll: "\u5220\u9664\u6240\u6709\u5F55\u97F3",
1405
+ deleteAllActive: "\u6240\u6709\u4F1A\u8BDD\u6B63\u5728\u5F55\u97F3\u6216\u7B49\u5F85\u8F6C\u5199\u3002\u8BF7\u505C\u6B62\u91C7\u96C6\u5E76\u7B49\u5F85\u5904\u7406\u5B8C\u6210\u540E\u518D\u6E05\u7406\u3002",
1406
+ deleteAllConfirm: "\u5220\u9664\u6240\u6709\u6765\u6E90\u7684\u5DF2\u4FDD\u5B58\u5F55\u97F3\uFF1F",
1407
+ deleteAllConfirmAction: "\u662F\uFF0C\u5220\u9664\u8FD9\u4E9B\u5F55\u97F3",
1408
+ deleteAllPreview: "\u5DF2\u9009\u62E9 {count} \u4E2A\u4F1A\u8BDD\uFF0C\u5305\u62EC {stars} \u4E2A\u52A0\u661F\u4F1A\u8BDD\u3002\u5C06\u4FDD\u7559 {busy} \u4E2A\u6B63\u5728\u5F55\u97F3\u6216\u5904\u7406\u7684\u4F1A\u8BDD\u3002",
1409
+ deleteAllProgress: "\u6B63\u5728\u5220\u9664 {done} \u9879\uFF0C\u5171 {count} \u9879\u2026",
1410
+ deleteAllResult: "\u5DF2\u5220\u9664\uFF1A{removed} \xB7 \u5DF2\u4FDD\u7559\u6216\u5DF2\u4E0D\u5B58\u5728\uFF1A{skipped} \xB7 \u5931\u8D25\uFF1A{failed}",
1411
+ deleteAllWarning: "\u6C38\u4E45\u5220\u9664\u6B64\u6D4F\u89C8\u5668\u548C\u7AD9\u70B9\u4E2D\u7684\u97F3\u9891\u3001\u8F6C\u5199\u53CA\u4E8B\u4EF6\u3002\u5305\u62EC\u52A0\u661F\u4F1A\u8BDD\uFF0C\u4E0D\u53D7\u6765\u6E90\u7B5B\u9009\u5F71\u54CD\u3002\u6B64\u786E\u8BA4\u540E\u521B\u5EFA\u7684\u65B0\u4F1A\u8BDD\u4E0D\u5305\u542B\u5728\u5185\u3002\u4E0D\u5F71\u54CD DSH \u5BF9\u8BDD\u3001\u5BFC\u51FA\u6587\u4EF6\u6216\u672A\u6765\u5F55\u97F3\u3002",
1013
1412
  deleteError: "\u65E0\u6CD5\u5220\u9664\u5F55\u97F3\u3002",
1413
+ download: "\u4E0B\u8F7D\u7247\u6BB5 WAV",
1414
+ downloadError: "\u65E0\u6CD5\u4E0B\u8F7D\u6B64\u7247\u6BB5\u3002",
1014
1415
  duration: "\u65F6\u957F",
1015
1416
  empty: "\u6CA1\u6709\u7B26\u5408\u7B5B\u9009\u6761\u4EF6\u7684\u5F55\u97F3\u4F1A\u8BDD\u3002",
1016
1417
  end: "\u7ED3\u675F",
1017
1418
  engine: "\u5F15\u64CE",
1018
1419
  error: "\u9519\u8BEF",
1420
+ eventAttention: "\u6253\u65AD / \u95EE\u9898",
1421
+ eventCapture: "\u91C7\u96C6 / \u6536\u542C",
1422
+ eventCommand: "\u8BED\u97F3\u547D\u4EE4",
1423
+ eventCount: "{count} \u4E2A\u4E8B\u4EF6",
1424
+ eventInspect: "\u70B9\u51FB\u6216\u6309 Enter \u67E5\u770B\u5168\u90E8\u4E8B\u4EF6",
1425
+ eventMore: "\u8FD8\u6709 {count} \u4E2A \u2014 \u67E5\u770B\u5217\u8868",
1426
+ eventPlayback: "\u64AD\u653E",
1019
1427
  eventTime: "\u4E8B\u4EF6\u65F6\u95F4",
1428
+ eventTrackHelp: "\u6807\u8BB0\u6309\u7C7B\u522B\u548C\u63A5\u8FD1\u7684\u91C7\u96C6\u97F3\u9891\u4F4D\u7F6E\u5206\u7EC4\uFF0C\u800C\u975E\u5B9E\u9645\u65F6\u95F4\u3002\u60AC\u505C\u6216\u805A\u7126\u53EF\u7ACB\u5373\u67E5\u770B\u8BE6\u60C5\uFF1B\u6FC0\u6D3B\u53EF\u67E5\u770B\u5B8C\u6574\u5217\u8868\u3002",
1429
+ eventWaiting: "\u7B49\u5F85",
1020
1430
  events: "\u52A9\u624B\u4E0E\u91C7\u96C6\u4E8B\u4EF6",
1021
1431
  eventsHelp: "\u6807\u8BB0\u663E\u793A\u5DF2\u91C7\u96C6\u97F3\u9891\u7684\u8FB9\u754C\u548C\u4E8B\u4EF6\u5B9E\u9645\u65F6\u95F4\u3002\u91C7\u96C6\u505C\u6B62\u540E\u7684\u4E8B\u4EF6\u53EF\u80FD\u5171\u7528\u6700\u7EC8\u8FB9\u754C\uFF1B\u4E0D\u4F1A\u5C06\u52A9\u624B\u97F3\u9891\u6DFB\u52A0\u5230\u5F55\u97F3\u3002",
1022
1432
  export: "\u5BFC\u51FA\u5B8C\u6574\u72EC\u7ACB JSON",
1023
1433
  exportError: "\u65E0\u6CD5\u5BFC\u51FA\u5F55\u97F3\u3002",
1024
1434
  failed: "\u5931\u8D25",
1025
1435
  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",
1436
+ inputStopComplete: "\u8BC6\u522B/\u97F3\u91CF\u8BA1\u505C\u6B62\u5DF2\u5B8C\u6210",
1026
1437
  interrupted: "\u5DF2\u4E2D\u65AD",
1027
1438
  loadError: "\u65E0\u6CD5\u52A0\u8F7D\u5F55\u97F3\u4F1A\u8BDD\u3002\u8BF7\u5237\u65B0\u91CD\u8BD5\u3002",
1028
1439
  loading: "\u6B63\u5728\u52A0\u8F7D\u5F55\u97F3\u2026",
1440
+ manualAction: "\u624B\u52A8",
1441
+ meetingId: "\u4F1A\u8BAE ID",
1029
1442
  memory: "\u4EC5\u5728\u5185\u5B58\u4E2D",
1030
1443
  microphone: "\u9EA6\u514B\u98CE",
1031
1444
  name: "\u4F1A\u8BDD\u540D\u79F0",
1032
1445
  nameError: "\u65E0\u6CD5\u4FDD\u5B58\u540D\u79F0\u3002\u8BF7\u5148\u52A0\u661F\u6807\uFF0C\u5E76\u4F7F\u7528\u4E0D\u8D85\u8FC7120\u4E2A\u5B57\u7B26\u7684\u5355\u884C\u540D\u79F0\u3002",
1033
1446
  noAttempts: "\u5C1A\u65E0\u8F6C\u5F55\u5C1D\u8BD5\u3002",
1034
1447
  open: "\u672A\u7ED3\u675F\u7247\u6BB5",
1448
+ originalName: "\u91C7\u96C6\u65F6\u7684\u539F\u59CB\u59D3\u540D",
1449
+ participant: "\u53C2\u4E0E\u8005",
1450
+ participantDetails: "\u91C7\u96C6\u65F6\u7684\u53C2\u4E0E\u8005\u8EAB\u4EFD",
1451
+ participantId: "\u53C2\u4E0E\u8005 ID",
1035
1452
  pending: "\u5904\u7406\u4E2D",
1036
1453
  persistent: "\u5DF2\u4FDD\u5B58\u5230\u672C\u5730",
1037
1454
  pinConfirmed: "\u786E\u8BA4\u65F6\u95F4",
@@ -1039,7 +1456,22 @@ var init_zh2 = __esm({
1039
1456
  pins: "\u8FB9\u754C\u6807\u8BB0",
1040
1457
  play: "\u64AD\u653E\u7247\u6BB5",
1041
1458
  playSession: "\u64AD\u653E\u5DF2\u4FDD\u5B58\u4F1A\u8BDD",
1459
+ queuedSegments: "\u6392\u961F\u7247\u6BB5\u6570",
1042
1460
  refresh: "\u5237\u65B0\u5F55\u97F3",
1461
+ requestBytes: "WAV \u8BF7\u6C42\u4F53\u5927\u5C0F",
1462
+ requestExpected: "\u9884\u671F\u8303\u56F4\u65F6\u957F",
1463
+ requestHelp: "\u8FD9\u4E9B\u6D4B\u91CF\u503C\u63CF\u8FF0\u6D4F\u89C8\u5668\u4E3A DSH \u51C6\u5907\u7684 WAV \u8BF7\u6C42\u4F53\uFF0C\u5E76\u4E0D\u8BC1\u660E\u670D\u52A1\u5668\u5DF2\u6536\u5230\u6216\u6A21\u578B\u5DF2\u5904\u7406\u3002\u4E0D\u5305\u62EC HTTP \u6807\u5934\u6216\u4E3B\u673A\u91CD\u65B0\u5C01\u88C5\u3002\u4E0D\u4F1A\u4FDD\u5B58\u989D\u5916\u7684\u97F3\u9891\u526F\u672C\u3002",
1464
+ requestInput: "\u8F93\u5165 PCM \u6837\u672C\u6570 / \u91C7\u6837\u7387",
1465
+ requestLanguage: "\u8BC6\u522B\u8BED\u8A00",
1466
+ requestMetadata: "HTTP \u8BF7\u6C42\u5143\u6570\u636E",
1467
+ requestPrepared: "\u8BF7\u6C42\u4F53\u51C6\u5907\u65F6\u95F4",
1468
+ requestSamples: "WAV \u6837\u672C\u6570 / \u91C7\u6837\u7387",
1469
+ requestStatus: "HTTP \u54CD\u5E94\u72B6\u6001",
1470
+ requestUnknown: "\u672A\u8BB0\u5F55 / \u4E0D\u53EF\u7528",
1471
+ retranscribe: "\u91CD\u65B0\u8F6C\u5F55",
1472
+ retranscribeError: "\u65E0\u6CD5\u91CD\u65B0\u8F6C\u5F55\u3002\u8BF7\u7ED3\u675F\u91C7\u96C6\uFF0C\u5E76\u68C0\u67E5\u6240\u9009\u8BC6\u522B\u5F15\u64CE\u3001\u5DF2\u4E0B\u8F7D\u6A21\u578B\u548C\u6D4F\u89C8\u5668\u652F\u6301\u60C5\u51B5\u3002",
1473
+ retranscribeHelp: "\u4F7F\u7528\u5F53\u524D Qwen/Whisper HTTP\u3001Whisper \xB7 Browser \u6216 Qwen \xB7 Browser \u8BBE\u7F6E\u53CA\u81F3\u5C11 800 \u6BEB\u79D2\u7684\u4E0A\u4E0B\u6587\u91CD\u65B0\u8F6C\u5F55\u5DF2\u4FDD\u5B58\u8BED\u97F3\u3002\u8BF7\u5148\u5728\u8BBE\u7F6E\u4E2D\u4E0B\u8F7D\u6D4F\u89C8\u5668\u672C\u5730\u6A21\u578B\uFF1B\u91CD\u8BD5\u7EDD\u4E0D\u4E0B\u8F7D\u6A21\u578B\u6216\u56DE\u9000\u5230 HTTP\u3002\u539F\u751F Browser \u9700\u8981\u7ECF\u8FC7\u9A8C\u8BC1\u7684\u5B89\u5168\u5916\u90E8\u97F3\u8F68\u652F\u6301\uFF0C\u7EDD\u4E0D\u56DE\u9000\u5230\u9EA6\u514B\u98CE\u3002\u4EC5\u6DFB\u52A0\u5386\u53F2\u5C1D\u8BD5\uFF0C\u4E0D\u4FEE\u6539\u8F93\u5165\u6846\u6216\u6267\u884C\u547D\u4EE4\u3002",
1474
+ retranscribing: "\u6B63\u5728\u91CD\u65B0\u8F6C\u5F55\u7247\u6BB5\u2026",
1043
1475
  saveName: "\u4FDD\u5B58\u540D\u79F0",
1044
1476
  saved: "\u5DF2\u4FDD\u5B58\u97F3\u9891",
1045
1477
  search: "\u641C\u7D22\u7247\u6BB5\u8F6C\u5F55",
@@ -1057,8 +1489,12 @@ var init_zh2 = __esm({
1057
1489
  starred: "\u53D7\u4FDD\u62A4\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u6E05\u7406",
1058
1490
  start: "\u5F00\u59CB",
1059
1491
  state: "\u72B6\u6001",
1060
- storageAudio: "\u5DF2\u5B58\u50A8\u97F3\u9891 (MB)",
1061
- storageTotal: "\u5DF2\u5B58\u50A8\u97F3\u9891\u603B\u91CF (MB)",
1492
+ storageAudio: "\u5DF2\u5B58\u50A8\u97F3\u9891",
1493
+ storageOverview: "\u5F55\u97F3\u5B58\u50A8",
1494
+ storagePartial: "\u81F3\u5C11 {size} \xB7 {count} \u4E2A\u4F1A\u8BDD\u672A\u6D4B\u91CF",
1495
+ 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
+ storageSessions: "\u6240\u6709\u6765\u6E90\u5171 {count} \u4E2A\u4F1A\u8BDD",
1497
+ storageTotal: "\u5DF2\u5B58\u50A8\u97F3\u9891\u603B\u91CF",
1062
1498
  storageUnknown: "\u4E0D\u53EF\u7528",
1063
1499
  success: "\u6210\u529F",
1064
1500
  timeline: "\u8BED\u97F3 / \u9759\u97F3\u65F6\u95F4\u7EBF",
@@ -1066,6 +1502,20 @@ var init_zh2 = __esm({
1066
1502
  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",
1067
1503
  unstar: "\u53D6\u6D88\u4FDD\u62A4",
1068
1504
  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",
1505
+ voiceCommandRecognized: "\u5DF2\u8BC6\u522B\u8BED\u97F3\u547D\u4EE4",
1506
+ waitCancelled: "\u7B49\u5F85\u5DF2\u53D6\u6D88",
1507
+ waitChanged: "\u7B49\u5F85\u539F\u56E0\u5DF2\u6539\u53D8",
1508
+ waitDelay: "\u5EF6\u8FDF",
1509
+ waitDelivery: "\u7B49\u5F85\u53D1\u9001",
1510
+ waitExternalSpeech: "\u5171\u4EAB\u6765\u6E90\u8BED\u97F3",
1511
+ waitInputStarting: "\u6B63\u5728\u542F\u52A8\u6536\u542C",
1512
+ waitPaused: "\u5DF2\u6682\u505C",
1513
+ waitReady: "\u53EF\u4EE5\u5F00\u59CB\u8BB2\u8BDD",
1514
+ waitSegmentGap: "\u7247\u6BB5\u95F4\u9694",
1515
+ waitSilence: "\u9759\u9ED8\u7B49\u5F85\u5EF6\u8FDF",
1516
+ waitSuspended: "\u8BED\u97F3\u5DF2\u6302\u8D77",
1517
+ waitTranscription: "\u7B49\u5F85\u8F6C\u5F55",
1518
+ waitUserSpeech: "\u9EA6\u514B\u98CE\u8BED\u97F3",
1069
1519
  window: "\u65F6\u95F4\u8F74\u8303\u56F4",
1070
1520
  windowEnd: "\u8303\u56F4\u7ED3\u675F",
1071
1521
  windowReset: "\u663E\u793A\u5B8C\u6574\u4F1A\u8BDD",
@@ -1146,7 +1596,7 @@ __export(apply_exports, {
1146
1596
  inject: () => inject
1147
1597
  });
1148
1598
  module.exports = __toCommonJS(apply_exports);
1149
- var import_react28 = __toESM(require("react"), 1);
1599
+ var import_react30 = __toESM(require("react"), 1);
1150
1600
 
1151
1601
  // ../live-voice/src/shared/design-system/buttons/IconButton.tsx
1152
1602
  var import_react2 = __toESM(require("react"), 1);
@@ -1375,7 +1825,7 @@ var import_react20 = __toESM(require("react"), 1);
1375
1825
  var import_client = require("react-dom/client");
1376
1826
 
1377
1827
  // src/DebuggerPanel.tsx
1378
- var import_react25 = __toESM(require("react"), 1);
1828
+ var import_react27 = __toESM(require("react"), 1);
1379
1829
 
1380
1830
  // ../live-voice/src/modules/core/diagnostics.ts
1381
1831
  var DIAGNOSTIC_KEY = /* @__PURE__ */ Symbol.for("dsh-live-voice.diagnostics.v1");
@@ -1510,23 +1960,426 @@ function StateTree({
1510
1960
  }
1511
1961
 
1512
1962
  // src/RecordingPanel.tsx
1963
+ var import_react25 = __toESM(require("react"), 1);
1964
+
1965
+ // src/formatBytes.ts
1966
+ function formatBytes(bytes, locale = "en") {
1967
+ const units = ["B", "KB", "MB", "GB", "TB"];
1968
+ let value = Math.max(0, Number.isFinite(bytes) ? bytes : 0);
1969
+ let unit = 0;
1970
+ while (value >= 1e3 && unit < units.length - 1) {
1971
+ value /= 1e3;
1972
+ unit++;
1973
+ }
1974
+ if (Math.round(value * 10) / 10 >= 1e3 && unit < units.length - 1) {
1975
+ value /= 1e3;
1976
+ unit++;
1977
+ }
1978
+ return new Intl.NumberFormat(locale, { maximumFractionDigits: 1, useGrouping: false }).format(value) + " " + units[unit];
1979
+ }
1980
+
1981
+ // src/RecordingStorage.tsx
1982
+ var import_react22 = __toESM(require("react"), 1);
1983
+ var recordingBusy = (session) => session.state === "recording" || session.attempts.some((a) => a.status === "pending");
1984
+ function recordingStorage(sessions) {
1985
+ let bytes = 0, unknown = 0;
1986
+ for (const session of sessions) {
1987
+ if (typeof session.audioBytes === "number" && Number.isFinite(session.audioBytes) && session.audioBytes >= 0)
1988
+ bytes += session.audioBytes;
1989
+ else unknown++;
1990
+ }
1991
+ return { bytes, unknown, count: sessions.length };
1992
+ }
1993
+ var fill = (text, values) => text.replace(/{([a-zA-Z]+)}/g, (all, key) => String(values[key] ?? all));
1994
+ function RecordingStorage({
1995
+ locale = "en",
1996
+ service,
1997
+ sessions,
1998
+ loading,
1999
+ failed,
2000
+ copy,
2001
+ refresh,
2002
+ onBusy
2003
+ }) {
2004
+ const [preview, setPreview] = import_react22.default.useState(null);
2005
+ const [busy, setBusy] = import_react22.default.useState(false);
2006
+ const [result, setResult] = import_react22.default.useState(null);
2007
+ const [progress, setProgress] = import_react22.default.useState(0);
2008
+ const lifetime = import_react22.default.useRef(0);
2009
+ const running = import_react22.default.useRef(false);
2010
+ const currentService = import_react22.default.useRef(service);
2011
+ currentService.current = service;
2012
+ const root = import_react22.default.useRef(null);
2013
+ const trigger = import_react22.default.useRef(null);
2014
+ const cancel = import_react22.default.useRef(null);
2015
+ const status = import_react22.default.useRef(null);
2016
+ const id = import_react22.default.useId();
2017
+ import_react22.default.useEffect(() => {
2018
+ ++lifetime.current;
2019
+ setPreview(null);
2020
+ setResult(null);
2021
+ setBusy(false);
2022
+ running.current = false;
2023
+ onBusy(false);
2024
+ const invalidate = () => {
2025
+ ++lifetime.current;
2026
+ running.current = false;
2027
+ };
2028
+ const hide = () => {
2029
+ invalidate();
2030
+ setPreview(null);
2031
+ setBusy(false);
2032
+ setResult(null);
2033
+ onBusy(false);
2034
+ };
2035
+ const win = root.current?.ownerDocument.defaultView;
2036
+ win?.addEventListener("pagehide", hide);
2037
+ return () => {
2038
+ invalidate();
2039
+ win?.removeEventListener("pagehide", hide);
2040
+ };
2041
+ }, [service]);
2042
+ import_react22.default.useEffect(() => {
2043
+ if (preview && !busy) cancel.current?.focus();
2044
+ }, [preview]);
2045
+ const total = recordingStorage(sessions);
2046
+ const eligible = sessions.filter((s) => !recordingBusy(s));
2047
+ const dismiss = () => {
2048
+ if (running.current) return;
2049
+ setPreview(null);
2050
+ trigger.current?.focus();
2051
+ };
2052
+ const startPreview = () => {
2053
+ if (running.current || loading || failed) return;
2054
+ const candidates = sessions.filter((s) => !recordingBusy(s));
2055
+ const size = recordingStorage(candidates);
2056
+ setResult(null);
2057
+ setPreview({
2058
+ ids: candidates.map((s) => s.id),
2059
+ bytes: size.bytes,
2060
+ unknown: size.unknown,
2061
+ stars: candidates.filter((s) => s.starred).length,
2062
+ busy: sessions.length - candidates.length
2063
+ });
2064
+ };
2065
+ const remove = async () => {
2066
+ if (!preview || running.current || failed || loading) return;
2067
+ running.current = true;
2068
+ setBusy(true);
2069
+ onBusy(true);
2070
+ setProgress(0);
2071
+ const token = lifetime.current;
2072
+ const valid = () => token === lifetime.current && currentService.current === service;
2073
+ const outcome = { removed: 0, skipped: preview.busy, failed: 0 };
2074
+ try {
2075
+ for (const recordingId of preview.ids) {
2076
+ if (!valid()) return;
2077
+ try {
2078
+ const latest = await service.get(recordingId);
2079
+ if (!valid()) return;
2080
+ if (!latest || recordingBusy(latest)) outcome.skipped++;
2081
+ else {
2082
+ await service.remove(recordingId);
2083
+ outcome.removed++;
2084
+ }
2085
+ } catch (error) {
2086
+ const code = error?.code;
2087
+ if (code === "active-recording" || code === "not-found") outcome.skipped++;
2088
+ else outcome.failed++;
2089
+ }
2090
+ if (!valid()) return;
2091
+ setProgress(outcome.removed + outcome.failed + outcome.skipped - preview.busy);
2092
+ }
2093
+ setResult(outcome);
2094
+ setPreview(null);
2095
+ } finally {
2096
+ if (valid()) {
2097
+ running.current = false;
2098
+ setBusy(false);
2099
+ onBusy(false);
2100
+ refresh();
2101
+ queueMicrotask(() => {
2102
+ if (valid()) status.current?.focus();
2103
+ });
2104
+ }
2105
+ }
2106
+ };
2107
+ return /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-recording-storage", ref: root }, /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-storage-overview", "aria-busy": loading || busy }, /* @__PURE__ */ import_react22.default.createElement("div", null, /* @__PURE__ */ import_react22.default.createElement("h3", null, copy.storageOverview), /* @__PURE__ */ import_react22.default.createElement("p", { className: "dlvd-audio-total" }, /* @__PURE__ */ import_react22.default.createElement("span", null, copy.storageTotal, ": "), /* @__PURE__ */ import_react22.default.createElement("strong", null, loading ? copy.loading : failed ? copy.storageUnknown : total.unknown ? fill(copy.storagePartial, {
2108
+ size: formatBytes(total.bytes, locale),
2109
+ count: total.unknown
2110
+ }) : formatBytes(total.bytes, locale))), /* @__PURE__ */ import_react22.default.createElement("p", { className: "dlvd-storage-count" }, failed || loading ? "\u2014" : fill(copy.storageSessions, { count: total.count }))), /* @__PURE__ */ import_react22.default.createElement(
2111
+ "button",
2112
+ {
2113
+ ref: trigger,
2114
+ type: "button",
2115
+ className: "dlvd-delete-all",
2116
+ disabled: loading || failed || busy || !eligible.length,
2117
+ onClick: startPreview
2118
+ },
2119
+ copy.deleteAll
2120
+ )), /* @__PURE__ */ import_react22.default.createElement("p", { className: "dlvd-storage-scope" }, copy.storageScope), !loading && !failed && sessions.length > 0 && eligible.length === 0 && /* @__PURE__ */ import_react22.default.createElement("p", { role: "status" }, copy.deleteAllActive), preview && /* @__PURE__ */ import_react22.default.createElement(
2121
+ "section",
2122
+ {
2123
+ className: "dlvd-delete-all-confirm",
2124
+ role: "group",
2125
+ "aria-labelledby": id,
2126
+ onKeyDown: (event) => {
2127
+ if (event.key === "Escape" && !busy) {
2128
+ event.stopPropagation();
2129
+ dismiss();
2130
+ }
2131
+ }
2132
+ },
2133
+ /* @__PURE__ */ import_react22.default.createElement("h4", { id }, copy.deleteAllConfirm),
2134
+ /* @__PURE__ */ import_react22.default.createElement("p", null, fill(copy.deleteAllPreview, {
2135
+ count: preview.ids.length,
2136
+ stars: preview.stars,
2137
+ busy: preview.busy
2138
+ })),
2139
+ /* @__PURE__ */ import_react22.default.createElement("p", null, copy.storageAudio, ":", " ", preview.unknown ? fill(copy.storagePartial, {
2140
+ size: formatBytes(preview.bytes, locale),
2141
+ count: preview.unknown
2142
+ }) : formatBytes(preview.bytes, locale)),
2143
+ /* @__PURE__ */ import_react22.default.createElement("p", null, copy.deleteAllWarning),
2144
+ /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react22.default.createElement("button", { ref: cancel, type: "button", disabled: busy, onClick: dismiss }, copy.cancel), /* @__PURE__ */ import_react22.default.createElement(
2145
+ "button",
2146
+ {
2147
+ type: "button",
2148
+ className: "dlvd-delete-all",
2149
+ disabled: busy || loading || failed,
2150
+ onClick: () => void remove()
2151
+ },
2152
+ copy.deleteAllConfirmAction
2153
+ )),
2154
+ busy && /* @__PURE__ */ import_react22.default.createElement("p", { role: "status" }, fill(copy.deleteAllProgress, { done: progress, count: preview.ids.length }))
2155
+ ), /* @__PURE__ */ import_react22.default.createElement(
2156
+ "p",
2157
+ {
2158
+ ref: status,
2159
+ tabIndex: -1,
2160
+ className: "dlvd-storage-result",
2161
+ role: result?.failed ? "alert" : "status"
2162
+ },
2163
+ result && fill(copy.deleteAllResult, result)
2164
+ ));
2165
+ }
2166
+
2167
+ // src/EventTimeline.tsx
1513
2168
  var import_react23 = __toESM(require("react"), 1);
1514
2169
 
2170
+ // src/eventTimelineModel.ts
2171
+ var eventCategories = ["waiting", "playback", "capture", "command", "attention"];
2172
+ function eventCategory(kind) {
2173
+ if (kind === "voice-command-recognized") return "command";
2174
+ if (kind.startsWith("assistant-wait")) return "waiting";
2175
+ if (kind.includes("error") || kind.includes("failed") || kind.includes("cancel") || kind.includes("interruption"))
2176
+ return "attention";
2177
+ if (kind.startsWith("capture-") || kind === "input-stop-complete") return "capture";
2178
+ return "playback";
2179
+ }
2180
+ function groupTimelineEvents(events, start, end, width) {
2181
+ const span = Math.max(1, end - start);
2182
+ const slots = Math.max(1, Math.floor(Math.max(1, width) / 36));
2183
+ const groups = /* @__PURE__ */ new Map();
2184
+ for (const event of events) {
2185
+ if (event.atSample < start || event.atSample > end) continue;
2186
+ const category = eventCategory(event.kind);
2187
+ const fraction = (event.atSample - start) / span;
2188
+ const slot = Math.min(slots - 1, Math.floor(fraction * slots));
2189
+ const key = category + ":" + slot;
2190
+ const group = groups.get(key);
2191
+ if (group) group.events.push(event);
2192
+ else groups.set(key, { key, category, fraction, events: [event] });
2193
+ }
2194
+ return [...groups.values()].map((group) => ({
2195
+ ...group,
2196
+ events: [...group.events].sort(
2197
+ (a, b) => a.occurredAt.localeCompare(b.occurredAt) || a.id.localeCompare(b.id)
2198
+ ),
2199
+ // Center each occupied bucket; avoids overlapping buttons and clipping at either boundary.
2200
+ fraction: (Number(group.key.split(":")[1]) + 0.5) / slots
2201
+ })).sort(
2202
+ (a, b) => eventCategories.indexOf(a.category) - eventCategories.indexOf(b.category) || a.fraction - b.fraction
2203
+ );
2204
+ }
2205
+
2206
+ // src/EventTimeline.tsx
2207
+ 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";
2209
+ return /* @__PURE__ */ import_react23.default.createElement(
2210
+ "svg",
2211
+ {
2212
+ width: "16",
2213
+ height: "16",
2214
+ viewBox: "0 0 24 24",
2215
+ fill: "none",
2216
+ stroke: "currentColor",
2217
+ strokeWidth: "1.8",
2218
+ strokeLinecap: "round",
2219
+ strokeLinejoin: "round",
2220
+ "aria-hidden": "true"
2221
+ },
2222
+ /* @__PURE__ */ import_react23.default.createElement("path", { d: path })
2223
+ );
2224
+ }
2225
+ function EventTimeline({
2226
+ events,
2227
+ start,
2228
+ end,
2229
+ copy,
2230
+ format,
2231
+ name,
2232
+ details,
2233
+ onSelect
2234
+ }) {
2235
+ const root = import_react23.default.useRef(null);
2236
+ const lanes = import_react23.default.useRef(null);
2237
+ const bubble = import_react23.default.useRef(null);
2238
+ const [width, setWidth] = import_react23.default.useState(720);
2239
+ const [active, setActive] = import_react23.default.useState(
2240
+ null
2241
+ );
2242
+ const tooltipId = import_react23.default.useId();
2243
+ const groups = import_react23.default.useMemo(
2244
+ () => groupTimelineEvents(events, start, end, width),
2245
+ [events, start, end, width]
2246
+ );
2247
+ const group = groups.find((item) => item.key === active?.key);
2248
+ const labels = {
2249
+ waiting: copy.eventWaiting,
2250
+ playback: copy.eventPlayback,
2251
+ capture: copy.eventCapture,
2252
+ command: copy.eventCommand,
2253
+ attention: copy.eventAttention
2254
+ };
2255
+ const timestamp = (event) => new Date(event.occurredAt).toLocaleString() + "." + new Date(event.occurredAt).getMilliseconds().toString().padStart(3, "0");
2256
+ const describe = (item) => item.events.length === 1 ? name(item.events[0].kind) + " \xB7 " + timestamp(item.events[0]) : labels[item.category] + " \xB7 " + copy.eventCount.replace("{count}", String(item.events.length));
2257
+ import_react23.default.useLayoutEffect(() => {
2258
+ const node = lanes.current;
2259
+ const win = node?.ownerDocument.defaultView;
2260
+ if (!node || !win) return;
2261
+ const update = () => setWidth(node.getBoundingClientRect().width || 720);
2262
+ update();
2263
+ const observer = win.ResizeObserver ? new win.ResizeObserver(update) : null;
2264
+ observer?.observe(node);
2265
+ win.addEventListener("resize", update);
2266
+ return () => {
2267
+ observer?.disconnect();
2268
+ win.removeEventListener("resize", update);
2269
+ };
2270
+ }, []);
2271
+ import_react23.default.useEffect(() => setActive(null), [start, end]);
2272
+ import_react23.default.useLayoutEffect(() => {
2273
+ const node = bubble.current;
2274
+ const anchor = active?.anchor;
2275
+ const doc = root.current?.ownerDocument;
2276
+ const win = doc?.defaultView;
2277
+ if (!node || !anchor || !group || !win) return;
2278
+ node.showPopover?.();
2279
+ const position = () => {
2280
+ const rect = anchor.getBoundingClientRect();
2281
+ const size = node.getBoundingClientRect();
2282
+ node.style.left = Math.max(
2283
+ 8,
2284
+ Math.min(rect.left + rect.width / 2 - size.width / 2, win.innerWidth - size.width - 8)
2285
+ ) + "px";
2286
+ const top = rect.top - size.height - 8;
2287
+ node.style.top = Math.max(8, top >= 8 ? top : Math.min(rect.bottom + 8, win.innerHeight - size.height - 8)) + "px";
2288
+ };
2289
+ position();
2290
+ const close = () => setActive(null);
2291
+ const key = (event) => {
2292
+ if (event.key === "Escape") {
2293
+ event.stopPropagation();
2294
+ close();
2295
+ }
2296
+ };
2297
+ win.addEventListener("scroll", position, true);
2298
+ win.addEventListener("resize", close);
2299
+ doc.addEventListener("keydown", key, true);
2300
+ doc.addEventListener("pointerdown", close, true);
2301
+ win.addEventListener("pagehide", close);
2302
+ return () => {
2303
+ node.hidePopover?.();
2304
+ win.removeEventListener("scroll", position, true);
2305
+ win.removeEventListener("resize", close);
2306
+ doc.removeEventListener("keydown", key, true);
2307
+ doc.removeEventListener("pointerdown", close, true);
2308
+ win.removeEventListener("pagehide", close);
2309
+ };
2310
+ }, [active, group, copy]);
2311
+ 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(
2313
+ EventGlyph,
2314
+ {
2315
+ kind: category === "waiting" ? "wait" : category === "capture" ? "capture-start" : category === "attention" ? "error" : category === "command" ? "voice-command" : "assistant-start"
2316
+ }
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(
2318
+ "div",
2319
+ {
2320
+ className: "dlvd-event-lane",
2321
+ key: category,
2322
+ "data-category": category,
2323
+ role: "group",
2324
+ "aria-label": labels[category]
2325
+ },
2326
+ groups.filter((item) => item.category === category).map((item) => /* @__PURE__ */ import_react23.default.createElement(
2327
+ "button",
2328
+ {
2329
+ type: "button",
2330
+ key: item.key,
2331
+ className: "dlvd-event-marker",
2332
+ "data-category": category,
2333
+ "data-event": item.events.length === 1 ? item.events[0].kind : "group",
2334
+ "data-count": item.events.length,
2335
+ style: { left: item.fraction * 100 + "%" },
2336
+ "aria-label": describe(item) + " \xB7 " + copy.eventInspect,
2337
+ "aria-describedby": active?.key === item.key ? tooltipId : void 0,
2338
+ onMouseEnter: (event) => show(item, event.currentTarget),
2339
+ onMouseLeave: () => setActive(null),
2340
+ onFocus: (event) => show(item, event.currentTarget),
2341
+ onBlur: () => setActive(null),
2342
+ onClick: () => {
2343
+ setActive(null);
2344
+ onSelect(item.events);
2345
+ }
2346
+ },
2347
+ /* @__PURE__ */ import_react23.default.createElement(EventGlyph, { kind: item.events[0].kind }),
2348
+ item.events.length > 1 && /* @__PURE__ */ import_react23.default.createElement("span", { className: "dlvd-event-count" }, item.events.length)
2349
+ ))
2350
+ ))), group && root.current && /* @__PURE__ */ import_react23.default.createElement(
2351
+ "div",
2352
+ {
2353
+ ref: bubble,
2354
+ id: tooltipId,
2355
+ role: "tooltip",
2356
+ popover: "manual",
2357
+ className: "dlvd-event-tooltip",
2358
+ style: { pointerEvents: "none" },
2359
+ "data-category": group.category
2360
+ },
2361
+ /* @__PURE__ */ import_react23.default.createElement("strong", null, labels[group.category], " \xB7", " ", copy.eventCount.replace("{count}", String(group.events.length))),
2362
+ /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-event-tooltip-list" }, group.events.slice(0, 5).map((event) => /* @__PURE__ */ import_react23.default.createElement("div", { key: event.id }, /* @__PURE__ */ import_react23.default.createElement("b", null, name(event.kind)), /* @__PURE__ */ import_react23.default.createElement("time", { dateTime: event.occurredAt }, timestamp(event)), /* @__PURE__ */ import_react23.default.createElement("span", null, copy.captured, ": ", format(event.atSample)), details(event) && /* @__PURE__ */ import_react23.default.createElement("span", null, details(event))))),
2363
+ group.events.length > 5 && /* @__PURE__ */ import_react23.default.createElement("div", null, copy.eventMore.replace("{count}", String(group.events.length - 5))),
2364
+ /* @__PURE__ */ import_react23.default.createElement("footer", null, copy.eventInspect)
2365
+ ));
2366
+ }
2367
+
1515
2368
  // src/TimelineWindow.tsx
1516
- var import_react22 = __toESM(require("react"), 1);
2369
+ var import_react24 = __toESM(require("react"), 1);
1517
2370
  function TimelineWindow({ total, start, end, copy, format, onChange, onReset }) {
1518
- const track = import_react22.default.useRef(null);
1519
- const drag = import_react22.default.useRef(null);
2371
+ const track = import_react24.default.useRef(null);
2372
+ const drag = import_react24.default.useRef(null);
1520
2373
  const stopDrag = () => {
1521
2374
  const current = drag.current;
1522
2375
  drag.current = null;
1523
2376
  if (current?.element.hasPointerCapture(current.pointer))
1524
2377
  current.element.releasePointerCapture(current.pointer);
1525
2378
  };
1526
- import_react22.default.useEffect(() => {
2379
+ import_react24.default.useEffect(() => {
1527
2380
  if (total <= 0) stopDrag();
1528
2381
  }, [total]);
1529
- import_react22.default.useEffect(() => stopDrag, []);
2382
+ import_react24.default.useEffect(() => stopDrag, []);
1530
2383
  const change = (part, delta, initialStart = start, initialEnd = end) => {
1531
2384
  if (total <= 0) return;
1532
2385
  if (part === "start")
@@ -1601,7 +2454,7 @@ function TimelineWindow({ total, start, end, copy, format, onChange, onReset })
1601
2454
  onKeyDown: (event) => keyboard(event, part)
1602
2455
  });
1603
2456
  const percent = (value) => total > 0 ? value / total * 100 : 0;
1604
- return /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-timeline-window", role: "group", "aria-label": copy.window }, /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-window-selector" }, /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-window-values" }, /* @__PURE__ */ import_react22.default.createElement("span", null, copy.windowStart, ": ", format(start)), /* @__PURE__ */ import_react22.default.createElement("span", null, copy.windowEnd, ": ", format(end))), /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-window-track", ref: track }, /* @__PURE__ */ import_react22.default.createElement(
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(
1605
2458
  "button",
1606
2459
  {
1607
2460
  type: "button",
@@ -1617,7 +2470,7 @@ function TimelineWindow({ total, start, end, copy, format, onChange, onReset })
1617
2470
  style: { left: percent(start) + "%", width: percent(end - start) + "%" },
1618
2471
  ...handlers("range")
1619
2472
  }
1620
- ), /* @__PURE__ */ import_react22.default.createElement(
2473
+ ), /* @__PURE__ */ import_react24.default.createElement(
1621
2474
  "button",
1622
2475
  {
1623
2476
  type: "button",
@@ -1634,7 +2487,7 @@ function TimelineWindow({ total, start, end, copy, format, onChange, onReset })
1634
2487
  style: { left: percent(start) + "%" },
1635
2488
  ...handlers("start")
1636
2489
  }
1637
- ), /* @__PURE__ */ import_react22.default.createElement(
2490
+ ), /* @__PURE__ */ import_react24.default.createElement(
1638
2491
  "button",
1639
2492
  {
1640
2493
  type: "button",
@@ -1651,7 +2504,7 @@ function TimelineWindow({ total, start, end, copy, format, onChange, onReset })
1651
2504
  style: { left: percent(end) + "%" },
1652
2505
  ...handlers("end")
1653
2506
  }
1654
- ))), /* @__PURE__ */ import_react22.default.createElement(
2507
+ ))), /* @__PURE__ */ import_react24.default.createElement(
1655
2508
  "button",
1656
2509
  {
1657
2510
  type: "button",
@@ -1660,7 +2513,7 @@ function TimelineWindow({ total, start, end, copy, format, onChange, onReset })
1660
2513
  "aria-label": copy.windowReset,
1661
2514
  onClick: onReset
1662
2515
  },
1663
- /* @__PURE__ */ import_react22.default.createElement(
2516
+ /* @__PURE__ */ import_react24.default.createElement(
1664
2517
  "svg",
1665
2518
  {
1666
2519
  width: "18",
@@ -1674,7 +2527,7 @@ function TimelineWindow({ total, start, end, copy, format, onChange, onReset })
1674
2527
  "aria-hidden": "true",
1675
2528
  focusable: "false"
1676
2529
  },
1677
- /* @__PURE__ */ import_react22.default.createElement("path", { d: "M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5M8 8l-5-5m13 5 5-5M8 16l-5 5m13-5 5 5" })
2530
+ /* @__PURE__ */ import_react24.default.createElement("path", { d: "M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5M8 8l-5-5m13 5 5-5M8 16l-5 5m13-5 5 5" })
1678
2531
  )
1679
2532
  ));
1680
2533
  }
@@ -1841,14 +2694,31 @@ function createRecordingInspector(target, changed) {
1841
2694
  }
1842
2695
 
1843
2696
  // src/RecordingPanel.tsx
1844
- var megabytes = (bytes) => (bytes / 1e6).toFixed(3) + " MB";
2697
+ function SegmentIcon({ kind }) {
2698
+ return /* @__PURE__ */ import_react25.default.createElement(
2699
+ "svg",
2700
+ {
2701
+ width: "18",
2702
+ height: "18",
2703
+ viewBox: "0 0 24 24",
2704
+ fill: "none",
2705
+ stroke: "currentColor",
2706
+ strokeWidth: "1.8",
2707
+ strokeLinecap: "round",
2708
+ strokeLinejoin: "round",
2709
+ "aria-hidden": "true",
2710
+ focusable: "false"
2711
+ },
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" })
2713
+ );
2714
+ }
1845
2715
  function duration(samples, rate) {
1846
2716
  const milliseconds = Math.max(0, Math.round(samples / Math.max(1, rate) * 1e3));
1847
2717
  const seconds = Math.floor(milliseconds / 1e3);
1848
2718
  return [Math.floor(seconds / 3600), Math.floor(seconds / 60) % 60, seconds % 60].map((n) => String(n).padStart(2, "0")).join(":") + "." + String(milliseconds % 1e3).padStart(3, "0");
1849
2719
  }
1850
2720
  var sourceKey = (session) => JSON.stringify([session.kind, session.sourceId]);
1851
- var sourceName = (session, copy) => (session.kind === "microphone" ? copy.recordings.microphone : copy.recordings.shared) + " \xB7 " + (session.label || session.sourceId) + " \xB7 " + session.sourceId;
2721
+ var sourceName = (session, copy) => (session.participant?.provider === "attendee" ? copy.recordings.participant : session.kind === "microphone" ? copy.recordings.microphone : copy.recordings.shared) + " \xB7 " + (session.label || session.sourceId);
1852
2722
  function stateName(session, copy) {
1853
2723
  return {
1854
2724
  recording: copy.recordings.active,
@@ -1857,8 +2727,12 @@ function stateName(session, copy) {
1857
2727
  failed: copy.recordings.failed
1858
2728
  }[session.state];
1859
2729
  }
1860
- function RecordingPanel({ copy, target }) {
1861
- const [view, setView] = import_react23.default.useState({
2730
+ function RecordingPanel({
2731
+ copy,
2732
+ target,
2733
+ locale = "en"
2734
+ }) {
2735
+ const [view, setView] = import_react25.default.useState({
1862
2736
  service: null,
1863
2737
  sessions: [],
1864
2738
  selected: null,
@@ -1866,9 +2740,10 @@ function RecordingPanel({ copy, target }) {
1866
2740
  loading: true,
1867
2741
  failed: false
1868
2742
  });
1869
- const inspector = import_react23.default.useRef(null);
1870
- const [filter, setFilter] = import_react23.default.useState("");
1871
- import_react23.default.useEffect(() => {
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(() => {
1872
2747
  const current = createRecordingInspector(target, setView);
1873
2748
  inspector.current = current;
1874
2749
  current.discover();
@@ -1885,12 +2760,16 @@ function RecordingPanel({ copy, target }) {
1885
2760
  ...new Map(view.sessions.map((session) => [sourceKey(session), session])).values()
1886
2761
  ];
1887
2762
  const visible = view.sessions.filter((session) => !filter || sourceKey(session) === filter).slice().sort((a, b) => b.startedAt.localeCompare(a.startedAt));
1888
- import_react23.default.useEffect(() => {
2763
+ import_react25.default.useEffect(() => {
2764
+ if (!view.loading && !view.failed && filter && !view.sessions.some((session) => sourceKey(session) === filter))
2765
+ setFilter("");
2766
+ }, [filter, view.sessions, view.loading, view.failed]);
2767
+ import_react25.default.useEffect(() => {
1889
2768
  if (!view.loading && filter && visible.length && !visible.some((s) => s.id === view.selected?.id))
1890
2769
  inspector.current?.select(visible[0].id);
1891
2770
  }, [filter, view.selected?.id, view.loading, visible[0]?.id]);
1892
2771
  const selected = view.selected && visible.some((s) => s.id === view.selected?.id) ? view.selected : null;
1893
- return /* @__PURE__ */ import_react23.default.createElement("section", { className: "dlvd-recordings", "aria-label": copy.recordings.title }, /* @__PURE__ */ import_react23.default.createElement("p", null, copy.recordings.help), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react23.default.createElement("label", null, copy.recordings.sourceFilter, " ", /* @__PURE__ */ import_react23.default.createElement("select", { value: filter, onChange: (event) => setFilter(event.target.value) }, /* @__PURE__ */ import_react23.default.createElement("option", { value: "" }, copy.recordings.allSources), sources.map((session) => /* @__PURE__ */ import_react23.default.createElement("option", { key: sourceKey(session), value: sourceKey(session) }, sourceName(session, copy))))), /* @__PURE__ */ import_react23.default.createElement(
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(
1894
2773
  "button",
1895
2774
  {
1896
2775
  type: "button",
@@ -1900,22 +2779,34 @@ function RecordingPanel({ copy, target }) {
1900
2779
  }
1901
2780
  },
1902
2781
  copy.recordings.refresh
1903
- )), view.failed && /* @__PURE__ */ import_react23.default.createElement("p", { role: "alert" }, copy.recordings.loadError), !view.service ? /* @__PURE__ */ import_react23.default.createElement("p", { role: "status" }, copy.recordings.unavailable) : /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, null, view.loading && /* @__PURE__ */ import_react23.default.createElement("p", { role: "status" }, copy.recordings.loading), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dlvd-audio-total" }, copy.recordings.storageTotal, ":", " ", view.sessions.every((s) => s.audioBytes !== void 0) ? megabytes(view.sessions.reduce((sum, s) => sum + (s.audioBytes ?? 0), 0)) : copy.recordings.storageUnknown), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-recording-columns" }, /* @__PURE__ */ import_react23.default.createElement(
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(
2783
+ RecordingStorage,
2784
+ {
2785
+ locale,
2786
+ service: view.service,
2787
+ sessions: view.sessions,
2788
+ loading: view.loading,
2789
+ failed: view.failed,
2790
+ copy: copy.recordings,
2791
+ refresh: () => inspector.current?.refresh(),
2792
+ onBusy: setClearing
2793
+ }
2794
+ ), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-recording-columns" }, /* @__PURE__ */ import_react25.default.createElement(
1904
2795
  "div",
1905
2796
  {
1906
2797
  className: "dlvd-session-list dlvd-scroll",
1907
2798
  tabIndex: 0,
1908
2799
  "aria-label": copy.recordings.title
1909
2800
  },
1910
- !visible.length && !view.loading && /* @__PURE__ */ import_react23.default.createElement("p", null, copy.recordings.empty),
1911
- sources.filter((source) => !filter || sourceKey(source) === filter).map((source) => /* @__PURE__ */ import_react23.default.createElement("section", { key: sourceKey(source), "aria-label": sourceName(source, copy) }, /* @__PURE__ */ import_react23.default.createElement("h3", null, sourceName(source, copy)), /* @__PURE__ */ import_react23.default.createElement("table", null, /* @__PURE__ */ import_react23.default.createElement("thead", null, /* @__PURE__ */ import_react23.default.createElement("tr", null, /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.date), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.duration), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.saved))), /* @__PURE__ */ import_react23.default.createElement("tbody", null, visible.filter((session) => sourceKey(session) === sourceKey(source)).map((session) => /* @__PURE__ */ import_react23.default.createElement("tr", { key: session.id, "data-selected": session.id === selected?.id }, /* @__PURE__ */ import_react23.default.createElement("td", null, /* @__PURE__ */ import_react23.default.createElement(
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(
1912
2803
  "button",
1913
2804
  {
1914
2805
  type: "button",
1915
2806
  "aria-pressed": session.id === selected?.id,
1916
2807
  onClick: () => inspector.current?.select(session.id)
1917
2808
  },
1918
- session.starred && /* @__PURE__ */ import_react23.default.createElement(
2809
+ session.starred && /* @__PURE__ */ import_react25.default.createElement(
1919
2810
  "span",
1920
2811
  {
1921
2812
  role: "img",
@@ -1925,12 +2816,15 @@ function RecordingPanel({ copy, target }) {
1925
2816
  "\u2605"
1926
2817
  ),
1927
2818
  " ",
1928
- session.name && /* @__PURE__ */ import_react23.default.createElement("strong", null, session.name, /* @__PURE__ */ import_react23.default.createElement("br", null)),
1929
- /* @__PURE__ */ import_react23.default.createElement("time", { dateTime: session.startedAt }, new Date(session.startedAt).toLocaleString())
1930
- )), /* @__PURE__ */ import_react23.default.createElement("td", null, stateName(session, copy)), /* @__PURE__ */ import_react23.default.createElement("td", null, duration(session.capturedSamples, session.sampleRate)), /* @__PURE__ */ import_react23.default.createElement("td", null, duration(session.savedSamples, session.sampleRate), /* @__PURE__ */ import_react23.default.createElement("br", null), session.storage === "indexeddb" || session.persistent ? copy.recordings.persistent : copy.recordings.memory)))))))
1931
- ), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-recording-detail dlvd-scroll", tabIndex: 0 }, selected ? /* @__PURE__ */ import_react23.default.createElement(
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(
1932
2825
  RecordingDetail,
1933
2826
  {
2827
+ locale,
1934
2828
  key: selected.id,
1935
2829
  copy,
1936
2830
  session: selected,
@@ -1938,33 +2832,38 @@ function RecordingPanel({ copy, target }) {
1938
2832
  segments: view.segments,
1939
2833
  refresh: () => inspector.current?.refresh()
1940
2834
  }
1941
- ) : /* @__PURE__ */ import_react23.default.createElement("p", null, copy.recordings.selectSession)))));
2835
+ ) : /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.selectSession)))));
1942
2836
  }
1943
2837
  function RecordingDetail({
2838
+ locale,
1944
2839
  copy,
1945
2840
  session,
1946
2841
  service,
1947
2842
  segments,
1948
2843
  refresh
1949
2844
  }) {
1950
- const container = import_react23.default.useRef(null);
1951
- const audio = import_react23.default.useRef(null);
1952
- const playback = import_react23.default.useRef(null);
1953
- const download = import_react23.default.useRef(null);
1954
- const generation = import_react23.default.useRef(0);
1955
- const playGeneration = import_react23.default.useRef(0);
1956
- const [error, setError] = import_react23.default.useState(null);
1957
- const [playing, setPlaying] = import_react23.default.useState(false);
1958
- const [exporting, setExporting] = import_react23.default.useState(false);
1959
- const [deleting, setDeleting] = import_react23.default.useState(false);
1960
- const [starring, setStarring] = import_react23.default.useState(false);
1961
- const [confirmDelete, setConfirmDelete] = import_react23.default.useState(false);
1962
- const [name, setName] = import_react23.default.useState(session.name ?? "");
1963
- const [naming, setNaming] = import_react23.default.useState(false);
1964
- import_react23.default.useEffect(() => {
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(() => {
1965
2863
  setName(session.name ?? "");
1966
2864
  }, [session.id, session.name, service]);
1967
- const [selectedSegment, setSelectedSegment] = import_react23.default.useState("");
2865
+ const [selectedSegment, setSelectedSegment] = import_react25.default.useState("");
2866
+ const [selectedEvents, setSelectedEvents] = import_react25.default.useState([]);
1968
2867
  const clearPlayback = (player = audio.current) => {
1969
2868
  ++playGeneration.current;
1970
2869
  if (player?.hasAttribute("src")) {
@@ -1979,12 +2878,13 @@ function RecordingDetail({
1979
2878
  if (download.current) download.current.api.revokeObjectURL(download.current.url);
1980
2879
  download.current = null;
1981
2880
  };
1982
- import_react23.default.useEffect(() => {
2881
+ import_react25.default.useEffect(() => {
1983
2882
  setStarring(false);
1984
2883
  const owner = container.current?.ownerDocument.defaultView;
1985
2884
  const player = audio.current;
1986
2885
  const release = () => {
1987
2886
  ++generation.current;
2887
+ retry.current?.abort();
1988
2888
  clearPlayback(player);
1989
2889
  clearDownload();
1990
2890
  };
@@ -1994,7 +2894,7 @@ function RecordingDetail({
1994
2894
  release();
1995
2895
  };
1996
2896
  }, [service, session.id]);
1997
- const play = async (segment) => {
2897
+ const play = async (segment, autoplay = true) => {
1998
2898
  clearPlayback();
1999
2899
  const token = generation.current;
2000
2900
  const request = playGeneration.current;
@@ -2010,7 +2910,8 @@ function RecordingDetail({
2010
2910
  const url = api.createObjectURL(blob);
2011
2911
  playback.current = { url, api };
2012
2912
  player.src = url;
2013
- await player.play();
2913
+ player.load();
2914
+ if (autoplay) await player.play();
2014
2915
  } catch {
2015
2916
  if (token === generation.current && request === playGeneration.current) {
2016
2917
  clearPlayback();
@@ -2021,6 +2922,61 @@ function RecordingDetail({
2021
2922
  if (token === generation.current && request === playGeneration.current) setPlaying(false);
2022
2923
  }
2023
2924
  };
2925
+ const saveBlob = (blob, filename) => {
2926
+ clearDownload();
2927
+ const doc = container.current?.ownerDocument;
2928
+ const api = doc?.defaultView?.URL;
2929
+ if (!doc || !api) return;
2930
+ const url = api.createObjectURL(blob);
2931
+ download.current = { url, api };
2932
+ const link = doc.createElement("a");
2933
+ link.href = url;
2934
+ link.download = filename;
2935
+ doc.body.appendChild(link);
2936
+ try {
2937
+ link.click();
2938
+ } finally {
2939
+ link.remove();
2940
+ }
2941
+ };
2942
+ const downloadSegment = async (segment) => {
2943
+ if (downloading || exporting) return;
2944
+ const token = generation.current;
2945
+ setDownloading(true);
2946
+ setError(null);
2947
+ try {
2948
+ const blob = await service.audio(session.id, segment.startSample, segment.endSample);
2949
+ if (token !== generation.current) return;
2950
+ saveBlob(
2951
+ blob,
2952
+ "recording-" + session.id.replace(/[^a-zA-Z0-9_-]/g, "_") + "-" + segment.startSample + "-" + segment.endSample + ".wav"
2953
+ );
2954
+ } catch {
2955
+ if (token === generation.current) setError("downloadError");
2956
+ } finally {
2957
+ if (token === generation.current) setDownloading(false);
2958
+ }
2959
+ };
2960
+ const retranscribe = async (segment) => {
2961
+ if (!service.retranscribe || retry.current) return;
2962
+ const controller = new AbortController();
2963
+ retry.current = controller;
2964
+ const token = generation.current;
2965
+ setRetrying(segment.id);
2966
+ setSelectedSegment(segment.id);
2967
+ setError(null);
2968
+ try {
2969
+ await service.retranscribe(session.id, segment.id, controller.signal);
2970
+ } catch {
2971
+ if (token === generation.current) setError("retranscribeError");
2972
+ } finally {
2973
+ if (retry.current === controller) retry.current = null;
2974
+ if (token === generation.current) {
2975
+ setRetrying("");
2976
+ refresh();
2977
+ }
2978
+ }
2979
+ };
2024
2980
  const exportSession = async () => {
2025
2981
  const token = generation.current;
2026
2982
  setExporting(true);
@@ -2029,20 +2985,7 @@ function RecordingDetail({
2029
2985
  try {
2030
2986
  const blob = await service.export(session.id);
2031
2987
  if (token !== generation.current) return;
2032
- const doc = container.current?.ownerDocument;
2033
- const api = doc?.defaultView?.URL;
2034
- if (!doc || !api) return;
2035
- const url = api.createObjectURL(blob);
2036
- download.current = { url, api };
2037
- const link = doc.createElement("a");
2038
- link.href = url;
2039
- link.download = "recording-" + session.id.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json";
2040
- doc.body.appendChild(link);
2041
- try {
2042
- link.click();
2043
- } finally {
2044
- link.remove();
2045
- }
2988
+ saveBlob(blob, "recording-" + session.id.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
2046
2989
  } catch {
2047
2990
  if (token === generation.current) {
2048
2991
  clearDownload();
@@ -2080,6 +3023,15 @@ function RecordingDetail({
2080
3023
  }
2081
3024
  };
2082
3025
  const eventName = (kind) => ({
3026
+ "voice-command-recognized": copy.recordings.voiceCommandRecognized,
3027
+ "assistant-wait-start": copy.recordings.assistantWaitStart,
3028
+ "assistant-wait-end": copy.recordings.assistantWaitEnd,
3029
+ "assistant-play-request": copy.recordings.assistantPlayRequest,
3030
+ "assistant-interruption-request": copy.recordings.assistantInterruptionRequest,
3031
+ "assistant-cancel": copy.recordings.assistantCancel,
3032
+ "assistant-error": copy.recordings.assistantError,
3033
+ "input-stop-complete": copy.recordings.inputStopComplete,
3034
+ "capture-gate-failed": copy.recordings.captureGateFailed,
2083
3035
  "assistant-start": copy.recordings.assistantStart,
2084
3036
  "assistant-end": copy.recordings.assistantEnd,
2085
3037
  "assistant-pause": copy.recordings.assistantPause,
@@ -2088,6 +3040,46 @@ function RecordingDetail({
2088
3040
  "capture-start": copy.recordings.captureStart,
2089
3041
  "capture-end": copy.recordings.captureEnd
2090
3042
  })[kind] ?? kind;
3043
+ const commandName = (command) => ({
3044
+ send: copy.recordings.commandSend,
3045
+ queue: copy.recordings.commandQueue,
3046
+ end: copy.recordings.commandEnd,
3047
+ mute: copy.recordings.commandMute,
3048
+ resume: copy.recordings.commandResume,
3049
+ stopSpeaking: copy.recordings.commandStopSpeaking,
3050
+ clear: copy.recordings.commandClear
3051
+ })[command] ?? command;
3052
+ const commandsFor = (segment) => (session.events ?? []).filter(
3053
+ (event) => event.kind === "voice-command-recognized" && event.details?.segmentId === segment.id
3054
+ );
3055
+ const eventDetails = (event) => {
3056
+ const details = event.details;
3057
+ if (!details) return "";
3058
+ const reasons = {
3059
+ "user-speech": copy.recordings.waitUserSpeech,
3060
+ "external-speech": copy.recordings.waitExternalSpeech,
3061
+ transcription: copy.recordings.waitTranscription,
3062
+ delivery: copy.recordings.waitDelivery,
3063
+ "input-starting": copy.recordings.waitInputStarting,
3064
+ "silence-delay": copy.recordings.waitSilence,
3065
+ "segment-gap": copy.recordings.waitSegmentGap,
3066
+ paused: copy.recordings.waitPaused,
3067
+ suspended: copy.recordings.waitSuspended
3068
+ };
3069
+ return [
3070
+ details.command && commandName(details.command),
3071
+ ...(details.reasons ?? []).map((reason) => reasons[reason] ?? reason),
3072
+ details.outcome && {
3073
+ ready: copy.recordings.waitReady,
3074
+ changed: copy.recordings.waitChanged,
3075
+ cancelled: copy.recordings.waitCancelled
3076
+ }[details.outcome],
3077
+ details.queuedSegments !== void 0 && copy.recordings.queuedSegments + ": " + details.queuedSegments,
3078
+ details.pendingTranscriptions !== void 0 && copy.recordings.waitTranscription + ": " + details.pendingTranscriptions,
3079
+ details.delayMs !== void 0 && copy.recordings.waitDelay + ": " + details.delayMs + " ms",
3080
+ details.automatic !== void 0 && (details.automatic ? copy.recordings.automaticAction : copy.recordings.manualAction)
3081
+ ].filter(Boolean).join(" \xB7 ");
3082
+ };
2091
3083
  const removeSession = async () => {
2092
3084
  const token = ++generation.current;
2093
3085
  setExporting(false);
@@ -2108,13 +3100,13 @@ function RecordingDetail({
2108
3100
  if (token === generation.current) setDeleting(false);
2109
3101
  }
2110
3102
  };
2111
- const [search, setSearch] = import_react23.default.useState("");
2112
- import_react23.default.useEffect(() => setSearch(""), [session.id, service]);
2113
- const matchedSegments = import_react23.default.useMemo(() => searchSegments(segments, search), [segments, search]);
3103
+ const [search, setSearch] = import_react25.default.useState("");
3104
+ import_react25.default.useEffect(() => setSearch(""), [session.id, service]);
3105
+ const matchedSegments = import_react25.default.useMemo(() => searchSegments(segments, search), [segments, search]);
2114
3106
  const length = Math.max(1, session.savedSamples);
2115
- const [windowStart, setWindowStart] = import_react23.default.useState(0);
2116
- const [windowEnd, setWindowEnd] = import_react23.default.useState(null);
2117
- import_react23.default.useEffect(() => {
3107
+ const [windowStart, setWindowStart] = import_react25.default.useState(0);
3108
+ const [windowEnd, setWindowEnd] = import_react25.default.useState(null);
3109
+ import_react25.default.useEffect(() => {
2118
3110
  setWindowStart(0);
2119
3111
  setWindowEnd(null);
2120
3112
  }, [session.id, service]);
@@ -2123,8 +3115,8 @@ function RecordingDetail({
2123
3115
  const viewLength = viewEnd - viewStart;
2124
3116
  const inWindow = (sample) => sample >= viewStart && sample <= viewEnd;
2125
3117
  const position = (sample) => (sample - viewStart) / viewLength * 100 + "%";
2126
- const segmentName = (segment) => (segment.kind === "speech" ? copy.recordings.speech : copy.recordings.silence) + " \xB7 " + duration(segment.startSample, session.sampleRate) + " \u2014 " + duration(segment.endSample, session.sampleRate);
2127
- return /* @__PURE__ */ import_react23.default.createElement("section", { ref: container, "aria-label": sourceName(session, copy) }, /* @__PURE__ */ import_react23.default.createElement("h3", null, session.name || sourceName(session, copy)), session.starred && service.setName && /* @__PURE__ */ import_react23.default.createElement(
3118
+ 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);
3119
+ 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(
2128
3120
  "form",
2129
3121
  {
2130
3122
  className: "dlvd-toolbar",
@@ -2133,7 +3125,7 @@ function RecordingDetail({
2133
3125
  void saveName();
2134
3126
  }
2135
3127
  },
2136
- /* @__PURE__ */ import_react23.default.createElement("label", null, copy.recordings.name, " ", /* @__PURE__ */ import_react23.default.createElement(
3128
+ /* @__PURE__ */ import_react25.default.createElement("label", null, copy.recordings.name, " ", /* @__PURE__ */ import_react25.default.createElement(
2137
3129
  "input",
2138
3130
  {
2139
3131
  value: name,
@@ -2142,7 +3134,7 @@ function RecordingDetail({
2142
3134
  onInput: (event) => setName(event.currentTarget.value)
2143
3135
  }
2144
3136
  )),
2145
- /* @__PURE__ */ import_react23.default.createElement(
3137
+ /* @__PURE__ */ import_react25.default.createElement(
2146
3138
  "button",
2147
3139
  {
2148
3140
  type: "submit",
@@ -2150,7 +3142,7 @@ function RecordingDetail({
2150
3142
  },
2151
3143
  copy.recordings.saveName
2152
3144
  )
2153
- ), /* @__PURE__ */ import_react23.default.createElement("dl", { className: "dlvd-recording-meta" }, /* @__PURE__ */ import_react23.default.createElement("dt", null, copy.recordings.date), /* @__PURE__ */ import_react23.default.createElement("dd", null, new Date(session.startedAt).toLocaleString()), /* @__PURE__ */ import_react23.default.createElement("dt", null, copy.recordings.state), /* @__PURE__ */ import_react23.default.createElement("dd", null, stateName(session, copy)), /* @__PURE__ */ import_react23.default.createElement("dt", null, copy.recordings.storageAudio), /* @__PURE__ */ import_react23.default.createElement("dd", null, session.audioBytes === void 0 ? copy.recordings.storageUnknown : megabytes(session.audioBytes)), /* @__PURE__ */ import_react23.default.createElement("dt", null, copy.recordings.captured), /* @__PURE__ */ import_react23.default.createElement("dd", null, duration(session.capturedSamples, session.sampleRate)), /* @__PURE__ */ import_react23.default.createElement("dt", null, copy.recordings.saved), /* @__PURE__ */ import_react23.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_react23.default.createElement(import_react23.default.Fragment, null, /* @__PURE__ */ import_react23.default.createElement("dt", null, copy.recordings.end), /* @__PURE__ */ import_react23.default.createElement("dd", null, new Date(session.endedAt).toLocaleString()))), session.error && /* @__PURE__ */ import_react23.default.createElement("p", { role: "alert" }, copy.recordings.error, ": ", session.error), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react23.default.createElement(
3145
+ ), /* @__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(
2154
3146
  "button",
2155
3147
  {
2156
3148
  type: "button",
@@ -2161,7 +3153,7 @@ function RecordingDetail({
2161
3153
  onClick: () => void toggleStarred()
2162
3154
  },
2163
3155
  session.starred ? "\u2605" : "\u2606"
2164
- ), /* @__PURE__ */ import_react23.default.createElement(
3156
+ ), /* @__PURE__ */ import_react25.default.createElement(
2165
3157
  "button",
2166
3158
  {
2167
3159
  type: "button",
@@ -2169,46 +3161,54 @@ function RecordingDetail({
2169
3161
  onClick: () => void play()
2170
3162
  },
2171
3163
  copy.recordings.playSession
2172
- ), /* @__PURE__ */ import_react23.default.createElement("button", { type: "button", disabled: exporting || deleting, onClick: () => void exportSession() }, copy.recordings.export), /* @__PURE__ */ import_react23.default.createElement(
3164
+ ), /* @__PURE__ */ import_react25.default.createElement(
3165
+ "button",
3166
+ {
3167
+ type: "button",
3168
+ disabled: exporting || downloading || deleting,
3169
+ onClick: () => void exportSession()
3170
+ },
3171
+ copy.recordings.export
3172
+ ), /* @__PURE__ */ import_react25.default.createElement(
2173
3173
  "button",
2174
3174
  {
2175
3175
  type: "button",
2176
- disabled: deleting || starring || naming,
3176
+ disabled: deleting || starring || naming || !!retrying,
2177
3177
  onClick: () => setConfirmDelete(true)
2178
3178
  },
2179
3179
  copy.recordings.delete
2180
- )), confirmDelete && /* @__PURE__ */ import_react23.default.createElement(
3180
+ )), confirmDelete && /* @__PURE__ */ import_react25.default.createElement(
2181
3181
  "div",
2182
3182
  {
2183
3183
  className: "dlvd-delete-confirm",
2184
3184
  role: "group",
2185
3185
  "aria-label": copy.recordings.confirmDelete
2186
3186
  },
2187
- /* @__PURE__ */ import_react23.default.createElement("p", null, copy.recordings.confirmDelete),
2188
- /* @__PURE__ */ import_react23.default.createElement(
3187
+ /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.confirmDelete),
3188
+ /* @__PURE__ */ import_react25.default.createElement(
2189
3189
  "button",
2190
3190
  {
2191
3191
  type: "button",
2192
- disabled: deleting || starring || naming,
3192
+ disabled: deleting || starring || naming || !!retrying,
2193
3193
  onClick: () => void removeSession()
2194
3194
  },
2195
3195
  copy.recordings.delete
2196
3196
  ),
2197
3197
  " ",
2198
- /* @__PURE__ */ import_react23.default.createElement("button", { type: "button", disabled: deleting, onClick: () => setConfirmDelete(false) }, copy.recordings.cancel)
2199
- ), error && /* @__PURE__ */ import_react23.default.createElement("p", { role: "alert" }, copy.recordings[error]), /* @__PURE__ */ import_react23.default.createElement(
3198
+ /* @__PURE__ */ import_react25.default.createElement("button", { type: "button", disabled: deleting, onClick: () => setConfirmDelete(false) }, copy.recordings.cancel)
3199
+ ), 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(
2200
3200
  "audio",
2201
3201
  {
2202
3202
  ref: audio,
2203
3203
  controls: true,
2204
- preload: "none",
3204
+ preload: "metadata",
2205
3205
  "aria-label": copy.recordings.audio,
2206
3206
  onError: () => {
2207
3207
  clearPlayback();
2208
3208
  setError("audioError");
2209
3209
  }
2210
3210
  }
2211
- ), /* @__PURE__ */ import_react23.default.createElement("h4", null, copy.recordings.timeline), /* @__PURE__ */ import_react23.default.createElement(
3211
+ ), /* @__PURE__ */ import_react25.default.createElement("h4", null, copy.recordings.timeline), /* @__PURE__ */ import_react25.default.createElement(
2212
3212
  TimelineWindow,
2213
3213
  {
2214
3214
  key: session.id,
@@ -2226,12 +3226,12 @@ function RecordingDetail({
2226
3226
  setWindowEnd(null);
2227
3227
  }
2228
3228
  }
2229
- ), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-timeline", role: "group", "aria-label": copy.recordings.timeline }, segments.filter((segment) => segment.endSample > viewStart && segment.startSample < viewEnd).map((segment) => /* @__PURE__ */ import_react23.default.createElement(
3229
+ ), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-timeline", role: "group", "aria-label": copy.recordings.timeline }, segments.filter((segment) => segment.endSample > viewStart && segment.startSample < viewEnd).map((segment) => /* @__PURE__ */ import_react25.default.createElement(
2230
3230
  "button",
2231
3231
  {
2232
3232
  key: segment.id,
2233
3233
  type: "button",
2234
- "data-kind": segment.kind,
3234
+ "data-kind": commandsFor(segment).length ? "command" : segment.kind,
2235
3235
  "aria-label": segmentName(segment),
2236
3236
  title: segmentName(segment),
2237
3237
  "aria-pressed": selectedSegment === segment.id,
@@ -2239,27 +3239,10 @@ function RecordingDetail({
2239
3239
  left: position(Math.max(viewStart, segment.startSample)),
2240
3240
  width: (Math.min(viewEnd, segment.endSample) - Math.max(viewStart, segment.startSample)) / viewLength * 100 + "%"
2241
3241
  },
2242
- onClick: () => {
2243
- clearPlayback();
2244
- setPlaying(false);
2245
- setSelectedSegment(segment.id);
2246
- }
3242
+ disabled: deleting || segment.endSample <= segment.startSample,
3243
+ onClick: () => void play(segment, false)
2247
3244
  }
2248
- )), (session.events ?? []).filter((event) => inWindow(event.atSample)).map((event, index) => /* @__PURE__ */ import_react23.default.createElement(
2249
- "span",
2250
- {
2251
- key: event.id,
2252
- className: "dlvd-event-pin",
2253
- "data-event": event.kind,
2254
- style: {
2255
- left: position(event.atSample),
2256
- top: index % 3 * 12
2257
- },
2258
- title: eventName(event.kind) + " \xB7 " + new Date(event.occurredAt).toLocaleString(),
2259
- "aria-hidden": "true"
2260
- },
2261
- "\u25C6"
2262
- )), session.pins.filter((pin) => inWindow(pin.atSample)).map((pin) => /* @__PURE__ */ import_react23.default.createElement(
3245
+ )), session.pins.filter((pin) => inWindow(pin.atSample)).map((pin) => /* @__PURE__ */ import_react25.default.createElement(
2263
3246
  "span",
2264
3247
  {
2265
3248
  key: pin.id,
@@ -2268,7 +3251,37 @@ function RecordingDetail({
2268
3251
  style: { left: position(pin.atSample) },
2269
3252
  title: (pin.kind === "speech-start" ? copy.recordings.speech : copy.recordings.silence) + " \xB7 " + duration(pin.atSample, session.sampleRate)
2270
3253
  }
2271
- ))), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-timeline-legend" }, /* @__PURE__ */ import_react23.default.createElement("span", { "data-kind": "speech" }, copy.recordings.speech), " \xB7 ", /* @__PURE__ */ import_react23.default.createElement("span", { "data-kind": "silence" }, copy.recordings.silence), " \xB7 ", duration(viewStart, session.sampleRate), " \u2013", " ", duration(Math.min(viewEnd, session.savedSamples), session.sampleRate)), /* @__PURE__ */ import_react23.default.createElement("h4", null, copy.recordings.events), /* @__PURE__ */ import_react23.default.createElement("p", null, copy.recordings.eventsHelp), /* @__PURE__ */ import_react23.default.createElement("table", { className: "dlvd-recording-events" }, /* @__PURE__ */ import_react23.default.createElement("thead", null, /* @__PURE__ */ import_react23.default.createElement("tr", null, /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.eventTime), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.captured))), /* @__PURE__ */ import_react23.default.createElement("tbody", null, (session.events ?? []).map((event) => /* @__PURE__ */ import_react23.default.createElement("tr", { key: event.id }, /* @__PURE__ */ import_react23.default.createElement("td", null, eventName(event.kind)), /* @__PURE__ */ import_react23.default.createElement("td", null, /* @__PURE__ */ import_react23.default.createElement("time", { dateTime: event.occurredAt }, new Date(event.occurredAt).toLocaleString(), ".", new Date(event.occurredAt).getMilliseconds().toString().padStart(3, "0"))), /* @__PURE__ */ import_react23.default.createElement("td", null, duration(event.atSample, session.sampleRate)))))), /* @__PURE__ */ import_react23.default.createElement("h4", null, copy.recordings.segments), /* @__PURE__ */ import_react23.default.createElement("label", null, copy.recordings.search, /* @__PURE__ */ import_react23.default.createElement(
3254
+ ))), /* @__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(
3255
+ EventTimeline,
3256
+ {
3257
+ events: session.events ?? [],
3258
+ start: viewStart,
3259
+ end: viewEnd,
3260
+ copy: copy.recordings,
3261
+ format: (sample) => duration(sample, session.sampleRate),
3262
+ name: eventName,
3263
+ details: eventDetails,
3264
+ onSelect: (events) => {
3265
+ setSelectedEvents(events.map((event) => event.id));
3266
+ const row = Array.from(
3267
+ container.current?.querySelectorAll("[data-recording-event]") ?? []
3268
+ ).find((node) => node.dataset.recordingEvent === events[0].id);
3269
+ row?.scrollIntoView?.({ block: "nearest" });
3270
+ row?.focus();
3271
+ }
3272
+ }
3273
+ ), /* @__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(
3274
+ "tr",
3275
+ {
3276
+ key: event.id,
3277
+ "data-recording-event": event.id,
3278
+ "data-selected": selectedEvents.includes(event.id),
3279
+ tabIndex: -1
3280
+ },
3281
+ /* @__PURE__ */ import_react25.default.createElement("td", null, eventName(event.kind), event.details && /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-event-details" }, eventDetails(event))),
3282
+ /* @__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"))),
3283
+ /* @__PURE__ */ import_react25.default.createElement("td", null, duration(event.atSample, session.sampleRate))
3284
+ )))), /* @__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(
2272
3285
  "input",
2273
3286
  {
2274
3287
  type: "search",
@@ -2277,24 +3290,51 @@ function RecordingDetail({
2277
3290
  "aria-label": copy.recordings.search,
2278
3291
  onInput: (event) => setSearch(event.currentTarget.value)
2279
3292
  }
2280
- )), search.trim() && matchedSegments.length === 0 && /* @__PURE__ */ import_react23.default.createElement("p", { role: "status" }, copy.recordings.searchEmpty), /* @__PURE__ */ import_react23.default.createElement("table", { className: "dlvd-segments" }, /* @__PURE__ */ import_react23.default.createElement("thead", null, /* @__PURE__ */ import_react23.default.createElement("tr", null, /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.start), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.end), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.duration), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.audio))), /* @__PURE__ */ import_react23.default.createElement("tbody", null, matchedSegments.map((segment) => /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, { key: segment.id }, /* @__PURE__ */ import_react23.default.createElement("tr", { "data-selected": selectedSegment === segment.id }, /* @__PURE__ */ import_react23.default.createElement("td", null, segment.kind === "speech" ? copy.recordings.speech : copy.recordings.silence, segment.open && /* @__PURE__ */ import_react23.default.createElement("small", null, copy.recordings.open)), /* @__PURE__ */ import_react23.default.createElement("td", null, duration(segment.startSample, session.sampleRate)), /* @__PURE__ */ import_react23.default.createElement("td", null, duration(segment.endSample, session.sampleRate)), /* @__PURE__ */ import_react23.default.createElement("td", null, duration(segment.endSample - segment.startSample, session.sampleRate)), /* @__PURE__ */ import_react23.default.createElement("td", null, /* @__PURE__ */ import_react23.default.createElement(
3293
+ )), 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(
2281
3294
  "button",
2282
3295
  {
2283
3296
  type: "button",
3297
+ className: "dlvd-segment-action",
3298
+ title: copy.recordings.play,
2284
3299
  disabled: deleting || segment.endSample <= segment.startSample,
2285
- "aria-label": copy.recordings.play + " \xB7 " + segmentName(segment),
3300
+ "aria-label": copy.recordings.play + " \xB7 " + sourceName(session, copy) + " \xB7 " + segmentName(segment),
2286
3301
  onClick: () => void play(segment)
2287
3302
  },
2288
- copy.recordings.play
2289
- ))), segment.kind === "speech" && /* @__PURE__ */ import_react23.default.createElement("tr", null, /* @__PURE__ */ import_react23.default.createElement("td", { colSpan: 5 }, /* @__PURE__ */ import_react23.default.createElement("details", { open: !!search.trim() || selectedSegment === segment.id || void 0 }, /* @__PURE__ */ import_react23.default.createElement("summary", null, copy.recordings.attempts, " \xB7 ", segment.attempts.length), !segment.attempts.length && /* @__PURE__ */ import_react23.default.createElement("p", null, copy.recordings.noAttempts), segment.attempts.map((attempt) => /* @__PURE__ */ import_react23.default.createElement("article", { className: "dlvd-attempt", key: attempt.id }, /* @__PURE__ */ import_react23.default.createElement("div", null, copy.recordings.engine, ": ", attempt.engine, " \xB7", {
3303
+ /* @__PURE__ */ import_react25.default.createElement(SegmentIcon, { kind: "play" })
3304
+ ), /* @__PURE__ */ import_react25.default.createElement(
3305
+ "button",
3306
+ {
3307
+ type: "button",
3308
+ className: "dlvd-segment-action",
3309
+ disabled: downloading || exporting || deleting || segment.endSample <= segment.startSample,
3310
+ title: copy.recordings.download,
3311
+ "aria-label": copy.recordings.download + " \xB7 " + segmentName(segment),
3312
+ onClick: () => void downloadSegment(segment)
3313
+ },
3314
+ /* @__PURE__ */ import_react25.default.createElement(SegmentIcon, { kind: "download" })
3315
+ ), segment.kind === "speech" && service.retranscribe && /* @__PURE__ */ import_react25.default.createElement(
3316
+ "button",
3317
+ {
3318
+ type: "button",
3319
+ className: "dlvd-segment-action",
3320
+ disabled: deleting || !!retrying || session.state === "recording" || segment.open || segment.endSample <= segment.startSample || segment.attempts.some((a) => a.status === "pending"),
3321
+ title: copy.recordings.retranscribeHelp,
3322
+ "aria-label": copy.recordings.retranscribe + " \xB7 " + segmentName(segment),
3323
+ onClick: () => void retranscribe(segment)
3324
+ },
3325
+ /* @__PURE__ */ import_react25.default.createElement(SegmentIcon, { kind: "retry" })
3326
+ ))), 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", {
2290
3327
  pending: copy.recordings.pending,
2291
3328
  success: copy.recordings.success,
2292
3329
  error: copy.recordings.error
2293
- }[attempt.status]), /* @__PURE__ */ import_react23.default.createElement("time", { dateTime: attempt.createdAt }, new Date(attempt.createdAt).toLocaleString()), attempt.finishedAt && /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, null, " ", "\u2014", " ", /* @__PURE__ */ import_react23.default.createElement("time", { dateTime: attempt.finishedAt }, new Date(attempt.finishedAt).toLocaleString())), /* @__PURE__ */ import_react23.default.createElement("div", null, duration(attempt.startSample, session.sampleRate), " \u2014", " ", duration(attempt.endSample, session.sampleRate)), attempt.text != null && /* @__PURE__ */ import_react23.default.createElement("p", { className: "dlvd-transcript" }, attempt.text), attempt.error && /* @__PURE__ */ import_react23.default.createElement("p", { className: "dlvd-attempt-error" }, copy.recordings.error, ": ", attempt.error)))))))))), /* @__PURE__ */ import_react23.default.createElement("details", null, /* @__PURE__ */ import_react23.default.createElement("summary", null, copy.recordings.pins, " \xB7 ", session.pins.length), /* @__PURE__ */ import_react23.default.createElement("table", null, /* @__PURE__ */ import_react23.default.createElement("thead", null, /* @__PURE__ */ import_react23.default.createElement("tr", null, /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.start), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.pinConfirmed), /* @__PURE__ */ import_react23.default.createElement("th", { scope: "col" }, copy.recordings.pinDelay))), /* @__PURE__ */ import_react23.default.createElement("tbody", null, session.pins.map((pin) => /* @__PURE__ */ import_react23.default.createElement("tr", { key: pin.id }, /* @__PURE__ */ import_react23.default.createElement("td", null, pin.kind === "speech-start" ? copy.recordings.speech : copy.recordings.silence), /* @__PURE__ */ import_react23.default.createElement("td", null, duration(pin.atSample, session.sampleRate)), /* @__PURE__ */ import_react23.default.createElement("td", null, duration(pin.confirmedAtSample, session.sampleRate)), /* @__PURE__ */ import_react23.default.createElement("td", null, duration(pin.delaySamples, session.sampleRate))))))));
3330
+ }[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(
3331
+ attempt.endSample - attempt.startSample,
3332
+ session.sampleRate
3333
+ )), /* @__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))))))));
2294
3334
  }
2295
3335
 
2296
3336
  // src/RuntimePanel.tsx
2297
- var import_react24 = __toESM(require("react"), 1);
3337
+ var import_react26 = __toESM(require("react"), 1);
2298
3338
  function RuntimePanel({ data, copy }) {
2299
3339
  const unknown = copy.runtimes.unavailable;
2300
3340
  const bytes = (n) => typeof n === "number" && Number.isFinite(n) ? (n / 1073741824).toFixed(3) + " GiB (" + Math.round(n).toLocaleString() + " B)" : unknown;
@@ -2310,41 +3350,42 @@ function RuntimePanel({ data, copy }) {
2310
3350
  released: copy.runtimes.released,
2311
3351
  cancelled: copy.runtimes.cancelled
2312
3352
  })[v] ?? unknown;
2313
- const row = (label, value) => /* @__PURE__ */ import_react24.default.createElement(import_react24.default.Fragment, { key: label }, /* @__PURE__ */ import_react24.default.createElement("dt", null, label), /* @__PURE__ */ import_react24.default.createElement("dd", null, value));
2314
- return /* @__PURE__ */ import_react24.default.createElement("section", { className: "dlvd-runtimes dlvd-scroll", tabIndex: 0, "aria-label": copy.runtimes.title }, /* @__PURE__ */ import_react24.default.createElement("h3", null, copy.runtimes.title, " ", /* @__PURE__ */ import_react24.default.createElement(Hint, { label: copy.runtimes.title }, copy.runtimes.help)), /* @__PURE__ */ import_react24.default.createElement("dl", { className: "dlvd-recording-meta" }, row(
3353
+ 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));
3354
+ 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(
2315
3355
  copy.runtimes.versions,
2316
3356
  (data.versions?.onnx ?? unknown) + " / " + (data.versions?.transformers ?? unknown)
2317
3357
  ), row(copy.runtimes.id, data.runtimeId ?? unknown), row(
2318
3358
  copy.runtimes.gpu,
2319
3359
  data.gpu === "available" ? copy.runtimes.yes : data.gpu === "unavailable" ? copy.runtimes.no : unknown
2320
- ), 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_react24.default.createElement("article", { className: "dlvd-runtime-card", key: model.key }, /* @__PURE__ */ import_react24.default.createElement("h3", null, model.name), /* @__PURE__ */ import_react24.default.createElement("dl", { className: "dlvd-recording-meta" }, row(copy.runtimes.status, status(model.phase)), row(
3360
+ ), 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(
2321
3361
  copy.runtimes.device,
2322
3362
  model.device === "webgpu" ? "WebGPU" : model.device === "wasm" ? "WebAssembly CPU" : unknown
2323
3363
  ), 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(
2324
3364
  copy.runtimes.ratio,
2325
3365
  model.workerInferenceMs != null && model.audioMs > 0 ? (model.workerInferenceMs / model.audioMs).toFixed(2) + "\xD7" : unknown
2326
- ), 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_react24.default.createElement("p", { role: "alert" }, copy.runtimes.error, ": ", /* @__PURE__ */ import_react24.default.createElement("code", null, model.error)))), /* @__PURE__ */ import_react24.default.createElement("h3", null, copy.runtimes.events), /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-history" }, !data.events?.length && /* @__PURE__ */ import_react24.default.createElement("p", null, copy.commons.empty), [...data.events ?? []].reverse().map((event) => /* @__PURE__ */ import_react24.default.createElement("div", { key: event.sequence, "data-level": event.status === "error" ? "error" : "debug" }, /* @__PURE__ */ import_react24.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_react24.default.createElement(import_react24.default.Fragment, null, " ", "\xB7 ", /* @__PURE__ */ import_react24.default.createElement("code", null, event.code))))));
3366
+ ), 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))))));
2327
3367
  }
2328
3368
 
2329
3369
  // src/DebuggerPanel.tsx
2330
3370
  function DebuggerPanel({
2331
3371
  copy,
3372
+ locale = "en",
2332
3373
  onClose,
2333
3374
  target = window
2334
3375
  }) {
2335
- const [tab, setTab] = import_react25.default.useState("recordings");
2336
- const tabsId = import_react25.default.useId();
2337
- const [frozen, setFrozen] = import_react25.default.useState(false);
2338
- const [refreshHz, setRefreshHz] = import_react25.default.useState(10);
2339
- const updateRef = import_react25.default.useRef(null);
2340
- const [content, setContent] = import_react25.default.useState(false);
2341
- const [module2, setModule] = import_react25.default.useState("");
2342
- const [view, setView] = import_react25.default.useState(null);
2343
- const [failed, setFailed] = import_react25.default.useState(false);
2344
- const [eventsPaused, setEventsPaused] = import_react25.default.useState(false);
2345
- const pausedRef = import_react25.default.useRef(false);
2346
- const inspectorRef = import_react25.default.useRef(null);
2347
- import_react25.default.useEffect(() => {
3376
+ const [tab, setTab] = import_react27.default.useState("recordings");
3377
+ const tabsId = import_react27.default.useId();
3378
+ const [frozen, setFrozen] = import_react27.default.useState(false);
3379
+ const [refreshHz, setRefreshHz] = import_react27.default.useState(10);
3380
+ const updateRef = import_react27.default.useRef(null);
3381
+ const [content, setContent] = import_react27.default.useState(false);
3382
+ const [module2, setModule] = import_react27.default.useState("");
3383
+ const [view, setView] = import_react27.default.useState(null);
3384
+ const [failed, setFailed] = import_react27.default.useState(false);
3385
+ const [eventsPaused, setEventsPaused] = import_react27.default.useState(false);
3386
+ const pausedRef = import_react27.default.useRef(false);
3387
+ const inspectorRef = import_react27.default.useRef(null);
3388
+ import_react27.default.useEffect(() => {
2348
3389
  if (frozen) return;
2349
3390
  let inspector = null;
2350
3391
  let source = null;
@@ -2377,7 +3418,7 @@ function DebuggerPanel({
2377
3418
  inspectorRef.current = null;
2378
3419
  };
2379
3420
  }, [frozen, content, target]);
2380
- import_react25.default.useEffect(() => {
3421
+ import_react27.default.useEffect(() => {
2381
3422
  if (frozen) return;
2382
3423
  const timer = setInterval(() => updateRef.current?.(), 1e3 / refreshHz);
2383
3424
  return () => clearInterval(timer);
@@ -2386,10 +3427,10 @@ function DebuggerPanel({
2386
3427
  const runtimeData = view?.state?.runtimes;
2387
3428
  const hasRuntimes = !!runtimeData?.models?.length;
2388
3429
  const tabs = hasRuntimes ? ["recordings", "states", "runtimes"] : ["recordings", "states"];
2389
- import_react25.default.useEffect(() => {
3430
+ import_react27.default.useEffect(() => {
2390
3431
  if (tab === "runtimes" && !hasRuntimes) setTab("states");
2391
3432
  }, [tab, hasRuntimes]);
2392
- return /* @__PURE__ */ import_react25.default.createElement("aside", { className: "dlvd-panel", "aria-label": copy.commons.title }, /* @__PURE__ */ import_react25.default.createElement("header", null, /* @__PURE__ */ import_react25.default.createElement("strong", null, copy.commons.title), /* @__PURE__ */ import_react25.default.createElement("span", null, refreshHz, " Hz"), /* @__PURE__ */ import_react25.default.createElement("button", { type: "button", onClick: onClose, "aria-label": copy.commons.close }, "\xD7")), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-tabs", role: "tablist", "aria-label": copy.commons.title }, tabs.map((name) => /* @__PURE__ */ import_react25.default.createElement(
3433
+ 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(
2393
3434
  "button",
2394
3435
  {
2395
3436
  type: "button",
@@ -2411,7 +3452,7 @@ function DebuggerPanel({
2411
3452
  }
2412
3453
  },
2413
3454
  name === "recordings" ? copy.recordings.title : name === "runtimes" ? copy.runtimes.title : copy.commons.states
2414
- ))), /* @__PURE__ */ import_react25.default.createElement(
3455
+ ))), /* @__PURE__ */ import_react27.default.createElement(
2415
3456
  "div",
2416
3457
  {
2417
3458
  className: "dlvd-tab-panel",
@@ -2420,8 +3461,8 @@ function DebuggerPanel({
2420
3461
  "aria-labelledby": tabsId + "-recordings-tab",
2421
3462
  hidden: tab !== "recordings"
2422
3463
  },
2423
- tab === "recordings" && /* @__PURE__ */ import_react25.default.createElement(RecordingPanel, { copy, target })
2424
- ), hasRuntimes && /* @__PURE__ */ import_react25.default.createElement(
3464
+ tab === "recordings" && /* @__PURE__ */ import_react27.default.createElement(RecordingPanel, { copy, locale, target })
3465
+ ), hasRuntimes && /* @__PURE__ */ import_react27.default.createElement(
2425
3466
  "div",
2426
3467
  {
2427
3468
  className: "dlvd-tab-panel",
@@ -2430,8 +3471,8 @@ function DebuggerPanel({
2430
3471
  "aria-labelledby": tabsId + "-runtimes-tab",
2431
3472
  hidden: tab !== "runtimes"
2432
3473
  },
2433
- tab === "runtimes" && /* @__PURE__ */ import_react25.default.createElement(RuntimePanel, { data: runtimeData, copy })
2434
- ), /* @__PURE__ */ import_react25.default.createElement(
3474
+ tab === "runtimes" && /* @__PURE__ */ import_react27.default.createElement(RuntimePanel, { data: runtimeData, copy })
3475
+ ), /* @__PURE__ */ import_react27.default.createElement(
2435
3476
  "div",
2436
3477
  {
2437
3478
  className: "dlvd-tab-panel",
@@ -2440,15 +3481,15 @@ function DebuggerPanel({
2440
3481
  "aria-labelledby": tabsId + "-states-tab",
2441
3482
  hidden: tab !== "states"
2442
3483
  },
2443
- /* @__PURE__ */ import_react25.default.createElement("p", null, copy.commons.help),
2444
- /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react25.default.createElement("label", null, copy.commons.refreshRate, /* @__PURE__ */ import_react25.default.createElement(
3484
+ /* @__PURE__ */ import_react27.default.createElement("p", null, copy.commons.help),
3485
+ /* @__PURE__ */ import_react27.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react27.default.createElement("label", null, copy.commons.refreshRate, /* @__PURE__ */ import_react27.default.createElement(
2445
3486
  "select",
2446
3487
  {
2447
3488
  value: refreshHz,
2448
3489
  onChange: (event) => setRefreshHz(Number(event.target.value))
2449
3490
  },
2450
- [1, 2, 5, 10, 20].map((hz) => /* @__PURE__ */ import_react25.default.createElement("option", { key: hz, value: hz }, hz, " Hz"))
2451
- )), /* @__PURE__ */ import_react25.default.createElement("button", { type: "button", onClick: () => setFrozen(!frozen) }, frozen ? copy.commons.resume : copy.commons.freeze), /* @__PURE__ */ import_react25.default.createElement("label", null, /* @__PURE__ */ import_react25.default.createElement(
3491
+ [1, 2, 5, 10, 20].map((hz) => /* @__PURE__ */ import_react27.default.createElement("option", { key: hz, value: hz }, hz, " Hz"))
3492
+ )), /* @__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(
2452
3493
  "input",
2453
3494
  {
2454
3495
  type: "checkbox",
@@ -2460,7 +3501,7 @@ function DebuggerPanel({
2460
3501
  }
2461
3502
  }
2462
3503
  ), copy.commons.content)),
2463
- failed ? /* @__PURE__ */ import_react25.default.createElement("p", { role: "alert" }, copy.commons.error) : !view ? /* @__PURE__ */ import_react25.default.createElement("p", { role: "status" }, copy.commons.waiting) : /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-columns" }, /* @__PURE__ */ import_react25.default.createElement("section", { className: "dlvd-state-pane", "aria-label": copy.commons.states }, /* @__PURE__ */ import_react25.default.createElement("h3", null, copy.commons.states), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-scroll", tabIndex: 0 }, /* @__PURE__ */ import_react25.default.createElement(StateTree, { name: "context", value: view.state, omitted: copy.commons.omitted }))), /* @__PURE__ */ import_react25.default.createElement("section", { className: "dlvd-event-pane", "aria-label": copy.commons.transitions }, /* @__PURE__ */ import_react25.default.createElement("h3", null, copy.commons.transitions), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react25.default.createElement(
3504
+ 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(
2464
3505
  "button",
2465
3506
  {
2466
3507
  type: "button",
@@ -2473,7 +3514,7 @@ function DebuggerPanel({
2473
3514
  }
2474
3515
  },
2475
3516
  eventsPaused ? copy.commons.eventsResume : copy.commons.eventsPause
2476
- ), /* @__PURE__ */ import_react25.default.createElement(
3517
+ ), /* @__PURE__ */ import_react27.default.createElement(
2477
3518
  "button",
2478
3519
  {
2479
3520
  type: "button",
@@ -2483,14 +3524,14 @@ function DebuggerPanel({
2483
3524
  }
2484
3525
  },
2485
3526
  copy.commons.eventsClear
2486
- )), /* @__PURE__ */ import_react25.default.createElement("label", null, copy.commons.filter, /* @__PURE__ */ import_react25.default.createElement("select", { value: module2, onChange: (event) => setModule(event.target.value) }, /* @__PURE__ */ import_react25.default.createElement("option", { value: "" }, copy.commons.all), modules.map((name) => /* @__PURE__ */ import_react25.default.createElement("option", { key: name }, name)))), /* @__PURE__ */ import_react25.default.createElement("div", { className: "dlvd-history dlvd-scroll", tabIndex: 0 }, view.transitions.filter((entry) => !module2 || entry.module === module2).slice().reverse().map((entry) => /* @__PURE__ */ import_react25.default.createElement("details", { key: entry.sequence, "data-level": entry.level }, /* @__PURE__ */ import_react25.default.createElement("summary", null, /* @__PURE__ */ import_react25.default.createElement("small", null, new Date(entry.timeMs).toLocaleTimeString()), " ", /* @__PURE__ */ import_react25.default.createElement("code", null, entry.sessionId, " / ", entry.module)), /* @__PURE__ */ import_react25.default.createElement(
3527
+ )), /* @__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(
2487
3528
  StateTree,
2488
3529
  {
2489
3530
  name: "before",
2490
3531
  value: entry.before,
2491
3532
  omitted: copy.commons.omitted
2492
3533
  }
2493
- ), /* @__PURE__ */ import_react25.default.createElement(StateTree, { name: "after", value: entry.after, omitted: copy.commons.omitted }))), !view.transitions.length && /* @__PURE__ */ import_react25.default.createElement("p", null, copy.commons.empty))))
3534
+ ), /* @__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))))
2494
3535
  ));
2495
3536
  }
2496
3537
 
@@ -2522,7 +3563,7 @@ function normalizeDebuggerLocale(locale) {
2522
3563
  }
2523
3564
 
2524
3565
  // 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
2525
- var import_react26 = require("react");
3566
+ var import_react28 = require("react");
2526
3567
 
2527
3568
  // 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
2528
3569
  function detectLanguage() {
@@ -2555,21 +3596,21 @@ function deepFreeze(value) {
2555
3596
 
2556
3597
  // 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
2557
3598
  function createTranslationRuntime({ availableLangs, defaultLang, onLanguageLoaded }) {
2558
- const context = (0, import_react26.createContext)({});
3599
+ const context = (0, import_react28.createContext)({});
2559
3600
  function useTranslation(selector) {
2560
- const value = (0, import_react26.useContext)(context);
3601
+ const value = (0, import_react28.useContext)(context);
2561
3602
  if (!value.current)
2562
3603
  throw new Error("useTranslation must be used within TranslationProvider.");
2563
3604
  return { ...value, scoped: selector ? selector(value.current.lang) : value.current.lang };
2564
3605
  }
2565
3606
  function TranslationProvider({ children }) {
2566
- const [saved, setSaved] = (0, import_react26.useState)();
2567
- const [language, setCurrentLanguage] = (0, import_react26.useState)();
3607
+ const [saved, setSaved] = (0, import_react28.useState)();
3608
+ const [language, setCurrentLanguage] = (0, import_react28.useState)();
2568
3609
  function setLanguage(nextLanguage, save) {
2569
3610
  if (save)
2570
3611
  setSaved(nextLanguage);
2571
3612
  }
2572
- (0, import_react26.useEffect)(() => {
3613
+ (0, import_react28.useEffect)(() => {
2573
3614
  void loadLanguage(availableLangs, defaultLang, saved).then((loadedLanguage) => {
2574
3615
  setCurrentLanguage(loadedLanguage);
2575
3616
  onLanguageLoaded?.(loadedLanguage);
@@ -2577,7 +3618,7 @@ function createTranslationRuntime({ availableLangs, defaultLang, onLanguageLoade
2577
3618
  }, [saved]);
2578
3619
  if (!language)
2579
3620
  return null;
2580
- return (0, import_react26.createElement)(context.Provider, { value: { setLanguage, available: availableLangs, current: language, saved } }, children);
3621
+ return (0, import_react28.createElement)(context.Provider, { value: { setLanguage, available: availableLangs, current: language, saved } }, children);
2581
3622
  }
2582
3623
  return { TranslationProvider, useTranslation };
2583
3624
  }
@@ -2592,28 +3633,28 @@ var {
2592
3633
  });
2593
3634
 
2594
3635
  // src/i18n/DshLanguageBoundary.tsx
2595
- var import_react27 = __toESM(require("react"), 1);
3636
+ var import_react29 = __toESM(require("react"), 1);
2596
3637
  var fallback = Object.freeze({ active: "en", revision: 0 });
2597
3638
  function DshLanguageBoundary({ locale, children }) {
2598
3639
  const { current, setLanguage } = useDebuggerLanguage();
2599
- const subscribe = import_react27.default.useCallback(
3640
+ const subscribe = import_react29.default.useCallback(
2600
3641
  (listener) => locale?.subscribe?.(listener) ?? (() => {
2601
3642
  }),
2602
3643
  [locale]
2603
3644
  );
2604
- const snapshot = import_react27.default.useCallback(() => locale?.getSnapshot?.() ?? fallback, [locale]);
2605
- const state = import_react27.default.useSyncExternalStore(subscribe, snapshot, snapshot);
3645
+ const snapshot = import_react29.default.useCallback(() => locale?.getSnapshot?.() ?? fallback, [locale]);
3646
+ const state = import_react29.default.useSyncExternalStore(subscribe, snapshot, snapshot);
2606
3647
  const expected = normalizeDebuggerLocale(state.active);
2607
- import_react27.default.useEffect(() => {
3648
+ import_react29.default.useEffect(() => {
2608
3649
  if (current.locale !== expected) setLanguage(expected, true);
2609
3650
  }, [current.locale, expected, setLanguage]);
2610
- const initialized = import_react27.default.useRef(false);
3651
+ const initialized = import_react29.default.useRef(false);
2611
3652
  if (current.locale === expected) initialized.current = true;
2612
3653
  return initialized.current ? children : null;
2613
3654
  }
2614
3655
  function withDebuggerLanguage(Component, locale) {
2615
3656
  function Localized(props) {
2616
- return /* @__PURE__ */ import_react27.default.createElement(DebuggerTranslationProvider, null, /* @__PURE__ */ import_react27.default.createElement(DshLanguageBoundary, { locale }, /* @__PURE__ */ import_react27.default.createElement(Component, { ...props })));
3657
+ return /* @__PURE__ */ import_react29.default.createElement(DebuggerTranslationProvider, null, /* @__PURE__ */ import_react29.default.createElement(DshLanguageBoundary, { locale }, /* @__PURE__ */ import_react29.default.createElement(Component, { ...props })));
2617
3658
  }
2618
3659
  Localized.displayName = "DebuggerLanguage(" + (Component.displayName || Component.name || "Component") + ")";
2619
3660
  return Localized;
@@ -2649,6 +3690,12 @@ function registerDshLocales(ctx) {
2649
3690
 
2650
3691
  // src/styles.ts
2651
3692
  var styles = `
3693
+ .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}
3694
+
3695
+ .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}
3696
+
3697
+ .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}
3698
+ .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}
2652
3699
  .dlvd-timeline-window{display:flex;gap:12px;align-items:center}.dlvd-window-selector{flex:1;min-width:0;padding:0 10px}.dlvd-window-values{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;font-variant-numeric:tabular-nums}.dlvd-window-track{position:relative;height:32px;touch-action:none;user-select:none}.dlvd-window-track:before{content:"";position:absolute;left:0;right:0;top:14px;height:4px;border-radius:4px;background:var(--dsw-alias-border-l2,#ffffff40)}.dlvd-panel .dlvd-window-selection{position:absolute;top:11px;height:10px;min-width:1px;padding:0;border:0;border-radius:4px;background:var(--dsw-alias-state-business-primary,#7aaaff);cursor:grab;touch-action:none}.dlvd-panel .dlvd-window-selection:active{cursor:grabbing}.dlvd-panel .dlvd-window-handle{position:absolute;top:0;width:20px;height:32px;transform:translateX(-50%);padding:0;border:0;border-radius:4px;background:transparent;cursor:ew-resize;touch-action:none;z-index:1}.dlvd-window-handle:before{content:"";position:absolute;width:12px;height:12px;left:4px;top:10px;border-radius:50%;background:var(--dsw-alias-label-primary,#f9fafb);box-shadow:0 0 0 1px #0004}.dlvd-panel .dlvd-window-reset{display:inline-flex;align-items:center;justify-content:center;padding:6px;flex:none}.dlvd-window-track button:disabled{cursor:default}
2653
3700
  .dlvd-event-pin{position:absolute;transform:translateX(-100%);color:#ffbd66;font-size:14px;line-height:14px;pointer-events:none;z-index:1}.dlvd-event-pin[data-event=capture-gated]{color:#f88888}.dlvd-recordings input{font:inherit;background:var(--dsw-alias-bg-layer-1,#232324);color:inherit;border:1px solid var(--dsw-alias-border-l2,#ffffff1f);border-radius:6px;padding:5px;max-width:100%;box-sizing:border-box}
2654
3701
 
@@ -2721,11 +3768,19 @@ function apply(ctx) {
2721
3768
  notify();
2722
3769
  };
2723
3770
  function Host() {
2724
- const { scoped: labels } = useDebuggerLanguage();
2725
- import_react28.default.useEffect(() => {
3771
+ const { scoped: labels, current } = useDebuggerLanguage();
3772
+ import_react30.default.useEffect(() => {
2726
3773
  if (popup && !popup.closed) popup.document.title = labels.commons.title;
2727
3774
  }, [labels.commons.title]);
2728
- return /* @__PURE__ */ import_react28.default.createElement(DebuggerPanel, { copy: labels, target: window, onClose: () => queueMicrotask(close) });
3775
+ return /* @__PURE__ */ import_react30.default.createElement(
3776
+ DebuggerPanel,
3777
+ {
3778
+ copy: labels,
3779
+ locale: current.locale,
3780
+ target: window,
3781
+ onClose: () => queueMicrotask(close)
3782
+ }
3783
+ );
2729
3784
  }
2730
3785
  const open = () => {
2731
3786
  if (popup && !popup.closed) {
@@ -2759,7 +3814,7 @@ function apply(ctx) {
2759
3814
  container.dataset.plugin = namespace;
2760
3815
  doc.body.appendChild(container);
2761
3816
  root = (0, import_client.createRoot)(container);
2762
- root.render(/* @__PURE__ */ import_react28.default.createElement(LocalizedHost, null));
3817
+ root.render(/* @__PURE__ */ import_react30.default.createElement(LocalizedHost, null));
2763
3818
  enabled = true;
2764
3819
  closedTimer = setInterval(() => {
2765
3820
  if (popup?.closed) close();
@@ -2774,9 +3829,9 @@ function apply(ctx) {
2774
3829
  }
2775
3830
  };
2776
3831
  function Settings() {
2777
- const failed = import_react28.default.useSyncExternalStore(subscribe, () => blocked);
3832
+ const failed = import_react30.default.useSyncExternalStore(subscribe, () => blocked);
2778
3833
  const { scoped: labels } = useDebuggerLanguage();
2779
- return /* @__PURE__ */ import_react28.default.createElement("div", { className: "dlv-diagnostics-launcher" }, /* @__PURE__ */ import_react28.default.createElement("button", { type: "button", className: "dlv-diagnostics-button", onClick: open }, /* @__PURE__ */ import_react28.default.createElement(Icon, { name: "bug" }), labels.commons.diagnostics), /* @__PURE__ */ import_react28.default.createElement(Hint, { label: labels.commons.diagnostics }, labels.commons.activate), failed && /* @__PURE__ */ import_react28.default.createElement("p", { role: "alert" }, labels.commons.popupBlocked));
3834
+ 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));
2780
3835
  }
2781
3836
  const LocalizedHost = withDebuggerLanguage(Host, ctx.locale);
2782
3837
  const LocalizedSettings = withDebuggerLanguage(Settings, ctx.locale);