Deepfake Authenticator commited on
Commit Β·
cede9a4
1
Parent(s): 7f580a0
feat: make nav links functional (Dashboard/Agents/Logs/Network)
Browse files- Dashboard: navigates to hero/upload section
- Agents: modal showing all 5 AI agents with descriptions and tech stack
- Logs: modal showing session history (last 20 analyses with verdict/confidence)
- Network: modal showing live health check, model status, API endpoint
- Escape key closes any open modal
- Click outside modal to dismiss
- Logs auto-populate after each analysis
- frontend-vanilla/index.html +218 -5
frontend-vanilla/index.html
CHANGED
|
@@ -273,12 +273,12 @@
|
|
| 273 |
<nav class="fixed top-0 left-0 w-full z-50 flex justify-between items-center px-6 h-16 bg-emerald-950/20 backdrop-blur-xl border-b border-emerald-500/20 shadow-[0_4px_20px_rgba(0,0,0,0.5)] font-['Space_Grotesk'] tracking-wider uppercase text-xs">
|
| 274 |
<!-- Brand -->
|
| 275 |
<div class="flex items-center gap-8">
|
| 276 |
-
<span class="text-2xl font-black tracking-tighter text-emerald-400 drop-shadow-[0_0_8px_rgba(0,255,156,0.5)]">AUTHRIX AI</span>
|
| 277 |
<div class="hidden md:flex items-center gap-6">
|
| 278 |
-
<a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm
|
| 279 |
-
<a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm
|
| 280 |
-
<a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm
|
| 281 |
-
<a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm
|
| 282 |
</div>
|
| 283 |
</div>
|
| 284 |
<!-- Actions -->
|
|
@@ -736,6 +736,126 @@
|
|
| 736 |
</div>
|
| 737 |
</section>
|
| 738 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 739 |
<!-- βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 740 |
FOOTER
|
| 741 |
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -->
|
|
@@ -920,6 +1040,7 @@ async function analyzeVideo() {
|
|
| 920 |
}
|
| 921 |
const data = await res.json();
|
| 922 |
stopPipelineAnimation();
|
|
|
|
| 923 |
renderResults(data);
|
| 924 |
showState('resultsSection');
|
| 925 |
} catch (err) {
|
|
@@ -1292,6 +1413,98 @@ function escHtml(str) {
|
|
| 1292 |
.replace(/>/g, '>')
|
| 1293 |
.replace(/"/g, '"');
|
| 1294 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1295 |
</script>
|
| 1296 |
</body>
|
| 1297 |
</html>
|
|
|
|
| 273 |
<nav class="fixed top-0 left-0 w-full z-50 flex justify-between items-center px-6 h-16 bg-emerald-950/20 backdrop-blur-xl border-b border-emerald-500/20 shadow-[0_4px_20px_rgba(0,0,0,0.5)] font-['Space_Grotesk'] tracking-wider uppercase text-xs">
|
| 274 |
<!-- Brand -->
|
| 275 |
<div class="flex items-center gap-8">
|
| 276 |
+
<span onclick="showState('heroSection')" class="text-2xl font-black tracking-tighter text-emerald-400 drop-shadow-[0_0_8px_rgba(0,255,156,0.5)] cursor-pointer">AUTHRIX AI</span>
|
| 277 |
<div class="hidden md:flex items-center gap-6">
|
| 278 |
+
<a onclick="showState('heroSection')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Dashboard</a>
|
| 279 |
+
<a onclick="openModal('agentsModal')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Agents</a>
|
| 280 |
+
<a onclick="openModal('logsModal')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Logs</a>
|
| 281 |
+
<a onclick="openModal('networkModal')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Network</a>
|
| 282 |
</div>
|
| 283 |
</div>
|
| 284 |
<!-- Actions -->
|
|
|
|
| 736 |
</div>
|
| 737 |
</section>
|
| 738 |
|
| 739 |
+
<!-- βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 740 |
+
MODALS
|
| 741 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -->
|
| 742 |
+
<style>
|
| 743 |
+
.modal-backdrop {
|
| 744 |
+
position:fixed;inset:0;z-index:200;
|
| 745 |
+
background:rgba(0,0,0,0.8);backdrop-filter:blur(6px);
|
| 746 |
+
display:flex;align-items:center;justify-content:center;padding:24px;
|
| 747 |
+
animation:fadeIn 0.2s ease;
|
| 748 |
+
}
|
| 749 |
+
.modal-box {
|
| 750 |
+
background:#141e17;border:1px solid rgba(0,255,156,0.2);border-radius:12px;
|
| 751 |
+
width:100%;max-width:640px;max-height:80vh;overflow-y:auto;
|
| 752 |
+
box-shadow:0 0 60px rgba(0,255,156,0.1);animation:slideUp 0.25s ease;
|
| 753 |
+
}
|
| 754 |
+
@keyframes slideUp{from{opacity:0;transform:translateY(20px);}to{opacity:1;transform:none;}}
|
| 755 |
+
.modal-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;border-bottom:1px solid rgba(0,255,156,0.1);}
|
| 756 |
+
.modal-body{padding:20px 24px 24px;}
|
| 757 |
+
.modal-close{background:none;border:none;cursor:pointer;color:#849587;font-size:18px;padding:4px 8px;border-radius:4px;transition:color 0.2s;font-family:inherit;}
|
| 758 |
+
.modal-close:hover{color:#ffb4ab;}
|
| 759 |
+
.agent-row{display:flex;align-items:flex-start;gap:14px;padding:14px;border-radius:8px;background:rgba(35,44,37,0.3);border:1px solid rgba(0,255,156,0.08);margin-bottom:10px;}
|
| 760 |
+
.agent-icon{width:40px;height:40px;border-radius:8px;flex-shrink:0;background:rgba(0,255,156,0.08);border:1px solid rgba(0,255,156,0.2);display:flex;align-items:center;justify-content:center;font-size:18px;}
|
| 761 |
+
.log-row{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid rgba(255,255,255,0.05);font-family:'Space Grotesk',monospace;font-size:12px;gap:12px;}
|
| 762 |
+
.log-row:last-child{border-bottom:none;}
|
| 763 |
+
.net-row{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border-radius:6px;background:rgba(35,44,37,0.3);border:1px solid rgba(0,255,156,0.08);margin-bottom:8px;font-family:'Space Grotesk',monospace;font-size:12px;}
|
| 764 |
+
</style>
|
| 765 |
+
|
| 766 |
+
<!-- Agents Modal -->
|
| 767 |
+
<div id="agentsModal" style="display:none;" class="modal-backdrop" onclick="if(event.target===this)closeModal('agentsModal')">
|
| 768 |
+
<div class="modal-box">
|
| 769 |
+
<div class="modal-header">
|
| 770 |
+
<div>
|
| 771 |
+
<div style="font-size:11px;font-weight:700;letter-spacing:0.15em;color:#00ff9c;text-transform:uppercase;">AGENT PIPELINE</div>
|
| 772 |
+
<div style="font-size:12px;color:#849587;margin-top:4px;">5 specialized AI agents run in sequence</div>
|
| 773 |
+
</div>
|
| 774 |
+
<button class="modal-close" onclick="closeModal('agentsModal')">β</button>
|
| 775 |
+
</div>
|
| 776 |
+
<div class="modal-body">
|
| 777 |
+
<div class="agent-row">
|
| 778 |
+
<div class="agent-icon">π¬</div>
|
| 779 |
+
<div>
|
| 780 |
+
<div style="font-size:14px;font-weight:600;color:#dae5da;">Frame Analyzer Agent</div>
|
| 781 |
+
<div style="font-size:12px;color:#849587;margin-top:4px;line-height:1.5;">Extracts 40 frames uniformly across the full video using OpenCV. Uniform temporal sampling ensures coverage regardless of video length.</div>
|
| 782 |
+
<div style="margin-top:6px;font-size:10px;font-weight:700;letter-spacing:0.1em;color:rgba(0,255,156,0.5);text-transform:uppercase;">OpenCV Β· Uniform sampling Β· Max 40 frames</div>
|
| 783 |
+
</div>
|
| 784 |
+
</div>
|
| 785 |
+
<div class="agent-row">
|
| 786 |
+
<div class="agent-icon">π€</div>
|
| 787 |
+
<div>
|
| 788 |
+
<div style="font-size:14px;font-weight:600;color:#dae5da;">Face Detector Agent</div>
|
| 789 |
+
<div style="font-size:12px;color:#849587;margin-top:4px;line-height:1.5;">Detects and crops faces using MediaPipe. Applies a quality gate β blurry crops (blur < 40) are discarded before model inference.</div>
|
| 790 |
+
<div style="margin-top:6px;font-size:10px;font-weight:700;letter-spacing:0.1em;color:rgba(0,255,156,0.5);text-transform:uppercase;">MediaPipe Β· Quality gate Β· 20% padding</div>
|
| 791 |
+
</div>
|
| 792 |
+
</div>
|
| 793 |
+
<div class="agent-row">
|
| 794 |
+
<div class="agent-icon">π§ </div>
|
| 795 |
+
<div>
|
| 796 |
+
<div style="font-size:14px;font-weight:600;color:#dae5da;">Decision Agent (ViT Ensemble)</div>
|
| 797 |
+
<div style="font-size:12px;color:#849587;margin-top:4px;line-height:1.5;">Runs two Vision Transformer models on each face crop. Scores aggregated with mean + median blend and adaptive thresholding based on frame consistency.</div>
|
| 798 |
+
<div style="margin-top:6px;font-size:10px;font-weight:700;letter-spacing:0.1em;color:rgba(0,255,156,0.5);text-transform:uppercase;">dima806 (99.3%) Β· prithivMLmods (92.1%) Β· Adaptive threshold</div>
|
| 799 |
+
</div>
|
| 800 |
+
</div>
|
| 801 |
+
<div class="agent-row">
|
| 802 |
+
<div class="agent-icon">ποΈ</div>
|
| 803 |
+
<div>
|
| 804 |
+
<div style="font-size:14px;font-weight:600;color:#dae5da;">Audio Analysis Agent</div>
|
| 805 |
+
<div style="font-size:12px;color:#849587;margin-top:4px;line-height:1.5;">Extracts audio via moviepy, runs librosa heuristics (pitch variance, MFCC delta, spectral flatness) and Wav2Vec2 ASVspoof model. Detects AI voices and audio-visual mismatch.</div>
|
| 806 |
+
<div style="margin-top:6px;font-size:10px;font-weight:700;letter-spacing:0.1em;color:rgba(0,227,253,0.5);text-transform:uppercase;">Wav2Vec2 (92.8%) Β· Librosa Β· AV mismatch detection</div>
|
| 807 |
+
</div>
|
| 808 |
+
</div>
|
| 809 |
+
<div class="agent-row">
|
| 810 |
+
<div class="agent-icon">π</div>
|
| 811 |
+
<div>
|
| 812 |
+
<div style="font-size:14px;font-weight:600;color:#dae5da;">Report Generator Agent</div>
|
| 813 |
+
<div style="font-size:12px;color:#849587;margin-top:4px;line-height:1.5;">Combines visual + audio scores into a final verdict. AV_MISMATCH (face-swap with dubbed audio) overrides visual REAL verdict. Generates insights and frame timeline.</div>
|
| 814 |
+
<div style="margin-top:6px;font-size:10px;font-weight:700;letter-spacing:0.1em;color:rgba(0,255,156,0.5);text-transform:uppercase;">AV mismatch Β· Confidence calibration Β· Insight generation</div>
|
| 815 |
+
</div>
|
| 816 |
+
</div>
|
| 817 |
+
</div>
|
| 818 |
+
</div>
|
| 819 |
+
</div>
|
| 820 |
+
|
| 821 |
+
<!-- Logs Modal -->
|
| 822 |
+
<div id="logsModal" style="display:none;" class="modal-backdrop" onclick="if(event.target===this)closeModal('logsModal')">
|
| 823 |
+
<div class="modal-box">
|
| 824 |
+
<div class="modal-header">
|
| 825 |
+
<div>
|
| 826 |
+
<div style="font-size:11px;font-weight:700;letter-spacing:0.15em;color:#00ff9c;text-transform:uppercase;">ANALYSIS LOGS</div>
|
| 827 |
+
<div style="font-size:12px;color:#849587;margin-top:4px;">Session history (last 20 analyses)</div>
|
| 828 |
+
</div>
|
| 829 |
+
<button class="modal-close" onclick="closeModal('logsModal')">β</button>
|
| 830 |
+
</div>
|
| 831 |
+
<div class="modal-body">
|
| 832 |
+
<div id="logsContent">
|
| 833 |
+
<div style="font-size:12px;color:#849587;text-align:center;padding:32px 0;opacity:0.5;">
|
| 834 |
+
No analyses run this session.<br/>Upload a video to begin.
|
| 835 |
+
</div>
|
| 836 |
+
</div>
|
| 837 |
+
</div>
|
| 838 |
+
</div>
|
| 839 |
+
</div>
|
| 840 |
+
|
| 841 |
+
<!-- Network Modal -->
|
| 842 |
+
<div id="networkModal" style="display:none;" class="modal-backdrop" onclick="if(event.target===this)closeModal('networkModal')">
|
| 843 |
+
<div class="modal-box">
|
| 844 |
+
<div class="modal-header">
|
| 845 |
+
<div>
|
| 846 |
+
<div style="font-size:11px;font-weight:700;letter-spacing:0.15em;color:#00ff9c;text-transform:uppercase;">NETWORK STATUS</div>
|
| 847 |
+
<div style="font-size:12px;color:#849587;margin-top:4px;">Model endpoints and system health</div>
|
| 848 |
+
</div>
|
| 849 |
+
<button class="modal-close" onclick="closeModal('networkModal')">β</button>
|
| 850 |
+
</div>
|
| 851 |
+
<div class="modal-body">
|
| 852 |
+
<div id="networkContent">
|
| 853 |
+
<div style="font-size:12px;color:#849587;text-align:center;padding:24px 0;opacity:0.5;">Checking...</div>
|
| 854 |
+
</div>
|
| 855 |
+
</div>
|
| 856 |
+
</div>
|
| 857 |
+
</div>
|
| 858 |
+
|
| 859 |
<!-- βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 860 |
FOOTER
|
| 861 |
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -->
|
|
|
|
| 1040 |
}
|
| 1041 |
const data = await res.json();
|
| 1042 |
stopPipelineAnimation();
|
| 1043 |
+
logAnalysis(data, selectedFile?.name);
|
| 1044 |
renderResults(data);
|
| 1045 |
showState('resultsSection');
|
| 1046 |
} catch (err) {
|
|
|
|
| 1413 |
.replace(/>/g, '>')
|
| 1414 |
.replace(/"/g, '"');
|
| 1415 |
}
|
| 1416 |
+
|
| 1417 |
+
// ββ Modal helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1418 |
+
function openModal(id) {
|
| 1419 |
+
const el = document.getElementById(id);
|
| 1420 |
+
if (!el) return;
|
| 1421 |
+
el.style.display = 'flex';
|
| 1422 |
+
if (id === 'networkModal') refreshNetworkModal();
|
| 1423 |
+
if (id === 'logsModal') refreshLogsModal();
|
| 1424 |
+
}
|
| 1425 |
+
function closeModal(id) {
|
| 1426 |
+
const el = document.getElementById(id);
|
| 1427 |
+
if (el) el.style.display = 'none';
|
| 1428 |
+
}
|
| 1429 |
+
// Close on Escape
|
| 1430 |
+
document.addEventListener('keydown', e => {
|
| 1431 |
+
if (e.key === 'Escape') {
|
| 1432 |
+
['agentsModal','logsModal','networkModal'].forEach(closeModal);
|
| 1433 |
+
}
|
| 1434 |
+
});
|
| 1435 |
+
|
| 1436 |
+
// ββ Logs modal ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1437 |
+
const _sessionLogs = [];
|
| 1438 |
+
|
| 1439 |
+
function logAnalysis(data, filename) {
|
| 1440 |
+
_sessionLogs.unshift({
|
| 1441 |
+
ts: new Date().toLocaleTimeString(),
|
| 1442 |
+
file: filename || 'unknown',
|
| 1443 |
+
result: data.result,
|
| 1444 |
+
confidence: data.confidence,
|
| 1445 |
+
duration: data.metadata?.video_duration_sec ?? 'β',
|
| 1446 |
+
procTime: data.processing_time_sec ?? 'β',
|
| 1447 |
+
});
|
| 1448 |
+
if (_sessionLogs.length > 20) _sessionLogs.pop();
|
| 1449 |
+
}
|
| 1450 |
+
|
| 1451 |
+
function refreshLogsModal() {
|
| 1452 |
+
const el = document.getElementById('logsContent');
|
| 1453 |
+
if (!_sessionLogs.length) {
|
| 1454 |
+
el.innerHTML = `<div class="font-data-mono text-data-mono text-on-surface-variant text-xs text-center py-8 opacity-50">
|
| 1455 |
+
No analyses run this session.<br/>Upload a video to begin.</div>`;
|
| 1456 |
+
return;
|
| 1457 |
+
}
|
| 1458 |
+
el.innerHTML = _sessionLogs.map(log => {
|
| 1459 |
+
const isFake = log.result === 'FAKE';
|
| 1460 |
+
const color = isFake ? '#ffb4ab' : '#00ff9c';
|
| 1461 |
+
return `
|
| 1462 |
+
<div class="log-row">
|
| 1463 |
+
<div style="display:flex;align-items:center;gap:10px;flex:1;min-width:0;">
|
| 1464 |
+
<span class="material-symbols-outlined text-[14px]" style="color:${color};flex-shrink:0;">${isFake ? 'gpp_bad' : 'verified_user'}</span>
|
| 1465 |
+
<span style="color:#dae5da;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px;">${escHtml(log.file)}</span>
|
| 1466 |
+
</div>
|
| 1467 |
+
<div style="display:flex;align-items:center;gap:16px;flex-shrink:0;">
|
| 1468 |
+
<span style="color:${color};font-weight:700;">${log.result}</span>
|
| 1469 |
+
<span style="color:#849587;">${log.confidence}%</span>
|
| 1470 |
+
<span style="color:#849587;font-size:11px;">${log.ts}</span>
|
| 1471 |
+
</div>
|
| 1472 |
+
</div>`;
|
| 1473 |
+
}).join('');
|
| 1474 |
+
}
|
| 1475 |
+
|
| 1476 |
+
// ββ Network modal βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1477 |
+
async function refreshNetworkModal() {
|
| 1478 |
+
const el = document.getElementById('networkContent');
|
| 1479 |
+
el.innerHTML = `<div class="font-data-mono text-data-mono text-on-surface-variant text-xs text-center py-6 opacity-50">Checking...</div>`;
|
| 1480 |
+
|
| 1481 |
+
let health = null;
|
| 1482 |
+
try {
|
| 1483 |
+
const r = await fetch(API_BASE + '/health');
|
| 1484 |
+
health = await r.json();
|
| 1485 |
+
} catch (_) {}
|
| 1486 |
+
|
| 1487 |
+
const rows = [
|
| 1488 |
+
{ label: 'API Server', value: health ? 'ONLINE' : 'OFFLINE', ok: !!health },
|
| 1489 |
+
{ label: 'Visual Models', value: health?.model ?? 'β', ok: health?.ready },
|
| 1490 |
+
{ label: 'dima806 ViT (99.3%)', value: health?.ready ? 'LOADED' : 'PENDING', ok: health?.ready },
|
| 1491 |
+
{ label: 'prithivMLmods ViT', value: health?.ready ? 'LOADED' : 'PENDING', ok: health?.ready },
|
| 1492 |
+
{ label: 'Audio Wav2Vec2', value: health?.ready ? 'LOADED' : 'PENDING', ok: health?.ready },
|
| 1493 |
+
{ label: 'Inference Mode', value: 'LOCAL (CPU)', ok: true },
|
| 1494 |
+
{ label: 'Endpoint', value: API_BASE, ok: true },
|
| 1495 |
+
];
|
| 1496 |
+
|
| 1497 |
+
el.innerHTML = rows.map(r => `
|
| 1498 |
+
<div class="net-row">
|
| 1499 |
+
<span style="color:#849587;letter-spacing:0.08em;">${r.label}</span>
|
| 1500 |
+
<span style="color:${r.ok ? '#00ff9c' : '#ffb4ab'};font-weight:700;">${escHtml(String(r.value))}</span>
|
| 1501 |
+
</div>`).join('') +
|
| 1502 |
+
`<div style="margin-top:14px;padding-top:12px;border-top:1px solid rgba(255,255,255,0.05);">
|
| 1503 |
+
<button onclick="refreshNetworkModal()" style="background:none;border:1px solid rgba(0,255,156,0.3);color:#00ff9c;padding:6px 14px;border-radius:4px;font-family:inherit;font-size:11px;letter-spacing:0.1em;cursor:pointer;transition:all 0.2s;" onmouseover="this.style.background='rgba(0,255,156,0.08)'" onmouseout="this.style.background='none'">
|
| 1504 |
+
β» REFRESH
|
| 1505 |
+
</button>
|
| 1506 |
+
</div>`;
|
| 1507 |
+
}
|
| 1508 |
</script>
|
| 1509 |
</body>
|
| 1510 |
</html>
|