/* ============================================================ page-bots.jsx — Bot management: list, provision, register ============================================================ */ /* Re-bind parseDetail from primitives.jsx (defined there, exported via window) */ const parseDetail = window.parseDetail || ((body, status) => body?.message || `HTTP ${status}`); /* ── Password once callout ── */ function PasswordOnceCallout({ password, onConfirmed }) { const [copied, setCopied] = useState(false); const [confirmed, setConf] = useState(false); const copy = async () => { try { await navigator.clipboard.writeText(password); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (_e) { // Fallback for HTTP origins / Safari quirks. const ta = document.createElement('textarea'); ta.value = password; ta.setAttribute('readonly', ''); ta.style.position = 'absolute'; ta.style.left = '-9999px'; document.body.appendChild(ta); ta.select(); let ok = false; try { ok = document.execCommand && document.execCommand('copy'); } catch (_) { ok = false; } document.body.removeChild(ta); if (ok) { setCopied(true); setTimeout(() => setCopied(false), 2000); } else alert((window.appT||(s=>s))('Copy failed. Select the password manually and copy with Cmd/Ctrl+C.')); } }; return (
{(window.appT||(s=>s))('Copy this password now — it will never be shown again')}
{password}
{confirmed && ( )}
); } /* ── Edit Prompt Modal ── */ function EditPromptModal({ bot, onClose, onSaved }) { const [prompt, setPrompt] = useState(bot.system_prompt || ''); const [name, setName] = useState(bot.name || ''); const [busy, setBusy] = useState(false); const [err, setErr] = useState(''); const toast = useToast(); useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [onClose]); const save = async () => { setBusy(true); setErr(''); try { const r = await fetch(`/api/bots/${bot.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin', body: JSON.stringify({ name: name.trim() || bot.name, system_prompt: prompt }), }); if (!r.ok) { const body = await r.json().catch(() => null); throw new Error(parseDetail(body, r.status)); } toast.push({ msg: `${(window.appT||(s=>s))('Saved')} ${name.trim() || bot.name}`, kind: 'ok' }); onSaved && onSaved(); onClose(); } catch(e) { setErr(e.message); } finally { setBusy(false); } }; return (
e.stopPropagation()} style={{ position:'fixed', top:'50%', left:'50%', transform:'translate(-50%,-50%)', background:'var(--bg-elev-1)', border:'1px solid var(--line-hi)', borderRadius:'var(--r-lg)', padding: 24, width: 560, maxWidth:'95vw', boxShadow:'0 24px 64px -16px #00000080', zIndex: 301, maxHeight:'90vh', display:'flex', flexDirection:'column', }}>
{(window.appT||(s=>s))('Edit bot')}
{bot.matrix_id}
{err &&
{err}
} setName(e.target.value)} placeholder={bot.name} style={{ width:'100%', marginBottom: 14 }} />