返回 DeepSeek-Reasonix
ConnectionTitle.tsx
根目录 / desktop / frontend / src / components / ConnectionTitle.tsx
1 import { useRef, useState } from "react";
2 import { Check, Pencil, X } from "lucide-react";
3 import { useT } from "../lib/i18n";
4
5 export function ConnectionTitle({ label, busy, onSave }: {
6 label: string; busy: boolean; onSave: (label: string) => Promise<boolean>;
7 }) {
8 const t = useT();
9 const [editing, setEditing] = useState(false);
10 const [draft, setDraft] = useState(label);
11 const [saving, setSaving] = useState(false);
12 const [error, setError] = useState(false);
13 const pending = useRef(false);
14 const trigger = useRef<HTMLButtonElement>(null);
15 const close = () => { setEditing(false); setError(false); requestAnimationFrame(() => trigger.current?.focus()); };
16 const save = async () => {
17 if (pending.current || busy) return;
18 pending.current = true; setSaving(true); setError(false);
19 try {
20 if (await onSave(draft.trim())) close(); else setError(true);
21 } catch { setError(true); }
22 finally { pending.current = false; setSaving(false); }
23 };
24 if (!editing) return <button ref={trigger} type="button" className="connection-title" disabled={busy}
25 aria-label={`${t("settings.connections.rename")}: ${label}`}
26 onClick={() => { setDraft(label); setError(false); setEditing(true); }}>
27 {label}<Pencil size={15} aria-hidden="true" />
28 </button>;
29 return <span className="connection-title-editor">
30 <span className="connection-title-editor__row">
31 <input className="mem-input" aria-label={t("settings.connections.name")} value={draft}
32 ref={input => { if (input) input.focus(); }} disabled={saving || busy}
33 onChange={e => setDraft(e.target.value)}
34 onKeyDown={e => {
35 if (e.nativeEvent.isComposing || e.keyCode === 229) return;
36 if (e.key === "Enter") { e.preventDefault(); void save(); }
37 if (e.key === "Escape") { e.preventDefault(); e.stopPropagation(); if (!saving) close(); }
38 }} />
39 <button type="button" className="btn provider-icon-action" title={t("common.save")} aria-label={t("common.save")} disabled={saving || busy} onClick={() => void save()}><Check size={16} /></button>
40 <button type="button" className="btn provider-icon-action" title={t("common.cancel")} aria-label={t("common.cancel")} disabled={saving} onClick={close}><X size={16} /></button>
41 </span>
42 {error && <small role="alert">{t("settings.connections.renameFailed")}</small>}
43 </span>;
44 }
45
45 lines Plain Text