Skip to content

Play macOS's built-in sounds with new-email notifications, so Do Not Disturb silences them - #1207

Open
timche wants to merge 2 commits into
mainfrom
macos-builtin-notification-sounds
Open

timche wants to merge 2 commits into
mainfrom
macos-builtin-notification-sounds

Conversation

@timche

@timche timche commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

On macOS, Meru builds new-email notifications with silent: true and plays its own .wav from the renderer on the notification's show event. show fires even while Do Not Disturb or a Focus is suppressing the notification, so the sound plays through it, for example overnight. Every free user is affected, since an unlicensed copy always plays Linen.

Handing a bundled sound to macOS (#1102) was tried and closed. Measured on 20 Oct 2026 on macOS 27: NotificationCenter caches each app bundle's Contents/Resources listing. After an in-place update it plays the default alert for every bundled name, or nothing, until NotificationCenter restarts. Built-in sounds in /System/Library/Sounds always resolve, and Do Not Disturb silences a sound carried by the notification whatever its source.

Change

On macOS only:

  • New-email notifications carry the sound by name (sound: "Blow", silent: false), and nothing is played on show.
  • Sounds: the picker lists the 14 built-in sounds plus the existing System option. It stays a Pro feature. Free users get Blow, which is also the macOS default for new installs.
  • Stored values: a stored bundled name (Breeze, Chime, Duet, Knock, Linen) reads as Blow. No new migration. The existing >=3.35.0 step's accepted list gains the macOS names, because the ladder reruns on a fresh install's second launch and would otherwise reset a picked macOS sound to Linen.
  • Volume: the volume slider is hidden, since macOS sets the alert volume.
  • Preview: picking a sound previews it through /usr/bin/afplay in the main process (new IPC channel notifications.previewSound). The name is checked against the 14 known names.
  • Bursts: NotificationCenter presents and sounds only the last notification of a same-instant burst, so Play the new email sound once for a burst of notifications #1200's "first one sounds" rule would make a burst silent. When the 5 s quiet rule allows a sound, every notification posted in the same task carries it. The task is one inbox check's synchronous loop, closed by setImmediate. After that, the 5 s sliding rule applies as before.

Windows and Linux are unchanged: bundled sounds played by the renderer, and #1200's rule.

Proof

Signed arm64 builds (op run --env-file .env.signing.op -- bun run build:mac:dmg), unlicensed, fresh profile. A script counts renderer HTMLMediaElement.prototype.play calls and sends five notifications.showTestNotification back to back. Do Not Disturb is toggled with a Shortcut, and NotificationCenter and donotdisturbd logs are captured.

Before (main), Do Not Disturb on. The banners are suppressed, but Meru plays the sound anyway:

5x  outcome: suppressed; reason: mode configuration type
renderer plays: 1

After (this branch), Do Not Disturb on:

renderer plays, +8s: 0
5x  outcome: suppressed; reason: mode configuration type
Not playing sound for <ident:"0C69-BE02">: consoleConnected: true, isMuted: true, hasSound: true
Playing notification sound lines: 0

After, Do Not Disturb off, five at once:

renderer plays, +8s: 0
[com.apple.unc:sound] Playing notification sound { nam: Blow } for sh.zoid.meru
audio file path /System/Library/Sounds/Blow.aiff
Playing notification sound lines: 1

The Do Not Disturb-on run and the spaced runs were measured on the first commit, with Glass as the default: five notifications 1 s apart gave one sound, with the other four hasSound: false, and two 6 s apart gave two. The five-at-once Do Not Disturb-off run above is from the Blow commit.

bun run fmt:check, lint, types and bun test --isolate (1554 pass) are green.

Risk

  • Every macOS user's sound changes from Linen, or their pick, to Blow. The changelog line says so.
  • Send test notification is now silent under Do Not Disturb too.
  • The settings preview plays at output volume, which can be louder than the alert volume.

Revert the commit to restore the bundled sounds on macOS. Stored macOS names then fall back through the renderer path as Linen.

Not verified

  • What is actually heard. The headless Mac mini reports "Device is currently muted", so the play requests were checked in the log, not the audio output.
  • A licensed signed build. The picker was checked in a licensed dev run only, and the System option in a signed build was not run.
  • Focus modes other than Do Not Disturb, the multi-account wake case beyond unit tests, and upgrading an existing install in place.
  • Windows and Linux at runtime. Their code paths are unchanged and covered by unit tests.

Settings, Notifications, Sound on macOS with Blow selected

The macOS sound picker open, listing the built-in sounds

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