Skip to content

Commit 41612b3

Browse files
authored
Move auto-accept permissions to settings (anomalyco#21308)
1 parent c2d2ca3 commit 41612b3

4 files changed

Lines changed: 59 additions & 55 deletions

File tree

packages/app/e2e/prompt/prompt-shell.spec.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ToolPart } from "@opencode-ai/sdk/v2/client"
22
import { test, expect } from "../fixtures"
3-
import { withSession } from "../actions"
3+
import { closeDialog, openSettings, withSession } from "../actions"
44
import { promptModelSelector, promptSelector, promptVariantSelector } from "../selectors"
55

66
const isBash = (part: unknown): part is ToolPart => {
@@ -19,12 +19,15 @@ test("shell mode runs a command in the project directory", async ({ page, projec
1919
await withSession(project.sdk, `e2e shell ${Date.now()}`, async (session) => {
2020
project.trackSession(session.id)
2121
await project.gotoSession(session.id)
22-
const button = page.locator('[data-action="prompt-permissions"]').first()
23-
await expect(button).toBeVisible()
24-
if ((await button.getAttribute("aria-pressed")) !== "true") {
25-
await button.click()
26-
await expect(button).toHaveAttribute("aria-pressed", "true")
22+
const dialog = await openSettings(page)
23+
const toggle = dialog.locator('[data-action="settings-auto-accept-permissions"]').first()
24+
const input = toggle.locator('[data-slot="switch-input"]').first()
25+
await expect(toggle).toBeVisible()
26+
if ((await input.getAttribute("aria-checked")) !== "true") {
27+
await toggle.locator('[data-slot="switch-control"]').click()
28+
await expect(input).toHaveAttribute("aria-checked", "true")
2729
}
30+
await closeDialog(page, dialog)
2831
await project.shell(cmd)
2932

3033
await expect

packages/app/e2e/session/session-composer-dock.spec.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
type ComposerProbeState,
66
type ComposerWindow,
77
} from "../../src/testing/session-composer"
8-
import { cleanupSession, clearSessionDockSeed, seedSessionQuestion } from "../actions"
8+
import { cleanupSession, clearSessionDockSeed, closeDialog, openSettings, seedSessionQuestion } from "../actions"
99
import {
1010
permissionDockSelector,
1111
promptSelector,
@@ -65,12 +65,14 @@ async function clearPermissionDock(page: any, label: RegExp) {
6565
}
6666

6767
async function setAutoAccept(page: any, enabled: boolean) {
68-
const button = page.locator('[data-action="prompt-permissions"]').first()
69-
await expect(button).toBeVisible()
70-
const pressed = (await button.getAttribute("aria-pressed")) === "true"
71-
if (pressed === enabled) return
72-
await button.click()
73-
await expect(button).toHaveAttribute("aria-pressed", enabled ? "true" : "false")
68+
const dialog = await openSettings(page)
69+
const toggle = dialog.locator('[data-action="settings-auto-accept-permissions"]').first()
70+
const input = toggle.locator('[data-slot="switch-input"]').first()
71+
await expect(toggle).toBeVisible()
72+
const checked = (await input.getAttribute("aria-checked")) === "true"
73+
if (checked !== enabled) await toggle.locator('[data-slot="switch-control"]').click()
74+
await expect(input).toHaveAttribute("aria-checked", enabled ? "true" : "false")
75+
await closeDialog(page, dialog)
7476
}
7577

7678
async function expectQuestionBlocked(page: any) {
@@ -277,6 +279,7 @@ test("default dock shows prompt input", async ({ page, project }) => {
277279

278280
await expect(page.locator(sessionComposerDockSelector)).toBeVisible()
279281
await expect(page.locator(promptSelector)).toBeVisible()
282+
await expect(page.locator('[data-action="prompt-permissions"]')).toHaveCount(0)
280283
await expect(page.locator(questionDockSelector)).toHaveCount(0)
281284
await expect(page.locator(permissionDockSelector)).toHaveCount(0)
282285

@@ -290,10 +293,6 @@ test("default dock shows prompt input", async ({ page, project }) => {
290293
test("auto-accept toggle works before first submit", async ({ page, project }) => {
291294
await project.open()
292295

293-
const button = page.locator('[data-action="prompt-permissions"]').first()
294-
await expect(button).toBeVisible()
295-
await expect(button).toHaveAttribute("aria-pressed", "false")
296-
297296
await setAutoAccept(page, true)
298297
await setAutoAccept(page, false)
299298
})

packages/app/src/components/prompt-input.tsx

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,17 +1079,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
10791079
if (!id) return permission.isAutoAcceptingDirectory(sdk.directory)
10801080
return permission.isAutoAccepting(id, sdk.directory)
10811081
})
1082-
const acceptLabel = createMemo(() =>
1083-
language.t(accepting() ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable"),
1084-
)
1085-
const toggleAccept = () => {
1086-
if (!params.id) {
1087-
permission.toggleAutoAcceptDirectory(sdk.directory)
1088-
return
1089-
}
1090-
1091-
permission.toggleAutoAccept(params.id, sdk.directory)
1092-
}
10931082

10941083
const { abort, handleSubmit } = createPromptSubmit({
10951084
info,
@@ -1333,11 +1322,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
13331322
onMouseDown={(e) => {
13341323
const target = e.target
13351324
if (!(target instanceof HTMLElement)) return
1336-
if (
1337-
target.closest(
1338-
'[data-action="prompt-attach"], [data-action="prompt-submit"], [data-action="prompt-permissions"]',
1339-
)
1340-
) {
1325+
if (target.closest('[data-action="prompt-attach"], [data-action="prompt-submit"]')) {
13411326
return
13421327
}
13431328
editorRef?.focus()
@@ -1597,28 +1582,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
15971582
</TooltipKeybind>
15981583
</div>
15991584
</Show>
1600-
<TooltipKeybind
1601-
placement="top"
1602-
gutter={8}
1603-
title={acceptLabel()}
1604-
keybind={command.keybind("permissions.autoaccept")}
1605-
>
1606-
<Button
1607-
data-action="prompt-permissions"
1608-
variant="ghost"
1609-
onClick={toggleAccept}
1610-
classList={{
1611-
"h-7 w-7 p-0 shrink-0 flex items-center justify-center": true,
1612-
"text-text-base": !accepting(),
1613-
"hover:bg-surface-success-base": accepting(),
1614-
}}
1615-
style={control()}
1616-
aria-label={acceptLabel()}
1617-
aria-pressed={accepting()}
1618-
>
1619-
<Icon name="shield" size="small" classList={{ "text-icon-success-base": accepting() }} />
1620-
</Button>
1621-
</TooltipKeybind>
16221585
</div>
16231586
</div>
16241587
</div>

packages/app/src/components/settings-general.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import { TextField } from "@opencode-ai/ui/text-field"
88
import { Tooltip } from "@opencode-ai/ui/tooltip"
99
import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme/context"
1010
import { showToast } from "@opencode-ai/ui/toast"
11+
import { useParams } from "@solidjs/router"
1112
import { useLanguage } from "@/context/language"
13+
import { usePermission } from "@/context/permission"
1214
import { usePlatform } from "@/context/platform"
1315
import {
1416
monoDefault,
@@ -19,6 +21,7 @@ import {
1921
sansInput,
2022
useSettings,
2123
} from "@/context/settings"
24+
import { decode64 } from "@/utils/base64"
2225
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
2326
import { Link } from "./link"
2427
import { SettingsList } from "./settings-list"
@@ -64,7 +67,9 @@ const playDemoSound = (id: string | undefined) => {
6467
export const SettingsGeneral: Component = () => {
6568
const theme = useTheme()
6669
const language = useLanguage()
70+
const permission = usePermission()
6771
const platform = usePlatform()
72+
const params = useParams()
6873
const settings = useSettings()
6974

7075
onMount(() => {
@@ -76,6 +81,31 @@ export const SettingsGeneral: Component = () => {
7681
})
7782

7883
const linux = createMemo(() => platform.platform === "desktop" && platform.os === "linux")
84+
const dir = createMemo(() => decode64(params.dir))
85+
const accepting = createMemo(() => {
86+
const value = dir()
87+
if (!value) return false
88+
if (!params.id) return permission.isAutoAcceptingDirectory(value)
89+
return permission.isAutoAccepting(params.id, value)
90+
})
91+
92+
const toggleAccept = (checked: boolean) => {
93+
const value = dir()
94+
if (!value) return
95+
96+
if (!params.id) {
97+
if (permission.isAutoAcceptingDirectory(value) === checked) return
98+
permission.toggleAutoAcceptDirectory(value)
99+
return
100+
}
101+
102+
if (checked) {
103+
permission.enableAutoAccept(params.id, value)
104+
return
105+
}
106+
107+
permission.disableAutoAccept(params.id, value)
108+
}
79109

80110
const check = () => {
81111
if (!platform.checkUpdate) return
@@ -201,6 +231,15 @@ export const SettingsGeneral: Component = () => {
201231
/>
202232
</SettingsRow>
203233

234+
<SettingsRow
235+
title={language.t("command.permissions.autoaccept.enable")}
236+
description={language.t("toast.permissions.autoaccept.on.description")}
237+
>
238+
<div data-action="settings-auto-accept-permissions">
239+
<Switch checked={accepting()} disabled={!dir()} onChange={toggleAccept} />
240+
</div>
241+
</SettingsRow>
242+
204243
<SettingsRow
205244
title={language.t("settings.general.row.reasoningSummaries.title")}
206245
description={language.t("settings.general.row.reasoningSummaries.description")}

0 commit comments

Comments
 (0)