r-vasanthkumar73-dev's picture
Deploying backend and frontend folder modules.
099d157 verified
Raw
History Blame Contribute Delete
23.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Sentinel Interface β€” AI Engagement Monitor</title>
<meta name="description"
content="Multisource Emotion Detection and Engagement Optimization for E-Learning. Real-time face, speech, and text analysis.">
<link rel="stylesheet" href="/css/styles.css">
<link
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700;800&family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
rel="stylesheet">
<style>
.hero-section {
position: relative;
padding: 80px 48px 48px;
text-align: center;
overflow: hidden;
}
.hero-title {
font-family: var(--font-headline);
font-size: 3.5rem;
font-weight: 900;
line-height: 1.1;
margin-bottom: 16px;
letter-spacing: -0.03em;
}
.hero-sub {
font-size: 1.1rem;
color: var(--on-surface-variant);
max-width: 600px;
margin: 0 auto 48px;
line-height: 1.7;
}
.hero-glow {
position: absolute;
width: 600px;
height: 600px;
border-radius: 50%;
filter: blur(120px);
opacity: 0.15;
pointer-events: none;
z-index: 0;
}
.hero-glow.cyan {
background: var(--primary-container);
top: -200px;
left: 20%;
}
.hero-glow.purple {
background: var(--secondary-container);
top: -100px;
right: 10%;
}
.hero-glow.gold {
background: var(--tertiary-fixed-dim);
bottom: -300px;
left: 40%;
opacity: 0.08;
}
.cards-section {
padding: 0 48px 48px;
position: relative;
z-index: 1;
}
.stats-section {
padding: 0 48px 48px;
}
.hero-badge {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 16px;
border-radius: 999px;
background: rgba(0, 240, 255, 0.06);
border: 1px solid rgba(0, 240, 255, 0.15);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 600;
color: var(--primary-container);
margin-bottom: 24px;
}
.orbit-ring {
position: absolute;
border-radius: 50%;
border: 1px solid rgba(0, 240, 255, 0.05);
animation: rotate-slow 60s linear infinite;
pointer-events: none;
}
.orbit-ring:nth-child(1) {
width: 400px;
height: 400px;
top: -100px;
left: calc(50% - 200px);
}
.orbit-ring:nth-child(2) {
width: 600px;
height: 600px;
top: -200px;
left: calc(50% - 300px);
animation-duration: 90s;
animation-direction: reverse;
}
.orbit-ring:nth-child(3) {
width: 800px;
height: 800px;
top: -300px;
left: calc(50% - 400px);
animation-duration: 120s;
}
.orbit-dot {
position: absolute;
width: 4px;
height: 4px;
background: var(--primary-container);
border-radius: 50%;
box-shadow: 0 0 8px var(--primary-container);
}
.system-status {
display: flex;
align-items: center;
gap: 24px;
padding: 20px 28px;
border-radius: 16px;
background: rgba(26, 31, 47, 0.5);
border: 1px solid rgba(59, 73, 75, 0.1);
}
.system-status-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--on-surface-variant);
}
.system-status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
}
.system-status-dot.online {
background: #34c759;
box-shadow: 0 0 6px #34c759;
}
.system-status-dot.loading {
background: var(--tertiary-fixed-dim);
animation: pulse-glow 1.5s infinite;
}
.feature-grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
margin-bottom: 32px;
}
.bottom-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 24px;
}
@media (max-width: 1200px) {
.feature-grid-container {
grid-template-columns: 1fr;
}
.bottom-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<canvas id="particles-canvas"></canvas>
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-logo">S</div>
<nav class="sidebar-nav">
<a href="/" class="sidebar-link active" data-tooltip="Dashboard">
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 1;">dashboard</span>
<span class="label">Home</span>
</a>
<a href="./live.html" class="sidebar-link" data-tooltip="Live Analysis">
<span class="material-symbols-outlined">sensors</span>
<span class="label">Live</span>
</a>
<a href="./scan.html" class="sidebar-link" data-tooltip="Deep Scan">
<span class="material-symbols-outlined">analytics</span>
<span class="label">Scan</span>
</a>
<a href="./stats.html" class="sidebar-link" data-tooltip="Performance">
<span class="material-symbols-outlined">school</span>
<span class="label">Stats</span>
</a>
</nav>
</aside>
<!-- Top Bar -->
<header class="topbar">
<div style="display:flex;align-items:center;gap:16px;">
<h1 class="topbar-title gradient-text">The Sentinel Interface</h1>
<div class="status-pill">
<span class="status-dot"></span>
<span class="status-text">System Online</span>
</div>
</div>
<div style="display:flex;align-items:center;gap:16px;">
<button class="btn-primary" onclick="window.location.href='/live'" id="start-monitoring-btn">
<span class="material-symbols-outlined" style="font-size:18px;">sensors</span>
Start Monitoring
</button>
</div>
</header>
<!-- Main Content -->
<main class="main-content">
<!-- Hero -->
<section class="hero-section">
<div class="hero-glow cyan"></div>
<div class="hero-glow purple"></div>
<div class="hero-glow gold"></div>
<div class="orbit-ring">
<div class="orbit-dot" style="top:0;left:50%;"></div>
</div>
<div class="orbit-ring">
<div class="orbit-dot" style="top:50%;right:0;"></div>
</div>
<div class="orbit-ring">
<div class="orbit-dot" style="bottom:0;left:30%;"></div>
</div>
<div style="position:relative;z-index:1;">
<div class="hero-badge">
<span class="material-symbols-outlined" style="font-size:14px;">neurology</span>
Multisource AI Emotion Engine
</div>
<h2 class="hero-title animate-fade-in">
<span class="gradient-text">Multisource Emotion</span><br>
<span style="color:var(--on-surface);"> Recognition System</span>
</h2>
<p class="hero-sub animate-fade-in" style="animation-delay:0.2s;">
Real-time multimodal emotion detection using face recognition, speech prosody analysis, and text
sentiment.
</p>
<div
style="display:flex;gap:16px;justify-content:center;animation:fade-in 0.6s ease-out 0.4s forwards;opacity:0;">
<button class="btn-primary" onclick="window.location.href='/live'">
<span class="material-symbols-outlined" style="font-size:18px;">play_arrow</span>
Launch Live Pulse
</button>
<button class="btn-secondary" onclick="window.location.href='/scan'">
<span class="material-symbols-outlined" style="font-size:18px;">folder_open</span>
Deep Archive Scan
</button>
</div>
</div>
</section>
<!-- Feature Cards -->
<section class="cards-section stagger-children">
<div class="feature-grid-container">
<!-- Card 1: Sentinel Live Pulse -->
<div class="feature-card" onclick="window.location.href='/live'">
<div class="feature-card-inner">
<div class="feature-icon primary">
<span class="material-symbols-outlined"
style="font-variation-settings:'FILL' 1;">sensors</span>
</div>
<h3 class="feature-title" style="color:var(--primary);">Sentinel Live Pulse</h3>
<p class="feature-desc">
Real-time multimodal analysis engine. Live webcam with 468-point face mesh visualization,
audio frequency waveform recording, and instant text sentiment β€” all fused into a unified
engagement score.
</p>
<div class="feature-footer">
<span class="feature-tag">Real-time</span>
<span class="material-symbols-outlined"
style="color:var(--primary-container);font-size:20px;">arrow_forward</span>
</div>
<!-- Decorative scan line -->
<div
style="position:absolute;left:0;width:100%;height:2px;background:linear-gradient(90deg,transparent,var(--primary-container),transparent);animation:scan-line 3s linear infinite;opacity:0.3;">
</div>
</div>
</div>
<!-- Card 2: Deep Archive Scan -->
<div class="feature-card secondary" onclick="window.location.href='/scan'">
<div class="feature-card-inner">
<div class="feature-icon secondary">
<span class="material-symbols-outlined"
style="font-variation-settings:'FILL' 1;">query_stats</span>
</div>
<h3 class="feature-title" style="color:var(--secondary);">Deep Archive Scan</h3>
<p class="feature-desc">
Upload and analyze existing recordings. Process face images, audio clips, and text documents
through our AI pipeline for offline emotion detection and engagement scoring.
</p>
<div class="feature-footer">
<span class="feature-tag secondary">Upload & Analyze</span>
<span class="material-symbols-outlined"
style="color:var(--secondary);font-size:20px;">arrow_forward</span>
</div>
</div>
</div>
<!-- Card 3: Instructor Nexus -->
<div class="feature-card tertiary" onclick="window.location.href='/performance'">
<div class="feature-card-inner">
<div class="feature-icon tertiary">
<span class="material-symbols-outlined"
style="font-variation-settings:'FILL' 1;">school</span>
</div>
<h3 class="feature-title" style="color:var(--tertiary-fixed-dim);">Instructor Nexus</h3>
<p class="feature-desc">
Teacher control center for student performance. View current session metrics, historical
engagement trends,
emotion distribution charts, and per-student analytics.
</p>
<div class="feature-footer">
<span class="feature-tag tertiary">Performance Hub</span>
<span class="material-symbols-outlined"
style="color:var(--tertiary-fixed-dim);font-size:20px;">arrow_forward</span>
</div>
</div>
</div>
</div>
<!-- Bottom Row -->
<div class="bottom-grid">
<!-- System Status Card -->
<div class="glass-card" style="padding:28px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;">
<div>
<div
style="font-size:10px;text-transform:uppercase;letter-spacing:0.1em;color:var(--outline);font-weight:600;">
System Status</div>
<div style="font-family:var(--font-headline);font-size:1.25rem;font-weight:700;">AI Models &
Services</div>
</div>
<span class="material-symbols-outlined" style="color:var(--primary-container);">memory</span>
</div>
<div style="display:flex;flex-direction:column;gap:12px;" id="system-status-container">
<div class="system-status">
<div class="system-status-dot loading"></div>
<span style="flex:1;">Checking system status...</span>
</div>
</div>
</div>
<!-- Quick Stats -->
<div class="glass-card" style="padding:28px;">
<div
style="font-size:10px;text-transform:uppercase;letter-spacing:0.1em;color:var(--outline);font-weight:600;margin-bottom:12px;">
Quick Stats</div>
<div style="font-family:var(--font-headline);font-size:1.25rem;font-weight:700;margin-bottom:20px;">
Today's Overview</div>
<div style="display:flex;flex-direction:column;gap:12px;" id="quick-stats">
<div class="stat-card">
<div class="stat-label">Total Sessions</div>
<div class="stat-value" style="color:var(--primary-container);" id="stat-sessions">0</div>
</div>
<div class="stat-card">
<div class="stat-label">Avg Engagement</div>
<div class="stat-value" style="color:var(--secondary);" id="stat-engagement">β€”</div>
</div>
</div>
</div>
</div>
</section>
</main>
<script>
// ── Particle Background ──────────────────────────────
(function () {
const canvas = document.getElementById('particles-canvas');
const ctx = canvas.getContext('2d');
let particles = [];
const PARTICLE_COUNT = 60;
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
class Particle {
constructor() { this.reset(); }
reset() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = (Math.random() - 0.5) * 0.3;
this.vy = (Math.random() - 0.5) * 0.3;
this.radius = Math.random() * 1.5 + 0.5;
this.opacity = Math.random() * 0.3 + 0.1;
}
update() {
this.x += this.vx;
this.y += this.vy;
if (this.x < 0 || this.x > canvas.width) this.vx *= -1;
if (this.y < 0 || this.y > canvas.height) this.vy *= -1;
}
draw() {
const themeColor = document.documentElement.getAttribute('data-theme') === 'light' ? '2, 132, 199' : '0, 240, 255';
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = `rgba(${themeColor}, ${this.opacity})`;
ctx.fill();
}
}
for (let i = 0; i < PARTICLE_COUNT; i++) particles.push(new Particle());
function animate() {
const themeColor = document.documentElement.getAttribute('data-theme') === 'light' ? '2, 132, 199' : '0, 240, 255';
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(p => { p.update(); p.draw(); });
// Draw connections
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const dx = particles[i].x - particles[j].x;
const dy = particles[i].y - particles[j].y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 120) {
ctx.beginPath();
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.strokeStyle = `rgba(${themeColor}, ${0.03 * (1 - dist / 120)})`;
ctx.lineWidth = 0.5;
ctx.stroke();
}
}
}
requestAnimationFrame(animate);
}
animate();
})();
// ── 3D Tilt Effect on Feature Cards ──────────────────
document.querySelectorAll('.feature-card').forEach(card => {
const inner = card.querySelector('.feature-card-inner');
card.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
const rotateX = (0.5 - y) * 8;
const rotateY = (x - 0.5) * 8;
inner.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) translateY(-4px)`;
inner.style.setProperty('--mouse-x', (x * 100) + '%');
inner.style.setProperty('--mouse-y', (y * 100) + '%');
});
card.addEventListener('mouseleave', () => {
inner.style.transform = '';
});
});
// ── System Health Check ──────────────────────────────
async function checkSystemStatus() {
const container = document.getElementById('system-status-container');
try {
const res = await fetch('/api/health');
const data = await res.json();
container.innerHTML = `
<div class="system-status">
<div class="system-status-dot online"></div>
<span style="flex:1;font-weight:600;">API Server</span>
<span style="font-size:11px;color:var(--primary-container);">Online</span>
</div>
<div class="system-status">
<div class="system-status-dot ${data.models_loaded?.face ? 'online' : 'loading'}"></div>
<span style="flex:1;">Face CNN Model</span>
<span style="font-size:11px;color:${data.models_loaded?.face ? 'var(--primary-container)' : 'var(--tertiary-fixed-dim)'};">${data.models_loaded?.face ? 'Loaded' : 'Standby'}</span>
</div>
<div class="system-status">
<div class="system-status-dot ${data.models_loaded?.text ? 'online' : 'loading'}"></div>
<span style="flex:1;">Text Sentiment (DistilBERT)</span>
<span style="font-size:11px;color:${data.models_loaded?.text ? 'var(--primary-container)' : 'var(--tertiary-fixed-dim)'};">${data.models_loaded?.text ? 'Loaded' : 'Standby'}</span>
</div>
<div class="system-status">
<div class="system-status-dot ${data.models_loaded?.speech ? 'online' : 'loading'}"></div>
<span style="flex:1;">Speech Analyzer (librosa)</span>
<span style="font-size:11px;color:${data.models_loaded?.speech ? 'var(--primary-container)' : 'var(--tertiary-fixed-dim)'};">${data.models_loaded?.speech ? 'Loaded' : 'Standby'}</span>
</div>
`;
} catch (e) {
container.innerHTML = `
<div class="system-status">
<div class="system-status-dot" style="background:#ff3b30;"></div>
<span style="flex:1;color:var(--error);">Backend Offline</span>
<span style="font-size:11px;color:var(--error);">Start server first</span>
</div>
`;
}
}
// ── Load Quick Stats ─────────────────────────────────
async function loadStats() {
try {
const res = await fetch('/api/performance/default');
const data = await res.json();
document.getElementById('stat-sessions').textContent = data.overall_stats?.total_sessions || 0;
const avg = data.overall_stats?.avg_engagement;
document.getElementById('stat-engagement').textContent = avg ? avg.toFixed(1) + '%' : 'β€”';
} catch (e) {
// Backend not running
}
}
checkSystemStatus();
loadStats();
setInterval(checkSystemStatus, 15000);
</script>
</body>
</html>