summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-12 15:03:02 -0600
committerAdam <[email protected]>2026-02-12 15:03:05 -0600
commit4e0f509e7b7d84395a541bdfa658f6c98f588221 (patch)
tree45912ad3c9b0d9cc532fa4d72fe20938a1273090 /packages
parentff3b174c423d89b39ee8154863840e48c8aac371 (diff)
downloadopencode-4e0f509e7b7d84395a541bdfa658f6c98f588221.tar.gz
opencode-4e0f509e7b7d84395a541bdfa658f6c98f588221.zip
feat(app): option to turn off sound effects
Diffstat (limited to 'packages')
-rw-r--r--packages/app/e2e/selectors.ts3
-rw-r--r--packages/app/e2e/settings/settings.spec.ts25
-rw-r--r--packages/app/src/components/settings-general.tsx69
-rw-r--r--packages/app/src/context/notification.tsx8
-rw-r--r--packages/app/src/context/settings.tsx21
-rw-r--r--packages/app/src/pages/layout.tsx4
6 files changed, 106 insertions, 24 deletions
diff --git a/packages/app/e2e/selectors.ts b/packages/app/e2e/selectors.ts
index 842433891..52c9007ea 100644
--- a/packages/app/e2e/selectors.ts
+++ b/packages/app/e2e/selectors.ts
@@ -10,8 +10,11 @@ export const settingsNotificationsAgentSelector = '[data-action="settings-notifi
export const settingsNotificationsPermissionsSelector = '[data-action="settings-notifications-permissions"]'
export const settingsNotificationsErrorsSelector = '[data-action="settings-notifications-errors"]'
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 settingsSoundsPermissionsEnabledSelector = '[data-action="settings-sounds-permissions-enabled"]'
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 settingsReleaseNotesSelector = '[data-action="settings-release-notes"]'
diff --git a/packages/app/e2e/settings/settings.spec.ts b/packages/app/e2e/settings/settings.spec.ts
index 42534968b..9fbcf79f5 100644
--- a/packages/app/e2e/settings/settings.spec.ts
+++ b/packages/app/e2e/settings/settings.spec.ts
@@ -9,6 +9,7 @@ import {
settingsNotificationsPermissionsSelector,
settingsReleaseNotesSelector,
settingsSoundsAgentSelector,
+ settingsSoundsAgentEnabledSelector,
settingsSoundsErrorsSelector,
settingsSoundsPermissionsSelector,
settingsThemeSelector,
@@ -335,6 +336,30 @@ test("changing sound agent selection persists in localStorage", async ({ page, g
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 }) => {
await gotoSession()
diff --git a/packages/app/src/components/settings-general.tsx b/packages/app/src/components/settings-general.tsx
index c673cab80..439f542bb 100644
--- a/packages/app/src/components/settings-general.tsx
+++ b/packages/app/src/components/settings-general.tsx
@@ -306,39 +306,66 @@ export const SettingsGeneral: Component = () => {
title={language.t("settings.general.sounds.agent.title")}
description={language.t("settings.general.sounds.agent.description")}
>
- <Select
- data-action="settings-sounds-agent"
- {...soundSelectProps(
- () => settings.sounds.agent(),
- (id) => settings.sounds.setAgent(id),
- )}
- />
+ <div class="flex items-center gap-2">
+ <div data-action="settings-sounds-agent-enabled">
+ <Switch
+ checked={settings.sounds.agentEnabled()}
+ 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
title={language.t("settings.general.sounds.permissions.title")}
description={language.t("settings.general.sounds.permissions.description")}
>
- <Select
- data-action="settings-sounds-permissions"
- {...soundSelectProps(
- () => settings.sounds.permissions(),
- (id) => settings.sounds.setPermissions(id),
- )}
- />
+ <div class="flex items-center gap-2">
+ <div data-action="settings-sounds-permissions-enabled">
+ <Switch
+ checked={settings.sounds.permissionsEnabled()}
+ 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
title={language.t("settings.general.sounds.errors.title")}
description={language.t("settings.general.sounds.errors.description")}
>
- <Select
- data-action="settings-sounds-errors"
- {...soundSelectProps(
- () => settings.sounds.errors(),
- (id) => settings.sounds.setErrors(id),
- )}
- />
+ <div class="flex items-center gap-2">
+ <div data-action="settings-sounds-errors-enabled">
+ <Switch
+ checked={settings.sounds.errorsEnabled()}
+ 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>
</div>
</div>
diff --git a/packages/app/src/context/notification.tsx b/packages/app/src/context/notification.tsx
index bf880d115..04bc2fdaa 100644
--- a/packages/app/src/context/notification.tsx
+++ b/packages/app/src/context/notification.tsx
@@ -233,7 +233,9 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
if (!session) return
if (session.parentID) return
- playSound(soundSrc(settings.sounds.agent()))
+ if (settings.sounds.agentEnabled()) {
+ playSound(soundSrc(settings.sounds.agent()))
+ }
append({
directory,
@@ -260,7 +262,9 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
if (meta.disposed) 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
append({
diff --git a/packages/app/src/context/settings.tsx b/packages/app/src/context/settings.tsx
index a8efb1eac..d72d4ceb1 100644
--- a/packages/app/src/context/settings.tsx
+++ b/packages/app/src/context/settings.tsx
@@ -10,8 +10,11 @@ export interface NotificationSettings {
}
export interface SoundSettings {
+ agentEnabled: boolean
agent: string
+ permissionsEnabled: boolean
permissions: string
+ errorsEnabled: boolean
errors: string
}
@@ -57,8 +60,11 @@ const defaultSettings: Settings = {
errors: false,
},
sounds: {
+ agentEnabled: true,
agent: "staplebops-01",
+ permissionsEnabled: true,
permissions: "staplebops-02",
+ errorsEnabled: true,
errors: "nope-03",
},
}
@@ -168,14 +174,29 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
},
},
sounds: {
+ agentEnabled: withFallback(() => store.sounds?.agentEnabled, defaultSettings.sounds.agentEnabled),
+ setAgentEnabled(value: boolean) {
+ setStore("sounds", "agentEnabled", value)
+ },
agent: withFallback(() => store.sounds?.agent, defaultSettings.sounds.agent),
setAgent(value: string) {
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),
setPermissions(value: string) {
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),
setErrors(value: string) {
setStore("sounds", "errors", value)
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 5f001177f..7eb064f42 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -388,7 +388,9 @@ export default function Layout(props: ParentProps) {
alertedAtBySession.set(sessionKey, now)
if (e.details.type === "permission.asked") {
- playSound(soundSrc(settings.sounds.permissions()))
+ if (settings.sounds.permissionsEnabled()) {
+ playSound(soundSrc(settings.sounds.permissions()))
+ }
if (settings.notifications.permissions()) {
void platform.notify(title, description, href)
}