diff --git a/packages/app/src/components/deepagent-settings-ux.test.ts b/packages/app/src/components/deepagent-settings-ux.test.ts index 870cab56..8c2d60b5 100644 --- a/packages/app/src/components/deepagent-settings-ux.test.ts +++ b/packages/app/src/components/deepagent-settings-ux.test.ts @@ -23,9 +23,21 @@ describe("DeepAgent settings UX", () => { expect(v2.indexOf('data-action="settings-deepagent-prompt-mode"')).toBeLessThan( v2.indexOf('data-action="settings-deepagent-intelligence-model"'), ) - expect(v2.indexOf('data-action="settings-deepagent-intelligence-model"')).toBeLessThan( - v2.indexOf('data-action="settings-auto-accept-permissions"'), - ) + }) + + test("moves the permission approval control out of settings into the composer toolbar", async () => { + const v2 = await readFile(path.join(here, "settings-v2/general.tsx"), "utf8") + const composer = await readFile(path.join(here, "prompt-input.tsx"), "utf8") + const control = await readFile(path.join(here, "deepagent/approval-control.tsx"), "utf8") + + // The auto-accept toggle no longer lives in settings… + expect(v2).not.toContain('data-action="settings-auto-accept-permissions"') + // …it is a composer control next to the agent selector, backed by directory-level auto-accept. + expect(composer).toContain("ApprovalControl") + expect(control).toContain('"data-action": "prompt-approval"') + expect(control).toContain("toggleAutoAcceptDirectory") + expect(control).toContain("composer.approval.request") + expect(control).toContain("composer.approval.auto") }) test("routes the legacy settings dialog import to the unified settings page", async () => { diff --git a/packages/app/src/components/deepagent/approval-control.tsx b/packages/app/src/components/deepagent/approval-control.tsx new file mode 100644 index 00000000..5654f8ba --- /dev/null +++ b/packages/app/src/components/deepagent/approval-control.tsx @@ -0,0 +1,56 @@ +import { createMemo, type JSX } from "solid-js" +import { Select } from "@deepagent-code/ui/select" +import { useLanguage } from "@/context/language" +import { usePermission } from "@/context/permission" + +/** + * Approval-mode control for the composer toolbar, next to the agent (build/plan) selector. + * + * Mirrors Codex's approval selector UX, simplified to two options: the button shows the CURRENT mode + * ("Request approval" by default, "Auto-approve" when armed); clicking opens a small picker to switch. + * The mode is DIRECTORY-scoped (persists across sessions in the same workspace), backed by the existing + * permission context (isAutoAcceptingDirectory / toggleAutoAcceptDirectory) — the same state the old + * settings toggle drove, now surfaced where the user acts. + */ + +type ApprovalMode = "request" | "auto" + +export function ApprovalControl(props: { directory: string; triggerStyle?: JSX.CSSProperties; onAfter?: () => void }) { + const language = useLanguage() + const permission = usePermission() + + const auto = createMemo(() => (props.directory ? permission.isAutoAcceptingDirectory(props.directory) : false)) + const current = createMemo(() => (auto() ? "auto" : "request")) + + const options: ApprovalMode[] = ["request", "auto"] + const label = (mode: ApprovalMode) => + mode === "auto" + ? language.t("composer.approval.auto") + : language.t("composer.approval.request") + + const onSelect = (mode: ApprovalMode | undefined) => { + if (!mode || !props.directory) return + const isAuto = mode === "auto" + if (isAuto === auto()) return + // toggleAutoAcceptDirectory flips the directory-level state; only call it when the target differs. + permission.toggleAutoAcceptDirectory(props.directory) + props.onAfter?.() + } + + return ( +