Skip to content

Add microphone device selection and permission handling#212

Open
traumafactory0 wants to merge 1 commit intosohzm:masterfrom
traumafactory0:master
Open

Add microphone device selection and permission handling#212
traumafactory0 wants to merge 1 commit intosohzm:masterfrom
traumafactory0:master

Conversation

@traumafactory0
Copy link

@traumafactory0 traumafactory0 commented Feb 11, 2026

Problem

Some mics (e.g. Logitech G733) didn’t work: the app didn’t ask for permission in a way that exposes the device list, so sessions could fail.

What changed

  • Settings → Audio: “Allow access & list devices” triggers the system mic permission; user can then choose the device (e.g. G733). Choice is saved.
  • Session start: On Windows, mic is requested first (with saved device), then screen — so the permission dialog shows when you click Start.
  • Fallback: If screen+audio fails, we retry with screen-only so the session still starts.
  • UI: Device list is a dark-themed custom dropdown so it’s readable in the overlay.
  • Files: src/storage.js, src/utils/renderer.js, src/components/views/CustomizeView.js

Summary by CodeRabbit

Release Notes

  • New Features
    • Added microphone device selection dropdown in audio settings for choosing preferred input devices.
    • System automatically detects available audio input devices and handles permission requests.
    • Enhanced audio capture workflow with improved microphone handling across platforms.

@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

This change introduces audio input device selection functionality for the application. Users can now select a preferred microphone device from a dropdown in the customize view. The selected device is persisted in storage and applied as a constraint during audio capture operations in the renderer module.

Changes

Cohort / File(s) Summary
Audio Input Device UI
src/components/views/CustomizeView.js
New audio input device selection interface with dropdown, device list display, loading/error states, request device access button, and associated state management (audioInputDeviceId, audioInputDevices, audioInputDevicesLoading, audioInputDevicesError, audioDeviceDropdownOpen).
Storage Configuration
src/storage.js
Added audioInputDeviceId to DEFAULT_PREFERENCES object to persist user's selected microphone device choice.
Audio Device Enumeration & Capture
src/utils/renderer.js
New getAudioInputDevices() function to request mic permission and enumerate available input devices with labels. Applied selected device ID to audio constraints in startCapture flow. Enhanced Windows mic + display capture sequence with proper permission ordering and constraint passing.

Sequence Diagram

sequenceDiagram
    participant User as User (Settings)
    participant UI as CustomizeView
    participant Storage as Storage
    participant Browser as Browser API
    participant Capture as Renderer (Capture)
    
    User->>UI: Opens Settings
    UI->>Browser: getAudioInputDevices()
    Browser-->>Browser: Request mic permission
    Browser-->>UI: Returns device list [{deviceId, label}]
    UI->>UI: Displays dropdown with devices
    User->>UI: Selects device
    UI->>Storage: Save audioInputDeviceId
    
    User->>Capture: Start Recording
    Capture->>Storage: Load audioInputDeviceId
    Capture->>Browser: startCapture() with<br/>audio constraints (deviceId)
    Browser-->>Capture: Stream with selected device
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A whisker-twitch of joy, I say,
As microphones now have their say,
Select the voice you wish to hear,
From dropdowns bright and crystal clear!
Audio flows where users choose—
No device confusion, nothing to lose! 🎙️

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding microphone device selection UI and permission handling logic across multiple components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@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: 1

🤖 Fix all issues with AI agents
In `@src/components/views/CustomizeView.js`:
- Around line 489-494: The updated() hook never sees changes because
audioDeviceDropdownOpen is not declared as a reactive property; add
audioDeviceDropdownOpen to the component's static properties (with type: Boolean
and a default of false) so changedProperties.has('audioDeviceDropdownOpen')
becomes true when it toggles and the existing logic that sets
_audioDropdownListenerAdded and calls document.addEventListener('click',
this._closeAudioDropdown) will run; keep _audioDropdownListenerAdded and
_closeAudioDropdown as-is (or alternatively, if you prefer Option B, remove the
updated() logic and instead attach/detach the document click listener directly
inside the dropdown toggle handler).

Comment on lines +489 to +494
updated(changedProperties) {
if (changedProperties.has('audioDeviceDropdownOpen') && this.audioDeviceDropdownOpen && !this._audioDropdownListenerAdded) {
this._audioDropdownListenerAdded = true;
setTimeout(() => document.addEventListener('click', this._closeAudioDropdown), 0);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bug: outside-click handler is never attached — audioDeviceDropdownOpen is not a reactive property.

changedProperties.has('audioDeviceDropdownOpen') will always be false because audioDeviceDropdownOpen is not declared in static properties (lines 297–315). LitElement's changedProperties map only tracks reactive properties. As a result, _closeAudioDropdown is never registered on document, and clicking outside the dropdown won't close it.

A quick fix is to add audioDeviceDropdownOpen to static properties, or attach/detach the listener directly in the toggle handler instead of relying on updated().

Option A: declare it as a reactive property (simplest)
 static properties = {
     selectedProfile: { type: String },
     selectedLanguage: { type: String },
     selectedImageQuality: { type: String },
     layoutMode: { type: String },
     keybinds: { type: Object },
     googleSearchEnabled: { type: Boolean },
     backgroundTransparency: { type: Number },
     fontSize: { type: Number },
     theme: { type: String },
     onProfileChange: { type: Function },
     onLanguageChange: { type: Function },
     onImageQualityChange: { type: Function },
     onLayoutModeChange: { type: Function },
     isClearing: { type: Boolean },
     isRestoring: { type: Boolean },
     clearStatusMessage: { type: String },
     clearStatusType: { type: String },
+    audioDeviceDropdownOpen: { type: Boolean },
 };
Option B: attach listener directly in the toggle handler
 `@click`=${(e) => {
     e.stopPropagation();
     if (this.audioInputDevicesLoading || this.audioInputDevices.length === 0) return;
     this.audioDeviceDropdownOpen = !this.audioDeviceDropdownOpen;
+    if (this.audioDeviceDropdownOpen) {
+        setTimeout(() => document.addEventListener('click', this._closeAudioDropdown), 0);
+    } else {
+        document.removeEventListener('click', this._closeAudioDropdown);
+    }
     this.requestUpdate();
 }}

Then updated() can be removed entirely.

🤖 Prompt for AI Agents
In `@src/components/views/CustomizeView.js` around lines 489 - 494, The updated()
hook never sees changes because audioDeviceDropdownOpen is not declared as a
reactive property; add audioDeviceDropdownOpen to the component's static
properties (with type: Boolean and a default of false) so
changedProperties.has('audioDeviceDropdownOpen') becomes true when it toggles
and the existing logic that sets _audioDropdownListenerAdded and calls
document.addEventListener('click', this._closeAudioDropdown) will run; keep
_audioDropdownListenerAdded and _closeAudioDropdown as-is (or alternatively, if
you prefer Option B, remove the updated() logic and instead attach/detach the
document click listener directly inside the dropdown toggle handler).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant