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> |