/** * @file HomeDirSetting.tsx * @description One editable home directory (CLAUDE_HOME in Claude Code → * Overview, CODEX_HOME in Codex → Overview): a path field with Save, the * server's refusal inline, the saved value while the field differs from it, * and a warning when the real * environment sets the variable (the environment wins at the next start, so a * saved value would not survive a restart). The server validates and persists; * this component only loads, submits and reports. * @author Nguyễn Ngọc Trí Vĩ */ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { AlertTriangle, FolderOpen } from "lucide-react"; export interface HomeDirValue { value: string; envOwned: boolean; /** Shown after a save that succeeded only in part (e.g. a lane not updated). */ warning?: string | null; } interface Props { /** `
` — a deep-link target. */ id: string; title: string; description: string; /** Optional second line under the description. */ note?: string; placeholder: string; load: () => Promise; save: (path: string) => Promise; } /** * An editable home directory with its own load/save cycle. * @returns The section, or its skeleton-free shell while loading. */ export function HomeDirSetting({ id, title, description, note, placeholder, load, save }: Props) { const { t } = useTranslation("settings"); const [current, setCurrent] = useState(null); const [input, setInput] = useState(""); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); useEffect(() => { let live = true; load() .then((v) => { if (!live) return; setCurrent(v); setInput(v.value); }) .catch((err) => live && setError(err instanceof Error ? err.message : String(err))); return () => { live = false; }; // `load` is a stable api call per mount; re-running on identity would loop. // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleSave = async () => { if (!current || input === current.value) return; setSaving(true); setError(null); try { const next = await save(input); setCurrent(next); setInput(next.value); } catch (err) { setError(err instanceof Error ? err.message : t("homeDir.saveFailed")); } finally { setSaving(false); } }; return (

{title}

{description}

{note &&

{note}

}
{ setInput(e.target.value); setError(null); }} className="flex-1 bg-surface-4 border border-surface-3 rounded-lg px-3 py-2 text-sm text-fg-secondary font-mono focus:outline-none focus:border-violet-500/50" placeholder={placeholder} />
{error &&

{error}

} {/* The saved value, only while the field says something else. */} {current && input !== current.value && (

{t("homeDir.current")} {current.value}

)} {current?.envOwned && (

{t("homeDir.envOwned")}

)} {current?.warning && (

{current.warning}

)}
); }