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> | |
<!-- Bootstrap 5 --> | |
<link | |
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" | |
rel="stylesheet" | |
integrity="sha384-c7DF2uX692G1LB+AU6CtQbmu6Ubfbdr6FLllSVEW9v56MZPPiBYkoQ7RZ2hFOTg1" | |
crossorigin="anonymous" | |
/> | |
<!-- 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> | |
body { | |
background: #f8f9fa; | |
} | |
.hero { | |
background: url('image.jpeg') center/cover no-repeat; | |
height: 300px; | |
position: relative; | |
} | |
.hero h1 { | |
position: absolute; | |
bottom: 20px; | |
left: 20px; | |
color: white; | |
text-shadow: 0 0 10px rgba(0,0,0,0.7); | |
} | |
.offcanvas-body { | |
padding-top: 1rem; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- Navbar with sidebar toggle --> | |
<nav class="navbar navbar-light bg-white shadow-sm"> | |
<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 Sidebar --> | |
<div | |
class="offcanvas offcanvas-start" | |
tabindex="-1" | |
id="sidebar" | |
aria-labelledby="sidebarLabel" | |
> | |
<div class="offcanvas-header"> | |
<h5 id="sidebarLabel">Instructions</h5> | |
<button | |
type="button" | |
class="btn-close text-reset" | |
data-bs-dismiss="offcanvas" | |
aria-label="Close" | |
></button> | |
</div> | |
<div class="offcanvas-body"> | |
<ol> | |
<li>Enter your OpenAI API key below (password field).</li> | |
<li>Upload a PDF via the “Upload PDF” button.</li> | |
<li>Select one: | |
<ul> | |
<li>Generate Summary</li> | |
<li>Generate Quiz</li> | |
<li>Ask a Question</li> | |
<li>Generate Study Plan</li> | |
</ul> | |
</li> | |
<li>If asking a question, type it in.</li> | |
<li>Click “Generate” and then download your result as PDF.</li> | |
</ol> | |
</div> | |
</div> | |
<!-- Hero Section --> | |
<section class="hero mb-4"> | |
<h1 class="animate__animated animate__fadeInDown">Study Smarter</h1> | |
</section> | |
<!-- Main Content --> | |
<div class="container" data-aos="fade-up"> | |
<div class="row g-4"> | |
<!-- Controls Column --> | |
<div class="col-lg-4"> | |
<div class="card shadow-sm"> | |
<div class="card-body"> | |
<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 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> | |
</div> | |
<!-- Output Column --> | |
<div class="col-lg-8"> | |
<div class="card shadow-sm"> | |
<div class="card-body"> | |
<h5 class="card-title">Result</h5> | |
<div id="result" style="white-space: pre-wrap; min-height: 200px;"> | |
<!-- AI response appears here --> | |
</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> | |
</div> | |
<!-- Scripts --> | |
<!-- Bootstrap JS & dependencies --> | |
<script | |
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" | |
integrity="sha384-E6mRv7/D+K0PtPpK0O1J3DRgIh+tp3loX3f7ogf20+RskmIOrthyxU2048gASBZd" | |
crossorigin="anonymous" | |
></script> | |
<!-- PDF.js --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script> | |
<!-- jsPDF --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> | |
<!-- AOS --> | |
<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 = ''; | |
// Show/hide question input | |
document | |
.getElementById('optionSelect') | |
.addEventListener('change', (e) => { | |
const qg = document.getElementById('questionGroup'); | |
qg.classList.toggle( | |
'd-none', | |
e.target.value !== 'Ask a Question' | |
); | |
}); | |
// Load PDF and extract text | |
document | |
.getElementById('pdfUpload') | |
.addEventListener('change', async (e) => { | |
const file = e.target.files[0]; | |
if (!file) return; | |
const arrayBuffer = await file.arrayBuffer(); | |
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise; | |
let fullText = ''; | |
for (let i = 1; i <= pdf.numPages; i++) { | |
const page = await pdf.getPage(i); | |
const txt = await page.getTextContent(); | |
fullText += txt.items.map((it) => it.str).join(' ') + '\n\n'; | |
} | |
pdfText = fullText.trim(); | |
alert('PDF loaded: ' + pdf.numPages + ' pages'); | |
}); | |
// Generate & display, then prepare PDF download | |
document | |
.getElementById('generateBtn') | |
.addEventListener('click', async () => { | |
const key = document.getElementById('apiKey').value.trim(); | |
if (!key) return alert('Enter your OpenAI API key.'); | |
if (!pdfText) return alert('Please upload a PDF first.'); | |
const option = document.getElementById('optionSelect').value; | |
let systemPrompt = ''; | |
let userPrompt = pdfText; | |
if (option === 'Generate Summary') { | |
systemPrompt = 'You are a smart assistant. Give a summary of the PDF.'; | |
} else if (option === 'Generate Quiz') { | |
systemPrompt = | |
'You are a smart assistant. Generate 10 multiple-choice quiz questions with 4 options each (include the correct answer).'; | |
} else if (option === 'Ask a Question') { | |
systemPrompt = '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('Type your question.'); | |
userPrompt += '\n\nUser question: ' + q; | |
} else if (option === 'Generate Study Plan') { | |
systemPrompt = | |
'You are a smart assistant. Generate a balanced 7-day study plan dividing PDF content into 7 topics.'; | |
} | |
// Call OpenAI | |
const responseDiv = document.getElementById('result'); | |
responseDiv.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: systemPrompt }, | |
{ role: 'user', content: userPrompt }, | |
], | |
}), | |
}); | |
const data = await resp.json(); | |
const text = data.choices[0].message.content.trim(); | |
responseDiv.textContent = text; | |
// Generate PDF with jsPDF | |
const { jsPDF } = window.jspdf; | |
const doc = new jsPDF(); | |
const lines = doc.splitTextToSize(text, 180); | |
doc.setFontSize(12); | |
doc.text(lines, 10, 10); | |
// Show download button | |
const dl = document.getElementById('downloadBtn'); | |
dl.classList.remove('d-none'); | |
dl.onclick = () => doc.save(`${option.replace(/ /g, '_')}.pdf`); | |
}); | |
</script> | |
</body> | |
</html> |