Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>DriveSafe — AI Safety HUD & Conversational SLM</title> | |
| <!-- Google Fonts for High-Tech Dashboard Aesthetic --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Share+Tech+Mono&display=swap" rel="stylesheet"> | |
| <!-- Chart.js for High-Performance EKG Graph --> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <style> | |
| :root { | |
| --bg-color: #08090e; | |
| --panel-bg: rgba(15, 18, 28, 0.7); | |
| --border-glow: rgba(0, 255, 128, 0.15); | |
| --safe-color: #00ff80; | |
| --warn-color: #ffaa00; | |
| --danger-color: #ff2850; | |
| --accent-color: #00bfff; | |
| --text-color: #e2e8f0; | |
| --text-muted: #64748b; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| font-family: 'Outfit', sans-serif; | |
| -webkit-font-smoothing: antialiased; | |
| } | |
| body { | |
| background-color: var(--bg-color); | |
| background-image: | |
| radial-gradient(at 0% 0%, rgba(0, 191, 255, 0.05) 0px, transparent 50%), | |
| radial-gradient(at 100% 0%, rgba(0, 255, 128, 0.05) 0px, transparent 50%), | |
| radial-gradient(at 50% 100%, rgba(255, 40, 80, 0.03) 0px, transparent 50%); | |
| color: var(--text-color); | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* --- Header --- */ | |
| header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 20px 40px; | |
| background: rgba(8, 9, 14, 0.85); | |
| backdrop-filter: blur(12px); | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.05); | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| } | |
| .logo-section { | |
| display: flex; | |
| align-items: center; | |
| gap: 15px; | |
| } | |
| .logo-section h1 { | |
| font-size: 24px; | |
| font-weight: 800; | |
| letter-spacing: 2px; | |
| background: linear-gradient(to right, #00ff80, #00bfff); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .logo-section span { | |
| font-family: 'Share Tech Mono', monospace; | |
| font-size: 11px; | |
| background: rgba(0, 191, 255, 0.1); | |
| color: var(--accent-color); | |
| padding: 2px 8px; | |
| border: 1px solid rgba(0, 191, 255, 0.2); | |
| border-radius: 4px; | |
| } | |
| .connection-status { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-size: 13px; | |
| font-weight: 600; | |
| color: var(--safe-color); | |
| } | |
| .pulse-dot { | |
| width: 8px; | |
| height: 8px; | |
| background-color: var(--safe-color); | |
| border-radius: 50%; | |
| box-shadow: 0 0 10px var(--safe-color); | |
| animation: pulse 1.5s infinite; | |
| } | |
| /* --- Main Dashboard Container --- */ | |
| .dashboard-container { | |
| display: grid; | |
| grid-template-columns: 1.1fr 0.9fr; | |
| gap: 30px; | |
| padding: 30px 40px; | |
| flex-grow: 1; | |
| max-width: 1800px; | |
| margin: 0 auto; | |
| width: 100%; | |
| } | |
| .glass-panel { | |
| background: var(--panel-bg); | |
| backdrop-filter: blur(16px); | |
| border: 1px solid rgba(255, 255, 255, 0.05); | |
| border-radius: 20px; | |
| padding: 24px; | |
| box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4); | |
| transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .glass-panel::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 4px; | |
| background: linear-gradient(90deg, transparent, var(--border-glow), transparent); | |
| transition: all 0.4s ease; | |
| } | |
| /* --- LEFT COLUMN: CAMERA & VISUAL HUD --- */ | |
| .left-column { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 30px; | |
| } | |
| .video-panel { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .video-container { | |
| width: 100%; | |
| aspect-ratio: 4/3; | |
| background-color: #050608; | |
| border-radius: 12px; | |
| overflow: hidden; | |
| border: 1px solid rgba(255, 255, 255, 0.08); | |
| position: relative; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .video-stream { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| } | |
| .video-placeholder { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 15px; | |
| color: var(--text-muted); | |
| text-align: center; | |
| } | |
| .video-placeholder svg { | |
| width: 60px; | |
| height: 60px; | |
| stroke: var(--text-muted); | |
| animation: rotate-sync 4s linear infinite; | |
| } | |
| .hud-overlay-badge { | |
| position: absolute; | |
| top: 15px; | |
| right: 15px; | |
| background: rgba(0, 0, 0, 0.7); | |
| border: 1px solid var(--safe-color); | |
| padding: 4px 12px; | |
| border-radius: 6px; | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: var(--safe-color); | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| box-shadow: 0 0 10px rgba(0, 255, 128, 0.2); | |
| transition: all 0.3s ease; | |
| } | |
| /* --- Indicators Panel --- */ | |
| .indicators-grid { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 20px; | |
| } | |
| .metric-card { | |
| background: rgba(255, 255, 255, 0.02); | |
| border: 1px solid rgba(255, 255, 255, 0.04); | |
| border-radius: 14px; | |
| padding: 18px; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| text-align: center; | |
| position: relative; | |
| } | |
| .metric-title { | |
| font-size: 12px; | |
| color: var(--text-muted); | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| margin-bottom: 8px; | |
| } | |
| .metric-value { | |
| font-size: 32px; | |
| font-weight: 800; | |
| font-family: 'Share Tech Mono', monospace; | |
| } | |
| /* Gauge Meter styling */ | |
| .gauge-container { | |
| width: 100px; | |
| height: 100px; | |
| position: relative; | |
| margin-bottom: 5px; | |
| } | |
| .radial-gauge { | |
| width: 100%; | |
| height: 100%; | |
| transform: rotate(-90deg); | |
| } | |
| .gauge-circle { | |
| fill: none; | |
| stroke-width: 4; | |
| stroke: rgba(255, 255, 255, 0.05); | |
| } | |
| .gauge-bar { | |
| fill: none; | |
| stroke-width: 6; | |
| stroke-dasharray: 251.2; | |
| stroke-dashoffset: 251.2; | |
| stroke: var(--safe-color); | |
| stroke-linecap: round; | |
| transition: stroke-dashoffset 0.1s ease, stroke 0.3s ease; | |
| } | |
| .gauge-text { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| font-size: 20px; | |
| font-weight: 800; | |
| font-family: 'Share Tech Mono', monospace; | |
| color: var(--text-color); | |
| } | |
| /* --- RIGHT COLUMN: SLM TERMINAL & DIAGNOSTICS --- */ | |
| .right-column { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 30px; | |
| } | |
| /* Chart Panel */ | |
| .chart-container { | |
| height: 180px; | |
| width: 100%; | |
| } | |
| /* SLM Conversation terminal */ | |
| .terminal-panel { | |
| display: flex; | |
| flex-direction: column; | |
| height: 380px; | |
| } | |
| .terminal-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.05); | |
| padding-bottom: 12px; | |
| margin-bottom: 15px; | |
| } | |
| .terminal-title { | |
| font-size: 14px; | |
| font-weight: 700; | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| color: var(--accent-color); | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .terminal-body { | |
| flex-grow: 1; | |
| overflow-y: auto; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| padding-right: 5px; | |
| font-family: 'Outfit', sans-serif; | |
| scroll-behavior: smooth; | |
| } | |
| /* Scrollbar custom styling */ | |
| .terminal-body::-webkit-scrollbar { | |
| width: 4px; | |
| } | |
| .terminal-body::-webkit-scrollbar-thumb { | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 2px; | |
| } | |
| /* Chat bubbles */ | |
| .chat-bubble { | |
| max-width: 80%; | |
| padding: 12px 16px; | |
| border-radius: 12px; | |
| font-size: 14px; | |
| line-height: 1.5; | |
| animation: slide-up 0.3s ease; | |
| } | |
| .driver-bubble { | |
| align-self: flex-end; | |
| background: rgba(0, 191, 255, 0.08); | |
| border: 1px solid rgba(0, 191, 255, 0.15); | |
| color: #dbeafe; | |
| border-bottom-right-radius: 2px; | |
| } | |
| .slm-bubble { | |
| align-self: flex-start; | |
| background: rgba(0, 255, 128, 0.05); | |
| border: 1px solid rgba(0, 255, 128, 0.12); | |
| color: #dcfce7; | |
| border-bottom-left-radius: 2px; | |
| } | |
| .system-bubble { | |
| align-self: center; | |
| background: rgba(255, 40, 80, 0.08); | |
| border: 1px solid rgba(255, 40, 80, 0.15); | |
| color: #ffe4e6; | |
| max-width: 90%; | |
| font-family: 'Share Tech Mono', monospace; | |
| font-size: 12px; | |
| text-transform: uppercase; | |
| text-align: center; | |
| border-radius: 6px; | |
| padding: 6px 12px; | |
| } | |
| .bubble-label { | |
| font-size: 10px; | |
| font-weight: 800; | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| margin-bottom: 4px; | |
| display: block; | |
| } | |
| .driver-bubble .bubble-label { | |
| color: var(--accent-color); | |
| } | |
| .slm-bubble .bubble-label { | |
| color: var(--safe-color); | |
| } | |
| /* --- Control Desk Buttons --- */ | |
| .control-desk { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 15px; | |
| } | |
| .cyber-btn { | |
| background: rgba(255, 255, 255, 0.02); | |
| border: 1px solid rgba(255, 255, 255, 0.06); | |
| border-radius: 10px; | |
| padding: 12px; | |
| font-size: 13px; | |
| font-weight: 600; | |
| color: var(--text-color); | |
| cursor: pointer; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 6px; | |
| transition: all 0.2s ease; | |
| } | |
| .cyber-btn:hover { | |
| background: rgba(255, 255, 255, 0.06); | |
| border-color: rgba(255, 255, 255, 0.15); | |
| transform: translateY(-2px); | |
| } | |
| .cyber-btn:active { | |
| transform: translateY(0); | |
| } | |
| .cyber-btn svg { | |
| width: 20px; | |
| height: 20px; | |
| stroke: var(--text-color); | |
| transition: all 0.2s ease; | |
| } | |
| .cyber-btn.active { | |
| background: rgba(0, 255, 128, 0.08); | |
| border-color: var(--safe-color); | |
| color: var(--safe-color); | |
| } | |
| .cyber-btn.active svg { | |
| stroke: var(--safe-color); | |
| } | |
| .cyber-btn.danger { | |
| background: rgba(255, 40, 80, 0.05); | |
| border-color: rgba(255, 40, 80, 0.2); | |
| } | |
| .cyber-btn.danger:hover { | |
| background: rgba(255, 40, 80, 0.12); | |
| border-color: var(--danger-color); | |
| color: var(--danger-color); | |
| } | |
| .cyber-btn.danger:hover svg { | |
| stroke: var(--danger-color); | |
| } | |
| /* --- DYNAMIC STATE OVERLAYS (WARNING SHADOWS) --- */ | |
| .glass-panel.state-normal::before { | |
| background: linear-gradient(90deg, transparent, var(--safe-color), transparent); | |
| } | |
| .glass-panel.state-warn::before { | |
| background: linear-gradient(90deg, transparent, var(--warn-color), transparent); | |
| } | |
| .glass-panel.state-warn { | |
| border-color: rgba(255, 170, 0, 0.15); | |
| box-shadow: 0 0 20px rgba(255, 170, 0, 0.1); | |
| } | |
| .glass-panel.state-danger::before { | |
| background: linear-gradient(90deg, transparent, var(--danger-color), transparent); | |
| } | |
| .glass-panel.state-danger { | |
| border-color: rgba(255, 40, 80, 0.3); | |
| box-shadow: 0 0 30px rgba(255, 40, 80, 0.2); | |
| animation: pulse-border 1.5s infinite; | |
| } | |
| /* --- Animations --- */ | |
| @keyframes pulse { | |
| 0% { transform: scale(1); opacity: 1; box-shadow: 0 0 10px var(--safe-color); } | |
| 50% { transform: scale(1.1); opacity: 0.7; box-shadow: 0 0 20px var(--safe-color); } | |
| 100% { transform: scale(1); opacity: 1; box-shadow: 0 0 10px var(--safe-color); } | |
| } | |
| @keyframes pulse-border { | |
| 0% { border-color: rgba(255, 40, 80, 0.2); } | |
| 50% { border-color: rgba(255, 40, 80, 0.6); } | |
| 100% { border-color: rgba(255, 40, 80, 0.2); } | |
| } | |
| @keyframes rotate-sync { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| @keyframes slide-up { | |
| 0% { transform: translateY(10px); opacity: 0; } | |
| 100% { transform: translateY(0); opacity: 1; } | |
| } | |
| /* Responsive */ | |
| @media (max-width: 1200px) { | |
| .dashboard-container { | |
| grid-template-columns: 1fr; | |
| padding: 20px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="logo-section"> | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="url(#logo-grad)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> | |
| <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect> | |
| <path d="M7 11V7a5 5 0 0 1 10 0v4"></path> | |
| </svg> | |
| <svg style="width:0;height:0;position:absolute"> | |
| <linearGradient id="logo-grad" x1="0%" y1="0%" x2="100%" y2="0%"> | |
| <stop offset="0%" stop-color="#00ff80"/> | |
| <stop offset="100%" stop-color="#00bfff"/> | |
| </linearGradient> | |
| </svg> | |
| <h1>DRIVESAFE</h1> | |
| <span>LOCAL SLM V1.0</span> | |
| </div> | |
| <div class="connection-status"> | |
| <div class="pulse-dot"></div> | |
| HUD SYSTEM RUNNING | |
| </div> | |
| </header> | |
| <div class="dashboard-container"> | |
| <!-- LEFT COLUMN: VIDEO FEED & METRIC READOUTS --> | |
| <div class="left-column"> | |
| <!-- Video feed Panel --> | |
| <div class="glass-panel video-panel state-normal" id="main-panel"> | |
| <div class="video-container"> | |
| <img src="/video_feed" class="video-stream" id="video-feed" onerror="showPlaceholder()"> | |
| <div class="hud-overlay-badge" id="hud-state-badge">NORMAL</div> | |
| <div class="video-placeholder" id="video-placeholder" style="display:none;"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | |
| <line x1="1" y1="1" x2="23" y2="23"></line> | |
| <path d="M21 21H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h3m3-3h6l2 3h4a2 2 0 0 1 2 2v9.34"></path> | |
| <path d="M10.02 4.35L8.06 6H3"></path> | |
| <circle cx="12" cy="13" r="4"></circle> | |
| </svg> | |
| <p>Awaiting Safe Driving Camera Stream...</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Core Indicators grid --> | |
| <div class="indicators-grid"> | |
| <!-- Eye Aspect Ratio (EAR) Gauge --> | |
| <div class="metric-card"> | |
| <div class="metric-title">Eye Aspect Ratio</div> | |
| <div class="gauge-container"> | |
| <svg class="radial-gauge" viewBox="0 0 100 100"> | |
| <circle class="gauge-circle" cx="50" cy="50" r="40"></circle> | |
| <circle class="gauge-bar" id="ear-gauge" cx="50" cy="50" r="40"></circle> | |
| </svg> | |
| <div class="gauge-text" id="ear-val">0.00</div> | |
| </div> | |
| </div> | |
| <!-- Event counter --> | |
| <div class="metric-card"> | |
| <div class="metric-title">Drowsy Detections</div> | |
| <div class="metric-value" id="drowsiness-count" style="color: var(--safe-color);">0</div> | |
| </div> | |
| <!-- System FPS --> | |
| <div class="metric-card"> | |
| <div class="metric-title">Core Engine Speed</div> | |
| <div class="metric-value" id="engine-fps" style="color: var(--accent-color);">0 FPS</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- RIGHT COLUMN: CHAT LOG, CHART & DESK CONTROLS --> | |
| <div class="right-column"> | |
| <!-- Real-time Line Graph Panel --> | |
| <div class="glass-panel"> | |
| <div class="metric-title" style="margin-bottom:12px;">Active EKG Eye Waveform</div> | |
| <div class="chart-container"> | |
| <canvas id="earChart"></canvas> | |
| </div> | |
| </div> | |
| <!-- DriveSafe Conversational Terminal --> | |
| <div class="glass-panel terminal-panel"> | |
| <div class="terminal-header"> | |
| <div class="terminal-title"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path> | |
| </svg> | |
| COGNITIVE VOICE ASSISTANT | |
| </div> | |
| <span id="slm-indicator" style="font-size:10px; font-family:'Share Tech Mono', monospace; color: var(--safe-color);">SLM ONLINE</span> | |
| </div> | |
| <div class="terminal-body" id="chat-box"> | |
| <div class="chat-bubble slm-bubble"> | |
| <span class="bubble-label">DriveSafe SLM</span> | |
| Hello driver! I am active. Let's drive safely today. Ask me any question, or say "play music"! | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Cyber Control Desk --> | |
| <div class="control-desk"> | |
| <button class="cyber-btn active" id="toggle-tracking-btn" onclick="toggleTracking()"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path> | |
| <circle cx="12" cy="12" r="3"></circle> | |
| </svg> | |
| <span>ACTIVE TRACKING</span> | |
| </button> | |
| <button class="cyber-btn" onclick="triggerReset()"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67"></path> | |
| </svg> | |
| <span>SYSTEM RESET</span> | |
| </button> | |
| <button class="cyber-btn danger" onclick="triggerMusic()"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M9 18V5l12-2v13"></path> | |
| <circle cx="6" cy="18" r="3"></circle> | |
| <circle cx="18" cy="16" r="3"></circle> | |
| </svg> | |
| <span>PLAY BEATS</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Script file mapping --> | |
| <script src="/static/js/app.js"></script> | |
| <script> | |
| function showPlaceholder() { | |
| document.getElementById('video-feed').style.display = 'none'; | |
| document.getElementById('video-placeholder').style.display = 'flex'; | |
| } | |
| </script> | |
| </body> | |
| </html> | |