|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
<title>Smart Analyzer Pro</title> |
|
<style> |
|
:root { |
|
--primary-color: #4361ee; |
|
--secondary-color: #f1f5f9; |
|
--accent-color: #3f37c9; |
|
--text-color: #333; |
|
--light-text: #666; |
|
--btn-color: #4361ee; |
|
--btn-hover: #3f37c9; |
|
--card-bg: #fff; |
|
--card-shadow: rgba(0, 0, 0, 0.08); |
|
--border-radius: 10px; |
|
--transition: 0.3s; |
|
--max-width: 1200px; |
|
} |
|
|
|
* { box-sizing: border-box; } |
|
|
|
body { |
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
color: var(--text-color); |
|
background: var(--secondary-color); |
|
margin: 0; |
|
padding: 0; |
|
min-height: 100vh; |
|
} |
|
|
|
.app-header { |
|
background: var(--primary-color); |
|
color: white; |
|
padding: 20px 0; |
|
text-align: center; |
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); |
|
margin-bottom: 40px; |
|
} |
|
|
|
.app-title { |
|
margin: 0; |
|
font-size: 2.5rem; |
|
} |
|
|
|
.app-subtitle { |
|
margin: 10px 0 0; |
|
font-weight: normal; |
|
opacity: 0.9; |
|
} |
|
|
|
.container { |
|
width: 92%; |
|
max-width: var(--max-width); |
|
margin: 0 auto; |
|
padding: 0 20px 40px; |
|
} |
|
|
|
.grid { |
|
display: grid; |
|
grid-template-columns: 1fr; |
|
gap: 30px; |
|
} |
|
|
|
.section { |
|
background: var(--card-bg); |
|
padding: 25px; |
|
border-radius: var(--border-radius); |
|
box-shadow: 0 5px 15px var(--card-shadow); |
|
transition: transform var(--transition), box-shadow var(--transition); |
|
position: relative; |
|
overflow: hidden; |
|
} |
|
|
|
.section:hover { |
|
transform: translateY(-5px); |
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12); |
|
} |
|
|
|
.section::before { |
|
content: ''; |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 5px; |
|
background: var(--primary-color); |
|
} |
|
|
|
h2 { |
|
margin-top: 0; |
|
color: var(--primary-color); |
|
font-size: 1.5rem; |
|
padding-bottom: 10px; |
|
border-bottom: 2px solid var(--secondary-color); |
|
} |
|
|
|
.icon { |
|
display: inline-block; |
|
margin-right: 8px; |
|
vertical-align: middle; |
|
} |
|
|
|
.file-input-wrapper { |
|
position: relative; |
|
margin-top: 20px; |
|
} |
|
|
|
.file-input-label { |
|
display: block; |
|
background: var(--secondary-color); |
|
border: 2px dashed #ccc; |
|
border-radius: var(--border-radius); |
|
padding: 25px 15px; |
|
text-align: center; |
|
cursor: pointer; |
|
transition: all var(--transition); |
|
} |
|
|
|
.file-input-label:hover { |
|
border-color: var(--primary-color); |
|
background: #e8eeff; |
|
} |
|
|
|
.file-input { |
|
position: absolute; |
|
left: 0; |
|
top: 0; |
|
opacity: 0; |
|
width: 100%; |
|
height: 100%; |
|
cursor: pointer; |
|
} |
|
|
|
.file-name { |
|
margin-top: 10px; |
|
font-size: 0.9rem; |
|
color: var(--light-text); |
|
min-height: 20px; |
|
} |
|
|
|
input[type="text"] { |
|
width: 100%; |
|
padding: 12px 15px; |
|
margin-top: 15px; |
|
border: 1px solid #ddd; |
|
border-radius: var(--border-radius); |
|
font-size: 1rem; |
|
transition: border-color var(--transition); |
|
} |
|
|
|
input[type="text"]:focus { |
|
outline: none; |
|
border-color: var(--primary-color); |
|
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.15); |
|
} |
|
|
|
.btn { |
|
background: var(--btn-color); |
|
color: white; |
|
border: none; |
|
border-radius: var(--border-radius); |
|
padding: 12px 20px; |
|
font-size: 1rem; |
|
cursor: pointer; |
|
transition: all var(--transition); |
|
display: block; |
|
width: 100%; |
|
margin-top: 20px; |
|
font-weight: 600; |
|
} |
|
|
|
.btn:hover { |
|
background: var(--btn-hover); |
|
transform: translateY(-2px); |
|
} |
|
|
|
.btn:active { |
|
transform: translateY(0); |
|
} |
|
|
|
.results { |
|
margin-top: 25px; |
|
padding: 20px; |
|
background: var(--secondary-color); |
|
border-radius: var(--border-radius); |
|
border: 1px solid #ddd; |
|
min-height: 100px; |
|
position: relative; |
|
} |
|
|
|
.loader { |
|
display: none; |
|
text-align: center; |
|
padding: 20px 0; |
|
} |
|
|
|
.loader::after { |
|
content: ''; |
|
display: inline-block; |
|
width: 30px; |
|
height: 30px; |
|
border: 3px solid var(--secondary-color); |
|
border-radius: 50%; |
|
border-top-color: var(--primary-color); |
|
animation: spin 1s linear infinite; |
|
} |
|
|
|
@keyframes spin { |
|
to { transform: rotate(360deg); } |
|
} |
|
|
|
.processing .loader { |
|
display: block; |
|
} |
|
|
|
.processing .result-content { |
|
display: none; |
|
} |
|
|
|
@media (min-width: 768px) { |
|
.grid { |
|
grid-template-columns: repeat(2, 1fr); |
|
} |
|
} |
|
|
|
@media (min-width: 1024px) { |
|
.grid { |
|
grid-template-columns: repeat(3, 1fr); |
|
} |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<header class="app-header"> |
|
<h1 class="app-title">Smart Analyzer Pro</h1> |
|
<p class="app-subtitle">Analyze documents, answer questions, and interpret images with AI</p> |
|
</header> |
|
|
|
<div class="container"> |
|
<div class="grid"> |
|
<div class="section"> |
|
<h2><span class="icon">π</span>Summarize Document</h2> |
|
<div class="file-input-wrapper"> |
|
<label class="file-input-label"> |
|
<span>Drop your document here or click to browse</span> |
|
<input type="file" id="summary-file" class="file-input" accept=".pdf,.docx,.txt"> |
|
</label> |
|
<div id="summary-file-name" class="file-name"></div> |
|
</div> |
|
<button id="summarize-btn" class="btn">Generate Summary</button> |
|
<div id="summary-result" class="results"> |
|
<div class="loader"></div> |
|
<div class="result-content">Your summary will appear here.</div> |
|
</div> |
|
</div> |
|
|
|
<div class="section"> |
|
<h2><span class="icon">β</span>Ask a Question</h2> |
|
<div class="file-input-wrapper"> |
|
<label class="file-input-label"> |
|
<span>Drop your file here or click to browse</span> |
|
<input type="file" id="qa-file" class="file-input" accept=".pdf,.docx,.txt,image/*"> |
|
</label> |
|
<div id="qa-file-name" class="file-name"></div> |
|
</div> |
|
<input type="text" id="qa-question" placeholder="Type your question about the document..."> |
|
<button id="qa-btn" class="btn">Get Answer</button> |
|
<div id="qa-result" class="results"> |
|
<div class="loader"></div> |
|
<div class="result-content">Answer will appear here.</div> |
|
</div> |
|
</div> |
|
|
|
<div class="section"> |
|
<h2><span class="icon">πΌοΈ</span>Analyze Image</h2> |
|
<div class="file-input-wrapper"> |
|
<label class="file-input-label"> |
|
<span>Drop your image here or click to browse</span> |
|
<input type="file" id="image-file" class="file-input" accept="image/*"> |
|
</label> |
|
<div id="image-file-name" class="file-name"></div> |
|
</div> |
|
<button id="image-btn" class="btn">Generate Description</button> |
|
<div id="image-result" class="results"> |
|
<div class="loader"></div> |
|
<div class="result-content">Image description will appear here.</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
document.querySelectorAll('.file-input').forEach(input => { |
|
input.addEventListener('change', function() { |
|
const fileNameElement = document.getElementById(this.id + '-name'); |
|
fileNameElement.textContent = this.files[0] ? this.files[0].name : ''; |
|
}); |
|
}); |
|
|
|
async function sendData(url, fileInputId, extraData, resultId) { |
|
const fileInput = document.getElementById(fileInputId); |
|
const file = fileInput.files[0]; |
|
const resultElement = document.getElementById(resultId); |
|
const resultContent = resultElement.querySelector('.result-content'); |
|
|
|
if (!file) { |
|
resultContent.textContent = 'Please select a file.'; |
|
return; |
|
} |
|
|
|
const formData = new FormData(); |
|
formData.append(fileInputId.includes('image') ? 'image' : 'file', file); |
|
|
|
if (extraData) { |
|
for (const [key, value] of Object.entries(extraData)) { |
|
formData.append(key, value); |
|
} |
|
} |
|
|
|
|
|
resultElement.classList.add('processing'); |
|
|
|
try { |
|
const res = await fetch(url, { method: 'POST', body: formData }); |
|
const data = await res.json(); |
|
|
|
|
|
resultContent.textContent = data.result || data.summary || data.answer || |
|
data.description || data.error || JSON.stringify(data); |
|
} catch (err) { |
|
resultContent.textContent = 'Error: ' + err.message; |
|
} finally { |
|
|
|
resultElement.classList.remove('processing'); |
|
} |
|
} |
|
|
|
document.getElementById('summarize-btn').addEventListener('click', () => { |
|
sendData('/api/summarize', 'summary-file', null, 'summary-result'); |
|
}); |
|
|
|
document.getElementById('qa-btn').addEventListener('click', () => { |
|
const question = document.getElementById('qa-question').value.trim(); |
|
if (!question) { |
|
document.getElementById('qa-result').querySelector('.result-content').textContent = 'Please enter a question.'; |
|
return; |
|
} |
|
sendData('/api/qa', 'qa-file', { question }, 'qa-result'); |
|
}); |
|
|
|
document.getElementById('image-btn').addEventListener('click', () => { |
|
sendData('/api/caption', 'image-file', null, 'image-result'); |
|
}); |
|
</script> |
|
</body> |
|
</html> |