Move auto-accept permissions to settings (#21308)
parent
c2d2ca3522
commit
41612b3dbe
|
|
@ -1,6 +1,6 @@
|
||||||
import type { ToolPart } from "@opencode-ai/sdk/v2/client"
|
import type { ToolPart } from "@opencode-ai/sdk/v2/client"
|
||||||
import { test, expect } from "../fixtures"
|
import { test, expect } from "../fixtures"
|
||||||
import { withSession } from "../actions"
|
import { closeDialog, openSettings, withSession } from "../actions"
|
||||||
import { promptModelSelector, promptSelector, promptVariantSelector } from "../selectors"
|
import { promptModelSelector, promptSelector, promptVariantSelector } from "../selectors"
|
||||||
|
|
||||||
const isBash = (part: unknown): part is ToolPart => {
|
const isBash = (part: unknown): part is ToolPart => {
|
||||||
|
|
@ -19,12 +19,15 @@ test("shell mode runs a command in the project directory", async ({ page, projec
|
||||||
await withSession(project.sdk, `e2e shell ${Date.now()}`, async (session) => {
|
await withSession(project.sdk, `e2e shell ${Date.now()}`, async (session) => {
|
||||||
project.trackSession(session.id)
|
project.trackSession(session.id)
|
||||||
await project.gotoSession(session.id)
|
await project.gotoSession(session.id)
|
||||||
const button = page.locator('[data-action="prompt-permissions"]').first()
|
const dialog = await openSettings(page)
|
||||||
await expect(button).toBeVisible()
|
const toggle = dialog.locator('[data-action="settings-auto-accept-permissions"]').first()
|
||||||
if ((await button.getAttribute("aria-pressed")) !== "true") {
|
const input = toggle.locator('[data-slot="switch-input"]').first()
|
||||||
await button.click()
|
await expect(toggle).toBeVisible()
|
||||||
await expect(button).toHaveAttribute("aria-pressed", "true")
|
if ((await input.getAttribute("aria-checked")) !== "true") {
|
||||||
|
await toggle.locator('[data-slot="switch-control"]').click()
|
||||||
|
await expect(input).toHaveAttribute("aria-checked", "true")
|
||||||
}
|
}
|
||||||
|
await closeDialog(page, dialog)
|
||||||
await project.shell(cmd)
|
await project.shell(cmd)
|
||||||
|
|
||||||
await expect
|
await expect
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import {
|
||||||
type ComposerProbeState,
|
type ComposerProbeState,
|
||||||
type ComposerWindow,
|
type ComposerWindow,
|
||||||
} from "../../src/testing/session-composer"
|
} from "../../src/testing/session-composer"
|
||||||
import { cleanupSession, clearSessionDockSeed, seedSessionQuestion } from "../actions"
|
import { cleanupSession, clearSessionDockSeed, closeDialog, openSettings, seedSessionQuestion } from "../actions"
|
||||||
import {
|
import {
|
||||||
permissionDockSelector,
|
permissionDockSelector,
|
||||||
promptSelector,
|
promptSelector,
|
||||||
|
|
@ -65,12 +65,14 @@ async function clearPermissionDock(page: any, label: RegExp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setAutoAccept(page: any, enabled: boolean) {
|
async function setAutoAccept(page: any, enabled: boolean) {
|
||||||
const button = page.locator('[data-action="prompt-permissions"]').first()
|
const dialog = await openSettings(page)
|
||||||
await expect(button).toBeVisible()
|
const toggle = dialog.locator('[data-action="settings-auto-accept-permissions"]').first()
|
||||||
const pressed = (await button.getAttribute("aria-pressed")) === "true"
|
const input = toggle.locator('[data-slot="switch-input"]').first()
|
||||||
if (pressed === enabled) return
|
await expect(toggle).toBeVisible()
|
||||||
await button.click()
|
const checked = (await input.getAttribute("aria-checked")) === "true"
|
||||||
await expect(button).toHaveAttribute("aria-pressed", enabled ? "true" : "false")
|
if (checked !== enabled) await toggle.locator('[data-slot="switch-control"]').click()
|
||||||
|
await expect(input).toHaveAttribute("aria-checked", enabled ? "true" : "false")
|
||||||
|
await closeDialog(page, dialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function expectQuestionBlocked(page: any) {
|
async function expectQuestionBlocked(page: any) {
|
||||||
|
|
@ -277,6 +279,7 @@ test("default dock shows prompt input", async ({ page, project }) => {
|
||||||
|
|
||||||
await expect(page.locator(sessionComposerDockSelector)).toBeVisible()
|
await expect(page.locator(sessionComposerDockSelector)).toBeVisible()
|
||||||
await expect(page.locator(promptSelector)).toBeVisible()
|
await expect(page.locator(promptSelector)).toBeVisible()
|
||||||
|
await expect(page.locator('[data-action="prompt-permissions"]')).toHaveCount(0)
|
||||||
await expect(page.locator(questionDockSelector)).toHaveCount(0)
|
await expect(page.locator(questionDockSelector)).toHaveCount(0)
|
||||||
await expect(page.locator(permissionDockSelector)).toHaveCount(0)
|
await expect(page.locator(permissionDockSelector)).toHaveCount(0)
|
||||||
|
|
||||||
|
|
@ -290,10 +293,6 @@ test("default dock shows prompt input", async ({ page, project }) => {
|
||||||
test("auto-accept toggle works before first submit", async ({ page, project }) => {
|
test("auto-accept toggle works before first submit", async ({ page, project }) => {
|
||||||
await project.open()
|
await project.open()
|
||||||
|
|
||||||
const button = page.locator('[data-action="prompt-permissions"]').first()
|
|
||||||
await expect(button).toBeVisible()
|
|
||||||
await expect(button).toHaveAttribute("aria-pressed", "false")
|
|
||||||
|
|
||||||
await setAutoAccept(page, true)
|
await setAutoAccept(page, true)
|
||||||
await setAutoAccept(page, false)
|
await setAutoAccept(page, false)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1079,17 +1079,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||||
if (!id) return permission.isAutoAcceptingDirectory(sdk.directory)
|
if (!id) return permission.isAutoAcceptingDirectory(sdk.directory)
|
||||||
return permission.isAutoAccepting(id, sdk.directory)
|
return permission.isAutoAccepting(id, sdk.directory)
|
||||||
})
|
})
|
||||||
const acceptLabel = createMemo(() =>
|
|
||||||
language.t(accepting() ? "command.permissions.autoaccept.disable" : "command.permissions.autoaccept.enable"),
|
|
||||||
)
|
|
||||||
const toggleAccept = () => {
|
|
||||||
if (!params.id) {
|
|
||||||
permission.toggleAutoAcceptDirectory(sdk.directory)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
permission.toggleAutoAccept(params.id, sdk.directory)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { abort, handleSubmit } = createPromptSubmit({
|
const { abort, handleSubmit } = createPromptSubmit({
|
||||||
info,
|
info,
|
||||||
|
|
@ -1333,11 +1322,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||||
onMouseDown={(e) => {
|
onMouseDown={(e) => {
|
||||||
const target = e.target
|
const target = e.target
|
||||||
if (!(target instanceof HTMLElement)) return
|
if (!(target instanceof HTMLElement)) return
|
||||||
if (
|
if (target.closest('[data-action="prompt-attach"], [data-action="prompt-submit"]')) {
|
||||||
target.closest(
|
|
||||||
'[data-action="prompt-attach"], [data-action="prompt-submit"], [data-action="prompt-permissions"]',
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
editorRef?.focus()
|
editorRef?.focus()
|
||||||
|
|
@ -1597,28 +1582,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<TooltipKeybind
|
|
||||||
placement="top"
|
|
||||||
gutter={8}
|
|
||||||
title={acceptLabel()}
|
|
||||||
keybind={command.keybind("permissions.autoaccept")}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
data-action="prompt-permissions"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={toggleAccept}
|
|
||||||
classList={{
|
|
||||||
"h-7 w-7 p-0 shrink-0 flex items-center justify-center": true,
|
|
||||||
"text-text-base": !accepting(),
|
|
||||||
"hover:bg-surface-success-base": accepting(),
|
|
||||||
}}
|
|
||||||
style={control()}
|
|
||||||
aria-label={acceptLabel()}
|
|
||||||
aria-pressed={accepting()}
|
|
||||||
>
|
|
||||||
<Icon name="shield" size="small" classList={{ "text-icon-success-base": accepting() }} />
|
|
||||||
</Button>
|
|
||||||
</TooltipKeybind>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ import { TextField } from "@opencode-ai/ui/text-field"
|
||||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||||
import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme/context"
|
import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme/context"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
|
import { useParams } from "@solidjs/router"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
|
import { usePermission } from "@/context/permission"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
import {
|
import {
|
||||||
monoDefault,
|
monoDefault,
|
||||||
|
|
@ -19,6 +21,7 @@ import {
|
||||||
sansInput,
|
sansInput,
|
||||||
useSettings,
|
useSettings,
|
||||||
} from "@/context/settings"
|
} from "@/context/settings"
|
||||||
|
import { decode64 } from "@/utils/base64"
|
||||||
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
|
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
|
||||||
import { Link } from "./link"
|
import { Link } from "./link"
|
||||||
import { SettingsList } from "./settings-list"
|
import { SettingsList } from "./settings-list"
|
||||||
|
|
@ -64,7 +67,9 @@ const playDemoSound = (id: string | undefined) => {
|
||||||
export const SettingsGeneral: Component = () => {
|
export const SettingsGeneral: Component = () => {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
|
const permission = usePermission()
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
|
const params = useParams()
|
||||||
const settings = useSettings()
|
const settings = useSettings()
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|
@ -76,6 +81,31 @@ export const SettingsGeneral: Component = () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const linux = createMemo(() => platform.platform === "desktop" && platform.os === "linux")
|
const linux = createMemo(() => platform.platform === "desktop" && platform.os === "linux")
|
||||||
|
const dir = createMemo(() => decode64(params.dir))
|
||||||
|
const accepting = createMemo(() => {
|
||||||
|
const value = dir()
|
||||||
|
if (!value) return false
|
||||||
|
if (!params.id) return permission.isAutoAcceptingDirectory(value)
|
||||||
|
return permission.isAutoAccepting(params.id, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const toggleAccept = (checked: boolean) => {
|
||||||
|
const value = dir()
|
||||||
|
if (!value) return
|
||||||
|
|
||||||
|
if (!params.id) {
|
||||||
|
if (permission.isAutoAcceptingDirectory(value) === checked) return
|
||||||
|
permission.toggleAutoAcceptDirectory(value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
permission.enableAutoAccept(params.id, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
permission.disableAutoAccept(params.id, value)
|
||||||
|
}
|
||||||
|
|
||||||
const check = () => {
|
const check = () => {
|
||||||
if (!platform.checkUpdate) return
|
if (!platform.checkUpdate) return
|
||||||
|
|
@ -201,6 +231,15 @@ export const SettingsGeneral: Component = () => {
|
||||||
/>
|
/>
|
||||||
</SettingsRow>
|
</SettingsRow>
|
||||||
|
|
||||||
|
<SettingsRow
|
||||||
|
title={language.t("command.permissions.autoaccept.enable")}
|
||||||
|
description={language.t("toast.permissions.autoaccept.on.description")}
|
||||||
|
>
|
||||||
|
<div data-action="settings-auto-accept-permissions">
|
||||||
|
<Switch checked={accepting()} disabled={!dir()} onChange={toggleAccept} />
|
||||||
|
</div>
|
||||||
|
</SettingsRow>
|
||||||
|
|
||||||
<SettingsRow
|
<SettingsRow
|
||||||
title={language.t("settings.general.row.reasoningSummaries.title")}
|
title={language.t("settings.general.row.reasoningSummaries.title")}
|
||||||
description={language.t("settings.general.row.reasoningSummaries.description")}
|
description={language.t("settings.general.row.reasoningSummaries.description")}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue