study-sathi / static /js /design.js
YousifCreates's picture
Updated 5th chapter of OS
f6bb754
// static/js/design.js
// ── Marked.js config ──────────────────────────────────────────────────────────
marked.setOptions({
breaks: true,
gfm: true,
highlight: (code, lang) => {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(code, { language: lang }).value;
}
return hljs.highlightAuto(code).value;
}
});
// ── DOM refs ──────────────────────────────────────────────────────────────────
const chatWindow = document.getElementById("chatWindow");
const queryInput = document.getElementById("queryInput");
const sendBtn = document.getElementById("sendBtn");
const topicInput = document.getElementById("topicInput");
const clearBtn = document.getElementById("clearBtn");
const menuBtn = document.getElementById("menuBtn");
const sidebar = document.getElementById("sidebar");
const overlay = document.getElementById("overlay");
const sidebarClose = document.getElementById("sidebarClose");
const quickBtns = document.querySelectorAll(".quick-btn");
// ── Conversation history ──────────────────────────────────────────────────────
let history = [];
// ── Sidebar toggle ────────────────────────────────────────────────────────────
function openSidebar() {
sidebar.classList.add("open");
overlay.classList.add("show");
}
function closeSidebar() {
sidebar.classList.remove("open");
overlay.classList.remove("show");
}
menuBtn.addEventListener("click", openSidebar);
sidebarClose.addEventListener("click", closeSidebar);
overlay.addEventListener("click", closeSidebar);
// ── Auto-resize textarea ──────────────────────────────────────────────────────
function autoResize() {
queryInput.style.height = "auto";
queryInput.style.height = Math.min(queryInput.scrollHeight, 200) + "px";
}
queryInput.addEventListener("input", autoResize);
// ── Quick prompt buttons ──────────────────────────────────────────────────────
quickBtns.forEach(btn => {
btn.addEventListener("click", () => {
queryInput.value = btn.dataset.prompt;
autoResize();
queryInput.focus();
closeSidebar();
});
});
// ── Clear chat ────────────────────────────────────────────────────────────────
clearBtn.addEventListener("click", () => {
history = [];
const welcome = document.getElementById("welcomeMsg");
chatWindow.innerHTML = "";
if (welcome) chatWindow.appendChild(welcome);
});
// ── Scroll to bottom ──────────────────────────────────────────────────────────
function scrollToBottom() {
chatWindow.scrollTo({ top: chatWindow.scrollHeight, behavior: "smooth" });
}
// ── Add message bubble ────────────────────────────────────────────────────────
function addMessage(role, content) {
const msg = document.createElement("div");
msg.className = `message ${role}`;
const avatar = document.createElement("div");
avatar.className = "avatar";
avatar.textContent = role === "user" ? "YOU" : "SS";
const bubble = document.createElement("div");
bubble.className = "bubble";
if (role === "assistant") {
bubble.innerHTML = marked.parse(content);
bubble.querySelectorAll("pre code").forEach(el => hljs.highlightElement(el));
} else {
// user bubble: preserve newlines and show code syntax as plain text
bubble.style.whiteSpace = "pre-wrap";
bubble.textContent = content;
}
msg.appendChild(avatar);
msg.appendChild(bubble);
chatWindow.appendChild(msg);
scrollToBottom();
}
// ── Typing indicator ──────────────────────────────────────────────────────────
function showTyping() {
const msg = document.createElement("div");
msg.className = "message assistant";
msg.id = "typingIndicator";
const avatar = document.createElement("div");
avatar.className = "avatar";
avatar.textContent = "SS";
const bubble = document.createElement("div");
bubble.className = "bubble";
bubble.innerHTML = `
<div class="typing-indicator">
<span></span><span></span><span></span>
</div>`;
msg.appendChild(avatar);
msg.appendChild(bubble);
chatWindow.appendChild(msg);
scrollToBottom();
}
function hideTyping() {
const indicator = document.getElementById("typingIndicator");
if (indicator) indicator.remove();
}
// ── Send message ──────────────────────────────────────────────────────────────
async function sendMessage() {
const query = queryInput.value.trim();
if (!query) return;
const topic = topicInput.value.trim() || null;
addMessage("user", query);
history.push({ role: "user", content: query });
// reset input
queryInput.value = "";
queryInput.style.height = "auto";
sendBtn.disabled = true;
showTyping();
try {
const res = await fetch("/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, topic, history })
});
const data = await res.json();
hideTyping();
if (data.error) {
addMessage("assistant", `⚠️ **Error:** ${data.error}`);
} else {
addMessage("assistant", data.response);
history.push({ role: "assistant", content: data.response });
}
} catch (err) {
hideTyping();
addMessage("assistant", "⚠️ **Network error.** Flask server se connect nahi ho saka.");
} finally {
sendBtn.disabled = false;
queryInput.focus();
}
}
// ── Keyboard handling ─────────────────────────────────────────────────────────
queryInput.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
if (e.shiftKey) {
// Shift+Enter β€” insert a real newline and resize
// let the browser insert \n naturally, then resize
setTimeout(autoResize, 0);
return; // do NOT send
}
// plain Enter β€” send
e.preventDefault();
sendMessage();
}
});
// ── Send button ───────────────────────────────────────────────────────────────
sendBtn.addEventListener("click", sendMessage);