/* global React */ const { useState, useEffect } = React; // ============================================================ // PUBLISH MODAL // // Publishing is NOT something the leaderboard can do on its own. // `POST /api/v1/sessions/{id}/publish` is owner-gated: it needs a // COMPLETED training session *and* the `X-Session-Token` that was // handed out once when that session was created. That token only // exists in the browser that ran the training: i.e. the Playground // at /dashboard/, never here. // // So this modal routes the user to the real thing instead of // pretending. The previous version rendered a fake form that // reported "submitted · verified" for a hardcoded run (DreamerV3 / // 921 / r_8422) without touching the API: a visitor could "publish" // a run they never trained and see a success screen for it. // ============================================================ const PLAYGROUND_URL = '/dashboard/'; const PublishModal = ({ open, onClose }) => { if (!open) return null; const go = () => { window.location.href = PLAYGROUND_URL; }; return (
e.stopPropagation()} data-testid="publish-modal">
Submit to leaderboard
Publish a Playground run
Runs are published from the session that produced them: the Playground holds the owner token that proves the run is yours, and the leaderboard has no way to mint one.
  1. Open the Playground and pick an environment + algorithm.
  2. Train until the session reports completed.
  3. Hit ↗ Submit to RLX on that session.
  4. Add a social handle to publish verified, or skip it and the run waits in the admin review queue.
