adity_AI / templates /index.html
triflix's picture
Create templates/index.html
d957247 verified
<!-- templates/index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"/>
<title>ICIS Mini-Hub β€” Mobile</title>
<style>
/* Mobile-first simple UI */
:root{--bg:#0f172a;--card:#0b1220;--accent:#06b6d4;--muted:#94a3b8;--text:#e6eef8}
*{box-sizing:border-box}
html,body{height:100%;margin:0;background:linear-gradient(180deg,var(--bg),#071024);font-family:Inter,system-ui,Segoe UI,Roboto,"Helvetica Neue",Arial}
.app{max-width:480px;margin:0 auto;height:100vh;display:flex;flex-direction:column;gap:8px;padding:12px}
header{display:flex;align-items:center;gap:10px}
.brand{color:var(--text);font-weight:700;font-size:18px}
.sub{color:var(--muted);font-size:12px}
.chat-window{flex:1;background:transparent;padding:8px;overflow:auto;display:flex;flex-direction:column;gap:8px}
.bubble{max-width:86%;padding:10px 12px;border-radius:12px;color:var(--text);line-height:1.3}
.user{align-self:flex-end;background:linear-gradient(90deg,#0ea5a4,#06b6d4);border-bottom-right-radius:4px}
.bot{align-self:flex-start;background:var(--card);border-bottom-left-radius:4px;color:var(--text);box-shadow:0 2px 8px rgba(2,6,23,.6)}
.meta{font-size:11px;color:var(--muted);margin-top:6px}
.composer{display:flex;gap:8px;align-items:center;padding:8px;background:transparent}
.input{flex:1;display:flex;gap:8px;align-items:center;background:rgba(255,255,255,0.03);padding:8px;border-radius:999px}
input[type="text"]{flex:1;background:transparent;border:0;color:var(--text);outline:none;padding:6px 4px;font-size:15px}
.icon-btn{width:40px;height:40px;border-radius:10px;border:0;background:transparent;color:var(--muted);display:flex;align-items:center;justify-content:center;cursor:pointer}
.icon-btn[disabled]{opacity:0.35;cursor:not-allowed}
.send{background:var(--accent);color:#002; padding:8px 12px;border-radius:12px;border:0;font-weight:700}
.small{font-size:12px;color:var(--muted)}
footer.small{text-align:center;padding:6px 0;color:var(--muted);font-size:12px}
/* simple responsive tweaks */
@media(min-width:520px){.app{margin-top:30px;border-radius:12px;box-shadow:0 10px 30px rgba(2,6,23,.6);padding:16px}}
</style>
</head>
<body>
<div class="app" role="application">
<header>
<div>
<div class="brand">ICIS Mini-Hub</div>
<div class="sub">Mobile-first chat β€’ image/pdf single-attachment</div>
</div>
</header>
<main class="chat-window" id="chatWindow" aria-live="polite">
<!-- messages will appear here -->
<div class="meta small">Function used will show with chat replies. Image/PDF produce direct concise answers.</div>
</main>
<div class="composer" aria-label="Composer">
<div class="input" id="composer">
<button class="icon-btn" id="imgBtn" title="Attach image">πŸ–ΌοΈ</button>
<button class="icon-btn" id="pdfBtn" title="Attach PDF">πŸ“„</button>
<input type="file" id="imgInput" accept="image/*" style="display:none">
<input type="file" id="pdfInput" accept="application/pdf" style="display:none">
<input type="text" id="prompt" placeholder="Type a message or add prompt..." aria-label="Message">
</div>
<button class="send" id="sendBtn">Send</button>
</div>
<footer class="small">Only one file at a time. Attaching image disables PDF and vice versa.</footer>
</div>
<script>
// UI behavior: single attachment rule
const imgBtn = document.getElementById('imgBtn');
const pdfBtn = document.getElementById('pdfBtn');
const imgInput = document.getElementById('imgInput');
const pdfInput = document.getElementById('pdfInput');
const sendBtn = document.getElementById('sendBtn');
const promptIn = document.getElementById('prompt');
const chatWindow = document.getElementById('chatWindow');
let attachedFile = null; // {type: 'image'|'pdf', file: File}
function renderBubble(text, who='bot', meta='') {
const b = document.createElement('div');
b.className = 'bubble ' + (who==='user' ? 'user' : 'bot');
b.innerText = text;
chatWindow.appendChild(b);
if(meta){
const m = document.createElement('div');
m.className = 'meta small';
m.innerText = meta;
chatWindow.appendChild(m);
}
chatWindow.scrollTop = chatWindow.scrollHeight;
}
imgBtn.addEventListener('click', ()=> imgInput.click());
pdfBtn.addEventListener('click', ()=> pdfInput.click());
imgInput.addEventListener('change', (e)=>{
const f = e.target.files[0];
if(!f) return;
attachedFile = {type:'image', file:f};
// disable pdf btn
pdfBtn.disabled = true;
imgBtn.style.opacity = '0.9';
renderBubble('[Image attached: ' + f.name + ']', 'user');
});
pdfInput.addEventListener('change', (e)=>{
const f = e.target.files[0];
if(!f) return;
attachedFile = {type:'pdf', file:f};
imgBtn.disabled = true;
pdfBtn.style.opacity = '0.9';
renderBubble('[PDF attached: ' + f.name + ']', 'user');
});
// remove attachment if user clears input or selects new
function clearAttachment(){
attachedFile = null;
imgInput.value = '';
pdfInput.value = '';
imgBtn.disabled = false;
pdfBtn.disabled = false;
imgBtn.style.opacity = '1';
pdfBtn.style.opacity = '1';
}
// helper to post JSON
async function postJSON(url, body){
const resp = await fetch(url, {
method:'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify(body)
});
return resp.json();
}
// helper to post multipart
async function postForm(url, file, prompt){
const fd = new FormData();
fd.append('file', file);
fd.append('prompt', prompt || '');
const resp = await fetch(url, {method:'POST', body:fd});
return resp.json();
}
sendBtn.addEventListener('click', async ()=>{
const text = promptIn.value.trim();
if(!text && !attachedFile){
// nothing to do
return;
}
// show user message
if(text) renderBubble(text, 'user');
// if file attached show small indicator already added at attachment time
promptIn.value = '';
try {
let result = null;
if(attachedFile){
if(attachedFile.type === 'image'){
result = await postForm('/analyze_image', attachedFile.file, text);
// show direct answer (plain & concise)
renderBubble(result.response || result.error || 'No response', 'bot', 'Direct (image)');
} else if(attachedFile.type === 'pdf'){
result = await postForm('/summarize_pdf', attachedFile.file, text);
renderBubble(result.response || result.error || 'No response', 'bot', 'Direct (pdf)');
}
// after handling, clear attachment
clearAttachment();
} else {
// normal chat
const json = await postJSON('/chat', {query: text});
const func = json.function_used || 'chat';
const resp = json.response || json.error || 'No response';
renderBubble(resp, 'bot', 'Function: ' + func);
}
} catch (err) {
renderBubble('Network or server error', 'bot');
console.error(err);
}
});
// allow Enter to send
promptIn.addEventListener('keydown', (e)=>{
if(e.key === 'Enter' && !e.shiftKey){
e.preventDefault();
sendBtn.click();
}
});
</script>
</body>
</html>