Skip to content

fix: fixes the little mic icon when you stop talking in voice mode#9593

Closed
phact wants to merge 1 commit into
mainfrom
little-mic-icon-fix
Closed

fix: fixes the little mic icon when you stop talking in voice mode#9593
phact wants to merge 1 commit into
mainfrom
little-mic-icon-fix

Conversation

@phact
Copy link
Copy Markdown
Collaborator

@phact phact commented Aug 28, 2025

This pull request includes a small improvement to the audio input handling in the VoiceAssistant component. Now, when closing the audio input, the code ensures that the audioContext is properly closed and cleaned up.

Summary by CodeRabbit

  • Bug Fixes
    • Ensures voice input properly releases audio resources when closed, preventing stuck microphone states and reducing the chance of audio issues on subsequent uses.
  • Performance
    • Lowers CPU and memory usage by cleaning up audio processing after voice input ends, improving battery life and overall responsiveness.
  • Stability
    • Improves reliability of the voice assistant by avoiding lingering audio sessions, leading to smoother start/stop behavior across browsers and devices.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 28, 2025

Walkthrough

Adds a cleanup in VoiceAssistant.handleCloseAudioInput to close audioContextRef.current and reset it to null when present; no other functional changes.

Changes

Cohort / File(s) Summary
Voice Assistant Cleanup
src/frontend/src/modals/IOModal/components/chatView/chatInput/components/voice-assistant/voice-assistant.tsx
In handleCloseAudioInput, close audioContextRef.current if defined and set it to null; no signature or other logic changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant VA as VoiceAssistant
  participant AC as AudioContext (audioContextRef.current)

  User->>VA: handleCloseAudioInput()
  alt audioContext exists
    VA->>AC: close()
    AC-->>VA: closed
    VA->>VA: audioContextRef.current = null
  else no audioContext
    VA->>VA: no-op
  end
  VA-->>User: return
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch little-mic-icon-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions Bot added the bug Something isn't working label Aug 28, 2025
@sonarqubecloud
Copy link
Copy Markdown

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Aug 28, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/frontend/src/modals/IOModal/components/chatView/chatInput/components/voice-assistant/voice-assistant.tsx (2)

94-99: Bug: undefined identifier in hook deps (open is not in scope)

hasOpenAIAPIKey’s deps include open, which is not defined in this component scope. This will fail TypeScript/lint and can break hook memoization.

Apply this diff to fix the deps:

   const hasOpenAIAPIKey = useMemo(() => {
     return (
       variables?.find((variable) => variable === "OPENAI_API_KEY")?.length! > 0
     );
-  }, [variables, open, addKey]);
+  }, [variables, addKey]);

Optional clarity (same behavior, cleaner):

-  const hasOpenAIAPIKey = useMemo(() => {
-    return (
-      variables?.find((variable) => variable === "OPENAI_API_KEY")?.length! > 0
-    );
-  }, [variables, addKey]);
+  const hasOpenAIAPIKey = useMemo(
+    () => Boolean(variables?.some((v) => v === "OPENAI_API_KEY")),
+    [variables, addKey],
+  );

320-343: Remove setTimeout race; await AudioContext close before re-init

When reopening after settings, you close() the AudioContext and then rely on setTimeout(…, 100) to reinitialize. AudioContext.close() is async and timing can vary; racing re-init can cause intermittent errors or visual glitches (e.g., mic state).

Apply this diff to make re-init deterministic:

-    if (open) {
+    if (open) {
       stopRecording();
       if (audioContextRef.current) {
-        audioContextRef.current.close();
+        await audioContextRef.current.close();
         audioContextRef.current = null;
       }
       setIsRecording(false);
     } else {
       setRecordingTime(0);
       setBarHeights(Array(30).fill(20));
 
       if (hasOpenAIAPIKey) {
         if (audioContextRef.current) {
-          audioContextRef.current.close();
+          await audioContextRef.current.close();
           audioContextRef.current = null;
         }
         analyserRef.current = null;
 
-        setTimeout(() => {
-          initializeAudio();
-          startRecording();
-          setIsRecording(true);
-        }, 100);
+        await initializeAudio();
+        await startRecording();
+        setIsRecording(true);
       }
     }

Note: initializeAudio/startRecording already return promises (or can be made to). If they’re sync wrappers today, marking them async with await-ed internals is fine.

🧹 Nitpick comments (2)
src/frontend/src/modals/IOModal/components/chatView/chatInput/components/voice-assistant/voice-assistant.tsx (2)

278-289: Unmount effect dependencies: make it a true mount/unmount effect

This effect looks intended for mount work + cleanup. Tying it to setShowAudioInput is unusual and brittle; the setter identity is stable and won’t drive re-runs meaningfully.

-  }, [setShowAudioInput]);
+  }, []);

282-289: DRY up AudioContext shutdown into a helper

You close + null the AudioContext in three places. Centralize to avoid drift and future mistakes.

Example helper (place inside component body):

const closeAudioContext = async () => {
  const ctx = audioContextRef.current;
  if (!ctx) return;
  try {
    await ctx.close();
  } finally {
    audioContextRef.current = null;
  }
};

Then replace the repeated blocks with:

await closeAudioContext();

Also applies to: 320-325, 331-335, 303-306

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 59d64f0 and 8fdc433.

📒 Files selected for processing (1)
  • src/frontend/src/modals/IOModal/components/chatView/chatInput/components/voice-assistant/voice-assistant.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/frontend/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

src/frontend/src/**/*.{ts,tsx,js,jsx}: All frontend TypeScript and JavaScript code should be located under src/frontend/src/ and organized into components, pages, icons, stores, types, utils, hooks, services, and assets directories as per the specified directory layout.
Use React 18 with TypeScript for all UI components in the frontend.
Format all TypeScript and JavaScript code using the make format_frontend command.
Lint all TypeScript and JavaScript code using the make lint command.

Files:

  • src/frontend/src/modals/IOModal/components/chatView/chatInput/components/voice-assistant/voice-assistant.tsx
🔇 Additional comments (2)
src/frontend/src/modals/IOModal/components/chatView/chatInput/components/voice-assistant/voice-assistant.tsx (2)

303-306: AudioContext cleanup on close — good fix

Closing and nulling audioContextRef when the user clicks the close button is correct and aligns with the PR’s goal to stop the little mic icon from lingering. No issues with the sequence given stopRecording() is invoked first.


355-374: Confirm desired semantics for “Mute” toggle vs full stop

Toggling recording currently only disables/enables media tracks; it doesn’t stop the processor/WS pipeline. That’s fine if you want a quick “mute” without tearing down resources, but it will keep network/audio graph resources alive.

If the intent is to fully stop resources on mute:

-    if (isRecording) {
+    if (isRecording) {
       if (microphoneRef?.current && microphoneRef?.current?.mediaStream) {
         microphoneRef.current.mediaStream.getAudioTracks().forEach((track) => {
           track.enabled = false;
         });
       }
       setBarHeights(Array(30).fill(20));
-      setIsRecording(false);
+      stopRecording();
+      setIsRecording(false);
     } else {

Would you like me to propagate this change and test for regressions?

@Cristhianzl
Copy link
Copy Markdown
Member

hey @phact

I have this #9623 with the fix already.
Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants