/** * @file SessionDefaultsSettings — Settings → AI tools → Session defaults: the * account's own starting choices for a new Claude Code session (permission * mode, Bypass included) and a new Codex session (sandbox, approval, bypass), * which the workspace home's prompt box starts from (`lib/sessionDefaults.ts`, * synced per account). Turning bypass on as a default asks first; a pick in * the prompt box still changes only that one session. * @author Nguyễn Ngọc Trí Vĩ */ import { useState } from "react"; import { useTranslation } from "react-i18next"; import { ShieldAlert } from "lucide-react"; import type { CodexApproval, CodexSandbox } from "../../lib/api"; import { useAiTools } from "../../hooks/useAiTools"; import { confirmDialog } from "../../lib/confirm"; import { CODEX_APPROVALS, CODEX_SANDBOXES, DEFAULT_MODES, readCodexDefaults, readDefaultMode, writeCodexDefaults, writeDefaultMode, type DefaultMode, } from "../../lib/sessionDefaults"; import { SettingsRow, SettingsSection, SettingsSegmented, SettingsSwitchRow } from "./SettingsRow"; const MODE_KEYS: Record = { auto: "Auto", acceptEdits: "AcceptEdits", default: "Default", plan: "Plan", bypassPermissions: "Bypass", }; const SANDBOX_KEYS: Record = { "read-only": "sandboxReadOnly", "workspace-write": "sandboxWorkspaceWrite", "danger-full-access": "sandboxFullAccess", }; const APPROVAL_KEYS: Record = { "on-request": "approvalOnRequest", never: "approvalNever", }; function Danger({ children }: { children: string }) { return (

{children}

); } export function SessionDefaultsSettings() { const { t } = useTranslation("settings"); const aiTools = useAiTools(); const shown = (id: string) => aiTools.tools.some((x) => x.id === id && x.enabled); const [mode, setMode] = useState(() => readDefaultMode() as DefaultMode); const [codex, setCodex] = useState(readCodexDefaults); const askBypass = (tool: string) => confirmDialog({ title: tool === "Codex" ? t("sessionDefaults.codexConfirmTitle") : t("sessionDefaults.confirmTitle", { tool }), message: t("sessionDefaults.confirmBody", { tool }), confirmLabel: t("sessionDefaults.confirmLabel"), }); const pickMode = async (m: DefaultMode) => { if (m === "bypassPermissions" && !(await askBypass("Claude Code"))) return; writeDefaultMode(m); setMode(m); }; const patchCodex = (patch: Partial) => { writeCodexDefaults(patch); setCodex((c) => ({ ...c, ...patch })); }; const pickCodexBypass = async (on: boolean) => { if (on && !(await askBypass("Codex"))) return; patchCodex({ bypass: on }); }; if (!shown("claude") && !shown("codex")) return null; return ( {shown("claude") && ( <> void pickMode(m)} options={DEFAULT_MODES.map((m) => ({ value: m, label: t(`lanes:home.permission${MODE_KEYS[m]}`), }))} /> } /> {mode === "bypassPermissions" && {t("run:hint.permissionWarning")}} )} {shown("codex") && ( <> patchCodex({ sandbox })} options={CODEX_SANDBOXES.map((v) => ({ value: v, label: t(`run:fields.${SANDBOX_KEYS[v]}`), }))} /> } /> patchCodex({ approval })} options={CODEX_APPROVALS.map((v) => ({ value: v, label: t(`run:fields.${APPROVAL_KEYS[v]}`), }))} /> } /> void pickCodexBypass(on)} /> {codex.bypass && {t("run:hint.bypassWarning", { tool: "Codex" })}} )} ); }