document.addEventListener("DOMContentLoaded", () => {
// --- UI Selectors ---
const sidebar = document.getElementById("sidebar");
const openSidebarBtn = document.getElementById("open-sidebar");
const closeSidebarBtn = document.getElementById("close-sidebar");
const chatMessages = document.getElementById("chat-messages");
const chatViewport = document.getElementById("chat-viewport");
const chatForm = document.getElementById("chat-form");
const userInput = document.getElementById("user-input");
const usageStats = document.getElementById("usage-stats");
const authTrigger = document.getElementById("auth-trigger");
const authModal = document.getElementById("auth-modal");
const loginForm = document.getElementById("login-form");
const signupForm = document.getElementById("signup-form");
const logoutBtn = document.getElementById("logout-btn");
const historyList = document.getElementById("history-list");
const tabBtns = document.querySelectorAll(".tab-btn");
const cpuVal = document.getElementById("cpu-val");
const ramVal = document.getElementById("ram-val");
const sysPills = document.getElementById("sys-pills");
const totalQEl = document.getElementById("total-q");
let isStreaming = false;
// --- Sidebar & Navigation ---
openSidebarBtn.onclick = () => {
sidebar.classList.remove("sidebar-closed");
fetchHistory();
};
closeSidebarBtn.onclick = () => sidebar.classList.add("sidebar-closed");
// --- Auth Modal Logic ---
authTrigger.onclick = () => authModal.classList.remove("hidden");
document.querySelector(".close-modal-btn").onclick = () => authModal.classList.add("hidden");
tabBtns.forEach(btn => {
btn.onclick = () => {
tabBtns.forEach(b => b.classList.remove("active"));
btn.classList.add("active");
if(btn.dataset.tab === "login") {
loginForm.classList.remove("hidden");
signupForm.classList.add("hidden");
} else {
loginForm.classList.add("hidden");
signupForm.classList.remove("hidden");
}
};
});
loginForm.onsubmit = (e) => handleAuth(e, "login");
signupForm.onsubmit = (e) => handleAuth(e, "signup");
async function handleAuth(e, action) {
e.preventDefault();
const email = e.target.querySelector("input[type='email']").value;
const password = e.target.querySelector("input[type='password']").value;
try {
const res = await fetch(`/auth/${action}`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({email, password})
});
const data = await res.json();
if(data.success) location.reload();
else alert(data.error);
} catch(err) { alert("خطأ في الاتصال"); }
}
logoutBtn.onclick = async () => {
await fetch("/logout");
location.reload();
};
// --- Chat & Streaming ---
function appendMessage(text, type, meta = null) {
const div = document.createElement("div");
div.className = `message ${type === 'user' ? 'user-msg' : 'bot-msg'}`;
let metaHtml = meta ? `
` : '';
div.innerHTML = `
${text}
${metaHtml}
`;
chatMessages.appendChild(div);
chatViewport.scrollTop = chatViewport.scrollHeight;
return div;
}
chatForm.onsubmit = async (e) => {
e.preventDefault();
const q = userInput.value.trim();
const tier = document.getElementById("model-tier").value;
if(!q || isStreaming) return;
appendMessage(q, 'user');
userInput.value = "";
isStreaming = true;
const botMsgDiv = document.createElement("div");
botMsgDiv.className = "message bot-msg";
const stepsContainer = document.createElement("div");
stepsContainer.className = "steps-container";
const currentStepEl = document.createElement("div");
currentStepEl.className = "generating-status active-step";
currentStepEl.innerHTML = ` جاري البدء (${tier})...`;
botMsgDiv.appendChild(stepsContainer);
botMsgDiv.appendChild(currentStepEl);
chatMessages.appendChild(botMsgDiv);
let completedSteps = new Set();
try {
const res = await fetch("/chat", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({question: q, tier: tier})
});
if(!res.ok) {
const err = await res.json();
botMsgDiv.innerHTML = `❌ ${err.error}
`;
isStreaming = false; return;
}
const reader = res.body.getReader();
const decoder = new TextDecoder();
while(true) {
const {value, done} = await reader.read();
if(done) break;
const lines = decoder.decode(value).split("\n");
for(const line of lines) {
if(!line.trim()) continue;
const data = JSON.parse(line);
if(data.status === "Generating") {
const stepText = data.step;
const currentText = currentStepEl.querySelector("span").textContent;
if (currentText !== stepText && currentText !== `جاري البدء (${tier})...`) {
if (!completedSteps.has(currentText)) {
const doneStep = document.createElement("div");
doneStep.className = "step-item completed";
doneStep.innerHTML = ` ${currentText}`;
stepsContainer.appendChild(doneStep);
completedSteps.add(currentText);
}
}
currentStepEl.querySelector("span").textContent = stepText;
} else if(data.status === "Complete" || data.response_text) {
currentStepEl.remove();
const bubble = document.createElement("div");
bubble.className = "msg-bubble fade-in";
bubble.innerHTML = data.response_text;
const footer = document.createElement("div");
footer.className = "msg-footer";
footer.innerHTML = `
${data.duration.toFixed(2)}s
${data.token_count}
`;
botMsgDiv.appendChild(bubble);
botMsgDiv.appendChild(footer);
updateGlobalStatus();
}
}
}
} catch(err) {
botMsgDiv.innerHTML = `❌ فشل في الاتصال
`;
} finally {
isStreaming = false;
chatViewport.scrollTop = chatViewport.scrollHeight;
}
};
async function updateGlobalStatus() {
try {
const res = await fetch("/status");
const data = await res.json();
if(data.logged_in) {
authTrigger.classList.add("hidden");
logoutBtn.classList.remove("hidden");
sysPills.classList.remove("hidden");
usageStats.innerHTML = `
${data.user}
${data.tokens}/${data.max}
`;
} else {
authTrigger.classList.remove("hidden");
logoutBtn.classList.add("hidden");
sysPills.classList.add("hidden");
usageStats.innerHTML = `
ضيف
${data.tokens}/${data.max}
`;
}
cpuVal.textContent = data.cpu.toFixed(0);
ramVal.textContent = data.ram.toFixed(0);
totalQEl.textContent = data.total_questions;
} catch(e) {}
}
async function fetchHistory() {
try {
const res = await fetch("/history");
const data = await res.json();
historyList.innerHTML = "";
data.reverse().forEach(item => {
const div = document.createElement("div");
div.className = "history-item";
div.innerHTML = ` ${item.q.substring(0, 30)}...`;
div.onclick = () => {
chatMessages.innerHTML = "";
appendMessage(item.q, 'user');
appendMessage(item.a, 'bot', {duration: 0, token_count: item.tokens || 0});
sidebar.classList.add("sidebar-closed");
};
historyList.appendChild(div);
});
} catch(e) {}
}
setInterval(updateGlobalStatus, 10000);
updateGlobalStatus();
});