From 723ee6a86ed9c9a736452f610d824dc3e083e335 Mon Sep 17 00:00:00 2001 From: deepagent-ai Date: Fri, 10 Jul 2026 12:27:43 +0800 Subject: [PATCH] fix(app): composer approval control + settings cleanups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four settings/composer UX fixes. 1. Share URL i18n: the Sharing section (section title + shareUrl title/description/placeholder) was English-only, so it showed untranslated. Added zh + zht translations, and backfilled all 15 non-English locales so the settings-key parity test passes (it was already red on these keys + the earlier expertPanelDefault keys). 2. Approval control moved to the composer: removed the auto-accept row from settings and added an ApprovalControl next to the build/plan agent selector, modeled on Codex's approval selector — a two-option picker ("Request approval" default, "Auto-approve" when armed) whose button label reflects the current mode. Directory-scoped, backed by the existing permission context (isAutoAcceptingDirectory / toggleAutoAcceptDirectory). 3. Servers tab: the duplicate "Add server" + "Connect to server" buttons are merged into one "Add server" menu with two items (Add HTTP server / Connect to server), matching the single-entry status popover. 4. Import: the run button is now a full-width bar button under the options (with Cancel beside it while running) instead of a small right-aligned row control. Tests: i18n parity green; settings-UX test updated to assert the approval control moved to the composer. App suite 540 pass, typecheck clean. Co-Authored-By: Claude Opus 4.8 --- .../components/deepagent-settings-ux.test.ts | 18 +++++- .../components/deepagent/approval-control.tsx | 56 +++++++++++++++++++ packages/app/src/components/prompt-input.tsx | 6 ++ .../src/components/settings-v2/general.tsx | 38 ------------- .../components/settings-v2/import-history.tsx | 26 +++++---- .../src/components/settings-v2/servers.tsx | 24 ++++++-- .../components/settings-v2/settings-v2.css | 13 +++++ packages/app/src/i18n/ar.ts | 6 ++ packages/app/src/i18n/br.ts | 6 ++ packages/app/src/i18n/bs.ts | 6 ++ packages/app/src/i18n/da.ts | 6 ++ packages/app/src/i18n/de.ts | 6 ++ packages/app/src/i18n/en.ts | 3 + packages/app/src/i18n/es.ts | 6 ++ packages/app/src/i18n/fr.ts | 6 ++ packages/app/src/i18n/ja.ts | 6 ++ packages/app/src/i18n/ko.ts | 6 ++ packages/app/src/i18n/no.ts | 6 ++ packages/app/src/i18n/pl.ts | 6 ++ packages/app/src/i18n/ru.ts | 6 ++ packages/app/src/i18n/th.ts | 6 ++ packages/app/src/i18n/tr.ts | 6 ++ packages/app/src/i18n/uk.ts | 6 ++ packages/app/src/i18n/zh.ts | 9 +++ packages/app/src/i18n/zht.ts | 10 ++++ 25 files changed, 235 insertions(+), 58 deletions(-) create mode 100644 packages/app/src/components/deepagent/approval-control.tsx 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 ( +