dsh-live-voice-debugger 0.6.1 → 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 +15 -1
  2. package/lib/client.js +1644 -137
  3. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -73,33 +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",
79
+ assistantEnd: "Assistant playback ended",
80
+ assistantError: "Assistant speech failed",
81
+ assistantInterruptionRequest: "Pause requested after confirmed user speech",
82
+ assistantPause: "Assistant playback paused",
83
+ assistantPlayRequest: "Assistant playback requested",
84
+ assistantResume: "Assistant playback resumed",
85
+ assistantStart: "Assistant playback started",
86
+ assistantWaitEnd: "Assistant wait ended",
87
+ assistantWaitStart: "Assistant began waiting",
77
88
  attempts: "Transcription history",
78
89
  audio: "Recording playback",
79
90
  audioError: "Unable to load or play recording audio.",
91
+ automaticAction: "Automatic",
80
92
  cancel: "Cancel",
93
+ captureEnd: "Capture ended",
94
+ captureGateFailed: "Input stop failed before playback",
95
+ captureGated: "Microphone suspension requested for speaker playback",
96
+ captureStart: "Capture started",
81
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",
82
105
  completed: "Completed",
83
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.",
84
107
  date: "Started",
85
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.",
86
117
  deleteError: "Unable to delete the recording.",
118
+ download: "Download segment WAV",
119
+ downloadError: "Unable to download this segment.",
87
120
  duration: "Duration",
88
121
  empty: "No recording sessions match this filter.",
89
122
  end: "End",
90
123
  engine: "Engine",
91
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",
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",
135
+ events: "Assistant and capture events",
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.",
92
137
  export: "Export self-contained JSON",
93
138
  exportError: "Unable to export the recording.",
94
139
  failed: "Failed",
95
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",
96
142
  interrupted: "Interrupted",
97
143
  loadError: "Unable to load recording sessions. Try refreshing.",
98
144
  loading: "Loading recordings\u2026",
145
+ manualAction: "Manual",
146
+ meetingId: "Meeting ID",
99
147
  memory: "Memory only",
100
148
  microphone: "Microphone",
149
+ name: "Session name",
150
+ nameError: "Unable to save the name. Star the session and use a single line of at most 120 characters.",
101
151
  noAttempts: "No transcription attempts yet.",
102
152
  open: "Open segment",
153
+ originalName: "Original name at capture",
154
+ participant: "Participant",
155
+ participantDetails: "Participant identity at capture",
156
+ participantId: "Participant ID",
103
157
  pending: "Pending",
104
158
  persistent: "Saved locally",
105
159
  pinConfirmed: "Confirmed at",
