Skip to content

Add configurable feedback sounds (closes #107)#270

Open
swimmer-303 wants to merge 1 commit into
zachlatta:mainfrom
swimmer-303:split/feedback-sounds
Open

Add configurable feedback sounds (closes #107)#270
swimmer-303 wants to merge 1 commit into
zachlatta:mainfrom
swimmer-303:split/feedback-sounds

Conversation

@swimmer-303

@swimmer-303 swimmer-303 commented Jul 11, 2026

Copy link
Copy Markdown

Split out from #254. This PR carries only the feedback-sounds change — the menu-bar redesign and settings-sidebar redesign that shared the original combined commit are now separate PRs.

The hardcoded Tink/Pop/Basso start/stop/error sounds are now per-event settings with pickers and preview buttons covering all 14 built-in macOS alert sounds. Defaults match the old behavior.

Closes #107

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added configurable sounds for recording start, recording stop, and error notifications.
    • Added Settings pickers with preview buttons for built-in macOS alert sounds.
    • Selected sounds are saved and used throughout recording workflows.
  • Documentation

    • Updated the changelog with details about configurable recording feedback sounds.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds persisted configuration for recording start, stop, and error sounds, Settings pickers with previews, and runtime playback helpers that replace hard-coded macOS alert sounds.

Changes

Recording sound configuration

Layer / File(s) Summary
Sound settings persistence
Sources/AppState.swift
Adds persisted sound-name properties with defaults and helper methods for start, stop, and error playback.
Runtime sound playback
Sources/AppState.swift
Recording, permission, validation, and screenshot-error paths use the configured sound helpers.
Settings sound controls
Sources/SettingsView.swift, CHANGELOG.md
Adds catalog-backed pickers and preview buttons for the three recording events, with an Unreleased changelog entry.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SettingsView
  participant AppState
  participant UserDefaults
  participant macOS as macOS alert sounds

  SettingsView->>AppState: Select or preview a sound
  AppState->>UserDefaults: Persist selected sound name
  AppState->>macOS: Play configured start, stop, or error sound
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: configurable feedback sounds for recording events.
Linked Issues check ✅ Passed The PR adds per-event configurable start, stop, and error sounds with preview controls, matching #107's request for more options.
Out of Scope Changes check ✅ Passed The changes stay focused on feedback sound configuration and a related changelog entry.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
Sources/SettingsView.swift (2)

1334-1341: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit accessibility label to icon-only preview buttons.

.help("Preview this sound") alone doesn't clearly convey which sound is being previewed to VoiceOver users when there are three consecutive icon-only buttons. An explicit accessibilityLabel naming the event (e.g. "Preview (label) sound") would improve clarity.

♿️ Suggested fix
             Button {
                 appState.playAlertSound(named: selection.wrappedValue)
             } label: {
                 Image(systemName: "play.circle")
             }
             .buttonStyle(.plain)
             .foregroundStyle(.secondary)
             .help("Preview this sound")
+            .accessibilityLabel("Preview \(label) sound")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SettingsView.swift` around lines 1334 - 1341, Add an explicit
accessibility label to the icon-only preview Button in the sound settings
section, using the associated event name (for example, “Preview \(label) sound”)
so VoiceOver identifies which sound will play. Keep the existing help text and
button behavior unchanged.

1316-1343: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Per-row preview buttons don't surface the muted/low-volume hint.

The top-level "Preview" button (Lines 1271-1278) checks SystemAudioStatus.isDefaultOutputMuted()/defaultOutputVolume() and shows a hint when audio is muted or very low. The per-sound preview buttons added in soundPickerRow skip this check entirely, so a user previewing the stop/error sound via its dedicated button gets no explanation if nothing is audible.

♻️ Suggested fix: reuse the muted-hint check for row previews
     private func soundPickerRow(_ label: String, selection: Binding<String>) -> some View {
         var options = Self.systemSoundNames
         if !options.contains(selection.wrappedValue) {
             options.append(selection.wrappedValue)
         }
         return HStack(spacing: 8) {
             Text(label)
                 .font(.caption)
             Spacer()
             Picker("", selection: selection) {
                 ForEach(options, id: \.self) { name in
                     Text(name).tag(name)
                 }
             }
             .labelsHidden()
             .frame(width: 130)
             Button {
+                let muted = SystemAudioStatus.isDefaultOutputMuted()
+                let volume = SystemAudioStatus.defaultOutputVolume()
+                withAnimation(.easeInOut(duration: 0.2)) {
+                    showMutedHint = muted || (volume ?? 1) < 0.10
+                }
                 appState.playAlertSound(named: selection.wrappedValue)
             } label: {
                 Image(systemName: "play.circle")
             }
             .buttonStyle(.plain)
             .foregroundStyle(.secondary)
             .help("Preview this sound")
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SettingsView.swift` around lines 1316 - 1343, Update soundPickerRow
to perform the same muted/low-volume check used by the top-level Preview action
before or alongside appState.playAlertSound(named:). Reuse the existing
SystemAudioStatus helpers and hint presentation mechanism so each per-sound
preview button surfaces the muted/low-volume explanation consistently.
Sources/AppState.swift (1)

2442-2445: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a shared, typed catalog instead of raw String sound names.

startSoundName/stopSoundName/errorSoundName are plain strings validated only in SettingsView's picker catalog. A typo or externally-set defaults write value silently no-ops in NSSound(named:) with no fallback/validation here. An enum (or a shared constant list) for the 14 built-in sounds, referenced by both AppState and SettingsView, would give compile-time safety and a single source of truth.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/AppState.swift` around lines 2442 - 2445, Replace the raw string
sound-name properties used by playStartSound(), playStopSound(), and
playErrorSound() with a shared typed catalog (such as an enum) containing all 14
built-in sounds; update SettingsView’s picker to use that same catalog and map
persisted or externally supplied values through validated fallback logic before
calling playAlertSound, ensuring NSSound receives only known sound identifiers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@Sources/AppState.swift`:
- Around line 2442-2445: Replace the raw string sound-name properties used by
playStartSound(), playStopSound(), and playErrorSound() with a shared typed
catalog (such as an enum) containing all 14 built-in sounds; update
SettingsView’s picker to use that same catalog and map persisted or externally
supplied values through validated fallback logic before calling playAlertSound,
ensuring NSSound receives only known sound identifiers.

In `@Sources/SettingsView.swift`:
- Around line 1334-1341: Add an explicit accessibility label to the icon-only
preview Button in the sound settings section, using the associated event name
(for example, “Preview \(label) sound”) so VoiceOver identifies which sound will
play. Keep the existing help text and button behavior unchanged.
- Around line 1316-1343: Update soundPickerRow to perform the same
muted/low-volume check used by the top-level Preview action before or alongside
appState.playAlertSound(named:). Reuse the existing SystemAudioStatus helpers
and hint presentation mechanism so each per-sound preview button surfaces the
muted/low-volume explanation consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e253d5fc-3dc3-459b-9048-85c82a20e95b

📥 Commits

Reviewing files that changed from the base of the PR and between 7427ca9 and 4b422d3.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • Sources/AppState.swift
  • Sources/SettingsView.swift

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow custom sounds (or more options) for recording start, stop, and error feedback

1 participant