// Main App — top nav layout + admin login gate const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "queryMode": 0, "defaultLang": "zh", "accentColor": "#6366f1" }/*EDITMODE-END*/; // ── Admin Login Gate ── const AdminLogin = ({ lang, onSuccess, onCancel }) => { const t = lang === 'zh'; const [user, setUser] = React.useState(''); const [pass, setPass] = React.useState(''); const [showPass, setShowPass] = React.useState(false); const [error, setError] = React.useState(''); const [loading, setLoading] = React.useState(false); const handleLogin = () => { if (!user || !pass) { setError(t ? '請填寫帳號與密碼' : 'Please enter credentials'); return; } setLoading(true); setError(''); setTimeout(() => { if (user === 'admin' && pass === 'admin123') { onSuccess(); } else { setError(t ? '帳號或密碼錯誤' : 'Invalid credentials'); setLoading(false); } }, 800); }; return (
{/* Header */}
{t ? '後台管理登入' : 'Admin Login'}
{t ? '請輸入管理員帳號與密碼' : 'Enter admin credentials to continue'}
{/* Form */}
setUser(e.target.value)} placeholder={t ? '輸入管理員帳號' : 'Enter username'} icon="key" onKeyDown={e => e.key === 'Enter' && handleLogin()} />
setPass(e.target.value)} type={showPass ? 'text' : 'password'} placeholder={t ? '輸入密碼' : 'Enter password'} onKeyDown={e => e.key === 'Enter' && handleLogin()} style={{ width: '100%', background: TOKEN.surfaceRaised, border: `1px solid ${TOKEN.surfaceBorder}`, borderRadius: 8, padding: '9px 40px 9px 12px', color: TOKEN.text, fontSize: 14, outline: 'none', fontFamily: 'inherit' }} />
{error && (
{error}
)}
{loading ? (t ? '驗證中...' : 'Verifying...') : (t ? '登入後台' : 'Login')} {t ? '取消' : 'Cancel'}

{t ? '示範帳號:admin / admin123' : 'Demo: admin / admin123'}

); }; // ── Main App ── const App = () => { const [lang, setLang] = React.useState(TWEAK_DEFAULTS.defaultLang || 'zh'); const [page, setPage] = React.useState('select'); const [selectedShow, setSelectedShow] = React.useState(null); const [selectedEpisode, setSelectedEpisode] = React.useState(null); const [initSearch, setInitSearch] = React.useState(''); const [highlightTime, setHighlightTime] = React.useState(null); const [tweaksVisible, setTweaksVisible] = React.useState(false); const [tweaks, setTweaks] = React.useState(TWEAK_DEFAULTS); const [adminAuth, setAdminAuth] = React.useState(false); const [showAdminLogin, setShowAdminLogin] = React.useState(false); React.useEffect(() => { const handler = (e) => { if (e.data?.type === '__activate_edit_mode') setTweaksVisible(true); if (e.data?.type === '__deactivate_edit_mode') setTweaksVisible(false); }; window.addEventListener('message', handler); window.parent.postMessage({ type: '__edit_mode_available' }, '*'); return () => window.removeEventListener('message', handler); }, []); const applyTweak = (key, val) => { setTweaks(t => ({ ...t, [key]: val })); window.parent.postMessage({ type: '__edit_mode_set_keys', edits: { [key]: val } }, '*'); }; const handleAdminClick = () => { if (adminAuth) { setPage('admin-api'); } else { setShowAdminLogin(true); } }; const handleAdminSuccess = () => { setAdminAuth(true); setShowAdminLogin(false); setPage('admin-api'); }; const handleSetPage = (p) => { if (p === 'select') { setSelectedShow(null); setSelectedEpisode(null); } setPage(p); }; const t = lang === 'zh'; return (
setLang(l => l === 'zh' ? 'en' : 'zh')} onAdminClick={handleAdminClick} /> {/* Main content */}
{page === 'select' && { setSelectedShow(show); setPage('query'); }} />} {selectedShow && (page === 'query' || page === 'transcript') && (
setPage('select')} onOpenEpisode={(ep, ht) => { setSelectedEpisode(ep); setInitSearch(''); setHighlightTime(typeof ht === 'number' ? ht : null); setPage('transcript'); }} />
)} {page === 'transcript' && selectedEpisode && selectedShow && ( setPage('query')} /> )} {page.startsWith('admin') && }
{/* Admin login modal */} {showAdminLogin && ( setShowAdminLogin(false)} /> )} {/* Tweaks panel */} {tweaksVisible && (

Tweaks

{[[0, t ? '兩種都顯示' : 'Both'], [1, t ? '只顯示對話' : 'Chat only'], [2, t ? '只顯示搜尋' : 'Search only']].map(([v, label]) => ( ))}
{['#6366f1', '#22d3ee', '#f59e0b', '#22c55e', '#ec4899'].map(c => (
applyTweak('accentColor', c)} style={{ width: 26, height: 26, borderRadius: '50%', background: c, cursor: 'pointer', border: `2px solid ${tweaks.accentColor === c ? '#fff' : 'transparent'}`, transition: 'border 0.12s' }} /> ))}
)}
); }; const root = ReactDOM.createRoot(document.getElementById('root')); root.render();