// Claude connection UI: top-bar chip + modal for pasting key. // Depends on: window.CG (cg-claude.js), Icons function ClaudeChip({ onClick }) { const [, setTick] = React.useState(0); React.useEffect(() => CG.onStatus(() => setTick(t => t + 1)), []); const connected = CG.hasKey(); return ( ); } function ClaudeModal({ open, onClose }) { const [key, setKey] = React.useState(() => CG.getKey()); const [mode, setMode] = React.useState(() => CG.getMode()); const [testing, setTesting] = React.useState(false); const [testResult, setTestResult] = React.useState(null); const [showKey, setShowKey] = React.useState(false); React.useEffect(() => { if (open) { setKey(CG.getKey()); setMode(CG.getMode()); setTestResult(null); } }, [open]); if (!open) return null; const onSave = () => { CG.setKey(key); CG.setMode(mode); setTestResult({ ok: true, msg: 'saved · stored locally in your browser only' }); }; const onTest = async () => { if (!key.trim()) { setTestResult({ ok: false, msg: 'paste a key first' }); return; } CG.setKey(key); setTesting(true); setTestResult(null); try { const r = await CG.call({ agentId: '_connection_test', model: 'haiku', system: 'Respond with exactly the word "connected".', messages: [{ role: 'user', content: 'ping' }], maxTokens: 20, persist: false, }); setTestResult({ ok: true, msg: 'connected · ' + (r.text || '(empty)').slice(0, 60) }); } catch (e) { setTestResult({ ok: false, msg: e.message }); } finally { setTesting(false); } }; const onDisconnect = () => { CG.clearKey(); setKey(''); setTestResult({ ok: true, msg: 'disconnected · key removed from localStorage' }); }; const onClearAll = () => { if (!confirm('clear all stored exchange logs and ledger entries? (keeps your API key)')) return; CG.resetAllLogs(); CG.resetLedger(); setTestResult({ ok: true, msg: 'all stored exchanges + ledger wiped' }); }; return (
we call api.anthropic.com directly from your browser with your own key. it's stored in localStorage on this device and sent only to Anthropic. nothing routes through us.