@agimon-ai/doompi-voice 0.0.1-alpha.113 → 0.0.1-alpha.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -585,10 +585,12 @@ function Meter({ tone }) {
585
585
  * it out, so listening, hearing, transcribing and narrating each read as
586
586
  * themselves rather than as a word tucked inside a chip.
587
587
  */
588
- function VoiceActivitySection({ sessionId, sendSessionFrame, statuses }) {
588
+ function VoiceActivitySection({ sessionId, statuses }) {
589
589
  const view = voiceActivityView(statuses["doom-voice"]);
590
590
  const [liveStartPending, setLiveStartPending] = useState(false);
591
591
  const [liveStartError, setLiveStartError] = useState(null);
592
+ const [microphonePending, setMicrophonePending] = useState(false);
593
+ const [microphoneError, setMicrophoneError] = useState(null);
592
594
  const controlGlobalLive = async (action) => {
593
595
  if (sessionId === null || liveStartPending) return;
594
596
  setLiveStartPending(true);
@@ -677,13 +679,20 @@ function VoiceActivitySection({ sessionId, sendSessionFrame, statuses }) {
677
679
  }
678
680
  const mediaConflict = sessionId !== null && browserState?.sessionId === sessionId && browserState.phase === "conflict" && view.mode !== "off";
679
681
  const tone = mediaConflict ? "attention" : view.tone;
680
- const sendCommand = (message) => {
681
- if (sessionId !== null) sendSessionFrame(sessionId, {
682
- type: "prompt",
683
- message
684
- });
682
+ const toggleAutonomousMicrophone = async () => {
683
+ if (sessionId === null || microphonePending) return;
684
+ setMicrophonePending(true);
685
+ setMicrophoneError(null);
686
+ try {
687
+ const action = view.microphoneMuted ? "unmute" : "mute";
688
+ const result = await voice.session(sessionId).control({ body: { action } });
689
+ if (!result.ok) throw new Error(result.error || "Microphone control request failed.");
690
+ } catch (error) {
691
+ setMicrophoneError(error instanceof Error ? error.message : String(error));
692
+ } finally {
693
+ setMicrophonePending(false);
694
+ }
685
695
  };
686
- const toggleAutonomousMicrophone = () => sendCommand(`/voice-auto ${view.microphoneMuted ? "unmute" : "mute"}`);
687
696
  const showAutonomousMicrophoneControl = !mediaConflict && view.mode === "auto" && view.phase !== "starting" && view.phase !== "draining";
688
697
  return /* @__PURE__ */ jsxs("div", {
689
698
  "data-testid": "voice-activity",
@@ -722,7 +731,8 @@ function VoiceActivitySection({ sessionId, sendSessionFrame, statuses }) {
722
731
  "aria-label": `${view.microphoneMuted ? "unmute" : "mute"} autonomous voice microphone`,
723
732
  "aria-pressed": view.microphoneMuted,
724
733
  title: `${view.microphoneMuted ? "unmute" : "mute"} autonomous voice microphone`,
725
- onClick: toggleAutonomousMicrophone,
734
+ disabled: sessionId === null || microphonePending,
735
+ onClick: () => void toggleAutonomousMicrophone(),
726
736
  children: view.microphoneMuted ? "unmute" : "mute"
727
737
  }) : null,
728
738
  !mediaConflict && view.active ? /* @__PURE__ */ jsx(Meter, { tone: view.tone }) : null
@@ -733,6 +743,11 @@ function VoiceActivitySection({ sessionId, sendSessionFrame, statuses }) {
733
743
  className: "text-2xs leading-relaxed text-doom-yellow",
734
744
  children: ["Live control failed: ", liveStartError]
735
745
  }) : null,
746
+ microphoneError !== null ? /* @__PURE__ */ jsxs("span", {
747
+ role: "alert",
748
+ className: "text-2xs leading-relaxed text-doom-yellow",
749
+ children: ["Microphone control failed: ", microphoneError]
750
+ }) : null,
736
751
  mediaConflict || view.detail ? /* @__PURE__ */ jsx("span", {
737
752
  "data-testid": "voice-detail",
738
753
  className: "text-2xs leading-relaxed text-doom-faint",