Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>LectureLens AI — Study Smarter</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:ital,wght@0,300;0,400;0,500;1,300&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg: #0a0a0f; | |
| --surface: #12121a; | |
| --surface2: #1a1a26; | |
| --border: #2a2a3d; | |
| --accent: #7c6fff; | |
| --accent2: #ff6b9d; | |
| --accent3: #6bffb8; | |
| --text: #e8e8f0; | |
| --text-dim: #8888aa; | |
| --yellow: #ffd166; | |
| --blue: #118ab2; | |
| --green: #06d6a0; | |
| --pink: #ef476f; | |
| --purple: #9b5de5; | |
| --glow: rgba(124, 111, 255, 0.15); | |
| } | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| background: var(--bg); | |
| color: var(--text); | |
| font-family: 'DM Sans', sans-serif; | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| } | |
| /* Animated background */ | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| top: -50%; | |
| left: -50%; | |
| width: 200%; | |
| height: 200%; | |
| background: | |
| radial-gradient(ellipse at 20% 20%, rgba(124, 111, 255, 0.08) 0%, transparent 50%), | |
| radial-gradient(ellipse at 80% 80%, rgba(255, 107, 157, 0.06) 0%, transparent 50%), | |
| radial-gradient(ellipse at 50% 50%, rgba(107, 255, 184, 0.04) 0%, transparent 60%); | |
| animation: bgShift 15s ease-in-out infinite alternate; | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| @keyframes bgShift { | |
| 0% { transform: translate(0, 0) rotate(0deg); } | |
| 100% { transform: translate(-30px, -20px) rotate(1deg); } | |
| } | |
| .app-container { | |
| position: relative; | |
| z-index: 1; | |
| max-width: 1300px; | |
| margin: 0 auto; | |
| padding: 0 24px; | |
| } | |
| /* ===== HEADER ===== */ | |
| header { | |
| padding: 28px 0 20px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| border-bottom: 1px solid var(--border); | |
| margin-bottom: 40px; | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .logo-icon { | |
| width: 42px; | |
| height: 42px; | |
| background: linear-gradient(135deg, var(--accent), var(--accent2)); | |
| border-radius: 12px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 20px; | |
| box-shadow: 0 0 20px rgba(124, 111, 255, 0.3); | |
| } | |
| .logo-text { | |
| font-family: 'Syne', sans-serif; | |
| font-size: 22px; | |
| font-weight: 800; | |
| background: linear-gradient(90deg, var(--accent), var(--accent2)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .logo-sub { | |
| font-size: 11px; | |
| color: var(--text-dim); | |
| font-weight: 300; | |
| letter-spacing: 2px; | |
| text-transform: uppercase; | |
| margin-top: 2px; | |
| } | |
| .lang-selector { | |
| display: flex; | |
| gap: 8px; | |
| background: var(--surface); | |
| padding: 6px; | |
| border-radius: 12px; | |
| border: 1px solid var(--border); | |
| } | |
| .lang-btn { | |
| padding: 6px 16px; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-family: 'DM Sans', sans-serif; | |
| font-size: 13px; | |
| font-weight: 500; | |
| transition: all 0.2s; | |
| background: transparent; | |
| color: var(--text-dim); | |
| } | |
| .lang-btn.active { | |
| background: var(--accent); | |
| color: white; | |
| box-shadow: 0 0 12px rgba(124, 111, 255, 0.4); | |
| } | |
| /* ===== URL INPUT SECTION ===== */ | |
| .url-section { | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: 20px; | |
| padding: 32px; | |
| margin-bottom: 32px; | |
| transition: border-color 0.3s; | |
| } | |
| .url-section:hover { | |
| border-color: var(--accent); | |
| } | |
| .section-label { | |
| font-family: 'Syne', sans-serif; | |
| font-size: 13px; | |
| font-weight: 600; | |
| color: var(--accent); | |
| letter-spacing: 2px; | |
| text-transform: uppercase; | |
| margin-bottom: 16px; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .url-input-row { | |
| display: flex; | |
| gap: 12px; | |
| } | |
| .url-input { | |
| flex: 1; | |
| background: var(--bg); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 14px 20px; | |
| color: var(--text); | |
| font-family: 'DM Sans', sans-serif; | |
| font-size: 15px; | |
| transition: all 0.3s; | |
| outline: none; | |
| } | |
| .url-input:focus { | |
| border-color: var(--accent); | |
| box-shadow: 0 0 0 3px rgba(124, 111, 255, 0.15); | |
| } | |
| .url-input::placeholder { color: var(--text-dim); } | |
| .process-btn { | |
| background: linear-gradient(135deg, var(--accent), #9b8fff); | |
| border: none; | |
| border-radius: 12px; | |
| padding: 14px 28px; | |
| color: white; | |
| font-family: 'Syne', sans-serif; | |
| font-size: 14px; | |
| font-weight: 700; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| letter-spacing: 0.5px; | |
| white-space: nowrap; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .process-btn::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; left: -100%; | |
| width: 100%; height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent); | |
| transition: 0.5s; | |
| } | |
| .process-btn:hover::after { left: 100%; } | |
| .process-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(124, 111, 255, 0.4); } | |
| .process-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; } | |
| /* ===== STATUS BAR ===== */ | |
| .status-bar { | |
| display: none; | |
| align-items: center; | |
| gap: 10px; | |
| margin-top: 16px; | |
| padding: 12px 16px; | |
| border-radius: 10px; | |
| font-size: 14px; | |
| } | |
| .status-bar.loading { | |
| display: flex; | |
| background: rgba(124, 111, 255, 0.1); | |
| border: 1px solid rgba(124, 111, 255, 0.3); | |
| color: var(--accent); | |
| } | |
| .status-bar.success { | |
| display: flex; | |
| background: rgba(107, 255, 184, 0.1); | |
| border: 1px solid rgba(107, 255, 184, 0.3); | |
| color: var(--accent3); | |
| } | |
| .status-bar.error { | |
| display: flex; | |
| background: rgba(239, 71, 111, 0.1); | |
| border: 1px solid rgba(239, 71, 111, 0.3); | |
| color: var(--pink); | |
| } | |
| .spinner { | |
| width: 16px; height: 16px; | |
| border: 2px solid rgba(124, 111, 255, 0.3); | |
| border-top-color: var(--accent); | |
| border-radius: 50%; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| /* ===== MAIN GRID ===== */ | |
| .main-grid { | |
| display: none; | |
| grid-template-columns: 1fr 380px; | |
| gap: 24px; | |
| margin-bottom: 40px; | |
| } | |
| .main-grid.visible { display: grid; } | |
| /* ===== TOOLS TABS ===== */ | |
| .tools-panel { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| } | |
| .tabs-row { | |
| display: flex; | |
| gap: 8px; | |
| flex-wrap: wrap; | |
| } | |
| .tab-btn { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| padding: 10px 18px; | |
| border: 1px solid var(--border); | |
| border-radius: 10px; | |
| background: var(--surface); | |
| color: var(--text-dim); | |
| font-family: 'DM Sans', sans-serif; | |
| font-size: 13px; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: all 0.25s; | |
| } | |
| .tab-btn:hover { | |
| border-color: var(--accent); | |
| color: var(--text); | |
| } | |
| .tab-btn.active { | |
| background: var(--accent); | |
| border-color: var(--accent); | |
| color: white; | |
| box-shadow: 0 4px 15px rgba(124, 111, 255, 0.3); | |
| } | |
| /* ===== CONTENT PANELS ===== */ | |
| .content-panel { | |
| display: none; | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: 20px; | |
| padding: 28px; | |
| min-height: 400px; | |
| flex-direction: column; | |
| gap: 16px; | |
| } | |
| .content-panel.active { display: flex; } | |
| .panel-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-bottom: 8px; | |
| } | |
| .panel-title { | |
| font-family: 'Syne', sans-serif; | |
| font-size: 18px; | |
| font-weight: 700; | |
| color: var(--text); | |
| } | |
| .generate-btn { | |
| background: linear-gradient(135deg, var(--accent2), #ff8fb8); | |
| border: none; | |
| border-radius: 8px; | |
| padding: 8px 20px; | |
| color: white; | |
| font-family: 'DM Sans', sans-serif; | |
| font-size: 13px; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .generate-btn:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(255, 107, 157, 0.3); } | |
| .generate-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; } | |
| .panel-content { | |
| flex: 1; | |
| overflow-y: auto; | |
| color: var(--text); | |
| line-height: 1.7; | |
| font-size: 14px; | |
| } | |
| .panel-content::-webkit-scrollbar { width: 4px; } | |
| .panel-content::-webkit-scrollbar-track { background: var(--surface2); } | |
| .panel-content::-webkit-scrollbar-thumb { background: var(--accent); border-radius: 2px; } | |
| .empty-state { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| height: 250px; | |
| color: var(--text-dim); | |
| gap: 12px; | |
| text-align: center; | |
| } | |
| .empty-icon { font-size: 48px; opacity: 0.4; } | |
| /* ===== SUMMARY CONTENT ===== */ | |
| .summary-text h1, .summary-text h2, .summary-text h3 { | |
| font-family: 'Syne', sans-serif; | |
| color: var(--accent); | |
| margin: 16px 0 8px; | |
| } | |
| .summary-text h1 { font-size: 18px; } | |
| .summary-text h2 { font-size: 16px; color: var(--accent2); } | |
| .summary-text h3 { font-size: 14px; color: var(--accent3); } | |
| .summary-text p { margin-bottom: 10px; } | |
| .summary-text ul, .summary-text ol { padding-left: 20px; margin-bottom: 10px; } | |
| .summary-text li { margin-bottom: 4px; } | |
| /* ===== FLASHCARDS ===== */ | |
| .flashcards-grid { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| .flashcard { | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| overflow: hidden; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .flashcard:hover { border-color: var(--accent); transform: translateX(4px); } | |
| .card-question { | |
| padding: 14px 18px; | |
| font-weight: 500; | |
| color: var(--text); | |
| font-size: 14px; | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 10px; | |
| } | |
| .card-q-badge { | |
| background: var(--accent); | |
| color: white; | |
| font-size: 10px; | |
| font-weight: 700; | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| white-space: nowrap; | |
| margin-top: 2px; | |
| font-family: 'Syne', sans-serif; | |
| } | |
| .card-answer { | |
| display: none; | |
| padding: 12px 18px; | |
| border-top: 1px solid var(--border); | |
| color: var(--text-dim); | |
| font-size: 13px; | |
| line-height: 1.6; | |
| background: rgba(124, 111, 255, 0.05); | |
| } | |
| .flashcard.open .card-answer { display: block; } | |
| .card-a-badge { | |
| display: inline-block; | |
| background: var(--accent3); | |
| color: var(--bg); | |
| font-size: 10px; | |
| font-weight: 700; | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| margin-bottom: 6px; | |
| font-family: 'Syne', sans-serif; | |
| } | |
| /* ===== STICKY NOTES ===== */ | |
| .notes-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 12px; | |
| } | |
| .sticky-note { | |
| border-radius: 12px; | |
| padding: 16px; | |
| position: relative; | |
| transition: transform 0.2s; | |
| } | |
| .sticky-note:hover { transform: rotate(-1deg) scale(1.02); } | |
| .sticky-note.yellow { background: rgba(255, 209, 102, 0.15); border: 1px solid rgba(255, 209, 102, 0.3); } | |
| .sticky-note.blue { background: rgba(17, 138, 178, 0.15); border: 1px solid rgba(17, 138, 178, 0.3); } | |
| .sticky-note.green { background: rgba(6, 214, 160, 0.15); border: 1px solid rgba(6, 214, 160, 0.3); } | |
| .sticky-note.pink { background: rgba(239, 71, 111, 0.15); border: 1px solid rgba(239, 71, 111, 0.3); } | |
| .sticky-note.purple { background: rgba(155, 93, 229, 0.15); border: 1px solid rgba(155, 93, 229, 0.3); } | |
| .note-title { | |
| font-family: 'Syne', sans-serif; | |
| font-size: 13px; | |
| font-weight: 700; | |
| margin-bottom: 8px; | |
| color: var(--text); | |
| } | |
| .note-content { font-size: 12px; color: var(--text-dim); line-height: 1.5; } | |
| /* ===== FLOWCHART ===== */ | |
| .flowchart-container { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 0; | |
| padding: 16px; | |
| } | |
| .flow-node { | |
| padding: 12px 24px; | |
| border-radius: 10px; | |
| font-size: 13px; | |
| font-weight: 500; | |
| text-align: center; | |
| max-width: 280px; | |
| width: 100%; | |
| transition: transform 0.2s; | |
| font-family: 'DM Sans', sans-serif; | |
| } | |
| .flow-node:hover { transform: scale(1.03); } | |
| .flow-node.start { background: linear-gradient(135deg, var(--accent3), #00c984); color: #0a0a0f; font-weight: 700; border-radius: 50px; } | |
| .flow-node.end { background: linear-gradient(135deg, var(--accent2), #ff4785); color: white; font-weight: 700; border-radius: 50px; } | |
| .flow-node.process { background: var(--surface2); border: 1px solid var(--accent); color: var(--text); } | |
| .flow-node.decision { background: rgba(255, 209, 102, 0.15); border: 1px solid var(--yellow); color: var(--yellow); transform: rotate(0deg); border-radius: 4px; } | |
| .flow-arrow { | |
| width: 2px; | |
| height: 24px; | |
| background: linear-gradient(to bottom, var(--accent), transparent); | |
| position: relative; | |
| margin: 0 auto; | |
| } | |
| .flow-arrow::after { | |
| content: '▼'; | |
| position: absolute; | |
| bottom: -8px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| color: var(--accent); | |
| font-size: 10px; | |
| } | |
| /* ===== CHAT PANEL ===== */ | |
| .chat-panel { | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: 20px; | |
| display: flex; | |
| flex-direction: column; | |
| height: fit-content; | |
| position: sticky; | |
| top: 24px; | |
| } | |
| .chat-header { | |
| padding: 20px 24px; | |
| border-bottom: 1px solid var(--border); | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .chat-dot { | |
| width: 8px; height: 8px; | |
| border-radius: 50%; | |
| background: var(--accent3); | |
| box-shadow: 0 0 8px var(--accent3); | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.4; } | |
| } | |
| .chat-title { | |
| font-family: 'Syne', sans-serif; | |
| font-size: 15px; | |
| font-weight: 700; | |
| } | |
| .chat-messages { | |
| height: 380px; | |
| overflow-y: auto; | |
| padding: 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| .chat-messages::-webkit-scrollbar { width: 4px; } | |
| .chat-messages::-webkit-scrollbar-thumb { background: var(--accent); border-radius: 2px; } | |
| .msg { | |
| max-width: 90%; | |
| padding: 12px 16px; | |
| border-radius: 14px; | |
| font-size: 13px; | |
| line-height: 1.6; | |
| animation: msgIn 0.3s ease; | |
| } | |
| @keyframes msgIn { | |
| from { opacity: 0; transform: translateY(8px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .msg.user { | |
| align-self: flex-end; | |
| background: linear-gradient(135deg, var(--accent), #9b8fff); | |
| color: white; | |
| border-bottom-right-radius: 4px; | |
| } | |
| .msg.ai { | |
| align-self: flex-start; | |
| background: var(--surface2); | |
| color: var(--text); | |
| border-bottom-left-radius: 4px; | |
| border: 1px solid var(--border); | |
| } | |
| .msg.ai.typing { | |
| display: flex; | |
| gap: 4px; | |
| align-items: center; | |
| } | |
| .typing-dot { | |
| width: 6px; height: 6px; | |
| border-radius: 50%; | |
| background: var(--text-dim); | |
| animation: typingBounce 1.2s infinite; | |
| } | |
| .typing-dot:nth-child(2) { animation-delay: 0.2s; } | |
| .typing-dot:nth-child(3) { animation-delay: 0.4s; } | |
| @keyframes typingBounce { | |
| 0%, 60%, 100% { transform: translateY(0); } | |
| 30% { transform: translateY(-6px); } | |
| } | |
| .chat-input-row { | |
| padding: 16px; | |
| border-top: 1px solid var(--border); | |
| display: flex; | |
| gap: 8px; | |
| } | |
| .chat-input { | |
| flex: 1; | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: 10px; | |
| padding: 10px 14px; | |
| color: var(--text); | |
| font-family: 'DM Sans', sans-serif; | |
| font-size: 13px; | |
| outline: none; | |
| transition: border-color 0.2s; | |
| resize: none; | |
| } | |
| .chat-input:focus { border-color: var(--accent); } | |
| .chat-input::placeholder { color: var(--text-dim); } | |
| .send-btn { | |
| background: var(--accent); | |
| border: none; | |
| border-radius: 10px; | |
| width: 40px; | |
| height: 40px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: all 0.2s; | |
| flex-shrink: 0; | |
| align-self: flex-end; | |
| } | |
| .send-btn:hover { background: #9b8fff; transform: scale(1.05); } | |
| .send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; } | |
| /* ===== VIDEO INFO ===== */ | |
| .video-info-bar { | |
| display: none; | |
| background: linear-gradient(135deg, rgba(124, 111, 255, 0.1), rgba(255, 107, 157, 0.05)); | |
| border: 1px solid rgba(124, 111, 255, 0.2); | |
| border-radius: 12px; | |
| padding: 16px 20px; | |
| margin-bottom: 24px; | |
| align-items: center; | |
| gap: 16px; | |
| } | |
| .video-info-bar.visible { display: flex; } | |
| .video-thumb { font-size: 32px; } | |
| .video-details h3 { | |
| font-family: 'Syne', sans-serif; | |
| font-size: 15px; | |
| font-weight: 700; | |
| margin-bottom: 4px; | |
| } | |
| .video-details p { font-size: 12px; color: var(--text-dim); } | |
| /* ===== LOADING OVERLAY ===== */ | |
| .panel-loading { | |
| display: none; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 16px; | |
| height: 200px; | |
| color: var(--text-dim); | |
| } | |
| .panel-loading.visible { display: flex; } | |
| .big-spinner { | |
| width: 36px; height: 36px; | |
| border: 3px solid rgba(124, 111, 255, 0.2); | |
| border-top-color: var(--accent); | |
| border-radius: 50%; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| /* Responsive */ | |
| @media (max-width: 900px) { | |
| .main-grid { grid-template-columns: 1fr; } | |
| .notes-grid { grid-template-columns: 1fr; } | |
| .url-input-row { flex-direction: column; } | |
| .chat-panel { position: relative; top: 0; } | |
| .tabs-row { overflow-x: auto; flex-wrap: nowrap; padding-bottom: 8px; } | |
| .tab-btn { white-space: nowrap; font-size: 12px; padding: 8px 12px; } | |
| .lang-selector { flex-wrap: wrap; gap: 4px; } | |
| .lang-btn { font-size: 11px; padding: 4px 10px; } | |
| .logo-text { font-size: 18px; } | |
| header { flex-wrap: wrap; gap: 12px; } | |
| .panel-header { flex-wrap: wrap; gap: 8px; } | |
| .chat-messages { height: 280px; } | |
| .url-input { font-size: 14px; } | |
| .process-btn { width: 100%; } | |
| .video-info-bar { flex-wrap: wrap; } | |
| .generate-btn { font-size: 11px; padding: 6px 12px; } | |
| } | |
| @media (max-width: 480px) { | |
| .app-container { padding: 0 12px; } | |
| .url-section { padding: 20px 16px; } | |
| .content-panel { padding: 16px; } | |
| .logo-sub { display: none; } | |
| .flow-node { max-width: 100%; font-size: 12px; } | |
| .notes-grid { grid-template-columns: 1fr; } | |
| .panel-title { font-size: 15px; } | |
| .chat-messages { height: 240px; } | |
| .video-thumb img { width: 80px ; } | |
| header { padding: 16px 0; } | |
| .url-section { padding: 16px; margin-bottom: 16px; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <!-- HEADER --> | |
| <header> | |
| <div class="logo"> | |
| <div class="logo-icon">🔬</div> | |
| <div> | |
| <div class="logo-text">LectureLens AI</div> | |
| <div class="logo-sub">Study Smarter, Learn Deeper</div> | |
| </div> | |
| </div> | |
| <div class="lang-selector"> | |
| <button class="lang-btn active" onclick="setLang('english', this)">English</button> | |
| <button class="lang-btn" onclick="setLang('urdu', this)">اردو</button> | |
| <button class="lang-btn" onclick="setLang('roman_urdu', this)">Roman Urdu</button> | |
| </div> | |
| </header> | |
| <!-- URL INPUT --> | |
| <div class="url-section"> | |
| <div class="section-label">📺 Step 1 — Enter YouTube Lecture URL</div> | |
| <div class="url-input-row"> | |
| <input type="text" class="url-input" id="urlInput" | |
| placeholder="https://www.youtube.com/watch?v=..." | |
| onkeydown="if(event.key==='Enter') processVideo()"> | |
| <button class="process-btn" id="processBtn" onclick="processVideo()"> | |
| 🚀 Analyze Video | |
| </button> | |
| </div> | |
| <div class="status-bar" id="statusBar"> | |
| <div class="spinner" id="statusSpinner"></div> | |
| <span id="statusText">Processing video...</span> | |
| </div> | |
| </div> | |
| <!-- VIDEO INFO --> | |
| <div class="video-info-bar" id="videoInfoBar"> | |
| <div class="video-thumb"> | |
| <img id="videoThumbnail" src="" style="width:120px; border-radius:8px;" /> | |
| </div> | |
| <div class="video-details"> | |
| <h3 id="videoTitle">Video Title</h3> | |
| <p id="videoMeta">Transcript extracted and ready</p> | |
| </div> | |
| </div> | |
| <!-- MAIN GRID --> | |
| <div class="main-grid" id="mainGrid"> | |
| <!-- LEFT: TOOLS --> | |
| <div class="tools-panel"> | |
| <!-- TABS --> | |
| <div class="tabs-row"> | |
| <button class="tab-btn active" onclick="showTab('summary', this)">📋 Summary</button> | |
| <button class="tab-btn" onclick="showTab('flashcards', this)">🃏 Flashcards</button> | |
| <button class="tab-btn" onclick="showTab('notes', this)">📌 Sticky Notes</button> | |
| <button class="tab-btn" onclick="showTab('flowchart', this)">🔄 Flowchart</button> | |
| <button class="tab-btn" onclick="showTab('transcript', this)">📜 Transcript</button> | |
| <button class="tab-btn" onclick="showTab('quiz', this)">🧠 Quiz</button> | |
| <button class="tab-btn" onclick="showTab('compare', this)">🔀 Compare</button> | |
| </div> | |
| <!-- SUMMARY PANEL --> | |
| <div class="content-panel active" id="panel-summary"> | |
| <div class="panel-header"> | |
| <div class="panel-title">📋 Lecture Summary</div> | |
| <button class="generate-btn" id="summaryBtn" onclick="generateSummary()">Generate ✨</button> | |
| <button class="generate-btn" onclick="copyContent('summaryContent')" style="background: linear-gradient(135deg, #118ab2, #06d6a0);">📋 Copy</button> | |
| </div> | |
| <div class="panel-content" id="summaryContent"> | |
| <div class="empty-state"> | |
| <div class="empty-icon">📋</div> | |
| <div>Click "Generate" to create a comprehensive summary of the lecture.</div> | |
| </div> | |
| </div> | |
| <button class="generate-btn" id="exportBtn" | |
| onclick="exportPDF()" | |
| style="background: linear-gradient(135deg, #06d6a0, #00c984); margin-top: 10px;"> | |
| 📄 Export PDF | |
| </button> | |
| </div> | |
| <!-- FLASHCARDS PANEL --> | |
| <div class="content-panel" id="panel-flashcards"> | |
| <div class="panel-header"> | |
| <div class="panel-title">🃏 Flashcards</div> | |
| <button class="generate-btn" id="flashcardsBtn" onclick="generateFlashcards()">Generate ✨</button> | |
| <button class="generate-btn" onclick="copyContent('flashcardsContent')" style="background: linear-gradient(135deg, #118ab2, #06d6a0);">📋 Copy</button> | |
| <button class="generate-btn" onclick="exportSectionPDF('flashcardsContent')" style="background:linear-gradient(135deg, #06d6a0, #00c984);">📄 PDF</button> | |
| </div> | |
| <div class="panel-content" id="flashcardsContent"> | |
| <div class="empty-state"> | |
| <div class="empty-icon">🃏</div> | |
| <div>Generate flashcards to test your knowledge. Click on a card to reveal the answer!</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- NOTES PANEL --> | |
| <div class="content-panel" id="panel-notes"> | |
| <div class="panel-header"> | |
| <div class="panel-title">📌 Sticky Notes</div> | |
| <button class="generate-btn" id="notesBtn" onclick="generateNotes()">Generate ✨</button> | |
| <button class="generate-btn" onclick="copyContent('notesContent')" style="background: linear-gradient(135deg, #118ab2, #06d6a0);">📋 Copy</button> | |
| <button class="generate-btn" onclick="exportSectionPDF('notesContent')" style="background:linear-gradient(135deg, #06d6a0, #00c984);">📄 PDF</button> | |
| </div> | |
| <div class="panel-content" id="notesContent"> | |
| <div class="empty-state"> | |
| <div class="empty-icon">📌</div> | |
| <div>Generate sticky notes with key points from the lecture.</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- FLOWCHART PANEL --> | |
| <div class="content-panel" id="panel-flowchart"> | |
| <div class="panel-header"> | |
| <div class="panel-title">🔄 Concept Flowchart</div> | |
| <button class="generate-btn" id="flowchartBtn" onclick="generateFlowchart()">Generate ✨</button> | |
| <button class="generate-btn" onclick="exportSectionPDF('flowchartContent')" style="background:linear-gradient(135deg, #06d6a0, #00c984);">📄 PDF</button> | |
| </div> | |
| <div class="panel-content" id="flowchartContent"> | |
| <div class="empty-state"> | |
| <div class="empty-icon">🔄</div> | |
| <div>Generate a visual flowchart showing how concepts in the lecture connect.</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- panel-transcript --> | |
| <div class="content-panel" id="panel-transcript"> | |
| <div class="panel-header"> | |
| <div class="panel-title">📜 Full Transcript</div> | |
| <button class="generate-btn" onclick="exportSectionPDF('transcriptContent')" style="background:linear-gradient(135deg, #06d6a0, #00c984);">📄 PDF</button> | |
| </div> | |
| <div class="panel-content" id="transcriptContent"> | |
| <div class="empty-state"> | |
| <div class="empty-icon">📜</div> | |
| <div>Process a video to see transcript.</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- quiz panel --> | |
| <div class="content-panel" id="panel-quiz"> | |
| <div class="panel-header"> | |
| <div class="panel-title">🧠 Quiz Mode</div> | |
| <button class="generate-btn" onclick="generateQuiz()">Generate ✨</button> | |
| </div> | |
| <div class="panel-content" id="quizContent"> | |
| <div class="empty-state"> | |
| <div class="empty-icon">🧠</div> | |
| <div>Generate a quiz to test your knowledge!</div> | |
| </div> | |
| </div> | |
| <div id="quizScore" style="display:none; padding:12px; text-align:center; font-family:'Syne',sans-serif; font-size:18px; color:var(--accent3);"></div> | |
| </div> | |
| <!-- CAMPARE VEDIO --> | |
| <div class="content-panel" id="panel-compare"> | |
| <div class="panel-header"> | |
| <div class="panel-title">🔀 Compare Videos</div> | |
| <button class="generate-btn" onclick="compareVideos()">Compare ✨</button> | |
| </div> | |
| <div style="padding:12px; display:flex; gap:8px;"> | |
| <input type="text" id="compareUrl" class="url-input" placeholder="Enter second YouTube URL..."> | |
| </div> | |
| <div class="panel-content" id="compareContent"> | |
| <div class="empty-state"> | |
| <div class="empty-icon">🔀</div> | |
| <div>Enter second video URL to compare!</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- RIGHT: CHAT --> | |
| <div class="chat-panel"> | |
| <div class="chat-header"> | |
| <div class="chat-dot"></div> | |
| <div class="chat-title">Ask LectureLens AI</div> | |
| </div> | |
| <div class="chat-messages" id="chatMessages"> | |
| <div class="msg ai"> | |
| 👋 Hi! I'm LectureLens AI. Process a YouTube lecture and then ask me anything about it. I'll answer based strictly on the video content! | |
| </div> | |
| </div> | |
| <div class="chat-input-row"> | |
| <textarea class="chat-input" id="chatInput" rows="2" | |
| placeholder="Ask about the lecture..." | |
| onkeydown="if(event.key==='Enter' && !event.shiftKey){ event.preventDefault(); sendMessage(); }"></textarea> | |
| <button class="send-btn" id="sendBtn" onclick="sendMessage()"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="white"> | |
| <path d="M2 21l21-9L2 3v7l15 2-15 2v7z"/> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let currentSessionId = null; | |
| let currentLanguage = 'english'; | |
| function setLang(lang, btn) { | |
| currentLanguage = lang; | |
| document.querySelectorAll('.lang-btn').forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| } | |
| function showStatus(type, message) { | |
| const bar = document.getElementById('statusBar'); | |
| const text = document.getElementById('statusText'); | |
| const spinner = document.getElementById('statusSpinner'); | |
| bar.className = 'status-bar ' + type; | |
| text.textContent = message; | |
| spinner.style.display = type === 'loading' ? 'block' : 'none'; | |
| if (type !== 'loading') { | |
| setTimeout(() => { bar.className = 'status-bar'; }, 4000); | |
| } | |
| } | |
| async function processVideo() { | |
| const url = document.getElementById('urlInput').value.trim(); | |
| if (!url) { showStatus('error', 'Please enter a YouTube URL'); return; } | |
| const btn = document.getElementById('processBtn'); | |
| btn.disabled = true; | |
| btn.textContent = '⏳ Processing...'; | |
| showStatus('loading', 'Extracting transcript from YouTube...'); | |
| try { | |
| const res = await fetch('/api/process', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ url }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success) { | |
| currentSessionId = data.session_id; | |
| showStatus('success', `✅ Video processed! ${data.transcript_length} characters extracted.`); | |
| document.getElementById('videoTitle').textContent = data.title; | |
| if (data.video_id) { | |
| const thumbImg = document.getElementById('videoThumbnail'); | |
| thumbImg.style.display = 'block'; | |
| thumbImg.src = `https://i.ytimg.com/vi/${data.video_id}/mqdefault.jpg`; | |
| thumbImg.onerror = () => { thumbImg.style.display = 'none'; }; | |
| } | |
| document.getElementById('videoMeta').textContent = | |
| `${data.transcript_length.toLocaleString()} characters · Session ready`; | |
| document.getElementById('videoInfoBar').classList.add('visible'); | |
| document.getElementById('mainGrid').classList.add('visible'); | |
| addMessage('ai', `🎓 I've analyzed the lecture "${data.title}". You can now ask me questions, generate summaries, flashcards, sticky notes, or a flowchart!`); | |
| if (data.transcript) { | |
| document.getElementById('transcriptContent').innerHTML = | |
| `<div style="line-height:1.8; font-size:13px; color:var(--text); white-space:pre-wrap">${data.transcript}</div>`; | |
| } | |
| } else { | |
| showStatus('error', data.error || 'Failed to process video'); | |
| } | |
| } catch (e) { | |
| showStatus('error', 'Network error. Please try again.'); | |
| } | |
| btn.disabled = false; | |
| btn.textContent = '🚀 Analyze Video'; | |
| } | |
| function showTab(tab, btn) { | |
| document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); | |
| document.querySelectorAll('.content-panel').forEach(p => p.classList.remove('active')); | |
| btn.classList.add('active'); | |
| document.getElementById('panel-' + tab).classList.add('active'); | |
| } | |
| async function generateSummary() { | |
| if (!currentSessionId) { alert('Please process a video first.'); return; } | |
| const btn = document.getElementById('summaryBtn'); | |
| const content = document.getElementById('summaryContent'); | |
| btn.disabled = true; | |
| btn.textContent = '⏳ Generating...'; | |
| content.innerHTML = '<div class="panel-loading visible"><div class="big-spinner"></div><div>Generating summary...</div></div>'; | |
| try { | |
| const res = await fetch('/api/summarize', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ session_id: currentSessionId, language: currentLanguage }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success) { | |
| const formatted = data.summary | |
| .replace(/^# (.+)$/gm, '<h1>$1</h1>') | |
| .replace(/^## (.+)$/gm, '<h2>$2</h2>') | |
| .replace(/^### (.+)$/gm, '<h3>$1</h3>') | |
| .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>') | |
| .replace(/^- (.+)$/gm, '<li>$1</li>') | |
| .replace(/^(\d+)\. (.+)$/gm, '<li>$2</li>') | |
| .replace(/\n\n/g, '</p><p>') | |
| .replace(/\n/g, '<br>'); | |
| content.innerHTML = `<div class="summary-text"><p>${formatted}</p></div>`; | |
| } else { | |
| content.innerHTML = `<div class="empty-state"><div>❌ ${data.error}</div></div>`; | |
| } | |
| } catch (e) { | |
| content.innerHTML = '<div class="empty-state"><div>❌ Network error</div></div>'; | |
| } | |
| btn.disabled = false; | |
| btn.textContent = 'Generate ✨'; | |
| } | |
| async function generateFlashcards() { | |
| if (!currentSessionId) { alert('Please process a video first.'); return; } | |
| const btn = document.getElementById('flashcardsBtn'); | |
| const content = document.getElementById('flashcardsContent'); | |
| btn.disabled = true; | |
| btn.textContent = '⏳ Generating...'; | |
| content.innerHTML = '<div class="panel-loading visible"><div class="big-spinner"></div><div>Creating flashcards...</div></div>'; | |
| try { | |
| const res = await fetch('/api/flashcards', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ session_id: currentSessionId, language: currentLanguage }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success && data.flashcards.length > 0) { | |
| const html = data.flashcards.map((card, i) => ` | |
| <div class="flashcard" onclick="this.classList.toggle('open')"> | |
| <div class="card-question"> | |
| <span class="card-q-badge">Q${i+1}</span> | |
| ${card.question} | |
| </div> | |
| <div class="card-answer"> | |
| <span class="card-a-badge">ANSWER</span><br> | |
| ${card.answer} | |
| </div> | |
| </div> | |
| `).join(''); | |
| content.innerHTML = `<div class="flashcards-grid">${html}</div>`; | |
| } else { | |
| content.innerHTML = `<div class="empty-state"><div>❌ ${data.error || 'Could not generate flashcards'}</div></div>`; | |
| } | |
| } catch (e) { | |
| content.innerHTML = '<div class="empty-state"><div>❌ Network error</div></div>'; | |
| } | |
| btn.disabled = false; | |
| btn.textContent = 'Generate ✨'; | |
| } | |
| async function generateNotes() { | |
| if (!currentSessionId) { alert('Please process a video first.'); return; } | |
| const btn = document.getElementById('notesBtn'); | |
| const content = document.getElementById('notesContent'); | |
| btn.disabled = true; | |
| btn.textContent = '⏳ Generating...'; | |
| content.innerHTML = '<div class="panel-loading visible"><div class="big-spinner"></div><div>Creating sticky notes...</div></div>'; | |
| try { | |
| const res = await fetch('/api/notes', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ session_id: currentSessionId, language: currentLanguage }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success && data.notes.length > 0) { | |
| const html = data.notes.map(note => ` | |
| <div class="sticky-note ${note.color || 'yellow'}"> | |
| <div class="note-title">${note.title}</div> | |
| <div class="note-content">${note.content}</div> | |
| </div> | |
| `).join(''); | |
| content.innerHTML = `<div class="notes-grid">${html}</div>`; | |
| } else { | |
| content.innerHTML = `<div class="empty-state"><div>❌ ${data.error || 'Could not generate notes'}</div></div>`; | |
| } | |
| } catch (e) { | |
| content.innerHTML = '<div class="empty-state"><div>❌ Network error</div></div>'; | |
| } | |
| btn.disabled = false; | |
| btn.textContent = 'Generate ✨'; | |
| } | |
| async function generateFlowchart() { | |
| if (!currentSessionId) { alert('Please process a video first.'); return; } | |
| const btn = document.getElementById('flowchartBtn'); | |
| const content = document.getElementById('flowchartContent'); | |
| btn.disabled = true; | |
| btn.textContent = '⏳ Generating...'; | |
| content.innerHTML = '<div class="panel-loading visible"><div class="big-spinner"></div><div>Building flowchart...</div></div>'; | |
| try { | |
| const res = await fetch('/api/flowchart', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ session_id: currentSessionId, language: currentLanguage }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success && data.flowchart.nodes) { | |
| const fc = data.flowchart; | |
| // Build adjacency for ordering | |
| const nodeMap = {}; | |
| fc.nodes.forEach(n => nodeMap[n.id] = n); | |
| // Simple linear render (ordered by edges) | |
| const visited = new Set(); | |
| const ordered = []; | |
| const edgeMap = {}; | |
| fc.edges.forEach(e => { edgeMap[e.from] = e.to; }); | |
| // Find start node | |
| const targets = new Set(fc.edges.map(e => e.to)); | |
| let start = fc.nodes.find(n => !targets.has(n.id) || n.type === 'start'); | |
| if (!start) start = fc.nodes[0]; | |
| let cur = start; | |
| while (cur && !visited.has(cur.id)) { | |
| ordered.push(cur); | |
| visited.add(cur.id); | |
| const nextId = edgeMap[cur.id]; | |
| cur = nextId ? nodeMap[nextId] : null; | |
| } | |
| // Add any remaining nodes | |
| fc.nodes.forEach(n => { if (!visited.has(n.id)) ordered.push(n); }); | |
| const html = ordered.map((node, i) => ` | |
| <div class="flow-node ${node.type || 'process'}">${node.label}</div> | |
| ${i < ordered.length - 1 ? '<div class="flow-arrow"></div>' : ''} | |
| `).join(''); | |
| content.innerHTML = `<div class="flowchart-container">${html}</div>`; | |
| } else { | |
| content.innerHTML = `<div class="empty-state"><div>❌ ${data.error || 'Could not generate flowchart'}</div></div>`; | |
| } | |
| } catch (e) { | |
| content.innerHTML = '<div class="empty-state"><div>❌ Network error</div></div>'; | |
| } | |
| btn.disabled = false; | |
| btn.textContent = 'Generate ✨'; | |
| } | |
| async function exportPDF() { | |
| if (!currentSessionId) { alert('Please process a video first.'); return; } | |
| const summaryContent = document.getElementById('summaryContent').innerText; | |
| if (!summaryContent || summaryContent.includes('Click "Generate"')) { | |
| alert('Please generate a summary first!'); | |
| return; | |
| } | |
| try { | |
| const res = await fetch('/api/export', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| session_id: currentSessionId, | |
| content: summaryContent, | |
| title: document.getElementById('videoTitle').textContent | |
| }) | |
| }); | |
| if (res.ok) { | |
| const blob = await res.blob(); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'lecturelens_export.pdf'; | |
| a.click(); | |
| } | |
| } catch (e) { | |
| alert('Export failed. Please try again.'); | |
| } | |
| } | |
| function addMessage(role, text) { | |
| const container = document.getElementById('chatMessages'); | |
| const div = document.createElement('div'); | |
| div.className = 'msg ' + role; | |
| div.textContent = text; | |
| container.appendChild(div); | |
| container.scrollTop = container.scrollHeight; | |
| return div; | |
| } | |
| function addTypingIndicator() { | |
| const container = document.getElementById('chatMessages'); | |
| const div = document.createElement('div'); | |
| div.className = 'msg ai typing'; | |
| div.id = 'typingIndicator'; | |
| div.innerHTML = '<div class="typing-dot"></div><div class="typing-dot"></div><div class="typing-dot"></div>'; | |
| container.appendChild(div); | |
| container.scrollTop = container.scrollHeight; | |
| } | |
| function removeTypingIndicator() { | |
| const el = document.getElementById('typingIndicator'); | |
| if (el) el.remove(); | |
| } | |
| function copyContent(elementId) { | |
| const text = document.getElementById(elementId).innerText; | |
| navigator.clipboard.writeText(text).then(() => { | |
| alert('✅ Copied to clipboard!'); | |
| }).catch(() => { | |
| alert('❌ Copy failed. Please try manually.'); | |
| }); | |
| } | |
| async function exportSectionPDF(elementId) { | |
| if (!currentSessionId) { alert('Please process a video first.'); return; } | |
| const content = document.getElementById(elementId).innerText; | |
| if (!content || content.includes('Generate')) { | |
| alert('Please generate content first!'); | |
| return; | |
| } | |
| try { | |
| const res = await fetch('/api/export', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| session_id: currentSessionId, | |
| content: content, | |
| title: document.getElementById('videoTitle').textContent | |
| }) | |
| }); | |
| if (res.ok) { | |
| const blob = await res.blob(); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `${elementId}_export.pdf`; | |
| a.click(); | |
| } | |
| } catch(e) { | |
| alert('Export failed!'); | |
| } | |
| } | |
| let quizData = []; | |
| let currentQ = 0; | |
| let score = 0; | |
| async function generateQuiz() { | |
| if (!currentSessionId) { alert('Please process a video first.'); return; } | |
| const content = document.getElementById('quizContent'); | |
| content.innerHTML = '<div class="panel-loading visible"><div class="big-spinner"></div><div>Generating quiz...</div></div>'; | |
| const res = await fetch('/api/quiz', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ session_id: currentSessionId, language: currentLanguage }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success && data.questions.length > 0) { | |
| quizData = data.questions; | |
| currentQ = 0; | |
| score = 0; | |
| document.getElementById('quizScore').style.display = 'none'; | |
| showQuestion(); | |
| } | |
| } | |
| function showQuestion() { | |
| if (currentQ >= quizData.length) { | |
| document.getElementById('quizContent').innerHTML = ''; | |
| document.getElementById('quizScore').style.display = 'block'; | |
| document.getElementById('quizScore').innerHTML = `🎉 Score: ${score}/${quizData.length}`; | |
| return; | |
| } | |
| const q = quizData[currentQ]; | |
| const html = ` | |
| <div style="padding:16px;"> | |
| <div style="font-weight:600; margin-bottom:16px; color:var(--text)">Q${currentQ+1}: ${q.question}</div> | |
| ${q.options.map(opt => ` | |
| <button onclick="checkAnswer('${opt[0]}', '${q.correct}')" | |
| style="display:block; width:100%; text-align:left; padding:10px 16px; margin-bottom:8px; | |
| background:var(--surface2); border:1px solid var(--border); border-radius:8px; | |
| color:var(--text); cursor:pointer; font-size:13px;"> | |
| ${opt} | |
| </button>`).join('')} | |
| </div>`; | |
| document.getElementById('quizContent').innerHTML = html; | |
| } | |
| function checkAnswer(selected, correct) { | |
| if (selected === correct) score++; | |
| currentQ++; | |
| showQuestion(); | |
| } | |
| async function sendMessage() { | |
| if (!currentSessionId) { | |
| addMessage('ai', '⚠️ Please process a YouTube video first before asking questions.'); | |
| return; | |
| } | |
| const input = document.getElementById('chatInput'); | |
| const question = input.value.trim(); | |
| if (!question) return; | |
| addMessage('user', question); | |
| input.value = ''; | |
| addTypingIndicator(); | |
| const sendBtn = document.getElementById('sendBtn'); | |
| sendBtn.disabled = true; | |
| try { | |
| const res = await fetch('/api/chat', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| session_id: currentSessionId, | |
| question, | |
| language: currentLanguage | |
| }) | |
| }); | |
| const data = await res.json(); | |
| removeTypingIndicator(); | |
| addMessage('ai', data.answer || data.error || 'Sorry, I could not answer that.'); | |
| } catch (e) { | |
| removeTypingIndicator(); | |
| addMessage('ai', '❌ Network error. Please try again.'); | |
| } | |
| sendBtn.disabled = false; | |
| } | |
| async function compareVideos() { | |
| if (!currentSessionId) { alert('Please process a video first.'); return; } | |
| const url2 = document.getElementById('compareUrl').value.trim(); | |
| if (!url2) { alert('Please enter second video URL!'); return; } | |
| const content = document.getElementById('compareContent'); | |
| content.innerHTML = '<div class="panel-loading visible"><div class="big-spinner"></div><div>Comparing videos...</div></div>'; | |
| try { | |
| const res = await fetch('/api/compare', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| session_id: currentSessionId, | |
| url2: url2, | |
| language: currentLanguage | |
| }) | |
| }); | |
| const data = await res.json(); | |
| if (data.success) { | |
| content.innerHTML = `<div class="summary-text" style="padding:8px"><p>${data.comparison.replace(/\n/g, '<br>')}</p></div>`; | |
| } else { | |
| content.innerHTML = `<div class="empty-state"><div>❌ ${data.error}</div></div>`; | |
| } | |
| } catch(e) { | |
| content.innerHTML = '<div class="empty-state"><div>❌ Network error</div></div>'; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |