دورة حياة Voice Overlay (macOS)
الجمهور المستهدف: المساهمون في تطبيق macOS. الهدف: إبقاء Voice Overlay متوقعة السلوك عندما يتداخل wake-word وpush-to-talk.الهدف الحالي
- إذا كانت الـ overlay ظاهرة بالفعل نتيجة wake-word وضغط المستخدم مفتاح الاختصار، فإن جلسة مفتاح الاختصار تتبنى النص الموجود بدلًا من إعادة تعيينه. وتبقى الـ overlay ظاهرة ما دام مفتاح الاختصار مضغوطًا. وعندما يحرر المستخدم المفتاح: يتم الإرسال إذا كان هناك نص بعد التشذيب، وإلا يتم الإخفاء.
- ما تزال wake-word وحدها ترسل تلقائيًا عند الصمت؛ بينما تقوم push-to-talk بالإرسال فورًا عند التحرير.
ما تم تنفيذه (9 ديسمبر 2025)
- أصبحت جلسات الـ overlay الآن تحمل token لكل عملية التقاط (wake-word أو push-to-talk). ويتم إسقاط تحديثات partial/final/send/dismiss/level عندما لا يتطابق token، مما يمنع callbacks القديمة.
- تتبنى push-to-talk أي نص ظاهر في الـ overlay كبادئة (لذلك فإن الضغط على مفتاح الاختصار بينما تكون wake overlay ظاهرة يبقي النص ويُلحق الكلام الجديد). وهي تنتظر حتى 1.5 ثانية للحصول على نص نهائي قبل العودة إلى النص الحالي.
- يتم إصدار سجلات chime/overlay عند المستوى
infoضمن الفئاتvoicewake.overlayوvoicewake.pttوvoicewake.chime(بدء الجلسة، والنص الجزئي، والنص النهائي، والإرسال، والإخفاء، وسبب chime).
الخطوات التالية
- VoiceSessionCoordinator (actor)
- يملك
VoiceSessionواحدة فقط في كل مرة. - API (قائم على token):
beginWakeCaptureوbeginPushToTalkوupdatePartialوendCaptureوcancelوapplyCooldown. - يسقط callbacks التي تحمل tokens قديمة (لمنع أدوات التعرف القديمة من إعادة فتح الـ overlay).
- يملك
- VoiceSession (model)
- الحقول:
tokenوsource(wakeWord|pushToTalk) والنص الملتزم/المتغير، وأعلام chime، والمؤقتات (الإرسال التلقائي، والخمول)، وoverlayMode(display|editing|sending)، وموعد نهائي للـ cooldown.
- الحقول:
- ربط الـ overlay
- تقوم
VoiceSessionPublisher(ObservableObject) بعكس الجلسة النشطة إلى SwiftUI. - يقوم
VoiceWakeOverlayViewبالعرض فقط عبر الناشر؛ ولا يغيّر singletons العامة مباشرة أبدًا. - تستدعي إجراءات المستخدم في الـ overlay (
sendNowوdismissوedit) الـ coordinator مرة أخرى باستخدام token الخاص بالجلسة.
- تقوم
- مسار إرسال موحّد
- عند
endCapture: إذا كان النص المشذب فارغًا ← إخفاء؛ وإلاperformSend(session:)(يشغّل chime الإرسال مرة واحدة، ثم يمرر، ثم يُخفي). - Push-to-talk: من دون تأخير؛ wake-word: تأخير اختياري للإرسال التلقائي.
- طبّق cooldown قصيرة على وقت تشغيل wake بعد انتهاء push-to-talk حتى لا تُعيد wake-word التشغيل فورًا.
- عند
- التسجيل
- يصدر الـ coordinator سجلات
.infoفي subsystem ai.openclaw، والفئتينvoicewake.overlayوvoicewake.chime. - الأحداث الأساسية:
session_startedوadopted_by_push_to_talkوpartialوfinalizedوsendوdismissوcancelوcooldown.
- يصدر الـ coordinator سجلات
قائمة التحقق من تصحيح الأخطاء
-
قم ببث السجلات أثناء إعادة إنتاج overlay العالقة:
- تحقق من وجود token واحدة فقط لجلسة نشطة؛ يجب أن يُسقط الـ coordinator callbacks القديمة.
-
تأكد من أن تحرير push-to-talk يستدعي دائمًا
endCaptureباستخدام token النشطة؛ وإذا كان النص فارغًا، فتوقعdismissمن دون chime أو إرسال.
خطوات الترحيل (مقترحة)
- أضف
VoiceSessionCoordinatorوVoiceSessionوVoiceSessionPublisher. - أعد هيكلة
VoiceWakeRuntimeبحيث ينشئ/يحدّث/ينهي الجلسات بدلًا من لمسVoiceWakeOverlayControllerمباشرة. - أعد هيكلة
VoicePushToTalkبحيث تتبنى الجلسات الحالية وتستدعيendCaptureعند التحرير؛ ثم طبّق cooldown لوقت التشغيل. - اربط
VoiceWakeOverlayControllerبالناشر؛ وأزل الاستدعاءات المباشرة من runtime/PTT. - أضف اختبارات تكامل لتبني الجلسات، وcooldown، وإخفاء النص الفارغ.