Ciclo de vida de la superposición de voz (macOS)
Audiencia: colaboradores de la app para macOS. Objetivo: mantener la superposición de voz predecible cuando se superponen la palabra de activación y pulsar para hablar.Intención actual
- Si la superposición ya es visible por la palabra de activación y el usuario presiona la tecla rápida, la sesión de la tecla rápida adopta el texto existente en lugar de restablecerlo. La superposición permanece visible mientras se mantiene presionada la tecla. Cuando el usuario suelta: enviar si hay texto recortado; de lo contrario, descartar.
- La palabra de activación por sí sola aún envía automáticamente al detectar silencio; pulsar para hablar envía inmediatamente al soltar.
Implementado (9 de diciembre de 2025)
- Las sesiones de superposición ahora llevan un token por captura (palabra de activación o pulsar para hablar). Las actualizaciones parciales/finales/enviar/descartar/nivel se descartan cuando el token no coincide, evitando callbacks obsoletos.
- Pulsar para hablar adopta cualquier texto visible de la superposición como prefijo (por lo que presionar la tecla rápida mientras la superposición de palabra de activación está activa conserva el texto y agrega el nuevo discurso). Espera hasta 1.5 s por una transcripción final antes de recurrir al texto actual.
- El registro de timbres/superposición se emite en
infoen las categoríasvoicewake.overlay,voicewake.pttyvoicewake.chime(inicio de sesión, parcial, final, enviar, descartar, motivo del timbre).
Siguientes pasos
- VoiceSessionCoordinator (actor)
- Posee exactamente un
VoiceSessiona la vez. - API (basada en tokens):
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown. - Descarta callbacks que llevan tokens obsoletos (evita que reconocedores antiguos vuelvan a abrir la superposición).
- Posee exactamente un
- VoiceSession (modelo)
- Campos:
token,source(wakeWord|pushToTalk), texto comprometido/volátil, indicadores de timbre, temporizadores (envío automático, inactividad),overlayMode(display|editing|sending), fecha límite de enfriamiento.
- Campos:
- Vinculación de la superposición
VoiceSessionPublisher(ObservableObject) refleja la sesión activa en SwiftUI.VoiceWakeOverlayViewrenderiza solo a través del publicador; nunca muta directamente singletons globales.- Las acciones del usuario en la superposición (
sendNow,dismiss,edit) llaman de vuelta al coordinador con el token de la sesión.
- Ruta de envío unificada
- En
endCapture: si el texto recortado está vacío → descartar; de lo contrarioperformSend(session:)(reproduce el timbre de envío una sola vez, reenvía y descarta). - Pulsar para hablar: sin demora; palabra de activación: demora opcional para el envío automático.
- Aplique un breve enfriamiento al runtime de palabra de activación después de que finalice pulsar para hablar para que la palabra de activación no se dispare inmediatamente.
- En
- Registro
- El coordinador emite registros
.infoen el subsistemabot.molt, categoríasvoicewake.overlayyvoicewake.chime. - Eventos clave:
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown.
- El coordinador emite registros
Lista de verificación de depuración
-
Transmita los registros mientras reproduce una superposición pegajosa:
- Verifique que solo haya un token de sesión activo; el coordinador debe descartar los callbacks obsoletos.
-
Asegúrese de que al soltar pulsar para hablar siempre se llame a
endCapturecon el token activo; si el texto está vacío, esperedismisssin timbre ni envío.
Pasos de migración (sugeridos)
- Agregue
VoiceSessionCoordinator,VoiceSessionyVoiceSessionPublisher. - Refactorice
VoiceWakeRuntimepara crear/actualizar/finalizar sesiones en lugar de tocarVoiceWakeOverlayControllerdirectamente. - Refactorice
VoicePushToTalkpara adoptar sesiones existentes y llamar aendCaptureal soltar; aplique enfriamiento en el runtime. - Conecte
VoiceWakeOverlayControlleral publicador; elimine las llamadas directas desde el runtime/PTT. - Agregue pruebas de integración para adopción de sesiones, enfriamiento y descarte con texto vacío.