AE-Shree commited on
Commit ·
7186ef8
1
Parent(s): 0b1db59
Final Change
Browse files- frontend/dist/index.html +1 -1
- frontend/src/App.jsx +49 -26
- frontend/src/components/Dashboard.jsx +19 -19
frontend/dist/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<title>CLM Dashboard</title>
|
| 7 |
-
<script type="module" crossorigin src="/assets/index-
|
| 8 |
<link rel="stylesheet" crossorigin href="/assets/index-DHB-7DK0.css">
|
| 9 |
</head>
|
| 10 |
<body style="margin:0;background:#f1f5f9">
|
|
|
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<title>CLM Dashboard</title>
|
| 7 |
+
<script type="module" crossorigin src="/assets/index-CPBwWvcP.js"></script>
|
| 8 |
<link rel="stylesheet" crossorigin href="/assets/index-DHB-7DK0.css">
|
| 9 |
</head>
|
| 10 |
<body style="margin:0;background:#f1f5f9">
|
frontend/src/App.jsx
CHANGED
|
@@ -18,7 +18,7 @@ class ErrorBoundary extends React.Component {
|
|
| 18 |
<div style={{ background: '#fef2f2', border: '1px solid #fca5a5',
|
| 19 |
borderRadius: 12, padding: 24, margin: 24, fontFamily: 'system-ui,sans-serif' }}>
|
| 20 |
<div style={{ fontSize: 18, fontWeight: 800, color: '#dc2626', marginBottom: 8 }}>
|
| 21 |
-
|
| 22 |
</div>
|
| 23 |
<div style={{ fontSize: 13, color: '#7f1d1d', marginBottom: 12,
|
| 24 |
fontFamily: 'monospace', background: '#fff', padding: 12, borderRadius: 8,
|
|
@@ -28,7 +28,7 @@ class ErrorBoundary extends React.Component {
|
|
| 28 |
<button onClick={() => this.setState({ error: null })}
|
| 29 |
style={{ background: '#dc2626', color: '#fff', border: 'none',
|
| 30 |
borderRadius: 8, padding: '10px 18px', fontWeight: 700, cursor: 'pointer' }}>
|
| 31 |
-
|
| 32 |
</button>
|
| 33 |
</div>
|
| 34 |
)
|
|
@@ -38,51 +38,74 @@ class ErrorBoundary extends React.Component {
|
|
| 38 |
}
|
| 39 |
|
| 40 |
export default function App() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
return (
|
| 42 |
-
<div style={{ minHeight: '100vh', background:
|
| 43 |
-
|
|
|
|
|
|
|
| 44 |
<header style={{
|
| 45 |
-
background:
|
| 46 |
display: 'flex', alignItems: 'center', gap: 32, padding: '0 24px',
|
| 47 |
-
|
|
|
|
| 48 |
}}>
|
| 49 |
-
<div style={{ padding: '14px 0', whiteSpace: 'nowrap' }}>
|
| 50 |
-
<span style={{ fontSize:
|
| 51 |
-
|
|
|
|
| 52 |
</span>
|
| 53 |
-
<span style={{ fontSize: 12, color:
|
| 54 |
-
|
| 55 |
</span>
|
| 56 |
</div>
|
| 57 |
|
| 58 |
-
<
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
|
|
|
| 64 |
|
| 65 |
<a href="/docs" target="_blank" rel="noreferrer"
|
| 66 |
-
style={{ fontSize: 12, color:
|
| 67 |
-
padding: '6px 12px', border:
|
| 68 |
whiteSpace: 'nowrap' }}>
|
| 69 |
-
API Docs
|
| 70 |
</a>
|
| 71 |
</header>
|
| 72 |
|
| 73 |
-
{/*
|
| 74 |
<div style={{
|
| 75 |
-
background:
|
| 76 |
padding: '10px 24px', textAlign: 'center', fontSize: 13, color: '#fff',
|
| 77 |
}}>
|
| 78 |
-
|
| 79 |
-
Switch to <b>
|
| 80 |
</div>
|
| 81 |
|
| 82 |
-
{/*
|
| 83 |
<main style={{ maxWidth: 1400, margin: '0 auto', padding: 24 }}>
|
| 84 |
<ErrorBoundary>
|
| 85 |
-
<Dashboard />
|
| 86 |
</ErrorBoundary>
|
| 87 |
</main>
|
| 88 |
</div>
|
|
|
|
| 18 |
<div style={{ background: '#fef2f2', border: '1px solid #fca5a5',
|
| 19 |
borderRadius: 12, padding: 24, margin: 24, fontFamily: 'system-ui,sans-serif' }}>
|
| 20 |
<div style={{ fontSize: 18, fontWeight: 800, color: '#dc2626', marginBottom: 8 }}>
|
| 21 |
+
The dashboard hit a render error
|
| 22 |
</div>
|
| 23 |
<div style={{ fontSize: 13, color: '#7f1d1d', marginBottom: 12,
|
| 24 |
fontFamily: 'monospace', background: '#fff', padding: 12, borderRadius: 8,
|
|
|
|
| 28 |
<button onClick={() => this.setState({ error: null })}
|
| 29 |
style={{ background: '#dc2626', color: '#fff', border: 'none',
|
| 30 |
borderRadius: 8, padding: '10px 18px', fontWeight: 700, cursor: 'pointer' }}>
|
| 31 |
+
Reset Dashboard
|
| 32 |
</button>
|
| 33 |
</div>
|
| 34 |
)
|
|
|
|
| 38 |
}
|
| 39 |
|
| 40 |
export default function App() {
|
| 41 |
+
const [theme, setTheme] = React.useState(() => {
|
| 42 |
+
if (typeof window === 'undefined') return 'light'
|
| 43 |
+
return localStorage.getItem('clm-theme') || 'light'
|
| 44 |
+
})
|
| 45 |
+
|
| 46 |
+
React.useEffect(() => {
|
| 47 |
+
try { localStorage.setItem('clm-theme', theme) } catch {}
|
| 48 |
+
}, [theme])
|
| 49 |
+
|
| 50 |
+
const isDark = theme === 'dark'
|
| 51 |
+
const palette = isDark
|
| 52 |
+
? { bg: '#0b1220', headerBg: '#0f172a', headerBorder: '#1e293b',
|
| 53 |
+
text: '#e2e8f0', subText: '#94a3b8', border: '#334155',
|
| 54 |
+
bannerFrom: '#1e3a8a', bannerTo: '#0c4a6e' }
|
| 55 |
+
: { bg: '#f1f5f9', headerBg: '#ffffff', headerBorder: '#e2e8f0',
|
| 56 |
+
text: '#0f172a', subText: '#64748b', border: '#cbd5e1',
|
| 57 |
+
bannerFrom: '#4f46e5', bannerTo: '#0ea5e9' }
|
| 58 |
+
|
| 59 |
return (
|
| 60 |
+
<div style={{ minHeight: '100vh', background: palette.bg,
|
| 61 |
+
fontFamily: 'system-ui,sans-serif', color: palette.text,
|
| 62 |
+
transition: 'background .25s ease, color .25s ease' }}>
|
| 63 |
+
{/* Header */}
|
| 64 |
<header style={{
|
| 65 |
+
background: palette.headerBg, position: 'sticky', top: 0, zIndex: 20,
|
| 66 |
display: 'flex', alignItems: 'center', gap: 32, padding: '0 24px',
|
| 67 |
+
borderBottom: `1px solid ${palette.headerBorder}`,
|
| 68 |
+
boxShadow: isDark ? '0 1px 3px rgba(0,0,0,.5)' : '0 1px 3px rgba(15,23,42,.06)',
|
| 69 |
}}>
|
| 70 |
+
<div style={{ padding: '14px 0', whiteSpace: 'nowrap', flex: 1 }}>
|
| 71 |
+
<span style={{ fontSize: 18, fontWeight: 800, color: palette.text,
|
| 72 |
+
letterSpacing: '-0.4px' }}>
|
| 73 |
+
Cognitive Load Manager
|
| 74 |
</span>
|
| 75 |
+
<span style={{ fontSize: 12, color: palette.subText, marginLeft: 10 }}>
|
| 76 |
+
OpenEnv
|
| 77 |
</span>
|
| 78 |
</div>
|
| 79 |
|
| 80 |
+
<button onClick={() => setTheme(isDark ? 'light' : 'dark')}
|
| 81 |
+
style={{ fontSize: 12, color: palette.text, background: 'transparent',
|
| 82 |
+
padding: '6px 14px', border: `1px solid ${palette.border}`,
|
| 83 |
+
borderRadius: 6, cursor: 'pointer', fontWeight: 600,
|
| 84 |
+
whiteSpace: 'nowrap' }}>
|
| 85 |
+
{isDark ? 'Light' : 'Dark'} mode
|
| 86 |
+
</button>
|
| 87 |
|
| 88 |
<a href="/docs" target="_blank" rel="noreferrer"
|
| 89 |
+
style={{ fontSize: 12, color: palette.subText, textDecoration: 'none',
|
| 90 |
+
padding: '6px 12px', border: `1px solid ${palette.border}`, borderRadius: 6,
|
| 91 |
whiteSpace: 'nowrap' }}>
|
| 92 |
+
API Docs
|
| 93 |
</a>
|
| 94 |
</header>
|
| 95 |
|
| 96 |
+
{/* Banner */}
|
| 97 |
<div style={{
|
| 98 |
+
background: `linear-gradient(135deg,${palette.bannerFrom} 0%,${palette.bannerTo} 100%)`,
|
| 99 |
padding: '10px 24px', textAlign: 'center', fontSize: 13, color: '#fff',
|
| 100 |
}}>
|
| 101 |
+
AI agent plays live — press <b>Play Episode</b> to start streaming.
|
| 102 |
+
Switch to <b>Manual</b> to control the agent yourself.
|
| 103 |
</div>
|
| 104 |
|
| 105 |
+
{/* Content */}
|
| 106 |
<main style={{ maxWidth: 1400, margin: '0 auto', padding: 24 }}>
|
| 107 |
<ErrorBoundary>
|
| 108 |
+
<Dashboard isDark={isDark} />
|
| 109 |
</ErrorBoundary>
|
| 110 |
</main>
|
| 111 |
</div>
|
frontend/src/components/Dashboard.jsx
CHANGED
|
@@ -122,7 +122,7 @@ function ScoringFormulaCard() {
|
|
| 122 |
|
| 123 |
<div style={{ marginBottom:16 }}>
|
| 124 |
<div style={{ fontSize:14, fontWeight:800, color:'#0f172a', marginBottom:4 }}>
|
| 125 |
-
|
| 126 |
</div>
|
| 127 |
<div style={{ fontSize:11, color:'#64748b' }}>
|
| 128 |
Each action is scored on 5 dimensions. Weights reflect cognitive-load research priorities.
|
|
@@ -318,7 +318,7 @@ export default function Dashboard() {
|
|
| 318 |
// When the server closes the stream after a clean episode end, the browser
|
| 319 |
// fires onerror. Ignore it — only show an error for genuine disconnects.
|
| 320 |
if (episodeDone.current) return
|
| 321 |
-
setError('Stream disconnected. Check backend is running, then press
|
| 322 |
setStreaming(false)
|
| 323 |
es.close(); esRef.current = null
|
| 324 |
}
|
|
@@ -347,7 +347,7 @@ export default function Dashboard() {
|
|
| 347 |
if (!r.ok) throw new Error(`HTTP ${r.status}`)
|
| 348 |
const d = await r.json()
|
| 349 |
setSession(d.session_id); setObs(d.observation)
|
| 350 |
-
setManLogs([{ type: 'system', msg: `
|
| 351 |
} catch (err) {
|
| 352 |
setError(err.message)
|
| 353 |
} finally { setLoading(false) }
|
|
@@ -373,7 +373,7 @@ export default function Dashboard() {
|
|
| 373 |
if (d.done) {
|
| 374 |
setManLogs(prev => [...prev, {
|
| 375 |
type: 'system',
|
| 376 |
-
msg: `
|
| 377 |
}])
|
| 378 |
setSession(null)
|
| 379 |
setManDone(true)
|
|
@@ -410,7 +410,7 @@ export default function Dashboard() {
|
|
| 410 |
marginBottom: 16, flexWrap: 'wrap' }}>
|
| 411 |
{/* Mode toggle */}
|
| 412 |
<div style={{ display: 'flex', background: '#e2e8f0', borderRadius: 10, padding: 3 }}>
|
| 413 |
-
{[['stream', '
|
| 414 |
<button key={id} onClick={() => setMode(id)}
|
| 415 |
style={{ padding: '7px 14px', borderRadius: 8, border: 'none',
|
| 416 |
background: mode === id ? '#fff' : 'transparent',
|
|
@@ -437,13 +437,13 @@ export default function Dashboard() {
|
|
| 437 |
? <button onClick={stopStream}
|
| 438 |
style={{ background: '#ef4444', color: '#fff', border: 'none',
|
| 439 |
borderRadius: 8, padding: '8px 20px', fontWeight: 700,
|
| 440 |
-
fontSize: 13, cursor: 'pointer' }}>
|
| 441 |
: !streamDone && (
|
| 442 |
<button onClick={() => startStream()}
|
| 443 |
style={{ background: '#6366f1', color: '#fff', border: 'none',
|
| 444 |
borderRadius: 8, padding: '8px 20px', fontWeight: 700,
|
| 445 |
fontSize: 13, cursor: 'pointer' }}>
|
| 446 |
-
|
| 447 |
</button>
|
| 448 |
)
|
| 449 |
) : (
|
|
@@ -452,7 +452,7 @@ export default function Dashboard() {
|
|
| 452 |
style={{ background: loading ? '#94a3b8' : '#6366f1', color: '#fff',
|
| 453 |
border: 'none', borderRadius: 8, padding: '8px 20px', fontWeight: 700,
|
| 454 |
fontSize: 13, cursor: loading ? 'not-allowed' : 'pointer' }}>
|
| 455 |
-
{loading ? 'Loading…' : sessionId ? '
|
| 456 |
</button>
|
| 457 |
)
|
| 458 |
)}
|
|
@@ -469,7 +469,7 @@ export default function Dashboard() {
|
|
| 469 |
<span style={{ fontSize: 13, fontWeight: 700, color: '#16a34a',
|
| 470 |
background: '#f0fdf4', border: '1px solid #bbf7d0', borderRadius: 8,
|
| 471 |
padding: '6px 14px' }}>
|
| 472 |
-
|
| 473 |
{typeof finalScore === 'number' ? ` · Score: ${finalScore.toFixed(4)}` : ''}
|
| 474 |
</span>
|
| 475 |
)}
|
|
@@ -480,10 +480,10 @@ export default function Dashboard() {
|
|
| 480 |
<div style={{ background: '#fef2f2', border: '1px solid #fca5a5',
|
| 481 |
borderRadius: 10, padding: '10px 14px', marginBottom: 14,
|
| 482 |
fontSize: 13, color: '#dc2626' }}>
|
| 483 |
-
|
| 484 |
<button onClick={() => setError(null)}
|
| 485 |
style={{ background: 'none', border: 'none', cursor: 'pointer',
|
| 486 |
-
color: '#dc2626', fontWeight: 700 }}>
|
| 487 |
</div>
|
| 488 |
)}
|
| 489 |
|
|
@@ -492,7 +492,7 @@ export default function Dashboard() {
|
|
| 492 |
<div key={i} style={{ background: '#fffbeb', border: '1px solid #fcd34d',
|
| 493 |
borderRadius: 10, padding: '10px 14px', marginBottom: 10,
|
| 494 |
fontSize: 13, color: '#92400e', fontWeight: 600 }}>
|
| 495 |
-
|
| 496 |
</div>
|
| 497 |
))}
|
| 498 |
|
|
@@ -509,7 +509,7 @@ export default function Dashboard() {
|
|
| 509 |
}}>
|
| 510 |
<div>
|
| 511 |
<div style={{ fontSize: 18, fontWeight: 900, color: '#fff', letterSpacing: '-0.3px' }}>
|
| 512 |
-
|
| 513 |
</div>
|
| 514 |
<div style={{ fontSize: 12, color: '#d1fae5', marginTop: 2 }}>
|
| 515 |
Final results frozen below — all charts and task data preserved
|
|
@@ -557,7 +557,7 @@ export default function Dashboard() {
|
|
| 557 |
<div style={card()}>
|
| 558 |
<div style={section}>Reward / Step</div>
|
| 559 |
<LineChart data={rewardTrace} color="#6366f1" height={130}
|
| 560 |
-
label="Press
|
| 561 |
{rewardTrace.length > 0 && (
|
| 562 |
<div style={{ display: 'flex', justifyContent: 'space-between',
|
| 563 |
fontSize: 11, color: '#64748b', marginTop: 6 }}>
|
|
@@ -619,7 +619,7 @@ export default function Dashboard() {
|
|
| 619 |
<div style={{ fontSize: 10, color: '#94a3b8', marginTop: 2 }}>
|
| 620 |
{(t.progress * 100).toFixed(0)}%
|
| 621 |
{t.deadline ? ` · deadline: step ${t.deadline}` : ''}
|
| 622 |
-
{t.is_interrupted ? '
|
| 623 |
</div>
|
| 624 |
</div>
|
| 625 |
)
|
|
@@ -680,7 +680,7 @@ export default function Dashboard() {
|
|
| 680 |
display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap',
|
| 681 |
}}>
|
| 682 |
<div style={{ fontSize: 18, fontWeight: 900, color: '#fff' }}>
|
| 683 |
-
|
| 684 |
</div>
|
| 685 |
<div style={{ fontSize: 12, color: '#c7d2fe' }}>
|
| 686 |
All results frozen — task log and reward chart preserved below
|
|
@@ -715,7 +715,7 @@ export default function Dashboard() {
|
|
| 715 |
<div style={section}>Task Queue</div>
|
| 716 |
{manTasks.length === 0 && (
|
| 717 |
<div style={{ color: '#cbd5e1', fontSize: 13, textAlign: 'center', padding: 24 }}>
|
| 718 |
-
Press
|
| 719 |
</div>
|
| 720 |
)}
|
| 721 |
{manTasks.map(task => {
|
|
@@ -761,12 +761,12 @@ export default function Dashboard() {
|
|
| 761 |
style={{ flex: 1, padding: 9, borderRadius: 8,
|
| 762 |
border: '1px solid #e2e8f0', background: '#f0fdf4',
|
| 763 |
color: '#16a34a', fontWeight: 700, cursor: 'pointer', fontSize: 13 }}>
|
| 764 |
-
|
| 765 |
<button onClick={() => handleAction('delay')}
|
| 766 |
style={{ flex: 1, padding: 9, borderRadius: 8,
|
| 767 |
border: '1px solid #e2e8f0', background: '#f8fafc',
|
| 768 |
color: '#64748b', fontWeight: 700, cursor: 'pointer', fontSize: 13 }}>
|
| 769 |
-
|
| 770 |
</div>
|
| 771 |
)}
|
| 772 |
</div>
|
|
|
|
| 122 |
|
| 123 |
<div style={{ marginBottom:16 }}>
|
| 124 |
<div style={{ fontSize:14, fontWeight:800, color:'#0f172a', marginBottom:4 }}>
|
| 125 |
+
Reward Scoring Formula
|
| 126 |
</div>
|
| 127 |
<div style={{ fontSize:11, color:'#64748b' }}>
|
| 128 |
Each action is scored on 5 dimensions. Weights reflect cognitive-load research priorities.
|
|
|
|
| 318 |
// When the server closes the stream after a clean episode end, the browser
|
| 319 |
// fires onerror. Ignore it — only show an error for genuine disconnects.
|
| 320 |
if (episodeDone.current) return
|
| 321 |
+
setError('Stream disconnected. Check backend is running, then press Play again.')
|
| 322 |
setStreaming(false)
|
| 323 |
es.close(); esRef.current = null
|
| 324 |
}
|
|
|
|
| 347 |
if (!r.ok) throw new Error(`HTTP ${r.status}`)
|
| 348 |
const d = await r.json()
|
| 349 |
setSession(d.session_id); setObs(d.observation)
|
| 350 |
+
setManLogs([{ type: 'system', msg: `Episode started (${difficulty})` }])
|
| 351 |
} catch (err) {
|
| 352 |
setError(err.message)
|
| 353 |
} finally { setLoading(false) }
|
|
|
|
| 373 |
if (d.done) {
|
| 374 |
setManLogs(prev => [...prev, {
|
| 375 |
type: 'system',
|
| 376 |
+
msg: `Done. Final score: ${d.info?.final_score?.toFixed(4) ?? 'N/A'}`,
|
| 377 |
}])
|
| 378 |
setSession(null)
|
| 379 |
setManDone(true)
|
|
|
|
| 410 |
marginBottom: 16, flexWrap: 'wrap' }}>
|
| 411 |
{/* Mode toggle */}
|
| 412 |
<div style={{ display: 'flex', background: '#e2e8f0', borderRadius: 10, padding: 3 }}>
|
| 413 |
+
{[['stream', 'Auto-Play'], ['manual', 'Manual']].map(([id, lbl]) => (
|
| 414 |
<button key={id} onClick={() => setMode(id)}
|
| 415 |
style={{ padding: '7px 14px', borderRadius: 8, border: 'none',
|
| 416 |
background: mode === id ? '#fff' : 'transparent',
|
|
|
|
| 437 |
? <button onClick={stopStream}
|
| 438 |
style={{ background: '#ef4444', color: '#fff', border: 'none',
|
| 439 |
borderRadius: 8, padding: '8px 20px', fontWeight: 700,
|
| 440 |
+
fontSize: 13, cursor: 'pointer' }}>Stop</button>
|
| 441 |
: !streamDone && (
|
| 442 |
<button onClick={() => startStream()}
|
| 443 |
style={{ background: '#6366f1', color: '#fff', border: 'none',
|
| 444 |
borderRadius: 8, padding: '8px 20px', fontWeight: 700,
|
| 445 |
fontSize: 13, cursor: 'pointer' }}>
|
| 446 |
+
Play Episode
|
| 447 |
</button>
|
| 448 |
)
|
| 449 |
) : (
|
|
|
|
| 452 |
style={{ background: loading ? '#94a3b8' : '#6366f1', color: '#fff',
|
| 453 |
border: 'none', borderRadius: 8, padding: '8px 20px', fontWeight: 700,
|
| 454 |
fontSize: 13, cursor: loading ? 'not-allowed' : 'pointer' }}>
|
| 455 |
+
{loading ? 'Loading…' : sessionId ? 'Reset' : 'Start'}
|
| 456 |
</button>
|
| 457 |
)
|
| 458 |
)}
|
|
|
|
| 469 |
<span style={{ fontSize: 13, fontWeight: 700, color: '#16a34a',
|
| 470 |
background: '#f0fdf4', border: '1px solid #bbf7d0', borderRadius: 8,
|
| 471 |
padding: '6px 14px' }}>
|
| 472 |
+
Episode #{episodeCount} Complete
|
| 473 |
{typeof finalScore === 'number' ? ` · Score: ${finalScore.toFixed(4)}` : ''}
|
| 474 |
</span>
|
| 475 |
)}
|
|
|
|
| 480 |
<div style={{ background: '#fef2f2', border: '1px solid #fca5a5',
|
| 481 |
borderRadius: 10, padding: '10px 14px', marginBottom: 14,
|
| 482 |
fontSize: 13, color: '#dc2626' }}>
|
| 483 |
+
{error} 
|
| 484 |
<button onClick={() => setError(null)}
|
| 485 |
style={{ background: 'none', border: 'none', cursor: 'pointer',
|
| 486 |
+
color: '#dc2626', fontWeight: 700 }}>x</button>
|
| 487 |
</div>
|
| 488 |
)}
|
| 489 |
|
|
|
|
| 492 |
<div key={i} style={{ background: '#fffbeb', border: '1px solid #fcd34d',
|
| 493 |
borderRadius: 10, padding: '10px 14px', marginBottom: 10,
|
| 494 |
fontSize: 13, color: '#92400e', fontWeight: 600 }}>
|
| 495 |
+
Schema Drift @ step {d.step}: {d.message}
|
| 496 |
</div>
|
| 497 |
))}
|
| 498 |
|
|
|
|
| 509 |
}}>
|
| 510 |
<div>
|
| 511 |
<div style={{ fontSize: 18, fontWeight: 900, color: '#fff', letterSpacing: '-0.3px' }}>
|
| 512 |
+
Episode #{episodeCount} Complete
|
| 513 |
</div>
|
| 514 |
<div style={{ fontSize: 12, color: '#d1fae5', marginTop: 2 }}>
|
| 515 |
Final results frozen below — all charts and task data preserved
|
|
|
|
| 557 |
<div style={card()}>
|
| 558 |
<div style={section}>Reward / Step</div>
|
| 559 |
<LineChart data={rewardTrace} color="#6366f1" height={130}
|
| 560 |
+
label="Press Play Episode to start" />
|
| 561 |
{rewardTrace.length > 0 && (
|
| 562 |
<div style={{ display: 'flex', justifyContent: 'space-between',
|
| 563 |
fontSize: 11, color: '#64748b', marginTop: 6 }}>
|
|
|
|
| 619 |
<div style={{ fontSize: 10, color: '#94a3b8', marginTop: 2 }}>
|
| 620 |
{(t.progress * 100).toFixed(0)}%
|
| 621 |
{t.deadline ? ` · deadline: step ${t.deadline}` : ''}
|
| 622 |
+
{t.is_interrupted ? ' (interrupted)' : ''}
|
| 623 |
</div>
|
| 624 |
</div>
|
| 625 |
)
|
|
|
|
| 680 |
display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap',
|
| 681 |
}}>
|
| 682 |
<div style={{ fontSize: 18, fontWeight: 900, color: '#fff' }}>
|
| 683 |
+
Manual Episode Complete
|
| 684 |
</div>
|
| 685 |
<div style={{ fontSize: 12, color: '#c7d2fe' }}>
|
| 686 |
All results frozen — task log and reward chart preserved below
|
|
|
|
| 715 |
<div style={section}>Task Queue</div>
|
| 716 |
{manTasks.length === 0 && (
|
| 717 |
<div style={{ color: '#cbd5e1', fontSize: 13, textAlign: 'center', padding: 24 }}>
|
| 718 |
+
Press Start to begin
|
| 719 |
</div>
|
| 720 |
)}
|
| 721 |
{manTasks.map(task => {
|
|
|
|
| 761 |
style={{ flex: 1, padding: 9, borderRadius: 8,
|
| 762 |
border: '1px solid #e2e8f0', background: '#f0fdf4',
|
| 763 |
color: '#16a34a', fontWeight: 700, cursor: 'pointer', fontSize: 13 }}>
|
| 764 |
+
Break</button>
|
| 765 |
<button onClick={() => handleAction('delay')}
|
| 766 |
style={{ flex: 1, padding: 9, borderRadius: 8,
|
| 767 |
border: '1px solid #e2e8f0', background: '#f8fafc',
|
| 768 |
color: '#64748b', fontWeight: 700, cursor: 'pointer', fontSize: 13 }}>
|
| 769 |
+
Delay</button>
|
| 770 |
</div>
|
| 771 |
)}
|
| 772 |
</div>
|