Approved runs land on the Playground Benchmarks board with the same field set as the Research and OptRL boards.
); }; const TweaksPanel = ({ tweaks, setTweaks, active, setActive, board, setBoard, algo, setAlgo, sortKey, setSortKey }) => { const [collapsed, setCollapsed] = useState(false); useEffect(() => { const onMsg = (e) => { if (e.data?.type === '__activate_edit_mode') setTweaks(true); if (e.data?.type === '__deactivate_edit_mode') setTweaks(false); }; window.addEventListener('message', onMsg); window.parent.postMessage({type: '__edit_mode_available'}, '*'); return () => window.removeEventListener('message', onMsg); }, []); if (!tweaks) return null; const tabs = ['home', 'leaderboards', 'playground', 'docs', 'admin']; const boards = ['published', 'optrl', 'playground']; const themes = ['light', 'dark', 'accent']; const sorts = ['meanReturn', 'successRate', 'stability', 'compute', 's2rGap']; const algos = (window.RLX_DATA?.playgroundAlgoGroups || []).flatMap(g => g.items.map(i => i.name)); const setTheme = (t) => { document.documentElement.setAttribute('data-theme', t === 'light' ? '' : t); window.parent.postMessage({type: '__edit_mode_set_keys', edits: {theme: t}}, '*'); }; const currentTheme = document.documentElement.getAttribute('data-theme') || 'light'; return (
setCollapsed(c => !c)}> ◆ Tweaks {collapsed ? '+' : '−'}
Active tab
{tabs.map(t => ( ))}
Color theme
{themes.map(t => ( ))}
Leaderboard
{boards.map(b => ( ))}
Playground algorithm
Sort column
{sorts.map(s => ( ))}
); }; // ============================================================ // USER AUTH MODAL (SIGN IN / SIGN UP) // ============================================================ const UserAuthModal = ({ open, onClose, onSuccess }) => { const [tab, setTab] = useState('login'); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [displayName, setDisplayName] = useState(''); const [socialHandle, setSocialHandle] = useState(''); const [busy, setBusy] = useState(false); const [err, setErr] = useState(null); if (!open) return null; const handleSubmit = async (e) => { e.preventDefault(); setBusy(true); setErr(null); try { if (tab === 'login') { await window.RLX_USER_API.login(email.trim(), password); } else { await window.RLX_USER_API.signup( email.trim(), password, displayName.trim() || undefined, socialHandle.trim() || undefined, ); } if (onSuccess) onSuccess(); onClose(); } catch (ex) { setErr(ex.message || 'Authentication failed'); } finally { setBusy(false); } }; return (
e.stopPropagation()} style={{maxWidth: 440}}>
OptRL Account
{tab === 'login' ? 'Sign In to OptRL' : 'Create an Account'}
{err && (
{err}
)}
setEmail(e.target.value)} placeholder="trader@firm.com" style={{width: '100%', padding: '8px 10px', fontFamily: 'var(--mono)', fontSize: 13, background: 'var(--paper)', border: '1px solid var(--rule)', color: 'var(--ink)'}} />
setPassword(e.target.value)} placeholder="Enter password" style={{width: '100%', padding: '8px 10px', fontFamily: 'var(--mono)', fontSize: 13, background: 'var(--paper)', border: '1px solid var(--rule)', color: 'var(--ink)'}} />
{tab === 'signup' && ( <>
setDisplayName(e.target.value)} placeholder="Alex Morgan" style={{width: '100%', padding: '8px 10px', fontFamily: 'var(--mono)', fontSize: 13, background: 'var(--paper)', border: '1px solid var(--rule)', color: 'var(--ink)'}} />
setSocialHandle(e.target.value)} placeholder="@alex_m" style={{width: '100%', padding: '8px 10px', fontFamily: 'var(--mono)', fontSize: 13, background: 'var(--paper)', border: '1px solid var(--rule)', color: 'var(--ink)'}} />
)}
); }; // ============================================================ // USER ENCRYPTED API KEYS MODAL // ============================================================ const UserApiKeysModal = ({ open, onClose }) => { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [apiKeyInput, setApiKeyInput] = useState(''); const [saving, setSaving] = useState(false); const [msg, setMsg] = useState(null); const [err, setErr] = useState(null); const fetchKeys = async () => { setLoading(true); setErr(null); try { const res = await window.RLX_USER_API.getApiKeys(); setData(res); } catch (ex) { setErr(ex.message || 'Failed to fetch API keys. Please ensure you are signed in.'); } finally { setLoading(false); } }; useEffect(() => { if (open) { fetchKeys(); setApiKeyInput(''); setMsg(null); } }, [open]); if (!open) return null; const handleSaveKey = async (e) => { e.preventDefault(); const cleanKey = apiKeyInput.trim(); if (!cleanKey) return; setSaving(true); setErr(null); setMsg(null); try { await window.RLX_USER_API.saveApiKey('alpha_vantage', cleanKey); setMsg('API key encrypted and saved successfully. Future sessions will auto-reuse it.'); setApiKeyInput(''); await fetchKeys(); } catch (ex) { setErr(ex.message || 'Failed to save API key'); } finally { setSaving(false); } }; const handleDeleteKey = async () => { if (!window.confirm('Delete your custom key? Sessions will automatically fall back to the system free tier key.')) return; setSaving(true); setErr(null); setMsg(null); try { await window.RLX_USER_API.deleteApiKey('alpha_vantage'); setMsg('Custom key removed. Free tier system key will now be used.'); await fetchKeys(); } catch (ex) { setErr(ex.message || 'Failed to remove API key'); } finally { setSaving(false); } }; const avKey = (data?.keys || []).find((k) => k.provider === 'alpha_vantage' && k.is_active); return (
e.stopPropagation()} style={{maxWidth: 520}}>
Encrypted Key Management
Third-Party API Credentials
{err && (
{err}
)} {msg && (
{msg}
)} {loading ? (
Loading credential status...
) : ( <>
Alpha Vantage Market Data {avKey ? ( Personal Key: {avKey.key_hint} ) : ( Free Tier (.env Key) )}
{avKey ? 'Your personal API key is stored with symmetric encryption. It is automatically injected into all your training sessions, hourly market data queries, and portfolio advisory inference.' : 'No custom key saved. Requests automatically use the shared free tier key configured on the server (25 requests/day limit).'}
{avKey && (
)}
setApiKeyInput(e.target.value)} placeholder="Enter Alpha Vantage API Key" style={{flex: 1, padding: '8px 10px', fontFamily: 'var(--mono)', fontSize: 13, background: 'var(--paper)', border: '1px solid var(--rule)', color: 'var(--ink)'}} />
Keys are encrypted server-side with AES-256 Fernet using the secure application secret before storage.
)}
); }; Object.assign(window, { PublishModal, TweaksPanel, UserAuthModal, UserApiKeysModal });