// 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 (
e.stopPropagation()} className="paper-card" style={{ maxWidth: 560, width: '100%', padding: 28, position: 'relative' }} >
connection

hook Clarguments up to Claude.

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.

anthropic api key
setKey(e.target.value)} style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8rem', flex: 1 }} />
get one at console.anthropic.com · needs billing enabled.
scenario data
{[ { v: 'fresh', l: 'fresh · start from blank' }, { v: 'canned', l: 'canned · replay scripted scenario' }, ].map(o => (
setMode(o.v)} style={{ flex: 1, justifyContent: 'center', border: '1px solid var(--rule)' }} >{o.l}
))}
{testResult && (
{testResult.msg}
)}
{CG.hasKey() && }
what uses what: haiku for live flag-checking (cheap, fast). sonnet for coach feedback, callout cards, refiner framings (richer reasoning).
); } Object.assign(window, { ClaudeChip, ClaudeModal });