Skip to content

fix(overlay/audio): finalize recording overlay and restore media output controls#506

Open
NathanSkene wants to merge 15 commits intoOpenWhispr:mainfrom
NathanSkene:pr/recording-overlay-refinements
Open

fix(overlay/audio): finalize recording overlay and restore media output controls#506
NathanSkene wants to merge 15 commits intoOpenWhispr:mainfrom
NathanSkene:pr/recording-overlay-refinements

Conversation

@NathanSkene
Copy link
Copy Markdown
Contributor

@NathanSkene NathanSkene commented Mar 26, 2026

Summary

Depends on #502 (recording overlay). Apply after that PR is merged, or review together.

  • finalize the recording overlay behavior and force-hide the legacy floating panel so duplicate indicators do not reappear
  • keep the refined overlay positioning and existing duplicate-indicator suppression fixes
  • restore separate dictation media controls for pausing media and muting system output
  • bundle the macOS media helper required for packaged media control
  • defer pause/mute work off the recording start hot path so dictation starts remain responsive

Verification

  • npm run typecheck
  • npm run build:renderer
  • Rebuilt and installed /Applications/OpenWhispr Dev.app during local validation

Note: this branch still includes the base overlay commits from #502, so the diff will shrink after that PR merges.

NathanSkene and others added 8 commits March 26, 2026 09:31
Adds a small always-on-top overlay window that appears during recording,
showing a live audio waveform. The overlay follows the cursor across
monitors and can be toggled in settings.

- New RecordingOverlay component with real-time waveform canvas
- RecordingOverlayWindow managed by windowManager
- Audio level forwarding from main process to overlay renderer
- Settings toggle for enabling/disabling overlay
…icate indicator

- Resize overlay pill from 320x64 to 160x32, reposition from top to bottom
- Toggle overlay visibility with recording state (show on start, hide on stop)
- Add recording-overlay-root to transparent background selector group
- Suppress DictationWidget indicator when recording overlay is active (BUG-01)
…bled

When the recording overlay pill is active, the mainWindow (floating icon)
is now hidden to prevent dual indicators on screen. The mainWindow is
shown first to deliver IPC messages, then immediately hidden. On recording
stop, the floating icon is restored. Applies to both tap-to-talk and
push-to-talk modes.

Addresses OVR-01.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ollowing

- Replace linear amplification (level * 3) with non-linear curve
  Math.pow(level * 20, 0.7) for visible bars during normal speech
- Reduce BUFFER_SIZE from 40 to 24 so all bars fit within canvas
- Expand canvas CSS from 60x16px to 120x28px to match bar geometry
- Update RECORDING_OVERLAY_CONFIG dimensions to 260x52px
- Add position parameter to getRecordingOverlayPosition (bottom-left,
  center, bottom-right) and pass panelStartPosition from windowManager
- Change default panelStartPosition from bottom-right to center

Addresses OVR-02 and OVR-03.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Panel

When overlay is enabled, send IPC directly to mainWindow webContents
without calling showDictationPanel() first. The webContents is alive
even when the window is hidden, so IPC still arrives. This prevents the
mainWindow from briefly appearing behind the overlay during recording.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the mainWindow is hidden during recording (overlay mode), Chromium
throttles its timers and callbacks. This prevents onAudioLevel from
firing, so the overlay never receives level updates and stays invisible.
Adding backgroundThrottling: false ensures audio level callbacks continue
when the window is hidden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When recording overlay is enabled, the floating icon (mainWindow) is now
fully replaced by the overlay pill:
- Suppress mainWindow on app launch (ready-to-show and safety timeout)
- Don't restore mainWindow after recording stops
- The mainWindow webContents stays alive for IPC but is never shown

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- React hooks must be called unconditionally
- Moved overlay suppression check after useEffect hook call
@NathanSkene
Copy link
Copy Markdown
Contributor Author

Manual validation update on macOS with the packaged installed app:

This refinement layer passed validation.

What I verified:

  • the smaller overlay pill is less intrusive in real use
  • the overlay positioning/refinement behavior works as expected
  • the duplicate blue indicator is suppressed when the overlay is enabled
  • the refined overlay behavior works correctly in the installed app during actual dictation use

This is the overlay variant I ended up using in the validated daily-driver build.

NathanSkene and others added 4 commits March 27, 2026 12:53
- Add kTCCServiceAudioCapture fallback for macOS 26 Tahoe TCC split
- Replace MediaRemote-first with AppleScript direct control of Spotify/Music
- Track individually paused apps for reliable resume

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… TypeScript errors

- Make onAudioLevel optional in audioManager.js setCallbacks (2 errors)
- Add recording overlay IPC declarations to electron.ts (3 errors)
- Add update notification IPC declarations to electron.ts (4 errors)
- Add showRecordingOverlay passthrough to useSettings.ts (2 errors)
- Add notifyRecordingOverlayEnabledChanged to electron.ts (1 error)
@NathanSkene NathanSkene changed the title fix(overlay): refine recording overlay positioning and suppress duplicate indicator fix(overlay/audio): finalize recording overlay and restore media output controls Mar 27, 2026
set itemName to name of mi
repeat with targetLabel in targetLabels
if itemName is (contents of targetLabel) then
click mi

Check failure

Code scanning / CodeQL

Incomplete string escaping or encoding High

This does not escape backslash characters in the input.
repeat with targetLabel in targetLabels
if itemName is (contents of targetLabel) then
click mi
return itemName

Check failure

Code scanning / CodeQL

Incomplete string escaping or encoding High

This does not escape backslash characters in the input.
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.

2 participants