import { useEffect, useState } from "react"; import { get, put } from "../../api"; import { confirmDialog, toast } from "../../dialogs"; import { useFetch } from "../../hooks"; import { useT } from "../../i18n"; import { Icon } from "../Icon"; import { ModelSelect } from "../ModelSelect"; import { ToolPicker, type ToolInfo } from "./AgentsEditor"; interface Mode { description?: string | null; prompt?: string | null; model?: string | null; tools?: string[] | null; read_only?: boolean | null; style?: "coding" | "chat" | null; } /** Modes a user creates for themselves; they appear in New session and the mode selector. */ export function PersonalModes() { const t = useT(); const tools = useFetch("/agents/tools").data ?? []; const [modes, setModes] = useState>(); const [open, setOpen] = useState(); const [name, setName] = useState(""); useEffect(() => { get>("/account/modes") .then(setModes) .catch((e) => toast(e)); }, []); const save = async (next: Record) => { try { setModes(await put>("/account/modes", next)); toast(t("settings.saved"), "success"); return true; } catch (e) { toast(e); return false; } }; const add = async () => { const n = name.trim(); if (!/^[a-zA-Z0-9_-]+$/.test(n)) return toast(t("settings.invalidName")); if (modes?.[n]) return toast(t("modes.exists", { name: n })); if (await save({ ...modes, [n]: { style: "chat", description: "", prompt: "" } })) { setName(""); setOpen(n); } }; const remove = async (n: string) => { if (!(await confirmDialog({ message: t("agents.deleteConfirm", { name: n }), danger: true, confirmLabel: t("common.delete") }))) return; const next = { ...modes }; delete next[n]; void save(next); }; if (!modes) return
{t("common.loading")}
; const names = Object.keys(modes).sort(); return ( <>
{t("modes.intro")}
{names.map((n) => ( setOpen(open === n ? undefined : n)} onSave={(m) => save({ ...modes, [n]: m })} onRemove={() => remove(n)} /> ))} {names.length === 0 &&
{t("modes.none")}
}

{t("agents.addMode")}

setName(e.target.value)} onKeyDown={(e) => e.key === "Enter" && void add()} />
); } function ModeRow({ name, mode, tools, open, onToggle, onSave, onRemove }: { name: string; mode: Mode; tools: ToolInfo[]; open: boolean; onToggle: () => void; onSave: (m: Mode) => Promise; onRemove: () => void }) { const t = useT(); const [form, setForm] = useState({ description: mode.description ?? "", prompt: mode.prompt ?? "", model: mode.model ?? "", tools: mode.tools ?? [], read_only: !!mode.read_only, style: (mode.style ?? "coding") as "coding" | "chat", }); const submit = () => void onSave({ description: form.description || null, prompt: form.prompt || null, model: form.model || null, tools: form.tools.length ? form.tools : null, read_only: form.read_only || null, style: form.style === "chat" ? "chat" : null, }); return (

{name} {form.style === "chat" ? t("agents.styleChatShort") : t("agents.styleCodingShort")} {form.read_only && {t("agents.readOnly")}}

{!open && mode.description &&
{mode.description}
} {open && ( <>
setForm({ ...form, description: e.target.value })} /> setForm({ ...form, model: v })} emptyLabel={t("agents.sessionModel")} /> setForm({ ...form, tools: v })} readOnly={form.read_only} />