askBot / index.html
Murali Chandran
sidebar color fix
eb57eed
Raw History Blame Contribute Delete
10.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ask Julia Assistant</title>
<script>
window.MathJax = { tex: { inlineMath: [['$', '$'], ['\\(', '\\)']] } };
</script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<style>
:root { --sidebar-width: 450px; }
body { font-family: 'Inter', sans-serif; background: #0f172a; color: #f8fafc; display: flex; height: 100vh; margin: 0; overflow: hidden; }
#content-area {
flex: 1; padding: 40px; overflow-y: auto;
transition: padding-right 0.3s ease;
}
body.sidebar-active #content-area {
padding-right: calc(var(--sidebar-width) + 40px);
}
#sidebar {
position: fixed; right: 0; top: 0; height: 100%;
width: var(--sidebar-width); background: #1e293b;
display: flex; flex-direction: column;
box-shadow: -5px 0 15px rgba(0,0,0,0.3);
transform: translateX(100%); transition: transform 0.3s ease;
z-index: 100; padding: 20px; box-sizing: border-box;
}
#sidebar.open { transform: translateX(0); }
#sidebar-tab {
position: absolute; left: -40px; top: 50%; transform: translateY(-50%);
width: 40px; height: 70px;
/* Sidebar match */
background: #1e293b;
/* The blue color from your original design */
color: #38bdf8;
border: none; border-radius: 8px 0 0 8px; cursor: pointer;
box-shadow: -5px 0 10px rgba(0,0,0,0.1); display: flex;
align-items: center; justify-content: center; font-size: 1.2rem;
border-left: 1px solid #334155; border-top: 1px solid #334155; border-bottom: 1px solid #334155;
padding: 0; transition: all 0.2s ease;
}
#sidebar-tab:hover {
background: #334155;
/* Brighter blue on hover */
color: #7dd3fc;
/* Subtle blue glow */
box-shadow: -2px 0 15px rgba(56, 189, 248, 0.3);
}
h2 { color: #38bdf8; }
.research-text { line-height: 1.8; font-size: 1.1rem; color: #cbd5e1; }
#chat-output { flex: 1; overflow-y: auto; margin-bottom: 10px; font-size: 0.95rem; line-height: 1.6; border-bottom: 1px solid #334155; padding-bottom: 10px; }
.ai-msg { background: #0f172a; padding: 15px; border-radius: 8px; margin-top: 10px; border-left: 4px solid #38bdf8; }
::selection { background: #38bdf8; color: #0f172a; }
.cursor-blink { animation: blink 1s step-end infinite; color: #38bdf8; }
@keyframes blink { 50% { opacity: 0; } }
/* FOOLPROOF FLEXBOX INPUT LAYOUT */
.input-container {
display: flex;
align-items: flex-end; /* Keeps button at the bottom when typing long messages */
gap: 10px;
margin-top: 10px;
width: 100%;
}
#chat-input {
flex: 1;
background: #0f172a; border: 1px solid #334155; color: white;
padding: 12px;
border-radius: 8px; box-sizing: border-box; outline: none;
resize: none; overflow-y: hidden; min-height: 45px; max-height: 150px;
font-family: inherit; font-size: 0.95rem;
}
#chat-input:focus { border-color: #38bdf8; }
#send-btn {
flex-shrink: 0; /* Prevents button from shrinking */
background: #38bdf8;
color: #0f172a;
border: none;
border-radius: 8px;
width: 45px;
height: 45px; /* Exactly matches the minimum height of the text area */
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.1s, background 0.2s, opacity 0.2s;
}
#send-btn:hover { background: #7dd3fc; }
#send-btn:active { transform: scale(0.9); }
/* Mobile Adjustment */
@media (max-width: 600px) {
:root { --sidebar-width: 100%; }
#sidebar-tab { width: 50px; height: 50px; left: -50px; }
}
</style>
</head>
<body>
<div id="content-area">
<h2>Research Material</h2>
<div class="research-text">
<p>In Flow Matching, the transition is defined by the ODE: $dx = v_t(x)dt$.
This vector field $v_t$ defines the velocity at each point in space-time to push samples from $p_0$ to $p_1$.</p>
<p>The forward diffusion process adds noise $q(x_t | x_{t-1}) = \mathcal{N}(x_t; \sqrt{1-\beta_t}x_{t-1}, \beta_t I)$.
As $t$ increases, the original structure of the image is lost to Gaussian randomness.</p>
<p>Copy any text above and paste it into the sidebar with your question!</p>
</div>
</div>
<div id="sidebar">
<button id="sidebar-tab" onclick="toggleSidebar()" title="Toggle Ask Julia">
<span id="tab-icon">◀</span>
</button>
<h3>Ask Julia</h3>
<div id="chat-output"> </div>
<div class="input-container">
<textarea id="chat-input" rows="1" placeholder="@bot explain..."></textarea>
<button id="send-btn" onclick="askBot()" title="Send">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
</button>
</div>
</div>
<script>
const sidebar = document.getElementById('sidebar');
const output = document.getElementById('chat-output');
const input = document.getElementById('chat-input');
const sendBtn = document.getElementById('send-btn');
const tabIcon = document.getElementById('tab-icon');
function toggleSidebar() {
sidebar.classList.toggle('open');
document.body.classList.toggle('sidebar-active');
if (sidebar.classList.contains('open')) {
tabIcon.innerText = "▶";
} else {
tabIcon.innerText = "◀";
}
}
input.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = this.scrollHeight + 'px';
this.style.overflowY = this.scrollHeight > 150 ? 'auto' : 'hidden';
});
function renderTextWithMath(text) {
const mathBlocks = [];
const mathRegex = /(\\$\\$[\\s\\S]*?\\$\\$|\\$[^\\$]*?\\$|\\\\\\[[\\s\\S]*?\\\\\\]|\\\\\\([\\s\\S]*?\\\\\\))/g;
let placeholderText = text.replace(mathRegex, (match) => {
mathBlocks.push(match);
return `@@MATH_BLOCK_${mathBlocks.length - 1}@@`;
});
let html = marked.parse(placeholderText);
mathBlocks.forEach((math, index) => {
html = html.replace(`@@MATH_BLOCK_${index}@@`, math);
});
return html;
}
function typeARResponse(text, element) {
return new Promise((resolve) => {
let i = 0;
let currentText = "";
const speed = 10;
const chunkSize = 2;
const interval = setInterval(() => {
currentText += text.substring(i, i + chunkSize);
i += chunkSize;
element.innerHTML = renderTextWithMath(currentText) + '<span class="cursor-blink">▌</span>';
output.scrollTop = output.scrollHeight;
if (i >= text.length) {
clearInterval(interval);
element.innerHTML = renderTextWithMath(text);
resolve();
}
}, speed);
});
}
async function askBot() {
const query = input.value.trim();
if(!query.includes('@bot')) return;
// Disable UI while waiting for response
sendBtn.style.opacity = "0.5";
sendBtn.disabled = true;
input.disabled = true;
output.innerHTML += `<div><b>You:</b> ${query}</div><div class="ai-msg" id="last-response">Thinking...</div>`;
input.value = "";
input.style.height = 'auto';
output.scrollTop = output.scrollHeight;
try {
const res = await fetch('/explain', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ page_text: "Context provided in query.", user_query: query })
});
const data = await res.json();
const lastResponse = document.getElementById('last-response');
lastResponse.removeAttribute('id');
await typeARResponse(data.reply, lastResponse);
if (window.MathJax) MathJax.typesetPromise([lastResponse]);
} catch (err) {
document.getElementById('last-response').innerHTML = "<span style='color: #f87171;'>Error: Could not connect to backend.</span>";
}
// Re-enable UI
sendBtn.style.opacity = "1";
sendBtn.disabled = false;
input.disabled = false;
input.focus();
output.scrollTop = output.scrollHeight;
}
input.addEventListener('keydown', (e) => {
if(e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
askBot();
}
});
</script>
</body>
</html>