feat(app): option to turn off sound effects

This commit is contained in:
Adam
2026-02-12 15:03:02 -06:00
parent ff3b174c42
commit 4e0f509e7b
6 changed files with 106 additions and 24 deletions

View File

@@ -10,8 +10,11 @@ export const settingsNotificationsAgentSelector = '[data-action="settings-notifi
export const settingsNotificationsPermissionsSelector = '[data-action="settings-notifications-permissions"]' export const settingsNotificationsPermissionsSelector = '[data-action="settings-notifications-permissions"]'
export const settingsNotificationsErrorsSelector = '[data-action="settings-notifications-errors"]' export const settingsNotificationsErrorsSelector = '[data-action="settings-notifications-errors"]'
export const settingsSoundsAgentSelector = '[data-action="settings-sounds-agent"]' export const settingsSoundsAgentSelector = '[data-action="settings-sounds-agent"]'
export const settingsSoundsAgentEnabledSelector = '[data-action="settings-sounds-agent-enabled"]'
export const settingsSoundsPermissionsSelector = '[data-action="settings-sounds-permissions"]' export const settingsSoundsPermissionsSelector = '[data-action="settings-sounds-permissions"]'
export const settingsSoundsPermissionsEnabledSelector = '[data-action="settings-sounds-permissions-enabled"]'
export const settingsSoundsErrorsSelector = '[data-action="settings-sounds-errors"]' export const settingsSoundsErrorsSelector = '[data-action="settings-sounds-errors"]'
export const settingsSoundsErrorsEnabledSelector = '[data-action="settings-sounds-errors-enabled"]'
export const settingsUpdatesStartupSelector = '[data-action="settings-updates-startup"]' export const settingsUpdatesStartupSelector = '[data-action="settings-updates-startup"]'
export const settingsReleaseNotesSelector = '[data-action="settings-release-notes"]' export const settingsReleaseNotesSelector = '[data-action="settings-release-notes"]'

View File

