Ciclo de vida da sobreposição de voz (macOS)
Público: colaboradores do app para macOS. Objetivo: manter a sobreposição de voz previsível quando a palavra de ativação e o push-to-talk se sobrepõem.Intenção atual
- Se a sobreposição já estiver visível por causa da palavra de ativação e o usuário pressionar a tecla de atalho, a sessão da tecla de atalho adota o texto existente em vez de redefini-lo. A sobreposição permanece visível enquanto a tecla de atalho estiver pressionada. Quando o usuário soltar: enviar se houver texto aparado; caso contrário, dispensar.
- A palavra de ativação sozinha ainda envia automaticamente no silêncio; push-to-talk envia imediatamente ao soltar.
Implementado (9 de dezembro de 2025)
- As sessões da sobreposição agora carregam um token por captura (palavra de ativação ou push-to-talk). Atualizações de parcial/final/envio/dispensa/nível são descartadas quando o token não corresponde, evitando callbacks antigos.
- Push-to-talk adota qualquer texto visível na sobreposição como prefixo (portanto, pressionar a tecla de atalho enquanto a sobreposição de ativação estiver visível preserva o texto e acrescenta nova fala). Ele espera até 1,5 s por uma transcrição final antes de recorrer ao texto atual.
- O registro em log do som/da sobreposição é emitido em
infonas categoriasvoicewake.overlay,voicewake.pttevoicewake.chime(início da sessão, parcial, final, envio, dispensa, motivo do som).
Próximas etapas
- VoiceSessionCoordinator (actor)
- Controla exatamente uma
VoiceSessionpor vez. - API (baseada em token):
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown. - Descarta callbacks que carregam tokens antigos (impede que reconhecedores antigos reabram a sobreposição).
- Controla exatamente uma
- VoiceSession (model)
- Campos:
token,source(wakeWord|pushToTalk), texto confirmado/volátil, sinalizadores de som, temporizadores (envio automático, inatividade),overlayMode(display|editing|sending), prazo de cooldown.
- Campos:
- Vinculação da sobreposição
VoiceSessionPublisher(ObservableObject) espelha a sessão ativa no SwiftUI.VoiceWakeOverlayViewrenderiza apenas via o publisher; nunca altera singletons globais diretamente.- As ações do usuário na sobreposição (
sendNow,dismiss,edit) chamam de volta o coordinator com o token da sessão.
- Caminho de envio unificado
- Em
endCapture: se o texto aparado estiver vazio → dispensar; caso contrário,performSend(session:)(toca o som de envio uma vez, encaminha, dispensa). - Push-to-talk: sem atraso; palavra de ativação: atraso opcional para envio automático.
- Aplique um cooldown curto ao runtime de ativação após o término do push-to-talk para que a palavra de ativação não dispare novamente imediatamente.
- Em
- Registro em log
- O coordinator emite logs
.infono subsistemaai.openclaw, nas categoriasvoicewake.overlayevoicewake.chime. - Eventos principais:
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown.
- O coordinator emite logs
Checklist de depuração
-
Transmita os logs enquanto reproduz uma sobreposição persistente:
- Verifique se há apenas um token de sessão ativo; callbacks antigos devem ser descartados pelo coordinator.
-
Garanta que a liberação do push-to-talk sempre chame
endCapturecom o token ativo; se o texto estiver vazio, esperedismisssem som nem envio.
Etapas de migração (sugeridas)
- Adicionar
VoiceSessionCoordinator,VoiceSessioneVoiceSessionPublisher. - Refatorar
VoiceWakeRuntimepara criar/atualizar/encerrar sessões em vez de tocarVoiceWakeOverlayControllerdiretamente. - Refatorar
VoicePushToTalkpara adotar sessões existentes e chamarendCaptureao soltar; aplicar cooldown no runtime. - Conectar
VoiceWakeOverlayControllerao publisher; remover chamadas diretas do runtime/PTT. - Adicionar testes de integração para adoção de sessão, cooldown e dispensa com texto vazio.