macOS companion app
Голосовой оверлей
Жизненный цикл голосового оверлея (macOS)
Аудитория: контрибьюторы приложения для macOS. Цель: сделать поведение голосового оверлея предсказуемым при наложении слова активации и режима «нажми и говори».
Текущий замысел
- Если оверлей уже виден из-за слова активации и пользователь нажимает горячую клавишу, сессия горячей клавиши принимает существующий текст вместо его сброса. Оверлей остается видимым, пока горячая клавиша удерживается. Когда пользователь отпускает ее: отправить, если после обрезки пробелов есть текст, иначе закрыть.
- Только слово активации по-прежнему автоматически отправляет при тишине; режим «нажми и говори» отправляет сразу при отпускании.
Реализовано (9 декабря 2025 г.)
- Сессии оверлея теперь несут токен для каждого захвата (слово активации или режим «нажми и говори»). Обновления partial/final/send/dismiss/level отбрасываются, если токен не совпадает, что предотвращает устаревшие обратные вызовы.
- Режим «нажми и говори» принимает любой видимый текст оверлея как префикс (поэтому нажатие горячей клавиши при открытом оверлее активации сохраняет текст и добавляет новую речь). Он ждет до 1,5 с финальную транскрипцию, прежде чем откатиться к текущему тексту.
- Логи звукового сигнала/оверлея выводятся на уровне
infoв категорияхvoicewake.overlay,voicewake.pttиvoicewake.chime(старт сессии, partial, final, send, dismiss, причина звукового сигнала).
Следующие шаги
- VoiceSessionCoordinator (актор)
- Владеет ровно одной
VoiceSessionза раз. - API (на основе токенов):
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown. - Отбрасывает обратные вызовы с устаревшими токенами (предотвращает повторное открытие оверлея старыми распознавателями).
- Владеет ровно одной
- VoiceSession (модель)
- Поля:
token,source(wakeWord|pushToTalk), зафиксированный/изменяемый текст, флаги звукового сигнала, таймеры (auto-send, idle),overlayMode(display|editing|sending), срок окончания cooldown.
- Поля:
- Привязка оверлея
VoiceSessionPublisher(ObservableObject) зеркалирует активную сессию в SwiftUI.VoiceWakeOverlayViewотображает состояние только через publisher; он никогда не изменяет глобальные singleton напрямую.- Действия пользователя в оверлее (
sendNow,dismiss,edit) вызывают координатор с токеном сессии.
- Единый путь отправки
- При
endCapture: если текст после обрезки пробелов пустой → закрыть; иначеperformSend(session:)(один раз воспроизводит звуковой сигнал отправки, пересылает, закрывает). - Режим «нажми и говори»: без задержки; слово активации: опциональная задержка для auto-send.
- Применить короткий cooldown к runtime слова активации после завершения режима «нажми и говори», чтобы слово активации не сработало повторно сразу.
- При
- Логирование
- Координатор выводит логи
.infoв subsystemai.openclaw, категорииvoicewake.overlayиvoicewake.chime. - Ключевые события:
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown.
- Координатор выводит логи
Контрольный список отладки
-
Потоково просматривайте логи при воспроизведении залипающего оверлея:
bash sudo log stream --predicate 'subsystem == "ai.openclaw" AND category CONTAINS "voicewake"' --level info --style compact -
Проверьте, что активен только один токен сессии; устаревшие обратные вызовы должны отбрасываться координатором.
-
Убедитесь, что отпускание в режиме «нажми и говори» всегда вызывает
endCaptureс активным токеном; если текст пустой, ожидайтеdismissбез звукового сигнала или отправки.
Шаги миграции (предлагаемые)
- Добавить
VoiceSessionCoordinator,VoiceSessionиVoiceSessionPublisher. - Отрефакторить
VoiceWakeRuntime, чтобы он создавал/обновлял/завершал сессии вместо прямого обращения кVoiceWakeOverlayController. - Отрефакторить
VoicePushToTalk, чтобы он принимал существующие сессии и вызывалendCaptureпри отпускании; применять cooldown runtime. - Подключить
VoiceWakeOverlayControllerк publisher; удалить прямые вызовы из runtime/PTT. - Добавить интеграционные тесты для принятия сессии, cooldown и закрытия при пустом тексте.
Связанные материалы
Was this useful?