@@ -9,6 +9,7 @@ import {
settingsNotificationsPermissionsSelector, settingsNotificationsPermissionsSelector,
settingsReleaseNotesSelector, settingsReleaseNotesSelector,
settingsSoundsAgentSelector, settingsSoundsAgentSelector,
settingsSoundsAgentEnabledSelector,
settingsSoundsErrorsSelector, settingsSoundsErrorsSelector,
settingsSoundsPermissionsSelector, settingsSoundsPermissionsSelector,
settingsThemeSelector, settingsThemeSelector,
@@ -335,6 +336,30 @@ test("changing sound agent selection persists in localStorage", async ({ page, g
expect(stored?.sounds?.agent).not.toBe("staplebops-01") expect(stored?.sounds?.agent).not.toBe("staplebops-01")
}) })
test("disabling agent sound disables sound selection", async ({ page, gotoSession }) => {
await gotoSession()
const dialog = await openSettings(page)
const select = dialog.locator(settingsSoundsAgentSelector)
const switchContainer = dialog.locator(settingsSoundsAgentEnabledSelector)
const trigger = select.locator('[data-slot="select-select-trigger"]')
await expect(select).toBeVisible()
await expect(switchContainer).toBeVisible()
await expect(trigger).toBeEnabled()
await switchContainer.locator('[data-slot="switch-control"]').click()
await page.waitForTimeout(100)
await expect(trigger).toBeDisabled()
const stored = await page.evaluate((key) => {
const raw = localStorage.getItem(key)
return raw ? JSON.parse(raw) : null
}, settingsKey)
expect(stored?.sounds?.agentEnabled).toBe(false)
})
test("changing permissions and errors sounds updates localStorage", async ({ page, gotoSession }) => { test("changing permissions and errors sounds updates localStorage", async ({ page, gotoSession }) => {
await gotoSession() await gotoSession()

View File

@@ -306,39 +306,66 @@ export const SettingsGeneral: Component = () => {
title={language.t("settings.general.sounds.agent.title")} title={language.t("settings.general.sounds.agent.title")}
description={language.t("settings.general.sounds.agent.description")} description={language.t("settings.general.sounds.agent.description")}
> >
<Select <div class="flex items-center gap-2">
data-action="settings-sounds-agent" <div data-action="settings-sounds-agent-enabled">
{...soundSelectProps( <Switch
() => settings.sounds.agent(), checked={settings.sounds.agentEnabled()}
(id) => settings.sounds.setAgent(id), onChange={(checked) => settings.sounds.setAgentEnabled(checked)}
)} />
/> </div>
<Select
disabled={!settings.sounds.agentEnabled()}
data-action="settings-sounds-agent"
{...soundSelectProps(
() => settings.sounds.agent(),
(id) => settings.sounds.setAgent(id),
)}
/>
</div>
</SettingsRow> </SettingsRow>
<SettingsRow <SettingsRow
title={language.t("settings.general.sounds.permissions.title")} title={language.t("settings.general.sounds.permissions.title")}
description={language.t("settings.general.sounds.permissions.description")} description={language.t("settings.general.sounds.permissions.description")}
> >
<Select <div class="flex items-center gap-2">
data-action="settings-sounds-permissions" <div data-action="settings-sounds-permissions-enabled">
{...soundSelectProps( <Switch
() => settings.sounds.permissions(), checked={settings.sounds.permissionsEnabled()}
(id) => settings.sounds.setPermissions(id), onChange={(checked) => settings.sounds.setPermissionsEnabled(checked)}
)} />
/> </div>
<Select
disabled={!settings.sounds.permissionsEnabled()}
data-action="settings-sounds-permissions"
{...soundSelectProps(
() => settings.sounds.permissions(),
(id) => settings.sounds.setPermissions(id),
)}
/>
</div>
</SettingsRow> </SettingsRow>
<SettingsRow <SettingsRow
title={language.t("settings.general.sounds.errors.title")} title={language.t("settings.general.sounds.errors.title")}
description={language.t("settings.general.sounds.errors.description")} description={language.t("settings.general.sounds.errors.description")}
> >
<Select <div class="flex items-center gap-2">
data-action="settings-sounds-errors" <div data-action="settings-sounds-errors-enabled">
{...soundSelectProps( <Switch
() => settings.sounds.errors(), checked={settings.sounds.errorsEnabled()}
(id) => settings.sounds.setErrors(id), onChange={(checked) => settings.sounds.setErrorsEnabled(checked)}
)} />
/> </div>
<Select
disabled={!settings.sounds.errorsEnabled()}
data-action="settings-sounds-errors"
{...soundSelectProps(
() => settings.sounds.errors(),
(id) => settings.sounds.setErrors(id),
)}
/>
</div>
</SettingsRow> </SettingsRow>
</div> </div>
</div> </div>

View File

@@ -233,7 +233,9 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
if (!session) return if (!session) return
if (session.parentID) return if (session.parentID) return
playSound(soundSrc(settings.sounds.agent())) if (settings.sounds.agentEnabled()) {
playSound(soundSrc(settings.sounds.agent()))
}
append({ append({
directory, directory,
@@ -260,7 +262,9 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
if (meta.disposed) return if (meta.disposed) return
if (session?.parentID) return if (session?.parentID) return
playSound(soundSrc(settings.sounds.errors())) if (settings.sounds.errorsEnabled()) {
playSound(soundSrc(settings.sounds.errors()))
}
const error = "error" in event.properties ? event.properties.error : undefined const error = "error" in event.properties ? event.properties.error : undefined
append({ append({

View File

@@ -10,8 +10,11 @@ export interface NotificationSettings {
} }
export interface SoundSettings { export interface SoundSettings {
agentEnabled: boolean
agent: string agent: string
permissionsEnabled: boolean
permissions: string permissions: string
errorsEnabled: boolean
errors: string errors: string
} }
@@ -57,8 +60,11 @@ const defaultSettings: Settings = {
errors: false, errors: false,
}, },
sounds: { sounds: {
agentEnabled: true,
agent: "staplebops-01", agent: "staplebops-01",
permissionsEnabled: true,
permissions: "staplebops-02", permissions: "staplebops-02",
errorsEnabled: true,
errors: "nope-03", errors: "nope-03",
}, },
} }
@@ -168,14 +174,29 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
}, },
}, },
sounds: { sounds: {
agentEnabled: withFallback(() => store.sounds?.agentEnabled, defaultSettings.sounds.agentEnabled),
setAgentEnabled(value: boolean) {
setStore("sounds", "agentEnabled", value)
},
agent: withFallback(() => store.sounds?.agent, defaultSettings.sounds.agent), agent: withFallback(() => store.sounds?.agent, defaultSettings.sounds.agent),
setAgent(value: string) { setAgent(value: string) {
setStore("sounds", "agent", value) setStore("sounds", "agent", value)
}, },
permissionsEnabled: withFallback(
() => store.sounds?.permissionsEnabled,
defaultSettings.sounds.permissionsEnabled,
),
setPermissionsEnabled(value: boolean) {
setStore("sounds", "permissionsEnabled", value)
},
permissions: withFallback(() => store.sounds?.permissions, defaultSettings.sounds.permissions), permissions: withFallback(() => store.sounds?.permissions, defaultSettings.sounds.permissions),
setPermissions(value: string) { setPermissions(value: string) {
setStore("sounds", "permissions", value) setStore("sounds", "permissions", value)
}, },
errorsEnabled: withFallback(() => store.sounds?.errorsEnabled, defaultSettings.sounds.errorsEnabled),
setErrorsEnabled(value: boolean) {
setStore("sounds", "errorsEnabled", value)
},
errors: withFallback(() => store.sounds?.errors, defaultSettings.sounds.errors), errors: withFallback(() => store.sounds?.errors, defaultSettings.sounds.errors),
setErrors(value: string) { setErrors(value: string) {
setStore("sounds", "errors", value) setStore("sounds", "errors", value)

View File

@@ -388,7 +388,9 @@ export default function Layout(props: ParentProps) {
alertedAtBySession.set(sessionKey, now) alertedAtBySession.set(sessionKey, now)
if (e.details.type === "permission.asked") { if (e.details.type === "permission.asked") {
playSound(soundSrc(settings.sounds.permissions())) if (settings.sounds.permissionsEnabled()) {
playSound(soundSrc(settings.sounds.permissions()))
}
if (settings.notifications.permissions()) { if (settings.notifications.permissions()) {
void platform.notify(title, description, href) void platform.notify(title, description, href)
} }