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, причина звукового сигнала).

Следующие шаги

  1. VoiceSessionCoordinator (актор)
    • Владеет ровно одной VoiceSession за раз.
    • API (на основе токенов): beginWakeCapture, beginPushToTalk, updatePartial, endCapture, cancel, applyCooldown.
    • Отбрасывает обратные вызовы с устаревшими токенами (предотвращает повторное открытие оверлея старыми распознавателями).
  2. VoiceSession (модель)
    • Поля: token, source (wakeWord|pushToTalk), зафиксированный/изменяемый текст, флаги звукового сигнала, таймеры (auto-send, idle), overlayMode (display|editing|sending), срок окончания cooldown.
  3. Привязка оверлея
    • VoiceSessionPublisher (ObservableObject) зеркалирует активную сессию в SwiftUI.
    • VoiceWakeOverlayView отображает состояние только через publisher; он никогда не изменяет глобальные singleton напрямую.
    • Действия пользователя в оверлее (sendNow, dismiss, edit) вызывают координатор с токеном сессии.
  4. Единый путь отправки
    • При endCapture: если текст после обрезки пробелов пустой → закрыть; иначе performSend(session:) (один раз воспроизводит звуковой сигнал отправки, пересылает, закрывает).
    • Режим «нажми и говори»: без задержки; слово активации: опциональная задержка для auto-send.
    • Применить короткий cooldown к runtime слова активации после завершения режима «нажми и говори», чтобы слово активации не сработало повторно сразу.
  5. Логирование
    • Координатор выводит логи .info в subsystem ai.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 без звукового сигнала или отправки.

Шаги миграции (предлагаемые)

  1. Добавить VoiceSessionCoordinator, VoiceSession и VoiceSessionPublisher.
  2. Отрефакторить VoiceWakeRuntime, чтобы он создавал/обновлял/завершал сессии вместо прямого обращения к VoiceWakeOverlayController.
  3. Отрефакторить VoicePushToTalk, чтобы он принимал существующие сессии и вызывал endCapture при отпускании; применять cooldown runtime.
  4. Подключить VoiceWakeOverlayController к publisher; удалить прямые вызовы из runtime/PTT.
  5. Добавить интеграционные тесты для принятия сессии, cooldown и закрытия при пустом тексте.

Связанные материалы

Was this useful?
On this page

On this page