@@ -107,8 +161,26 @@ var init_en2 = __esm({
107
161
  pins: "Boundary pins",
108
162
  play: "Play segment",
109
163
  playSession: "Play saved session",
164
+ queuedSegments: "Queued segments",
110
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",
180
+ saveName: "Save name",
111
181
  saved: "Saved audio",
182
+ search: "Search segment transcripts",
183
+ searchEmpty: "No transcribed segments match.",
112
184
  segments: "Segments",
113
185
  selectSession: "Select a recording session.",
114
186
  shared: "Shared audio",
@@ -122,12 +194,37 @@ var init_en2 = __esm({
122
194
  starred: "Protected from automatic cleanup",
123
195
  start: "Start",
124
196
  state: "State",
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",
203
+ storageUnknown: "Unavailable",
125
204
  success: "Success",
126
205
  timeline: "Speech / silence timeline",
127
206
  title: "Recordings",
128
207
  unavailable: "Recording service unavailable. Enable recording in Live Voice settings with a compatible runtime.",
129
208
  unstar: "Remove protection",
130
- 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."
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",
224
+ window: "Timeline window",
225
+ windowEnd: "Window end",
226
+ windowReset: "Show full session",
227
+ windowStart: "Window start"
131
228
  };
132
229
  en_default2 = Object.freeze(translation2);
133
230
  }
@@ -235,33 +332,87 @@ var init_es2 = __esm({
235
332
  "src/i18n/recordings/es.ts"() {
236
333
  translation6 = {
237
334
  active: "Grabando",
335
+ aliasAtCapture: "Alias al capturar",
238
336
  allSources: "Todas las fuentes",
337
+ assistantCancel: "Voz del asistente cancelada",
338
+ assistantEnd: "Reproducci\xF3n del asistente finalizada",
339
+ assistantError: "Fallo de voz del asistente",
340
+ assistantInterruptionRequest: "Pausa solicitada tras voz confirmada del usuario",
341
+ assistantPause: "Reproducci\xF3n del asistente pausada",
342
+ assistantPlayRequest: "Reproducci\xF3n del asistente solicitada",
343
+ assistantResume: "Reproducci\xF3n del asistente reanudada",
344
+ assistantStart: "Reproducci\xF3n del asistente iniciada",
345
+ assistantWaitEnd: "Espera del asistente finalizada",
346
+ assistantWaitStart: "El asistente comenz\xF3 a esperar",
239
347
  attempts: "Historial de transcripci\xF3n",
240
348
  audio: "Reproducci\xF3n de la grabaci\xF3n",
241
349
  audioError: "No se pudo cargar o reproducir el audio.",
350
+ automaticAction: "Autom\xE1tico",
242
351
  cancel: "Cancelar",
352
+ captureEnd: "Captura finalizada",
353
+ captureGateFailed: "Error al detener la escucha antes de reproducir",
354
+ captureGated: "Suspensi\xF3n del micr\xF3fono solicitada para reproducir por altavoz",
355
+ captureStart: "Captura iniciada",
243
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",
244
364
  completed: "Completada",
245
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.",
246
366
  date: "Inicio",
247
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.",
248
376
  deleteError: "No se pudo eliminar la grabaci\xF3n.",
377
+ download: "Descargar WAV del segmento",
378
+ downloadError: "No se pudo descargar este segmento.",
249
379
  duration: "Duraci\xF3n",
250
380
  empty: "Ninguna grabaci\xF3n coincide con el filtro.",
251
381
  end: "Fin",
252
382
  engine: "Motor",
253
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",
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",
394
+ events: "Eventos del asistente y la captura",
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.",
254
396
  export: "Exportar JSON completo",
255
397
  exportError: "No se pudo exportar la grabaci\xF3n.",
256
398
  failed: "Fallida",
257
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",
258
401
  interrupted: "Interrumpida",
259
402
  loadError: "No se pudieron cargar las grabaciones. Intente actualizar.",
260
403
  loading: "Cargando grabaciones\u2026",
404
+ manualAction: "Manual",
405
+ meetingId: "ID de reuni\xF3n",
261
406
  memory: "Solo en memoria",
262
407
  microphone: "Micr\xF3fono",
408
+ name: "Nombre de la sesi\xF3n",
409
+ nameError: "No se pudo guardar el nombre. Marca la sesi\xF3n con estrella y usa una l\xEDnea de hasta 120 caracteres.",
263
410
  noAttempts: "Todav\xEDa no hay intentos de transcripci\xF3n.",
264
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",
265
416
  pending: "Pendiente",
266
417
  persistent: "Guardada localmente",
267
418
  pinConfirmed: "Confirmado en",
@@ -269,8 +420,26 @@ var init_es2 = __esm({
269
420
  pins: "Marcadores de l\xEDmite",
270
421
  play: "Reproducir segmento",
271
422
  playSession: "Reproducir sesi\xF3n guardada",
423
+ queuedSegments: "Segmentos en cola",
272
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",
439
+ saveName: "Guardar nombre",
273
440
  saved: "Audio guardado",
441
+ search: "Buscar transcripciones de segmentos",
442
+ searchEmpty: "No hay segmentos transcritos que coincidan.",
274
443
  segments: "Segmentos",
275
444
  selectSession: "Seleccione una sesi\xF3n de grabaci\xF3n.",
276
445
  shared: "Audio compartido",
@@ -284,12 +453,37 @@ var init_es2 = __esm({
284
453
  starred: "Protegida de la limpieza autom\xE1tica",
285
454
  start: "Inicio",
286
455
  state: "Estado",
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",
462
+ storageUnknown: "No disponible",
287
463
  success: "\xC9xito",
288
464
  timeline: "Cronolog\xEDa de voz / silencio",
289
465
  title: "Grabaciones",
290
466
  unavailable: "Servicio de grabaci\xF3n no disponible. Active la grabaci\xF3n en los ajustes de Voz en vivo con un runtime compatible.",
291
467
  unstar: "Quitar protecci\xF3n",
292
- 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."
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",
483
+ window: "Intervalo de la l\xEDnea de tiempo",
484
+ windowEnd: "Fin del intervalo",
485
+ windowReset: "Mostrar toda la sesi\xF3n",
486
+ windowStart: "Inicio del intervalo"
293
487
  };
294
488
  es_default2 = Object.freeze(translation6);
295
489
  }
@@ -397,33 +591,87 @@ var init_fr2 = __esm({
397
591
  "src/i18n/recordings/fr.ts"() {
398
592
  translation10 = {
399
593
  active: "Enregistrement",
594
+ aliasAtCapture: "Alias \xE0 la capture",
400
595
  allSources: "Toutes les sources",
596
+ assistantCancel: "Parole de l\u2019assistant annul\xE9e",
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",
600
+ assistantPause: "Lecture de l\u2019assistant en pause",
601
+ assistantPlayRequest: "Lecture de l\u2019assistant demand\xE9e",
602
+ assistantResume: "Lecture de l\u2019assistant reprise",
603
+ assistantStart: "Lecture de l\u2019assistant d\xE9marr\xE9e",
604
+ assistantWaitEnd: "Attente de l\u2019assistant termin\xE9e",
605
+ assistantWaitStart: "L\u2019assistant commence \xE0 attendre",
401
606
  attempts: "Historique des transcriptions",
402
607
  audio: "Lecture de l\u2019enregistrement",
403
608
  audioError: "Impossible de charger ou lire l\u2019audio.",
609
+ automaticAction: "Automatique",
404
610
  cancel: "Annuler",
611
+ captureEnd: "Capture termin\xE9e",
612
+ captureGateFailed: "\xC9chec de l\u2019arr\xEAt de l\u2019\xE9coute avant lecture",
613
+ captureGated: "Suspension du microphone demand\xE9e pour la lecture sur haut-parleur",
614
+ captureStart: "Capture d\xE9marr\xE9e",
405
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",
406
623
  completed: "Termin\xE9",
407
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.",
408
625
  date: "D\xE9but",
409
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.",
410
635
  deleteError: "Impossible de supprimer l\u2019enregistrement.",
636
+ download: "T\xE9l\xE9charger le segment WAV",
637
+ downloadError: "Impossible de t\xE9l\xE9charger ce segment.",
411
638
  duration: "Dur\xE9e",
412
639
  empty: "Aucun enregistrement ne correspond au filtre.",
413
640
  end: "Fin",
414
641
  engine: "Moteur",
415
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",
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",
653
+ events: "\xC9v\xE9nements de l\u2019assistant et de la capture",
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.",
416
655
  export: "Exporter le JSON autonome",
417
656
  exportError: "Impossible d\u2019exporter l\u2019enregistrement.",
418
657
  failed: "\xC9chec",
419
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",
420
660
  interrupted: "Interrompu",
421
661
  loadError: "Impossible de charger les enregistrements. Actualisez la liste.",
422
662
  loading: "Chargement des enregistrements\u2026",
663
+ manualAction: "Manuel",
664
+ meetingId: "ID de r\xE9union",
423
665
  memory: "En m\xE9moire uniquement",
424
666
  microphone: "Microphone",
667
+ name: "Nom de la session",
668
+ nameError: "Impossible d\u2019enregistrer le nom. Marquez la session d\u2019une \xE9toile et utilisez une ligne de 120 caract\xE8res maximum.",
425
669
  noAttempts: "Aucune tentative de transcription.",
426
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",
427
675
  pending: "En attente",
428
676
  persistent: "Sauvegard\xE9 localement",
429
677
  pinConfirmed: "Confirm\xE9 \xE0",
@@ -431,8 +679,26 @@ var init_fr2 = __esm({
431
679
  pins: "Marqueurs de limite",
432
680
  play: "Lire le segment",
433
681
  playSession: "Lire la session sauvegard\xE9e",
682
+ queuedSegments: "Segments en attente",
434
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",
698
+ saveName: "Enregistrer le nom",
435
699
  saved: "Audio sauvegard\xE9",
700
+ search: "Rechercher dans les transcriptions",
701
+ searchEmpty: "Aucun segment transcrit ne correspond.",
436
702
  segments: "Segments",
437
703
  selectSession: "S\xE9lectionnez une session d\u2019enregistrement.",
438
704
  shared: "Audio partag\xE9",
@@ -446,12 +712,37 @@ var init_fr2 = __esm({
446
712
  starred: "Prot\xE9g\xE9 du nettoyage automatique",
447
713
  start: "D\xE9but",
448
714
  state: "\xC9tat",
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",
721
+ storageUnknown: "Indisponible",
449
722
  success: "R\xE9ussite",
450
723
  timeline: "Chronologie parole / silence",
451
724
  title: "Enregistrements",
452
725
  unavailable: "Service d\u2019enregistrement indisponible. Activez l\u2019enregistrement dans les param\xE8tres de Voix en direct avec un runtime compatible.",
453
726
  unstar: "Retirer la protection",
454
- 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."
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",
742
+ window: "Intervalle de la chronologie",
743
+ windowEnd: "Fin de l\u2019intervalle",
744
+ windowReset: "Afficher toute la session",
745
+ windowStart: "D\xE9but de l\u2019intervalle"
455
746
  };
456
747
  fr_default2 = Object.freeze(translation10);
457
748
  }
@@ -559,33 +850,87 @@ var init_hi2 = __esm({
559
850
  "src/i18n/recordings/hi.ts"() {
560
851
  translation14 = {
561
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",
562
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",
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",
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",
861
+ assistantResume: "\u0938\u0939\u093E\u092F\u0915 \u0915\u093E \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u092B\u093F\u0930 \u0936\u0941\u0930\u0942",
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",
563
865
  attempts: "\u091F\u094D\u0930\u093E\u0902\u0938\u0915\u094D\u0930\u093F\u092A\u094D\u0936\u0928 \u0907\u0924\u093F\u0939\u093E\u0938",
564
866
  audio: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u092A\u094D\u0932\u0947\u092C\u0948\u0915",
565
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",
566
869
  cancel: "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
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",
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",
873
+ captureStart: "\u0915\u0948\u092A\u094D\u091A\u0930 \u0936\u0941\u0930\u0942",
567
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",
568
882
  completed: "\u092A\u0942\u0930\u094D\u0923",
569
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",
570
884
  date: "\u0936\u0941\u0930\u0941\u0906\u0924",
571
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",
572
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",
573
897
  duration: "\u0905\u0935\u0927\u093F",
574
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",
575
899
  end: "\u0905\u0902\u0924",
576
900
  engine: "\u0907\u0902\u091C\u0928",
577
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",
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",
912
+ events: "\u0938\u0939\u093E\u092F\u0915 \u0914\u0930 \u0915\u0948\u092A\u094D\u091A\u0930 \u0915\u0940 \u0918\u091F\u0928\u093E\u090F\u0901",
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",
578
914
  export: "\u0938\u094D\u0935\u092F\u0902\u092A\u0942\u0930\u094D\u0923 JSON \u0928\u093F\u0930\u094D\u092F\u093E\u0924 \u0915\u0930\u0947\u0902",
579
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",
580
916
  failed: "\u0935\u093F\u092B\u0932",
581
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",
582
919
  interrupted: "\u092C\u093E\u0927\u093F\u0924",
583
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",
584
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",
585
924
  memory: "\u0915\u0947\u0935\u0932 \u092E\u0947\u092E\u094B\u0930\u0940 \u092E\u0947\u0902",
586
925
  microphone: "\u092E\u093E\u0907\u0915\u094D\u0930\u094B\u092B\u093C\u094B\u0928",
926
+ name: "\u0938\u0924\u094D\u0930 \u0915\u093E \u0928\u093E\u092E",
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",
587
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",
588
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",
589
934
  pending: "\u0932\u0902\u092C\u093F\u0924",
590
935
  persistent: "\u0938\u094D\u0925\u093E\u0928\u0940\u092F \u0930\u0942\u092A \u0938\u0947 \u0938\u0939\u0947\u091C\u093E",
591
936
  pinConfirmed: "\u092A\u0941\u0937\u094D\u091F\u093F \u0938\u092E\u092F",
@@ -593,8 +938,26 @@ var init_hi2 = __esm({
593
938
  pins: "\u0938\u0940\u092E\u093E \u091A\u093F\u0939\u094D\u0928",
594
939
  play: "\u0916\u0902\u0921 \u091A\u0932\u093E\u090F\u0901",
595
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",
596
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",
957
+ saveName: "\u0928\u093E\u092E \u0938\u0939\u0947\u091C\u0947\u0902",
597
958
  saved: "\u0938\u0939\u0947\u091C\u093E \u0911\u0921\u093F\u092F\u094B",
959
+ search: "\u0916\u0902\u0921\u094B\u0902 \u0915\u0947 \u092A\u094D\u0930\u0924\u093F\u0932\u0947\u0916 \u0916\u094B\u091C\u0947\u0902",
960
+ searchEmpty: "\u0915\u094B\u0908 \u092E\u0947\u0932 \u0916\u093E\u0924\u093E \u092A\u094D\u0930\u0924\u093F\u0932\u093F\u0916\u093F\u0924 \u0916\u0902\u0921 \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E\u0964",
598
961
  segments: "\u0916\u0902\u0921",
599
962
  selectSession: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917 \u0938\u0924\u094D\u0930 \u091A\u0941\u0928\u0947\u0902\u0964",
600
963
  shared: "\u0938\u093E\u091D\u093E \u0911\u0921\u093F\u092F\u094B",
@@ -608,12 +971,37 @@ var init_hi2 = __esm({
608
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",
609
972
  start: "\u0936\u0941\u0930\u0941\u0906\u0924",
610
973
  state: "\u0938\u094D\u0925\u093F\u0924\u093F",
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",
980
+ storageUnknown: "\u0905\u0928\u0941\u092A\u0932\u092C\u094D\u0927",
611
981
  success: "\u0938\u092B\u0932",
612
982
  timeline: "\u0906\u0935\u093E\u091C\u093C / \u092E\u094C\u0928 \u0938\u092E\u092F\u0930\u0947\u0916\u093E",
613
983
  title: "\u0930\u093F\u0915\u0949\u0930\u094D\u0921\u093F\u0902\u0917",
614
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",
615
985
  unstar: "\u0938\u0941\u0930\u0915\u094D\u0937\u093E \u0939\u091F\u093E\u090F\u0901",
616
- 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"
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",
1001
+ window: "\u091F\u093E\u0907\u092E\u0932\u093E\u0907\u0928 \u0915\u093E \u0939\u093F\u0938\u094D\u0938\u093E",
1002
+ windowEnd: "\u0939\u093F\u0938\u094D\u0938\u0947 \u0915\u093E \u0905\u0902\u0924",
1003
+ windowReset: "\u092A\u0942\u0930\u093E \u0938\u0924\u094D\u0930 \u0926\u093F\u0916\u093E\u090F\u0901",
1004
+ windowStart: "\u0939\u093F\u0938\u094D\u0938\u0947 \u0915\u0940 \u0936\u0941\u0930\u0941\u0906\u0924"
617
1005
  };
618
1006
  hi_default2 = Object.freeze(translation14);
619
1007
  }
@@ -721,33 +1109,87 @@ var init_pt_BR2 = __esm({
721
1109
  "src/i18n/recordings/pt-BR.ts"() {
722
1110
  translation18 = {
723
1111
  active: "Gravando",
1112
+ aliasAtCapture: "Apelido na captura",
724
1113
  allSources: "Todas as fontes",
1114
+ assistantCancel: "Fala do assistente cancelada",
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",
1118
+ assistantPause: "Reprodu\xE7\xE3o do assistente pausada",
1119
+ assistantPlayRequest: "Reprodu\xE7\xE3o do assistente solicitada",
1120
+ assistantResume: "Reprodu\xE7\xE3o do assistente retomada",
1121
+ assistantStart: "Reprodu\xE7\xE3o do assistente iniciada",
1122
+ assistantWaitEnd: "Espera do assistente encerrada",
1123
+ assistantWaitStart: "Assistente come\xE7ou a esperar",
725
1124
  attempts: "Hist\xF3rico de transcri\xE7\xE3o",
726
1125
  audio: "Reprodu\xE7\xE3o da grava\xE7\xE3o",
727
1126
  audioError: "N\xE3o foi poss\xEDvel carregar ou reproduzir o \xE1udio.",
1127
+ automaticAction: "Autom\xE1tico",
728
1128
  cancel: "Cancelar",
1129
+ captureEnd: "Captura encerrada",
1130
+ captureGateFailed: "Falha ao parar a escuta antes da reprodu\xE7\xE3o",
1131
+ captureGated: "Suspens\xE3o do microfone solicitada para reprodu\xE7\xE3o no alto-falante",
1132
+ captureStart: "Captura iniciada",
729
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",
730
1141
  completed: "Conclu\xEDda",
731
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.",
732
1143
  date: "In\xEDcio",
733
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.",
734
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.",
735
1156
  duration: "Dura\xE7\xE3o",
736
1157
  empty: "Nenhuma grava\xE7\xE3o corresponde ao filtro.",
737
1158
  end: "Fim",
738
1159
  engine: "Motor",
739
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",
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",
1171
+ events: "Eventos do assistente e da captura",
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.",
740
1173
  export: "Exportar JSON completo",
741
1174
  exportError: "N\xE3o foi poss\xEDvel exportar a grava\xE7\xE3o.",
742
1175
  failed: "Falhou",
743
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",
744
1178
  interrupted: "Interrompida",
745
1179
  loadError: "N\xE3o foi poss\xEDvel carregar as grava\xE7\xF5es. Tente atualizar.",
746
1180
  loading: "Carregando grava\xE7\xF5es\u2026",
1181
+ manualAction: "Manual",
1182
+ meetingId: "ID da reuni\xE3o",
747
1183
  memory: "Somente na mem\xF3ria",
748
1184
  microphone: "Microfone",
1185
+ name: "Nome da sess\xE3o",
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.",
749
1187
  noAttempts: "Nenhuma tentativa de transcri\xE7\xE3o ainda.",
750
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",
751
1193
  pending: "Pendente",
752
1194
  persistent: "Salva localmente",
753
1195
  pinConfirmed: "Confirmado em",
@@ -755,8 +1197,26 @@ var init_pt_BR2 = __esm({
755
1197
  pins: "Marcadores de limite",
756
1198
  play: "Reproduzir segmento",
757
1199
  playSession: "Reproduzir sess\xE3o salva",
1200
+ queuedSegments: "Segmentos na fila",
758
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",
1216
+ saveName: "Salvar nome",
759
1217
  saved: "\xC1udio salvo",
1218
+ search: "Pesquisar transcri\xE7\xF5es dos segmentos",
1219
+ searchEmpty: "Nenhum segmento transcrito corresponde \xE0 pesquisa.",
760
1220
  segments: "Segmentos",
761
1221
  selectSession: "Selecione uma sess\xE3o de grava\xE7\xE3o.",
762
1222
  shared: "\xC1udio compartilhado",
@@ -770,12 +1230,37 @@ var init_pt_BR2 = __esm({
770
1230
  starred: "Protegida da limpeza autom\xE1tica",
771
1231
  start: "In\xEDcio",
772
1232
  state: "Estado",
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",
1239
+ storageUnknown: "Indispon\xEDvel",
773
1240
  success: "Sucesso",
774
1241
  timeline: "Linha do tempo de fala / sil\xEAncio",
775
1242
  title: "Grava\xE7\xF5es",
776
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.",
777
1244
  unstar: "Remover prote\xE7\xE3o",
778
- 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."
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",
1260
+ window: "Recorte da linha do tempo",
1261
+ windowEnd: "Fim do recorte",
1262
+ windowReset: "Mostrar sess\xE3o inteira",
1263
+ windowStart: "In\xEDcio do recorte"
779
1264
  };
780
1265
  pt_BR_default2 = Object.freeze(translation18);
781
1266
  }
@@ -883,33 +1368,87 @@ var init_zh2 = __esm({
883
1368
  "src/i18n/recordings/zh.ts"() {
884
1369
  translation22 = {
885
1370
  active: "\u6B63\u5728\u5F55\u5236",
1371
+ aliasAtCapture: "\u91C7\u96C6\u65F6\u7684\u522B\u540D",
886
1372
  allSources: "\u6240\u6709\u6765\u6E90",
1373
+ assistantCancel: "\u52A9\u624B\u8BED\u97F3\u5DF2\u53D6\u6D88",
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",
1377
+ assistantPause: "\u52A9\u624B\u64AD\u653E\u6682\u505C",
1378
+ assistantPlayRequest: "\u5DF2\u8BF7\u6C42\u52A9\u624B\u64AD\u653E",
1379
+ assistantResume: "\u52A9\u624B\u64AD\u653E\u6062\u590D",
1380
+ assistantStart: "\u52A9\u624B\u5F00\u59CB\u64AD\u653E",
1381
+ assistantWaitEnd: "\u52A9\u624B\u7B49\u5F85\u7ED3\u675F",
1382
+ assistantWaitStart: "\u52A9\u624B\u5F00\u59CB\u7B49\u5F85",
887
1383
  attempts: "\u8F6C\u5F55\u5386\u53F2",
888
1384
  audio: "\u5F55\u97F3\u64AD\u653E",
889
1385
  audioError: "\u65E0\u6CD5\u52A0\u8F7D\u6216\u64AD\u653E\u5F55\u97F3\u97F3\u9891\u3002",
1386
+ automaticAction: "\u81EA\u52A8",
890
1387
  cancel: "\u53D6\u6D88",
1388
+ captureEnd: "\u91C7\u96C6\u7ED3\u675F",
1389
+ captureGateFailed: "\u64AD\u653E\u524D\u505C\u6B62\u6536\u542C\u5931\u8D25",
1390
+ captureGated: "\u5DF2\u8BF7\u6C42\u6682\u505C\u9EA6\u514B\u98CE\u4EE5\u901A\u8FC7\u626C\u58F0\u5668\u64AD\u653E",
1391
+ captureStart: "\u91C7\u96C6\u5F00\u59CB",
891
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",
892
1400
  completed: "\u5DF2\u5B8C\u6210",
893
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",
894
1402
  date: "\u5F00\u59CB\u65F6\u95F4",
895
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",
896
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",
897
1415
  duration: "\u65F6\u957F",
898
1416
  empty: "\u6CA1\u6709\u7B26\u5408\u7B5B\u9009\u6761\u4EF6\u7684\u5F55\u97F3\u4F1A\u8BDD\u3002",
899
1417
  end: "\u7ED3\u675F",
900
1418
  engine: "\u5F15\u64CE",
901
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",
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",
1430
+ events: "\u52A9\u624B\u4E0E\u91C7\u96C6\u4E8B\u4EF6",
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",
902
1432
  export: "\u5BFC\u51FA\u5B8C\u6574\u72EC\u7ACB JSON",
903
1433
  exportError: "\u65E0\u6CD5\u5BFC\u51FA\u5F55\u97F3\u3002",
904
1434
  failed: "\u5931\u8D25",
905
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",
906
1437
  interrupted: "\u5DF2\u4E2D\u65AD",
907
1438
  loadError: "\u65E0\u6CD5\u52A0\u8F7D\u5F55\u97F3\u4F1A\u8BDD\u3002\u8BF7\u5237\u65B0\u91CD\u8BD5\u3002",
908
1439
  loading: "\u6B63\u5728\u52A0\u8F7D\u5F55\u97F3\u2026",
1440
+ manualAction: "\u624B\u52A8",
1441
+ meetingId: "\u4F1A\u8BAE ID",
909
1442
  memory: "\u4EC5\u5728\u5185\u5B58\u4E2D",
910
1443
  microphone: "\u9EA6\u514B\u98CE",
1444
+ name: "\u4F1A\u8BDD\u540D\u79F0",
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",
911
1446
  noAttempts: "\u5C1A\u65E0\u8F6C\u5F55\u5C1D\u8BD5\u3002",
912
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",
913
1452
  pending: "\u5904\u7406\u4E2D",
914
1453
  persistent: "\u5DF2\u4FDD\u5B58\u5230\u672C\u5730",
915
1454
  pinConfirmed: "\u786E\u8BA4\u65F6\u95F4",
@@ -917,8 +1456,26 @@ var init_zh2 = __esm({
917
1456
  pins: "\u8FB9\u754C\u6807\u8BB0",
918
1457
  play: "\u64AD\u653E\u7247\u6BB5",
919
1458
  playSession: "\u64AD\u653E\u5DF2\u4FDD\u5B58\u4F1A\u8BDD",
1459
+ queuedSegments: "\u6392\u961F\u7247\u6BB5\u6570",
920
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",
1475
+ saveName: "\u4FDD\u5B58\u540D\u79F0",
921
1476
  saved: "\u5DF2\u4FDD\u5B58\u97F3\u9891",
1477
+ search: "\u641C\u7D22\u7247\u6BB5\u8F6C\u5F55",
1478
+ searchEmpty: "\u6CA1\u6709\u5339\u914D\u7684\u5DF2\u8F6C\u5F55\u7247\u6BB5\u3002",
922
1479
  segments: "\u7247\u6BB5",
923
1480
  selectSession: "\u8BF7\u9009\u62E9\u5F55\u97F3\u4F1A\u8BDD\u3002",
924
1481
  shared: "\u5171\u4EAB\u97F3\u9891",
@@ -932,12 +1489,37 @@ var init_zh2 = __esm({
932
1489
  starred: "\u53D7\u4FDD\u62A4\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u6E05\u7406",
933
1490
  start: "\u5F00\u59CB",
934
1491
  state: "\u72B6\u6001",
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",
1498
+ storageUnknown: "\u4E0D\u53EF\u7528",
935
1499
  success: "\u6210\u529F",
936
1500
  timeline: "\u8BED\u97F3 / \u9759\u97F3\u65F6\u95F4\u7EBF",
937
1501
  title: "\u5F55\u97F3",
938
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",
939
1503
  unstar: "\u53D6\u6D88\u4FDD\u62A4",
940
- 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"
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",
1519
+ window: "\u65F6\u95F4\u8F74\u8303\u56F4",
1520
+ windowEnd: "\u8303\u56F4\u7ED3\u675F",
1521
+ windowReset: "\u663E\u793A\u5B8C\u6574\u4F1A\u8BDD",
1522
+ windowStart: "\u8303\u56F4\u5F00\u59CB"
941
1523
  };
942
1524
  zh_default2 = Object.freeze(translation22);
943
1525
  }
@@ -1014,7 +1596,7 @@ __export(apply_exports, {
1014
1596
  inject: () => inject
1015
1597
  });
1016
1598
  module.exports = __toCommonJS(apply_exports);
1017
- var import_react27 = __toESM(require("react"), 1);
1599
+ var import_react30 = __toESM(require("react"), 1);
1018
1600
 
1019
1601
  // ../live-voice/src/shared/design-system/buttons/IconButton.tsx
1020
1602
  var import_react2 = __toESM(require("react"), 1);
@@ -1243,7 +1825,7 @@ var import_react20 = __toESM(require("react"), 1);
1243
1825
  var import_client = require("react-dom/client");
1244
1826
 
1245
1827
  // src/DebuggerPanel.tsx
1246
- var import_react24 = __toESM(require("react"), 1);
1828
+ var import_react27 = __toESM(require("react"), 1);
1247
1829
 
1248
1830
  // ../live-voice/src/modules/core/diagnostics.ts
1249
1831
  var DIAGNOSTIC_KEY = /* @__PURE__ */ Symbol.for("dsh-live-voice.diagnostics.v1");
@@ -1378,7 +1960,616 @@ function StateTree({
1378
1960
  }
1379
1961
 
1380
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
1381
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
2168
+ var import_react23 = __toESM(require("react"), 1);
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
+
2368
+ // src/TimelineWindow.tsx
2369
+ var import_react24 = __toESM(require("react"), 1);
2370
+ function TimelineWindow({ total, start, end, copy, format, onChange, onReset }) {
2371
+ const track = import_react24.default.useRef(null);
2372
+ const drag = import_react24.default.useRef(null);
2373
+ const stopDrag = () => {
2374
+ const current = drag.current;
2375
+ drag.current = null;
2376
+ if (current?.element.hasPointerCapture(current.pointer))
2377
+ current.element.releasePointerCapture(current.pointer);
2378
+ };
2379
+ import_react24.default.useEffect(() => {
2380
+ if (total <= 0) stopDrag();
2381
+ }, [total]);
2382
+ import_react24.default.useEffect(() => stopDrag, []);
2383
+ const change = (part, delta, initialStart = start, initialEnd = end) => {
2384
+ if (total <= 0) return;
2385
+ if (part === "start")
2386
+ onChange(Math.max(0, Math.min(initialEnd - 1, initialStart + delta)), initialEnd);
2387
+ else if (part === "end")
2388
+ onChange(initialStart, Math.min(total, Math.max(initialStart + 1, initialEnd + delta)));
2389
+ else {
2390
+ const offset = Math.max(-initialStart, Math.min(total - initialEnd, delta));
2391
+ onChange(initialStart + offset, initialEnd + offset);
2392
+ }
2393
+ };
2394
+ const begin = (event, part) => {
2395
+ if (total <= 0 || event.button !== 0 || drag.current) return;
2396
+ const width = track.current?.getBoundingClientRect().width ?? 0;
2397
+ if (width <= 0) return;
2398
+ event.preventDefault();
2399
+ event.currentTarget.focus();
2400
+ event.currentTarget.setPointerCapture(event.pointerId);
2401
+ drag.current = {
2402
+ pointer: event.pointerId,
2403
+ x: event.clientX,
2404
+ width,
2405
+ total,
2406
+ start,
2407
+ end,
2408
+ part,
2409
+ element: event.currentTarget
2410
+ };
2411
+ };
2412
+ const move = (event) => {
2413
+ const current = drag.current;
2414
+ if (!current || current.pointer !== event.pointerId) return;
2415
+ change(
2416
+ current.part,
2417
+ Math.round((event.clientX - current.x) * current.total / current.width),
2418
+ current.start,
2419
+ current.end
2420
+ );
2421
+ };
2422
+ const finish = (event) => {
2423
+ if (drag.current?.pointer !== event.pointerId) return;
2424
+ stopDrag();
2425
+ };
2426
+ const keyboard = (event, part) => {
2427
+ const step = Math.max(1, Math.round(total / (event.shiftKey ? 10 : 100)));
2428
+ const amount = {
2429
+ ArrowLeft: -step,
2430
+ ArrowDown: -step,
2431
+ ArrowRight: step,
2432
+ ArrowUp: step,
2433
+ PageDown: -step * 10,
2434
+ PageUp: step * 10
2435
+ }[event.key];
2436
+ let delta = amount;
2437
+ if (event.key === "Home") delta = part === "end" ? start + 1 - end : -start;
2438
+ if (event.key === "End") delta = part === "start" ? end - 1 - start : total - end;
2439
+ if (delta === void 0) return;
2440
+ event.preventDefault();
2441
+ change(part, delta);
2442
+ };
2443
+ const handlers = (part) => ({
2444
+ onPointerDown: (event) => begin(event, part),
2445
+ onPointerMove: move,
2446
+ onPointerUp: (event) => {
2447
+ move(event);
2448
+ finish(event);
2449
+ },
2450
+ onPointerCancel: finish,
2451
+ onLostPointerCapture: () => {
2452
+ drag.current = null;
2453
+ },
2454
+ onKeyDown: (event) => keyboard(event, part)
2455
+ });
2456
+ const percent = (value) => total > 0 ? value / total * 100 : 0;
2457
+ return /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-timeline-window", role: "group", "aria-label": copy.window }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-window-selector" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-window-values" }, /* @__PURE__ */ import_react24.default.createElement("span", null, copy.windowStart, ": ", format(start)), /* @__PURE__ */ import_react24.default.createElement("span", null, copy.windowEnd, ": ", format(end))), /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-window-track", ref: track }, /* @__PURE__ */ import_react24.default.createElement(
2458
+ "button",
2459
+ {
2460
+ type: "button",
2461
+ role: "slider",
2462
+ className: "dlvd-window-selection",
2463
+ "aria-label": copy.window,
2464
+ "aria-valuemin": 0,
2465
+ "aria-valuemax": Math.max(0, total - (end - start)),
2466
+ "aria-valuenow": start,
2467
+ "aria-valuetext": format(start) + " \u2013 " + format(end),
2468
+ "aria-orientation": "horizontal",
2469
+ disabled: total <= 0,
2470
+ style: { left: percent(start) + "%", width: percent(end - start) + "%" },
2471
+ ...handlers("range")
2472
+ }
2473
+ ), /* @__PURE__ */ import_react24.default.createElement(
2474
+ "button",
2475
+ {
2476
+ type: "button",
2477
+ role: "slider",
2478
+ className: "dlvd-window-handle",
2479
+ "data-edge": "start",
2480
+ "aria-label": copy.windowStart,
2481
+ "aria-valuemin": 0,
2482
+ "aria-valuemax": Math.max(0, end - 1),
2483
+ "aria-valuenow": start,
2484
+ "aria-valuetext": format(start),
2485
+ "aria-orientation": "horizontal",
2486
+ disabled: total <= 0,
2487
+ style: { left: percent(start) + "%" },
2488
+ ...handlers("start")
2489
+ }
2490
+ ), /* @__PURE__ */ import_react24.default.createElement(
2491
+ "button",
2492
+ {
2493
+ type: "button",
2494
+ role: "slider",
2495
+ className: "dlvd-window-handle",
2496
+ "data-edge": "end",
2497
+ "aria-label": copy.windowEnd,
2498
+ "aria-valuemin": Math.min(total, start + 1),
2499
+ "aria-valuemax": total,
2500
+ "aria-valuenow": end,
2501
+ "aria-valuetext": format(end),
2502
+ "aria-orientation": "horizontal",
2503
+ disabled: total <= 0,
2504
+ style: { left: percent(end) + "%" },
2505
+ ...handlers("end")
2506
+ }
2507
+ ))), /* @__PURE__ */ import_react24.default.createElement(
2508
+ "button",
2509
+ {
2510
+ type: "button",
2511
+ className: "dlvd-window-reset",
2512
+ title: copy.windowReset,
2513
+ "aria-label": copy.windowReset,
2514
+ onClick: onReset
2515
+ },
2516
+ /* @__PURE__ */ import_react24.default.createElement(
2517
+ "svg",
2518
+ {
2519
+ width: "18",
2520
+ height: "18",
2521
+ viewBox: "0 0 24 24",
2522
+ fill: "none",
2523
+ stroke: "currentColor",
2524
+ strokeWidth: "1.7",
2525
+ strokeLinecap: "round",
2526
+ strokeLinejoin: "round",
2527
+ "aria-hidden": "true",
2528
+ focusable: "false"
2529
+ },
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" })
2531
+ )
2532
+ ));
2533
+ }
2534
+
2535
+ // src/segmentSearch.ts
2536
+ var normalize = (text) => text.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
2537
+ function distance(a, b) {
2538
+ let row = Array.from({ length: b.length + 1 }, (_, i) => i);
2539
+ for (let i = 0; i < a.length; i++) {
2540
+ const next = [i + 1];
2541
+ for (let j = 0; j < b.length; j++)
2542
+ next.push(Math.min(next[j] + 1, row[j + 1] + 1, row[j] + (a[i] === b[j] ? 0 : 1)));
2543
+ row = next;
2544
+ }
2545
+ return row[b.length];
2546
+ }
2547
+ function searchSegments(segments, query) {
2548
+ if (!query.trim()) return segments;
2549
+ const needle = normalize(query.slice(0, 160));
2550
+ if (!needle) return [];
2551
+ const words = needle.split(" ");
2552
+ return segments.map((segment, index) => {
2553
+ const transcripts = segment.attempts.filter((a) => a.status === "success" && a.text?.trim()).map((a) => normalize(a.text));
2554
+ let score = Infinity;
2555
+ for (const text of transcripts) {
2556
+ const tokens = text.split(" ");
2557
+ if (text === needle) score = Math.min(score, 0);
2558
+ else if (text.includes(needle)) score = Math.min(score, 1);
2559
+ else if (words.every((word) => tokens.includes(word))) score = Math.min(score, 2);
2560
+ else if (words.every((word) => tokens.some((token) => token.includes(word))))
2561
+ score = Math.min(score, 3);
2562
+ else if (words.every(
2563
+ (word) => tokens.some((token) => {
2564
+ const limit = word.length < 4 ? 0 : word.length < 8 ? 1 : 2;
2565
+ return Math.abs(token.length - word.length) <= limit && distance(word, token) <= limit;
2566
+ })
2567
+ ))
2568
+ score = Math.min(score, 4);
2569
+ }
2570
+ return { segment, index, score };
2571
+ }).filter((hit) => Number.isFinite(hit.score)).sort((a, b) => a.score - b.score || a.index - b.index).map((hit) => hit.segment);
2572
+ }
1382
2573
 
1383
2574
  // ../live-voice/src/modules/recording/types.ts
1384
2575
  var RECORDING_KEY = /* @__PURE__ */ Symbol.for("dsh-live-voice.recordings.v1");
@@ -1503,13 +2694,31 @@ function createRecordingInspector(target, changed) {
1503
2694
  }
1504
2695
 
1505
2696
  // src/RecordingPanel.tsx
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
+ }
1506
2715
  function duration(samples, rate) {
1507
2716
  const milliseconds = Math.max(0, Math.round(samples / Math.max(1, rate) * 1e3));
1508
2717
  const seconds = Math.floor(milliseconds / 1e3);
1509
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");
1510
2719
  }
1511
2720
  var sourceKey = (session) => JSON.stringify([session.kind, session.sourceId]);
1512
- 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);
1513
2722
  function stateName(session, copy) {
1514
2723
  return {
1515
2724
  recording: copy.recordings.active,
@@ -1518,8 +2727,12 @@ function stateName(session, copy) {
1518
2727
  failed: copy.recordings.failed
1519
2728
  }[session.state];
1520
2729
  }
1521
- function RecordingPanel({ copy, target }) {
1522
- const [view, setView] = import_react22.default.useState({
2730
+ function RecordingPanel({
2731
+ copy,
2732
+ target,
2733
+ locale = "en"
2734
+ }) {
2735
+ const [view, setView] = import_react25.default.useState({
1523
2736
  service: null,
1524
2737
  sessions: [],
1525
2738
  selected: null,
@@ -1527,9 +2740,10 @@ function RecordingPanel({ copy, target }) {
1527
2740
  loading: true,
1528
2741
  failed: false
1529
2742
  });
1530
- const inspector = import_react22.default.useRef(null);
1531
- const [filter, setFilter] = import_react22.default.useState("");
1532
- import_react22.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(() => {
1533
2747
  const current = createRecordingInspector(target, setView);
1534
2748
  inspector.current = current;
1535
2749
  current.discover();
@@ -1546,12 +2760,16 @@ function RecordingPanel({ copy, target }) {
1546
2760
  ...new Map(view.sessions.map((session) => [sourceKey(session), session])).values()
1547
2761
  ];
1548
2762
  const visible = view.sessions.filter((session) => !filter || sourceKey(session) === filter).slice().sort((a, b) => b.startedAt.localeCompare(a.startedAt));
1549
- import_react22.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(() => {
1550
2768
  if (!view.loading && filter && visible.length && !visible.some((s) => s.id === view.selected?.id))
1551
2769
  inspector.current?.select(visible[0].id);
1552
2770
  }, [filter, view.selected?.id, view.loading, visible[0]?.id]);
1553
2771
  const selected = view.selected && visible.some((s) => s.id === view.selected?.id) ? view.selected : null;
1554
- return /* @__PURE__ */ import_react22.default.createElement("section", { className: "dlvd-recordings", "aria-label": copy.recordings.title }, /* @__PURE__ */ import_react22.default.createElement("p", null, copy.recordings.help), /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react22.default.createElement("label", null, copy.recordings.sourceFilter, " ", /* @__PURE__ */ import_react22.default.createElement("select", { value: filter, onChange: (event) => setFilter(event.target.value) }, /* @__PURE__ */ import_react22.default.createElement("option", { value: "" }, copy.recordings.allSources), sources.map((session) => /* @__PURE__ */ import_react22.default.createElement("option", { key: sourceKey(session), value: sourceKey(session) }, sourceName(session, copy))))), /* @__PURE__ */ import_react22.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(
1555
2773
  "button",
1556
2774
  {
1557
2775
  type: "button",
@@ -1561,22 +2779,34 @@ function RecordingPanel({ copy, target }) {
1561
2779
  }
1562
2780
  },
1563
2781
  copy.recordings.refresh
1564
- )), view.failed && /* @__PURE__ */ import_react22.default.createElement("p", { role: "alert" }, copy.recordings.loadError), !view.service ? /* @__PURE__ */ import_react22.default.createElement("p", { role: "status" }, copy.recordings.unavailable) : /* @__PURE__ */ import_react22.default.createElement(import_react22.default.Fragment, null, view.loading && /* @__PURE__ */ import_react22.default.createElement("p", { role: "status" }, copy.recordings.loading), /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-recording-columns" }, /* @__PURE__ */ import_react22.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(
1565
2795
  "div",
1566
2796
  {
1567
2797
  className: "dlvd-session-list dlvd-scroll",
1568
2798
  tabIndex: 0,
1569
2799
  "aria-label": copy.recordings.title
1570
2800
  },
1571
- !visible.length && !view.loading && /* @__PURE__ */ import_react22.default.createElement("p", null, copy.recordings.empty),
1572
- sources.filter((source) => !filter || sourceKey(source) === filter).map((source) => /* @__PURE__ */ import_react22.default.createElement("section", { key: sourceKey(source), "aria-label": sourceName(source, copy) }, /* @__PURE__ */ import_react22.default.createElement("h3", null, sourceName(source, copy)), /* @__PURE__ */ import_react22.default.createElement("table", null, /* @__PURE__ */ import_react22.default.createElement("thead", null, /* @__PURE__ */ import_react22.default.createElement("tr", null, /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.date), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.duration), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.saved))), /* @__PURE__ */ import_react22.default.createElement("tbody", null, visible.filter((session) => sourceKey(session) === sourceKey(source)).map((session) => /* @__PURE__ */ import_react22.default.createElement("tr", { key: session.id, "data-selected": session.id === selected?.id }, /* @__PURE__ */ import_react22.default.createElement("td", null, /* @__PURE__ */ import_react22.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(
1573
2803
  "button",
1574
2804
  {
1575
2805
  type: "button",
1576
2806
  "aria-pressed": session.id === selected?.id,
1577
2807
  onClick: () => inspector.current?.select(session.id)
1578
2808
  },
1579
- session.starred && /* @__PURE__ */ import_react22.default.createElement(
2809
+ session.starred && /* @__PURE__ */ import_react25.default.createElement(
1580
2810
  "span",
1581
2811
  {
1582
2812
  role: "img",
@@ -1586,11 +2816,15 @@ function RecordingPanel({ copy, target }) {
1586
2816
  "\u2605"
1587
2817
  ),
1588
2818
  " ",
1589
- /* @__PURE__ */ import_react22.default.createElement("time", { dateTime: session.startedAt }, new Date(session.startedAt).toLocaleString())
1590
- )), /* @__PURE__ */ import_react22.default.createElement("td", null, stateName(session, copy)), /* @__PURE__ */ import_react22.default.createElement("td", null, duration(session.capturedSamples, session.sampleRate)), /* @__PURE__ */ import_react22.default.createElement("td", null, duration(session.savedSamples, session.sampleRate), /* @__PURE__ */ import_react22.default.createElement("br", null), session.storage === "indexeddb" || session.persistent ? copy.recordings.persistent : copy.recordings.memory)))))))
1591
- ), /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-recording-detail dlvd-scroll", tabIndex: 0 }, selected ? /* @__PURE__ */ import_react22.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(
1592
2825
  RecordingDetail,
1593
2826
  {
2827
+ locale,
1594
2828
  key: selected.id,
1595
2829
  copy,
1596
2830
  session: selected,
@@ -1598,28 +2832,38 @@ function RecordingPanel({ copy, target }) {
1598
2832
  segments: view.segments,
1599
2833
  refresh: () => inspector.current?.refresh()
1600
2834
  }
1601
- ) : /* @__PURE__ */ import_react22.default.createElement("p", null, copy.recordings.selectSession)))));
2835
+ ) : /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.selectSession)))));
1602
2836
  }
1603
2837
  function RecordingDetail({
2838
+ locale,
1604
2839
  copy,
1605
2840
  session,
1606
2841
  service,
1607
2842
  segments,
1608
2843
  refresh
1609
2844
  }) {
1610
- const container = import_react22.default.useRef(null);
1611
- const audio = import_react22.default.useRef(null);
1612
- const playback = import_react22.default.useRef(null);
1613
- const download = import_react22.default.useRef(null);
1614
- const generation = import_react22.default.useRef(0);
1615
- const playGeneration = import_react22.default.useRef(0);
1616
- const [error, setError] = import_react22.default.useState(null);
1617
- const [playing, setPlaying] = import_react22.default.useState(false);
1618
- const [exporting, setExporting] = import_react22.default.useState(false);
1619
- const [deleting, setDeleting] = import_react22.default.useState(false);
1620
- const [starring, setStarring] = import_react22.default.useState(false);
1621
- const [confirmDelete, setConfirmDelete] = import_react22.default.useState(false);
1622
- const [selectedSegment, setSelectedSegment] = import_react22.default.useState("");
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(() => {
2863
+ setName(session.name ?? "");
2864
+ }, [session.id, session.name, service]);
2865
+ const [selectedSegment, setSelectedSegment] = import_react25.default.useState("");
2866
+ const [selectedEvents, setSelectedEvents] = import_react25.default.useState([]);
1623
2867
  const clearPlayback = (player = audio.current) => {
1624
2868
  ++playGeneration.current;
1625
2869
  if (player?.hasAttribute("src")) {
@@ -1634,12 +2878,13 @@ function RecordingDetail({
1634
2878
  if (download.current) download.current.api.revokeObjectURL(download.current.url);
1635
2879
  download.current = null;
1636
2880
  };
1637
- import_react22.default.useEffect(() => {
2881
+ import_react25.default.useEffect(() => {
1638
2882
  setStarring(false);
1639
2883
  const owner = container.current?.ownerDocument.defaultView;
1640
2884
  const player = audio.current;
1641
2885
  const release = () => {
1642
2886
  ++generation.current;
2887
+ retry.current?.abort();
1643
2888
  clearPlayback(player);
1644
2889
  clearDownload();
1645
2890
  };
@@ -1649,7 +2894,7 @@ function RecordingDetail({
1649
2894
  release();
1650
2895
  };
1651
2896
  }, [service, session.id]);
1652
- const play = async (segment) => {
2897
+ const play = async (segment, autoplay = true) => {
1653
2898
  clearPlayback();
1654
2899
  const token = generation.current;
1655
2900
  const request = playGeneration.current;
@@ -1665,7 +2910,8 @@ function RecordingDetail({
1665
2910
  const url = api.createObjectURL(blob);
1666
2911
  playback.current = { url, api };
1667
2912
  player.src = url;
1668
- await player.play();
2913
+ player.load();
2914
+ if (autoplay) await player.play();
1669
2915
  } catch {
1670
2916
  if (token === generation.current && request === playGeneration.current) {
1671
2917
  clearPlayback();
@@ -1676,6 +2922,61 @@ function RecordingDetail({
1676
2922
  if (token === generation.current && request === playGeneration.current) setPlaying(false);
1677
2923
  }
1678
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
+ };
1679
2980
  const exportSession = async () => {
1680
2981
  const token = generation.current;
1681
2982
  setExporting(true);
@@ -1684,20 +2985,7 @@ function RecordingDetail({
1684
2985
  try {
1685
2986
  const blob = await service.export(session.id);
1686
2987
  if (token !== generation.current) return;
1687
- const doc = container.current?.ownerDocument;
1688
- const api = doc?.defaultView?.URL;
1689
- if (!doc || !api) return;
1690
- const url = api.createObjectURL(blob);
1691
- download.current = { url, api };
1692
- const link = doc.createElement("a");
1693
- link.href = url;
1694
- link.download = "recording-" + session.id.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json";
1695
- doc.body.appendChild(link);
1696
- try {
1697
- link.click();
1698
- } finally {
1699
- link.remove();
1700
- }
2988
+ saveBlob(blob, "recording-" + session.id.replace(/[^a-zA-Z0-9_-]/g, "_") + ".json");
1701
2989
  } catch {
1702
2990
  if (token === generation.current) {
1703
2991
  clearDownload();
@@ -1720,6 +3008,78 @@ function RecordingDetail({
1720
3008
  if (token === generation.current) setStarring(false);
1721
3009
  }
1722
3010
  };
3011
+ const saveName = async () => {
3012
+ if (!service.setName) return;
3013
+ const token = generation.current;
3014
+ setNaming(true);
3015
+ setError(null);
3016
+ try {
3017
+ await service.setName(session.id, name);
3018
+ if (token === generation.current) refresh();
3019
+ } catch {
3020
+ if (token === generation.current) setError("nameError");
3021
+ } finally {
3022
+ if (token === generation.current) setNaming(false);
3023
+ }
3024
+ };
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,
3035
+ "assistant-start": copy.recordings.assistantStart,
3036
+ "assistant-end": copy.recordings.assistantEnd,
3037
+ "assistant-pause": copy.recordings.assistantPause,
3038
+ "assistant-resume": copy.recordings.assistantResume,
3039
+ "capture-gated": copy.recordings.captureGated,
3040
+ "capture-start": copy.recordings.captureStart,
3041
+ "capture-end": copy.recordings.captureEnd
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
+ };
1723
3083
  const removeSession = async () => {
1724
3084
  const token = ++generation.current;
1725
3085
  setExporting(false);
@@ -1740,20 +3100,60 @@ function RecordingDetail({
1740
3100
  if (token === generation.current) setDeleting(false);
1741
3101
  }
1742
3102
  };
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]);
1743
3106
  const length = Math.max(1, session.savedSamples);
1744
- const segmentName = (segment) => (segment.kind === "speech" ? copy.recordings.speech : copy.recordings.silence) + " \xB7 " + duration(segment.startSample, session.sampleRate) + " \u2014 " + duration(segment.endSample, session.sampleRate);
1745
- return /* @__PURE__ */ import_react22.default.createElement("section", { ref: container, "aria-label": sourceName(session, copy) }, /* @__PURE__ */ import_react22.default.createElement("h3", null, sourceName(session, copy)), /* @__PURE__ */ import_react22.default.createElement("dl", { className: "dlvd-recording-meta" }, /* @__PURE__ */ import_react22.default.createElement("dt", null, copy.recordings.date), /* @__PURE__ */ import_react22.default.createElement("dd", null, new Date(session.startedAt).toLocaleString()), /* @__PURE__ */ import_react22.default.createElement("dt", null, copy.recordings.state), /* @__PURE__ */ import_react22.default.createElement("dd", null, stateName(session, copy)), /* @__PURE__ */ import_react22.default.createElement("dt", null, copy.recordings.captured), /* @__PURE__ */ import_react22.default.createElement("dd", null, duration(session.capturedSamples, session.sampleRate)), /* @__PURE__ */ import_react22.default.createElement("dt", null, copy.recordings.saved), /* @__PURE__ */ import_react22.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_react22.default.createElement(import_react22.default.Fragment, null, /* @__PURE__ */ import_react22.default.createElement("dt", null, copy.recordings.end), /* @__PURE__ */ import_react22.default.createElement("dd", null, new Date(session.endedAt).toLocaleString()))), session.error && /* @__PURE__ */ import_react22.default.createElement("p", { role: "alert" }, copy.recordings.error, ": ", session.error), /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react22.default.createElement(
3107
+ const [windowStart, setWindowStart] = import_react25.default.useState(0);
3108
+ const [windowEnd, setWindowEnd] = import_react25.default.useState(null);
3109
+ import_react25.default.useEffect(() => {
3110
+ setWindowStart(0);
3111
+ setWindowEnd(null);
3112
+ }, [session.id, service]);
3113
+ const viewEnd = Math.max(1, Math.min(length, windowEnd ?? length));
3114
+ const viewStart = Math.max(0, Math.min(windowStart, viewEnd - 1));
3115
+ const viewLength = viewEnd - viewStart;
3116
+ const inWindow = (sample) => sample >= viewStart && sample <= viewEnd;
3117
+ const position = (sample) => (sample - viewStart) / viewLength * 100 + "%";
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(
3120
+ "form",
3121
+ {
3122
+ className: "dlvd-toolbar",
3123
+ onSubmit: (event) => {
3124
+ event.preventDefault();
3125
+ void saveName();
3126
+ }
3127
+ },
3128
+ /* @__PURE__ */ import_react25.default.createElement("label", null, copy.recordings.name, " ", /* @__PURE__ */ import_react25.default.createElement(
3129
+ "input",
3130
+ {
3131
+ value: name,
3132
+ maxLength: 120,
3133
+ disabled: naming || deleting,
3134
+ onInput: (event) => setName(event.currentTarget.value)
3135
+ }
3136
+ )),
3137
+ /* @__PURE__ */ import_react25.default.createElement(
3138
+ "button",
3139
+ {
3140
+ type: "submit",
3141
+ disabled: naming || deleting || starring || name === (session.name ?? "")
3142
+ },
3143
+ copy.recordings.saveName
3144
+ )
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(
1746
3146
  "button",
1747
3147
  {
1748
3148
  type: "button",
1749
3149
  "aria-label": session.starred ? copy.recordings.unstar : copy.recordings.star,
1750
3150
  "aria-pressed": !!session.starred,
1751
3151
  title: session.starred ? copy.recordings.unstarHelp : copy.recordings.starHelp,
1752
- disabled: starring || deleting,
3152
+ disabled: starring || deleting || naming,
1753
3153
  onClick: () => void toggleStarred()
1754
3154
  },
1755
3155
  session.starred ? "\u2605" : "\u2606"
1756
- ), /* @__PURE__ */ import_react22.default.createElement(
3156
+ ), /* @__PURE__ */ import_react25.default.createElement(
1757
3157
  "button",
1758
3158
  {
1759
3159
  type: "button",
@@ -1761,91 +3161,180 @@ function RecordingDetail({
1761
3161
  onClick: () => void play()
1762
3162
  },
1763
3163
  copy.recordings.playSession
1764
- ), /* @__PURE__ */ import_react22.default.createElement("button", { type: "button", disabled: exporting || deleting, onClick: () => void exportSession() }, copy.recordings.export), /* @__PURE__ */ import_react22.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(
1765
3173
  "button",
1766
3174
  {
1767
3175
  type: "button",
1768
- disabled: deleting || starring,
3176
+ disabled: deleting || starring || naming || !!retrying,
1769
3177
  onClick: () => setConfirmDelete(true)
1770
3178
  },
1771
3179
  copy.recordings.delete
1772
- )), confirmDelete && /* @__PURE__ */ import_react22.default.createElement(
3180
+ )), confirmDelete && /* @__PURE__ */ import_react25.default.createElement(
1773
3181
  "div",
1774
3182
  {
1775
3183
  className: "dlvd-delete-confirm",
1776
3184
  role: "group",
1777
3185
  "aria-label": copy.recordings.confirmDelete
1778
3186
  },
1779
- /* @__PURE__ */ import_react22.default.createElement("p", null, copy.recordings.confirmDelete),
1780
- /* @__PURE__ */ import_react22.default.createElement(
3187
+ /* @__PURE__ */ import_react25.default.createElement("p", null, copy.recordings.confirmDelete),
3188
+ /* @__PURE__ */ import_react25.default.createElement(
1781
3189
  "button",
1782
3190
  {
1783
3191
  type: "button",
1784
- disabled: deleting || starring,
3192
+ disabled: deleting || starring || naming || !!retrying,
1785
3193
  onClick: () => void removeSession()
1786
3194
  },
1787
3195
  copy.recordings.delete
1788
3196
  ),
1789
3197
  " ",
1790
- /* @__PURE__ */ import_react22.default.createElement("button", { type: "button", disabled: deleting, onClick: () => setConfirmDelete(false) }, copy.recordings.cancel)
1791
- ), error && /* @__PURE__ */ import_react22.default.createElement("p", { role: "alert" }, copy.recordings[error]), /* @__PURE__ */ import_react22.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(
1792
3200
  "audio",
1793
3201
  {
1794
3202
  ref: audio,
1795
3203
  controls: true,
1796
- preload: "none",
3204
+ preload: "metadata",
1797
3205
  "aria-label": copy.recordings.audio,
1798
3206
  onError: () => {
1799
3207
  clearPlayback();
1800
3208
  setError("audioError");
1801
3209
  }
1802
3210
  }
1803
- ), /* @__PURE__ */ import_react22.default.createElement("h4", null, copy.recordings.timeline), /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-timeline", role: "group", "aria-label": copy.recordings.timeline }, segments.map((segment) => /* @__PURE__ */ import_react22.default.createElement(
3211
+ ), /* @__PURE__ */ import_react25.default.createElement("h4", null, copy.recordings.timeline), /* @__PURE__ */ import_react25.default.createElement(
3212
+ TimelineWindow,
3213
+ {
3214
+ key: session.id,
3215
+ total: session.savedSamples,
3216
+ start: viewStart,
3217
+ end: Math.min(viewEnd, session.savedSamples),
3218
+ copy: copy.recordings,
3219
+ format: (sample) => duration(sample, session.sampleRate),
3220
+ onChange: (start, end) => {
3221
+ setWindowStart(start);
3222
+ setWindowEnd(end >= length ? null : end);
3223
+ },
3224
+ onReset: () => {
3225
+ setWindowStart(0);
3226
+ setWindowEnd(null);
3227
+ }
3228
+ }
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(
1804
3230
  "button",
1805
3231
  {
1806
3232
  key: segment.id,
1807
3233
  type: "button",
1808
- "data-kind": segment.kind,
3234
+ "data-kind": commandsFor(segment).length ? "command" : segment.kind,
1809
3235
  "aria-label": segmentName(segment),
1810
3236
  title: segmentName(segment),
1811
3237
  "aria-pressed": selectedSegment === segment.id,
1812
3238
  style: {
1813
- left: segment.startSample / length * 100 + "%",
1814
- width: Math.max(0, segment.endSample - segment.startSample) / length * 100 + "%"
3239
+ left: position(Math.max(viewStart, segment.startSample)),
3240
+ width: (Math.min(viewEnd, segment.endSample) - Math.max(viewStart, segment.startSample)) / viewLength * 100 + "%"
1815
3241
  },
1816
- onClick: () => {
1817
- clearPlayback();
1818
- setPlaying(false);
1819
- setSelectedSegment(segment.id);
1820
- }
3242
+ disabled: deleting || segment.endSample <= segment.startSample,
3243
+ onClick: () => void play(segment, false)
1821
3244
  }
1822
- )), session.pins.map((pin) => /* @__PURE__ */ import_react22.default.createElement(
3245
+ )), session.pins.filter((pin) => inWindow(pin.atSample)).map((pin) => /* @__PURE__ */ import_react25.default.createElement(
1823
3246
  "span",
1824
3247
  {
1825
3248
  key: pin.id,
1826
3249
  className: "dlvd-pin",
1827
3250
  "aria-hidden": "true",
1828
- style: { left: Math.min(length, pin.atSample) / length * 100 + "%" },
3251
+ style: { left: position(pin.atSample) },
1829
3252
  title: (pin.kind === "speech-start" ? copy.recordings.speech : copy.recordings.silence) + " \xB7 " + duration(pin.atSample, session.sampleRate)
1830
3253
  }
1831
- ))), /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlvd-timeline-legend" }, /* @__PURE__ */ import_react22.default.createElement("span", { "data-kind": "speech" }, copy.recordings.speech), " \xB7 ", /* @__PURE__ */ import_react22.default.createElement("span", { "data-kind": "silence" }, copy.recordings.silence), " \xB7 ", duration(session.savedSamples, session.sampleRate)), /* @__PURE__ */ import_react22.default.createElement("h4", null, copy.recordings.segments), /* @__PURE__ */ import_react22.default.createElement("table", { className: "dlvd-segments" }, /* @__PURE__ */ import_react22.default.createElement("thead", null, /* @__PURE__ */ import_react22.default.createElement("tr", null, /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.start), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.end), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.duration), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.audio))), /* @__PURE__ */ import_react22.default.createElement("tbody", null, segments.map((segment) => /* @__PURE__ */ import_react22.default.createElement(import_react22.default.Fragment, { key: segment.id }, /* @__PURE__ */ import_react22.default.createElement("tr", { "data-selected": selectedSegment === segment.id }, /* @__PURE__ */ import_react22.default.createElement("td", null, segment.kind === "speech" ? copy.recordings.speech : copy.recordings.silence, segment.open && /* @__PURE__ */ import_react22.default.createElement("small", null, copy.recordings.open)), /* @__PURE__ */ import_react22.default.createElement("td", null, duration(segment.startSample, session.sampleRate)), /* @__PURE__ */ import_react22.default.createElement("td", null, duration(segment.endSample, session.sampleRate)), /* @__PURE__ */ import_react22.default.createElement("td", null, duration(segment.endSample - segment.startSample, session.sampleRate)), /* @__PURE__ */ import_react22.default.createElement("td", null, /* @__PURE__ */ import_react22.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(
3285
+ "input",
3286
+ {
3287
+ type: "search",
3288
+ value: search,
3289
+ maxLength: 160,
3290
+ "aria-label": copy.recordings.search,
3291
+ onInput: (event) => setSearch(event.currentTarget.value)
3292
+ }
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(
1832
3294
  "button",
1833
3295
  {
1834
3296
  type: "button",
3297
+ className: "dlvd-segment-action",
3298
+ title: copy.recordings.play,
1835
3299
  disabled: deleting || segment.endSample <= segment.startSample,
1836
- "aria-label": copy.recordings.play + " \xB7 " + segmentName(segment),
3300
+ "aria-label": copy.recordings.play + " \xB7 " + sourceName(session, copy) + " \xB7 " + segmentName(segment),
1837
3301
  onClick: () => void play(segment)
1838
3302
  },
1839
- copy.recordings.play
1840
- ))), segment.kind === "speech" && /* @__PURE__ */ import_react22.default.createElement("tr", null, /* @__PURE__ */ import_react22.default.createElement("td", { colSpan: 5 }, /* @__PURE__ */ import_react22.default.createElement("details", { open: selectedSegment === segment.id || void 0 }, /* @__PURE__ */ import_react22.default.createElement("summary", null, copy.recordings.attempts, " \xB7 ", segment.attempts.length), !segment.attempts.length && /* @__PURE__ */ import_react22.default.createElement("p", null, copy.recordings.noAttempts), segment.attempts.map((attempt) => /* @__PURE__ */ import_react22.default.createElement("article", { className: "dlvd-attempt", key: attempt.id }, /* @__PURE__ */ import_react22.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", {
1841
3327
  pending: copy.recordings.pending,
1842
3328
  success: copy.recordings.success,
1843
3329
  error: copy.recordings.error
1844
- }[attempt.status]), /* @__PURE__ */ import_react22.default.createElement("time", { dateTime: attempt.createdAt }, new Date(attempt.createdAt).toLocaleString()), attempt.finishedAt && /* @__PURE__ */ import_react22.default.createElement(import_react22.default.Fragment, null, " ", "\u2014", " ", /* @__PURE__ */ import_react22.default.createElement("time", { dateTime: attempt.finishedAt }, new Date(attempt.finishedAt).toLocaleString())), /* @__PURE__ */ import_react22.default.createElement("div", null, duration(attempt.startSample, session.sampleRate), " \u2014", " ", duration(attempt.endSample, session.sampleRate)), attempt.text != null && /* @__PURE__ */ import_react22.default.createElement("p", { className: "dlvd-transcript" }, attempt.text), attempt.error && /* @__PURE__ */ import_react22.default.createElement("p", { className: "dlvd-attempt-error" }, copy.recordings.error, ": ", attempt.error)))))))))), /* @__PURE__ */ import_react22.default.createElement("details", null, /* @__PURE__ */ import_react22.default.createElement("summary", null, copy.recordings.pins, " \xB7 ", session.pins.length), /* @__PURE__ */ import_react22.default.createElement("table", null, /* @__PURE__ */ import_react22.default.createElement("thead", null, /* @__PURE__ */ import_react22.default.createElement("tr", null, /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.state), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.start), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.pinConfirmed), /* @__PURE__ */ import_react22.default.createElement("th", { scope: "col" }, copy.recordings.pinDelay))), /* @__PURE__ */ import_react22.default.createElement("tbody", null, session.pins.map((pin) => /* @__PURE__ */ import_react22.default.createElement("tr", { key: pin.id }, /* @__PURE__ */ import_react22.default.createElement("td", null, pin.kind === "speech-start" ? copy.recordings.speech : copy.recordings.silence), /* @__PURE__ */ import_react22.default.createElement("td", null, duration(pin.atSample, session.sampleRate)), /* @__PURE__ */ import_react22.default.createElement("td", null, duration(pin.confirmedAtSample, session.sampleRate)), /* @__PURE__ */ import_react22.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))))))));
1845
3334
  }
1846
3335
 
1847
3336
  // src/RuntimePanel.tsx
1848
- var import_react23 = __toESM(require("react"), 1);
3337
+ var import_react26 = __toESM(require("react"), 1);
1849
3338
  function RuntimePanel({ data, copy }) {
1850
3339
  const unknown = copy.runtimes.unavailable;
1851
3340
  const bytes = (n) => typeof n === "number" && Number.isFinite(n) ? (n / 1073741824).toFixed(3) + " GiB (" + Math.round(n).toLocaleString() + " B)" : unknown;
@@ -1861,41 +3350,42 @@ function RuntimePanel({ data, copy }) {
1861
3350
  released: copy.runtimes.released,
1862
3351
  cancelled: copy.runtimes.cancelled
1863
3352
  })[v] ?? unknown;
1864
- const row = (label, value) => /* @__PURE__ */ import_react23.default.createElement(import_react23.default.Fragment, { key: label }, /* @__PURE__ */ import_react23.default.createElement("dt", null, label), /* @__PURE__ */ import_react23.default.createElement("dd", null, value));
1865
- return /* @__PURE__ */ import_react23.default.createElement("section", { className: "dlvd-runtimes dlvd-scroll", tabIndex: 0, "aria-label": copy.runtimes.title }, /* @__PURE__ */ import_react23.default.createElement("h3", null, copy.runtimes.title, " ", /* @__PURE__ */ import_react23.default.createElement(Hint, { label: copy.runtimes.title }, copy.runtimes.help)), /* @__PURE__ */ import_react23.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(
1866
3355
  copy.runtimes.versions,
1867
3356
  (data.versions?.onnx ?? unknown) + " / " + (data.versions?.transformers ?? unknown)
1868
3357
  ), row(copy.runtimes.id, data.runtimeId ?? unknown), row(
1869
3358
  copy.runtimes.gpu,
1870
3359
  data.gpu === "available" ? copy.runtimes.yes : data.gpu === "unavailable" ? copy.runtimes.no : unknown
1871
- ), 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_react23.default.createElement("article", { className: "dlvd-runtime-card", key: model.key }, /* @__PURE__ */ import_react23.default.createElement("h3", null, model.name), /* @__PURE__ */ import_react23.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(
1872
3361
  copy.runtimes.device,
1873
3362
  model.device === "webgpu" ? "WebGPU" : model.device === "wasm" ? "WebAssembly CPU" : unknown
1874
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(
1875
3364
  copy.runtimes.ratio,
1876
3365
  model.workerInferenceMs != null && model.audioMs > 0 ? (model.workerInferenceMs / model.audioMs).toFixed(2) + "\xD7" : unknown
1877
- ), 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_react23.default.createElement("p", { role: "alert" }, copy.runtimes.error, ": ", /* @__PURE__ */ import_react23.default.createElement("code", null, model.error)))), /* @__PURE__ */ import_react23.default.createElement("h3", null, copy.runtimes.events), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dlvd-history" }, !data.events?.length && /* @__PURE__ */ import_react23.default.createElement("p", null, copy.commons.empty), [...data.events ?? []].reverse().map((event) => /* @__PURE__ */ import_react23.default.createElement("div", { key: event.sequence, "data-level": event.status === "error" ? "error" : "debug" }, /* @__PURE__ */ import_react23.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_react23.default.createElement(import_react23.default.Fragment, null, " ", "\xB7 ", /* @__PURE__ */ import_react23.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))))));
1878
3367
  }
1879
3368
 
1880
3369
  // src/DebuggerPanel.tsx
1881
3370
  function DebuggerPanel({
1882
3371
  copy,
3372
+ locale = "en",
1883
3373
  onClose,
1884
3374
  target = window
1885
3375
  }) {
1886
- const [tab, setTab] = import_react24.default.useState("recordings");
1887
- const tabsId = import_react24.default.useId();
1888
- const [frozen, setFrozen] = import_react24.default.useState(false);
1889
- const [refreshHz, setRefreshHz] = import_react24.default.useState(10);
1890
- const updateRef = import_react24.default.useRef(null);
1891
- const [content, setContent] = import_react24.default.useState(false);
1892
- const [module2, setModule] = import_react24.default.useState("");
1893
- const [view, setView] = import_react24.default.useState(null);
1894
- const [failed, setFailed] = import_react24.default.useState(false);
1895
- const [eventsPaused, setEventsPaused] = import_react24.default.useState(false);
1896
- const pausedRef = import_react24.default.useRef(false);
1897
- const inspectorRef = import_react24.default.useRef(null);
1898
- import_react24.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(() => {
1899
3389
  if (frozen) return;
1900
3390
  let inspector = null;
1901
3391
  let source = null;
@@ -1928,7 +3418,7 @@ function DebuggerPanel({
1928
3418
  inspectorRef.current = null;
1929
3419
  };
1930
3420
  }, [frozen, content, target]);
1931
- import_react24.default.useEffect(() => {
3421
+ import_react27.default.useEffect(() => {
1932
3422
  if (frozen) return;
1933
3423
  const timer = setInterval(() => updateRef.current?.(), 1e3 / refreshHz);
1934
3424
  return () => clearInterval(timer);
@@ -1937,10 +3427,10 @@ function DebuggerPanel({
1937
3427
  const runtimeData = view?.state?.runtimes;
1938
3428
  const hasRuntimes = !!runtimeData?.models?.length;
1939
3429
  const tabs = hasRuntimes ? ["recordings", "states", "runtimes"] : ["recordings", "states"];
1940
- import_react24.default.useEffect(() => {
3430
+ import_react27.default.useEffect(() => {
1941
3431
  if (tab === "runtimes" && !hasRuntimes) setTab("states");
1942
3432
  }, [tab, hasRuntimes]);
1943
- return /* @__PURE__ */ import_react24.default.createElement("aside", { className: "dlvd-panel", "aria-label": copy.commons.title }, /* @__PURE__ */ import_react24.default.createElement("header", null, /* @__PURE__ */ import_react24.default.createElement("strong", null, copy.commons.title), /* @__PURE__ */ import_react24.default.createElement("span", null, refreshHz, " Hz"), /* @__PURE__ */ import_react24.default.createElement("button", { type: "button", onClick: onClose, "aria-label": copy.commons.close }, "\xD7")), /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-tabs", role: "tablist", "aria-label": copy.commons.title }, tabs.map((name) => /* @__PURE__ */ import_react24.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(
1944
3434
  "button",
1945
3435
  {
1946
3436
  type: "button",
@@ -1962,7 +3452,7 @@ function DebuggerPanel({
1962
3452
  }
1963
3453
  },
1964
3454
  name === "recordings" ? copy.recordings.title : name === "runtimes" ? copy.runtimes.title : copy.commons.states
1965
- ))), /* @__PURE__ */ import_react24.default.createElement(
3455
+ ))), /* @__PURE__ */ import_react27.default.createElement(
1966
3456
  "div",
1967
3457
  {
1968
3458
  className: "dlvd-tab-panel",
@@ -1971,8 +3461,8 @@ function DebuggerPanel({
1971
3461
  "aria-labelledby": tabsId + "-recordings-tab",
1972
3462
  hidden: tab !== "recordings"
1973
3463
  },
1974
- tab === "recordings" && /* @__PURE__ */ import_react24.default.createElement(RecordingPanel, { copy, target })
1975
- ), hasRuntimes && /* @__PURE__ */ import_react24.default.createElement(
3464
+ tab === "recordings" && /* @__PURE__ */ import_react27.default.createElement(RecordingPanel, { copy, locale, target })
3465
+ ), hasRuntimes && /* @__PURE__ */ import_react27.default.createElement(
1976
3466
  "div",
1977
3467
  {
1978
3468
  className: "dlvd-tab-panel",
@@ -1981,8 +3471,8 @@ function DebuggerPanel({
1981
3471
  "aria-labelledby": tabsId + "-runtimes-tab",
1982
3472
  hidden: tab !== "runtimes"
1983
3473
  },
1984
- tab === "runtimes" && /* @__PURE__ */ import_react24.default.createElement(RuntimePanel, { data: runtimeData, copy })
1985
- ), /* @__PURE__ */ import_react24.default.createElement(
3474
+ tab === "runtimes" && /* @__PURE__ */ import_react27.default.createElement(RuntimePanel, { data: runtimeData, copy })
3475
+ ), /* @__PURE__ */ import_react27.default.createElement(
1986
3476
  "div",
1987
3477
  {
1988
3478
  className: "dlvd-tab-panel",
@@ -1991,15 +3481,15 @@ function DebuggerPanel({
1991
3481
  "aria-labelledby": tabsId + "-states-tab",
1992
3482
  hidden: tab !== "states"
1993
3483
  },
1994
- /* @__PURE__ */ import_react24.default.createElement("p", null, copy.commons.help),
1995
- /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react24.default.createElement("label", null, copy.commons.refreshRate, /* @__PURE__ */ import_react24.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(
1996
3486
  "select",
1997
3487
  {
1998
3488
  value: refreshHz,
1999
3489
  onChange: (event) => setRefreshHz(Number(event.target.value))
2000
3490
  },
2001
- [1, 2, 5, 10, 20].map((hz) => /* @__PURE__ */ import_react24.default.createElement("option", { key: hz, value: hz }, hz, " Hz"))
2002
- )), /* @__PURE__ */ import_react24.default.createElement("button", { type: "button", onClick: () => setFrozen(!frozen) }, frozen ? copy.commons.resume : copy.commons.freeze), /* @__PURE__ */ import_react24.default.createElement("label", null, /* @__PURE__ */ import_react24.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(
2003
3493
  "input",
2004
3494
  {
2005
3495
  type: "checkbox",
@@ -2011,7 +3501,7 @@ function DebuggerPanel({
2011
3501
  }
2012
3502
  }
2013
3503
  ), copy.commons.content)),
2014
- failed ? /* @__PURE__ */ import_react24.default.createElement("p", { role: "alert" }, copy.commons.error) : !view ? /* @__PURE__ */ import_react24.default.createElement("p", { role: "status" }, copy.commons.waiting) : /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-columns" }, /* @__PURE__ */ import_react24.default.createElement("section", { className: "dlvd-state-pane", "aria-label": copy.commons.states }, /* @__PURE__ */ import_react24.default.createElement("h3", null, copy.commons.states), /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-scroll", tabIndex: 0 }, /* @__PURE__ */ import_react24.default.createElement(StateTree, { name: "context", value: view.state, omitted: copy.commons.omitted }))), /* @__PURE__ */ import_react24.default.createElement("section", { className: "dlvd-event-pane", "aria-label": copy.commons.transitions }, /* @__PURE__ */ import_react24.default.createElement("h3", null, copy.commons.transitions), /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react24.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(
2015
3505
  "button",
2016
3506
  {
2017
3507
  type: "button",
@@ -2024,7 +3514,7 @@ function DebuggerPanel({
2024
3514
  }
2025
3515
  },
2026
3516
  eventsPaused ? copy.commons.eventsResume : copy.commons.eventsPause
2027
- ), /* @__PURE__ */ import_react24.default.createElement(
3517
+ ), /* @__PURE__ */ import_react27.default.createElement(
2028
3518
  "button",
2029
3519
  {
2030
3520
  type: "button",
@@ -2034,14 +3524,14 @@ function DebuggerPanel({
2034
3524
  }
2035
3525
  },
2036
3526
  copy.commons.eventsClear
2037
- )), /* @__PURE__ */ import_react24.default.createElement("label", null, copy.commons.filter, /* @__PURE__ */ import_react24.default.createElement("select", { value: module2, onChange: (event) => setModule(event.target.value) }, /* @__PURE__ */ import_react24.default.createElement("option", { value: "" }, copy.commons.all), modules.map((name) => /* @__PURE__ */ import_react24.default.createElement("option", { key: name }, name)))), /* @__PURE__ */ import_react24.default.createElement("div", { className: "dlvd-history dlvd-scroll", tabIndex: 0 }, view.transitions.filter((entry) => !module2 || entry.module === module2).slice().reverse().map((entry) => /* @__PURE__ */ import_react24.default.createElement("details", { key: entry.sequence, "data-level": entry.level }, /* @__PURE__ */ import_react24.default.createElement("summary", null, /* @__PURE__ */ import_react24.default.createElement("small", null, new Date(entry.timeMs).toLocaleTimeString()), " ", /* @__PURE__ */ import_react24.default.createElement("code", null, entry.sessionId, " / ", entry.module)), /* @__PURE__ */ import_react24.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(
2038
3528
  StateTree,
2039
3529
  {
2040
3530
  name: "before",
2041
3531
  value: entry.before,
2042
3532
  omitted: copy.commons.omitted
2043
3533
  }
2044
- ), /* @__PURE__ */ import_react24.default.createElement(StateTree, { name: "after", value: entry.after, omitted: copy.commons.omitted }))), !view.transitions.length && /* @__PURE__ */ import_react24.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))))
2045
3535
  ));
2046
3536
  }
2047
3537
 
@@ -2073,7 +3563,7 @@ function normalizeDebuggerLocale(locale) {
2073
3563
  }
2074
3564
 
2075
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
2076
- var import_react25 = require("react");
3566
+ var import_react28 = require("react");
2077
3567
 
2078
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
2079
3569
  function detectLanguage() {
@@ -2106,21 +3596,21 @@ function deepFreeze(value) {
2106
3596
 
2107
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
2108
3598
  function createTranslationRuntime({ availableLangs, defaultLang, onLanguageLoaded }) {
2109
- const context = (0, import_react25.createContext)({});
3599
+ const context = (0, import_react28.createContext)({});
2110
3600
  function useTranslation(selector) {
2111
- const value = (0, import_react25.useContext)(context);
3601
+ const value = (0, import_react28.useContext)(context);
2112
3602
  if (!value.current)
2113
3603
  throw new Error("useTranslation must be used within TranslationProvider.");
2114
3604
  return { ...value, scoped: selector ? selector(value.current.lang) : value.current.lang };
2115
3605
  }
2116
3606
  function TranslationProvider({ children }) {
2117
- const [saved, setSaved] = (0, import_react25.useState)();
2118
- const [language, setCurrentLanguage] = (0, import_react25.useState)();
3607
+ const [saved, setSaved] = (0, import_react28.useState)();
3608
+ const [language, setCurrentLanguage] = (0, import_react28.useState)();
2119
3609
  function setLanguage(nextLanguage, save) {
2120
3610
  if (save)
2121
3611
  setSaved(nextLanguage);
2122
3612
  }
2123
- (0, import_react25.useEffect)(() => {
3613
+ (0, import_react28.useEffect)(() => {
2124
3614
  void loadLanguage(availableLangs, defaultLang, saved).then((loadedLanguage) => {
2125
3615
  setCurrentLanguage(loadedLanguage);
2126
3616
  onLanguageLoaded?.(loadedLanguage);
@@ -2128,7 +3618,7 @@ function createTranslationRuntime({ availableLangs, defaultLang, onLanguageLoade
2128
3618
  }, [saved]);
2129
3619
  if (!language)
2130
3620
  return null;
2131
- return (0, import_react25.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);
2132
3622
  }
2133
3623
  return { TranslationProvider, useTranslation };
2134
3624
  }
@@ -2143,28 +3633,28 @@ var {
2143
3633
  });
2144
3634
 
2145
3635
  // src/i18n/DshLanguageBoundary.tsx
2146
- var import_react26 = __toESM(require("react"), 1);
3636
+ var import_react29 = __toESM(require("react"), 1);
2147
3637
  var fallback = Object.freeze({ active: "en", revision: 0 });
2148
3638
  function DshLanguageBoundary({ locale, children }) {
2149
3639
  const { current, setLanguage } = useDebuggerLanguage();
2150
- const subscribe = import_react26.default.useCallback(
3640
+ const subscribe = import_react29.default.useCallback(
2151
3641
  (listener) => locale?.subscribe?.(listener) ?? (() => {
2152
3642
  }),
2153
3643
  [locale]
2154
3644
  );
2155
- const snapshot = import_react26.default.useCallback(() => locale?.getSnapshot?.() ?? fallback, [locale]);
2156
- const state = import_react26.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);
2157
3647
  const expected = normalizeDebuggerLocale(state.active);
2158
- import_react26.default.useEffect(() => {
3648
+ import_react29.default.useEffect(() => {
2159
3649
  if (current.locale !== expected) setLanguage(expected, true);
2160
3650
  }, [current.locale, expected, setLanguage]);
2161
- const initialized = import_react26.default.useRef(false);
3651
+ const initialized = import_react29.default.useRef(false);
2162
3652
  if (current.locale === expected) initialized.current = true;
2163
3653
  return initialized.current ? children : null;
2164
3654
  }
2165
3655
  function withDebuggerLanguage(Component, locale) {
2166
3656
  function Localized(props) {
2167
- return /* @__PURE__ */ import_react26.default.createElement(DebuggerTranslationProvider, null, /* @__PURE__ */ import_react26.default.createElement(DshLanguageBoundary, { locale }, /* @__PURE__ */ import_react26.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 })));
2168
3658
  }
2169
3659
  Localized.displayName = "DebuggerLanguage(" + (Component.displayName || Component.name || "Component") + ")";
2170
3660
  return Localized;
@@ -2200,6 +3690,15 @@ function registerDshLocales(ctx) {
2200
3690
 
2201
3691
  // src/styles.ts
2202
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}
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}
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}
3701
+
2203
3702
  .dlvd-runtimes{overflow-wrap:anywhere}.dlvd-runtimes h3{margin:8px 0}.dlvd-runtime-card{border-top:1px solid var(--dsw-alias-border-l2,#ffffff1f);padding:12px 0}.dlvd-runtimes .dlvd-history>div{border-bottom:1px solid var(--dsw-alias-border-l1,#ffffff0f);padding:6px 0}.dlvd-tabs{flex-wrap:wrap}
2204
3703
 
2205
3704
  .dlvd-columns{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px;flex:1;min-height:0;overflow:hidden}.dlvd-state-pane,.dlvd-event-pane{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}.dlvd-event-pane{border-left:1px solid var(--dsw-alias-border-l1, #ffffff0f);padding-left:16px}.dlvd-columns h3{margin:0 0 10px}.dlvd-scroll{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:stable;padding-right:8px}.dlvd-scroll:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary, #7aaaff);outline-offset:-2px}.dlvd-history summary{overflow-wrap:anywhere}.dlvd-panel>header,.dlvd-panel>p,.dlvd-panel>.dlvd-toolbar{flex-shrink:0}
@@ -2269,11 +3768,19 @@ function apply(ctx) {
2269
3768
  notify();
2270
3769
  };
2271
3770
  function Host() {
2272
- const { scoped: labels } = useDebuggerLanguage();
2273
- import_react27.default.useEffect(() => {
3771
+ const { scoped: labels, current } = useDebuggerLanguage();
3772
+ import_react30.default.useEffect(() => {
2274
3773
  if (popup && !popup.closed) popup.document.title = labels.commons.title;
2275
3774
  }, [labels.commons.title]);
2276
- return /* @__PURE__ */ import_react27.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
+ );
2277
3784
  }
2278
3785
  const open = () => {
2279
3786
  if (popup && !popup.closed) {
@@ -2307,7 +3814,7 @@ function apply(ctx) {
2307
3814
  container.dataset.plugin = namespace;
2308
3815
  doc.body.appendChild(container);
2309
3816
  root = (0, import_client.createRoot)(container);
2310
- root.render(/* @__PURE__ */ import_react27.default.createElement(LocalizedHost, null));
3817
+ root.render(/* @__PURE__ */ import_react30.default.createElement(LocalizedHost, null));
2311
3818
  enabled = true;
2312
3819
  closedTimer = setInterval(() => {
2313
3820
  if (popup?.closed) close();
@@ -2322,9 +3829,9 @@ function apply(ctx) {
2322
3829
  }
2323
3830
  };
2324
3831
  function Settings() {
2325
- const failed = import_react27.default.useSyncExternalStore(subscribe, () => blocked);
3832
+ const failed = import_react30.default.useSyncExternalStore(subscribe, () => blocked);
2326
3833
  const { scoped: labels } = useDebuggerLanguage();
2327
- return /* @__PURE__ */ import_react27.default.createElement("div", { className: "dlv-diagnostics-launcher" }, /* @__PURE__ */ import_react27.default.createElement("button", { type: "button", className: "dlv-diagnostics-button", onClick: open }, /* @__PURE__ */ import_react27.default.createElement(Icon, { name: "bug" }), labels.commons.diagnostics), /* @__PURE__ */ import_react27.default.createElement(Hint, { label: labels.commons.diagnostics }, labels.commons.activate), failed && /* @__PURE__ */ import_react27.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));
2328
3835
  }
2329
3836
  const LocalizedHost = withDebuggerLanguage(Host, ctx.locale);
2330
3837
  const LocalizedSettings = withDebuggerLanguage(Settings, ctx.locale);