Spaces:
Running
Running
| <html lang="en" data-bs-theme="light"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>Educational Assistant</title> | |
| <!-- Google Font --> | |
| <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet" /> | |
| <!-- Bootstrap 5 --> | |
| <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" /> | |
| <!-- Animate.css --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> | |
| <!-- AOS --> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.css" rel="stylesheet" /> | |
| <style> | |
| :root { | |
| --clr-primary: #6c63ff; | |
| --clr-secondary: #ff6584; | |
| --clr-bg: linear-gradient(135deg, #e0e7ff, #fef6ff); | |
| --clr-card-bg: rgba(255, 255, 255, 0.7); | |
| --clr-glass: rgba(255, 255, 255, 0.15); | |
| --clr-border: rgba(255, 255, 255, 0.3); | |
| --clr-text: #2d2d2d; | |
| --transition: all 0.3s ease-in-out; | |
| --font: 'Poppins', sans-serif; | |
| } | |
| body { | |
| margin: 0; | |
| font-family: var(--font); | |
| background: var(--clr-bg); | |
| color: var(--clr-text); | |
| overflow-x: hidden; | |
| } | |
| .navbar { | |
| background: #ffffffdd; | |
| backdrop-filter: blur(10px); | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05); | |
| } | |
| .navbar-brand { | |
| font-weight: 600; | |
| color: var(--clr-primary) ; | |
| font-size: 1.4rem; | |
| } | |
| .btn-outline-primary { | |
| border-color: var(--clr-primary); | |
| color: var(--clr-primary); | |
| } | |
| .btn-outline-primary:hover { | |
| background: var(--clr-primary); | |
| color: white; | |
| } | |
| .hero { | |
| height: 300px; | |
| background: linear-gradient(135deg, rgba(108, 99, 255, 0.7), rgba(255, 101, 132, 0.6)), | |
| url('image.jpeg') center/cover no-repeat; | |
| position: relative; | |
| } | |
| .hero h1 { | |
| position: absolute; | |
| bottom: 30px; | |
| left: 30px; | |
| font-size: 3.5rem; | |
| color: white; | |
| text-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); | |
| } | |
| .card { | |
| background: var(--clr-card-bg); | |
| backdrop-filter: blur(16px); | |
| border: 1px solid var(--clr-border); | |
| border-radius: 1rem; | |
| box-shadow: 0 8px 30px rgba(0, 0, 0, 0.05); | |
| transition: var(--transition); | |
| } | |
| .card:hover { | |
| transform: translateY(-6px); | |
| box-shadow: 0 12px 40px rgba(0, 0, 0, 0.1); | |
| } | |
| .card-title { | |
| color: var(--clr-primary); | |
| font-weight: 600; | |
| } | |
| .form-control, .form-select { | |
| border-radius: 0.75rem; | |
| border: 1px solid #ddd; | |
| } | |
| .form-control:focus, .form-select:focus { | |
| border-color: var(--clr-primary); | |
| box-shadow: 0 0 0 0.25rem rgba(108, 99, 255, 0.25); | |
| } | |
| .btn-primary { | |
| background: linear-gradient(to right, var(--clr-primary), var(--clr-secondary)); | |
| border: none; | |
| border-radius: 0.75rem; | |
| transition: var(--transition); | |
| } | |
| .btn-primary:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 8px 18px rgba(108, 99, 255, 0.3); | |
| } | |
| .btn-success { | |
| background: var(--clr-primary); | |
| border: none; | |
| border-radius: 0.75rem; | |
| transition: var(--transition); | |
| } | |
| .btn-success:hover { | |
| background: var(--clr-secondary); | |
| transform: translateY(-2px); | |
| } | |
| #result { | |
| background: white; | |
| padding: 1rem; | |
| border-radius: 0.75rem; | |
| box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.05); | |
| white-space: pre-wrap; | |
| min-height: 200px; | |
| } | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--clr-primary); | |
| border-radius: 4px; | |
| } | |
| .offcanvas { | |
| backdrop-filter: blur(6px); | |
| } | |
| .offcanvas-header { | |
| border-bottom: 1px solid rgba(0, 0, 0, 0.1); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- NAVBAR --> | |
| <nav class="navbar navbar-light"> | |
| <div class="container-fluid"> | |
| <button class="btn btn-outline-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebar" aria-controls="sidebar"> | |
| ☰ Instructions | |
| </button> | |
| <span class="navbar-brand mb-0 h1">Educational Assistant</span> | |
| </div> | |
| </nav> | |
| <!-- OFFCANVAS --> | |
| <div class="offcanvas offcanvas-start" tabindex="-1" id="sidebar" aria-labelledby="sidebarLabel"> | |
| <div class="offcanvas-header"> | |
| <h5 id="sidebarLabel">How to Use</h5> | |
| <button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button> | |
| </div> | |
| <div class="offcanvas-body"> | |
| <ol> | |
| <li>Enter your OpenAI API key.</li> | |
| <li>Upload a PDF file.</li> | |
| <li>Select one of the following: | |
| <ul> | |
| <li>Generate Summary</li> | |
| <li>Generate Quiz</li> | |
| <li>Ask a Question</li> | |
| <li>Generate Study Plan</li> | |
| </ul> | |
| </li> | |
| <li>If “Ask a Question,” type it in the field.</li> | |
| <li>Click “Generate” and download the PDF.</li> | |
| </ol> | |
| </div> | |
| </div> | |
| <!-- HERO --> | |
| <section class="hero animate__animated animate__fadeIn"> | |
| <h1>Welcome to Your Study Buddy</h1> | |
| </section> | |
| <!-- MAIN --> | |
| <div class="container my-5" data-aos="fade-up"> | |
| <div class="row g-4"> | |
| <!-- LEFT PANEL --> | |
| <div class="col-lg-4"> | |
| <div class="card p-4"> | |
| <div class="mb-3"> | |
| <label for="apiKey" class="form-label">OpenAI API Key</label> | |
| <input type="password" class="form-control" id="apiKey" placeholder="sk-..." /> | |
| </div> | |
| <div class="mb-3"> | |
| <label for="pdfUpload" class="form-label">Upload PDF</label> | |
| <input class="form-control" type="file" id="pdfUpload" accept="application/pdf" /> | |
| </div> | |
| <div class="mb-3"> | |
| <label for="optionSelect" class="form-label">Option</label> | |
| <select class="form-select" id="optionSelect"> | |
| <option>Generate Summary</option> | |
| <option>Generate Quiz</option> | |
| <option>Ask a Question</option> | |
| <option>Generate Study Plan</option> | |
| </select> | |
| </div> | |
| <div class="mb-3 d-none" id="questionGroup"> | |
| <label for="questionInput" class="form-label">Your Question</label> | |
| <input type="text" class="form-control" id="questionInput" placeholder="Type your question..." /> | |
| </div> | |
| <div class="d-grid"> | |
| <button class="btn btn-primary" id="generateBtn">Generate</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- RIGHT PANEL --> | |
| <div class="col-lg-8"> | |
| <div class="card p-4"> | |
| <h5 class="card-title">Result</h5> | |
| <div id="result"></div> | |
| <div class="mt-3 text-end"> | |
| <button class="btn btn-success d-none" id="downloadBtn">Download as PDF</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- SCRIPTS --> | |
| <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.js"></script> | |
| <script> | |
| AOS.init(); | |
| const pdfjsLib = window['pdfjs-dist/build/pdf']; | |
| pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js'; | |
| let pdfText = ''; | |
| document.getElementById('optionSelect').addEventListener('change', (e) => { | |
| document.getElementById('questionGroup').classList.toggle('d-none', e.target.value !== 'Ask a Question'); | |
| }); | |
| document.getElementById('pdfUpload').addEventListener('change', async (e) => { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| const buffer = await file.arrayBuffer(); | |
| const pdf = await pdfjsLib.getDocument({ data: buffer }).promise; | |
| let text = ''; | |
| for (let i = 1; i <= pdf.numPages; i++) { | |
| const page = await pdf.getPage(i); | |
| const content = await page.getTextContent(); | |
| text += content.items.map((it) => it.str).join(' ') + '\n\n'; | |
| } | |
| pdfText = text.trim(); | |
| alert(`PDF loaded: ${pdf.numPages} pages`); | |
| }); | |
| document.getElementById('generateBtn').addEventListener('click', async () => { | |
| const key = document.getElementById('apiKey').value.trim(); | |
| if (!key) return alert('Please enter your OpenAI API key.'); | |
| if (!pdfText) return alert('Please upload a PDF first.'); | |
| const opt = document.getElementById('optionSelect').value; | |
| let sys = '', user = pdfText; | |
| if (opt === 'Generate Summary') { | |
| sys = 'You are a smart assistant. Give a summary of the PDF.'; | |
| } else if (opt === 'Generate Quiz') { | |
| sys = 'You are a smart assistant. Generate 10 multiple-choice quiz questions with 4 options each (include the correct answer).'; | |
| } else if (opt === 'Ask a Question') { | |
| sys = 'You are a smart assistant. Answer the user’s question based on the PDF.'; | |
| const q = document.getElementById('questionInput').value.trim(); | |
| if (!q) return alert('Please type your question.'); | |
| user += '\n\nUser question: ' + q; | |
| } else { | |
| sys = 'You are a smart assistant. Generate a balanced 7-day study plan dividing PDF content into 7 topics.'; | |
| } | |
| const resDiv = document.getElementById('result'); | |
| resDiv.textContent = 'Generating…'; | |
| const resp = await fetch('https://api.openai.com/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| Authorization: 'Bearer ' + key, | |
| }, | |
| body: JSON.stringify({ | |
| model: 'gpt-4o-mini', | |
| messages: [ | |
| { role: 'system', content: sys }, | |
| { role: 'user', content: user }, | |
| ], | |
| }), | |
| }); | |
| const data = await resp.json(); | |
| const text = data.choices[0].message.content.trim(); | |
| resDiv.textContent = text; | |
| const { jsPDF } = window.jspdf; | |
| const doc = new jsPDF(); | |
| const lines = doc.splitTextToSize(text, 180); | |
| doc.setFontSize(12); | |
| doc.text(lines, 10, 10); | |
| const dlBtn = document.getElementById('downloadBtn'); | |
| dlBtn.classList.remove('d-none'); | |
| dlBtn.onclick = () => doc.save(`${opt.replace(/ /g, '_')}.pdf`); | |
| }); | |
| </script> | |
| </body> | |
| </html> |