const chatDiv = document.getElementById("chat"); const promptInput = document.getElementById("prompt"); const sendBtn = document.getElementById("send"); const stopBtn = document.getElementById("stop"); const clearBtn = document.getElementById("clear"); const projectSelect = document.getElementById("project"); const addProjectBtn = document.getElementById("addProject"); const deleteProjectBtn = document.getElementById("deleteProject"); const loginBtn = document.getElementById("loginBtn"); let currentAbortController = null; let googleUserToken = null; // ---------- Google Identity Services ---------- window.onload = function () { if (typeof google !== 'undefined') { google.accounts.id.initialize({ client_id: window.GOOGLE_CLIENT_ID, callback: handleGoogleCredentialResponse }); // Render button if element exists const loginContainer = document.getElementById("login-button-container"); if (loginContainer) { google.accounts.id.renderButton(loginContainer, { theme: "outline", size: "large" }); } } checkLocalAuth(); }; function handleGoogleCredentialResponse(response) { googleUserToken = response.credential; localStorage.setItem("google_token", googleUserToken); updateAuthUI(true); } function checkLocalAuth() { const savedToken = localStorage.getItem("google_token"); if (savedToken) { googleUserToken = savedToken; updateAuthUI(true); } else { updateAuthUI(false); } } loginBtn.addEventListener("click", () => { if (googleUserToken) { // Log out action googleUserToken = null; localStorage.removeItem("google_token"); if (typeof google !== 'undefined') { google.accounts.id.cancel(); } updateAuthUI(false); } else { // Trigger Google Sign-In prompt if (typeof google !== 'undefined') { google.accounts.id.prompt(); } else { alert("Google Identity Services script still loading."); } } }); function updateAuthUI(isAuthenticated) { if (isAuthenticated) { loginBtn.textContent = "Log Out"; refreshProjects(); } else { loginBtn.textContent = "Log In with Google"; chatDiv.innerHTML = '
Please log in to use the coding assistant.
'; } } async function getHeaders(includeContentType = true) { if (!googleUserToken) { throw new Error("User not authenticated with Google."); } const headers = {}; headers["Authorization"] = `Bearer ${googleUserToken}`; if (includeContentType) headers["Content-Type"] = "application/json"; return headers; } // ---------- Project History Loading ---------- async function loadHistory(project) { chatDiv.innerHTML = '
Loading history...
'; try { const res = await fetch(`/history/${encodeURIComponent(project)}`); const data = await res.json(); chatDiv.innerHTML = ""; if (data.history && data.history.length > 0) { data.history.forEach(msg => { if (msg.role === "user") { appendAndScroll(makeUserNode(msg.content)); } else { const node = makeAssistantNode(); node.innerHTML = `Assistant:
${marked.parse(msg.content)}`; appendAndScroll(node); } }); if (typeof hljs !== 'undefined') hljs.highlightAll(); } else { chatDiv.innerHTML = '
New project started. No history found.
'; } } catch (err) { console.error("Error loading history:", err); chatDiv.innerHTML = '
Error loading history for this project.
'; } } projectSelect.addEventListener("change", () => { loadHistory(projectSelect.value); }); function makeUserNode(text) { const node = document.createElement("div"); node.className = "message user"; node.innerHTML = `You:
${marked.parse(text)}`; return node; } function makeAssistantNode() { const node = document.createElement("div"); node.className = "message assistant"; node.innerHTML = `Assistant:
...`; return node; } function appendAndScroll(node) { chatDiv.appendChild(node); chatDiv.scrollTop = chatDiv.scrollHeight; } async function refreshProjects() { if (!googleUserToken) return; const res = await fetch("/projects"); const data = await res.json(); const currentVal = projectSelect.value; projectSelect.innerHTML = ""; data.forEach(p => { const opt = document.createElement("option"); opt.value = p; opt.textContent = p; projectSelect.appendChild(opt); }); if (data.includes(currentVal)) { projectSelect.value = currentVal; } else if (data.length > 0) { projectSelect.value = data[0]; loadHistory(data[0]); } } addProjectBtn.addEventListener("click", async () => { const name = prompt("Project Name:"); if (!name) return; await fetch(`/add_project`, { method: "POST", headers: await getHeaders(), body: JSON.stringify({ project: name }) }); await refreshProjects(); }); deleteProjectBtn.addEventListener("click", async () => { const p = projectSelect.value; if (!p || !confirm(`Delete project ${p}?`)) return; await fetch(`/delete_project`, { method: "POST", headers: await getHeaders(), body: JSON.stringify({ project: p }) }); await refreshProjects(); }); sendBtn.addEventListener("click", async () => { const text = promptInput.value.trim(); if (!text) return; const project = projectSelect.value; const useWeb = document.getElementById("useWeb").checked; appendAndScroll(makeUserNode(text)); promptInput.value = ""; const assistantNode = makeAssistantNode(); appendAndScroll(assistantNode); currentAbortController = new AbortController(); try { let search_results = []; if (useWeb) { assistantNode.innerHTML = `Assistant:
Searching web...`; const sResp = await fetch("/search_web", { method: "POST", headers: await getHeaders(), body: JSON.stringify({ query: text }), signal: currentAbortController.signal }); const sData = await sResp.json(); search_results = sData.results || []; } const res = await fetch("/chat", { method: "POST", headers: await getHeaders(), body: JSON.stringify({ project, message: text, search_results }), signal: currentAbortController.signal }); const data = await res.json(); if (data.response) { assistantNode.innerHTML = `Assistant:
${marked.parse(data.response)}`; if (typeof hljs !== 'undefined') hljs.highlightAll(); } else if (data.error) { assistantNode.textContent = "Error: " + data.error; } } catch (err) { if (err.name === 'AbortError') { assistantNode.innerHTML += "
[Stopped]"; } else { assistantNode.textContent = "Error: " + err.message; } } finally { currentAbortController = null; chatDiv.scrollTop = chatDiv.scrollHeight; } }); clearBtn.addEventListener("click", () => { chatDiv.innerHTML = ""; }); async function uploadFile(project) { const fileInput = document.getElementById('fileInput'); if (!fileInput.files.length) { alert("Please select a file first."); return; } const formData = new FormData(); formData.append("file", fileInput.files[0]); try { const headers = await getHeaders(false); // browser sets multipart boundary const res = await fetch(`/upload_file/${project}`, { method: "POST", headers: headers, body: formData }); const data = await res.json(); if (data.status === "ok") { alert(`Uploaded: ${data.filename}`); fileInput.value = ""; } else { alert(`Failed: ${data.error}`); } } catch (err) { alert("Error uploading file."); } } promptInput.addEventListener("keydown", (e) => { if (e.ctrlKey && e.key === "Enter") sendBtn.click(); });