| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>CPP Bot</title> |
| <link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}"> |
| <link rel="alternate icon" href="{{ url_for('static', filename='favicon.ico') }}"> |
| <link rel="apple-touch-icon" href="{{ url_for('static', filename='favicon.png') }}"> |
|
|
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet"> |
|
|
| <style> |
| * { box-sizing: border-box; font-family: "Inter", sans-serif; } |
| |
| body { |
| margin: 0; |
| height: 100vh; |
| background: radial-gradient(circle at top, #0f172a, #020617); |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| overflow: hidden; |
| color: #e5e7eb; |
| } |
| |
| .card { |
| width: 420px; |
| height: 620px; |
| background: rgba(2, 6, 23, 0.88); |
| border-radius: 20px; |
| box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6); |
| display: flex; |
| flex-direction: column; |
| overflow: hidden; |
| border: 1px solid rgba(148, 163, 184, 0.15); |
| backdrop-filter: blur(12px); |
| position: relative; |
| } |
| |
| |
| .header { |
| padding: 16px 20px; |
| background: linear-gradient(135deg, #38bdf8, #818cf8); |
| color: #020617; |
| font-weight: 800; |
| font-size: 1.4rem; |
| letter-spacing: 0.5px; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .subtitle { |
| font-size: 0.75rem; |
| font-weight: 500; |
| opacity: 0.8; |
| margin-top: 2px; |
| } |
| |
| .clear-btn { |
| background: none; |
| border: none; |
| font-size: 1.3rem; |
| cursor: pointer; |
| color: #020617; |
| } |
| |
| |
| #messages { |
| flex: 1; |
| padding: 20px; |
| overflow-y: auto; |
| display: flex; |
| flex-direction: column; |
| gap: 12px; |
| } |
| |
| |
| .bubble { |
| max-width: 80%; |
| padding: 12px 14px; |
| border-radius: 14px; |
| line-height: 1.4; |
| animation: fadeIn 0.3s ease; |
| } |
| |
| .user { |
| align-self: flex-end; |
| background: linear-gradient(135deg, #38bdf8, #60a5fa); |
| color: #020617; |
| border-bottom-right-radius: 4px; |
| } |
| |
| .bot { |
| align-self: flex-start; |
| background: #0f172a; |
| color: #a7f3d0; |
| border: 1px solid rgba(148, 163, 184, 0.15); |
| border-bottom-left-radius: 4px; |
| } |
| |
| |
| .input-bar { |
| display: flex; |
| padding: 14px; |
| border-top: 1px solid rgba(148, 163, 184, 0.15); |
| background: rgba(2, 6, 23, 0.95); |
| } |
| |
| .input-bar input { |
| flex: 1; |
| padding: 10px 12px; |
| border-radius: 10px; |
| border: none; |
| outline: none; |
| background: #020617; |
| color: white; |
| font-size: 0.95rem; |
| } |
| |
| .input-bar button { |
| margin-left: 10px; |
| padding: 10px 16px; |
| border-radius: 10px; |
| border: none; |
| cursor: pointer; |
| background: linear-gradient(135deg, #38bdf8, #818cf8); |
| color: #020617; |
| font-weight: 600; |
| transition: transform 0.2s ease, box-shadow 0.2s ease; |
| } |
| |
| .input-bar button:hover { |
| transform: translateY(-2px); |
| box-shadow: 0 6px 20px rgba(56, 189, 248, 0.5); |
| } |
| |
| |
| .footer { |
| padding: 10px 14px; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| border-top: 1px solid rgba(148, 163, 184, 0.15); |
| background: rgba(2, 6, 23, 0.95); |
| font-size: 0.75rem; |
| } |
| |
| .footer button { |
| background: none; |
| border: none; |
| color: #93c5fd; |
| cursor: pointer; |
| margin-right: 10px; |
| } |
| |
| .footer button:hover { text-decoration: underline; } |
| |
| |
| .coffee { |
| position: fixed; |
| bottom: 20px; |
| right: 20px; |
| background: #facc15; |
| color: #020617; |
| padding: 12px 16px; |
| border-radius: 30px; |
| font-weight: 700; |
| text-decoration: none; |
| box-shadow: 0 10px 30px rgba(250, 204, 21, 0.5); |
| transition: transform 0.2s ease; |
| z-index: 999; |
| } |
| |
| .coffee:hover { transform: translateY(-3px) scale(1.05); } |
| |
| |
| .modal { |
| position: fixed; |
| inset: 0; |
| background: rgba(0, 0, 0, 0.6); |
| display: none; |
| justify-content: center; |
| align-items: center; |
| z-index: 1000; |
| } |
| |
| .modal-content { |
| width: 360px; |
| background: #020617; |
| padding: 20px; |
| border-radius: 14px; |
| border: 1px solid rgba(148, 163, 184, 0.2); |
| position: relative; |
| } |
| |
| .close-modal { |
| position: absolute; |
| top: 10px; |
| right: 12px; |
| background: none; |
| border: none; |
| font-size: 1.2rem; |
| cursor: pointer; |
| color: #93c5fd; |
| } |
| |
| .modal-content h3 { margin-top: 0; color: #38bdf8; } |
| |
| .modal-content textarea, |
| .modal-content input { |
| width: 100%; |
| margin-top: 10px; |
| padding: 8px; |
| border-radius: 6px; |
| border: none; |
| outline: none; |
| background: #0f172a; |
| color: white; |
| } |
| |
| .modal-content button.submit-btn { |
| margin-top: 12px; |
| padding: 8px 14px; |
| border-radius: 8px; |
| border: none; |
| cursor: pointer; |
| background: linear-gradient(135deg, #38bdf8, #818cf8); |
| color: #020617; |
| font-weight: 600; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(4px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <div class="card"> |
|
|
| <div class="header"> |
| <div> |
| CppSensei |
| <div class="subtitle">Domain‑Trained C++ AI Assistant</div> |
| </div> |
| <button class="clear-btn" onclick="clearChat()" title="Clear chat">🗑️</button> |
| </div> |
|
|
| <div id="messages"></div> |
|
|
| <div class="input-bar"> |
| <input id="question" placeholder="Ask a C++ question…" /> |
| <button onclick="sendQuestion()">Send</button> |
| </div> |
|
|
| <div class="footer"> |
| <div> |
| <button onclick="openAbout()">About</button> |
| <button onclick="openFeedback()">Feedback</button> |
| </div> |
| <div>© 2026 C++ Bot</div> |
| </div> |
|
|
| </div> |
|
|
| |
| <a class="coffee" href="https://buymeacoffee.com/siddharthmishra?status=1" target="_blank">☕ Buy me a coffee</a> |
|
|
| |
| <div class="modal" id="aboutModal" onclick="backdropClose(event, 'aboutModal')"> |
| <div class="modal-content"> |
| <button class="close-modal" onclick="closeAbout()">✖</button> |
| <h3>About C++ Bot</h3> |
| <p>This is a domain‑trained AI assistant built using Retrieval Augmented Generation with FAISS, Transformers, and Flask. It answers only from a curated C++ knowledge base.</p> |
| </div> |
| </div> |
|
|
| |
| <div class="modal" id="feedbackModal" onclick="backdropClose(event, 'feedbackModal')"> |
| <div class="modal-content"> |
| <button class="close-modal" onclick="closeFeedback()">✖</button> |
| <h3>Send Feedback</h3> |
| <input id="email" placeholder="Your email (optional)" /> |
| <textarea id="feedback" rows="5" placeholder="Your suggestion or feedback..."></textarea> |
| <button class="submit-btn" onclick="submitFeedback()">Submit</button> |
| </div> |
| </div> |
|
|
| <script> |
| const messages = document.getElementById("messages"); |
| const input = document.getElementById("question"); |
| |
| input.addEventListener("keydown", function (e) { |
| if (e.key === "Enter") sendQuestion(); |
| }); |
| |
| document.addEventListener("keydown", function (e) { |
| if (e.key === "Escape") { |
| closeAbout(); |
| closeFeedback(); |
| } |
| }); |
| |
| function addMessage(text, type) { |
| const div = document.createElement("div"); |
| div.className = `bubble ${type}`; |
| div.textContent = text; |
| messages.appendChild(div); |
| messages.scrollTop = messages.scrollHeight; |
| } |
| |
| function clearChat() { |
| if (confirm("Clear the chat?")) { |
| messages.innerHTML = ""; |
| } |
| } |
| |
| async function sendQuestion() { |
| const question = input.value.trim(); |
| if (!question) return; |
| |
| addMessage(question, "user"); |
| input.value = ""; |
| |
| const typing = document.createElement("div"); |
| typing.className = "bubble bot"; |
| typing.textContent = "AI is thinking…"; |
| messages.appendChild(typing); |
| messages.scrollTop = messages.scrollHeight; |
| |
| try { |
| const response = await fetch("/ask", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ question }) |
| }); |
| |
| const data = await response.json(); |
| messages.removeChild(typing); |
| addMessage(data.answer, "bot"); |
| |
| } catch (error) { |
| messages.removeChild(typing); |
| addMessage("Server error. Please try again.", "bot"); |
| } |
| } |
| |
| function openAbout() { document.getElementById("aboutModal").style.display = "flex"; } |
| function closeAbout() { document.getElementById("aboutModal").style.display = "none"; } |
| function openFeedback() { document.getElementById("feedbackModal").style.display = "flex"; } |
| function closeFeedback() { document.getElementById("feedbackModal").style.display = "none"; } |
| |
| function backdropClose(event, modalId) { |
| if (event.target.id === modalId) { |
| document.getElementById(modalId).style.display = "none"; |
| } |
| } |
| |
| async function submitFeedback() { |
| const feedback = document.getElementById("feedback").value; |
| const email = document.getElementById("email").value; |
| |
| if (!feedback) return alert("Please write some feedback first."); |
| |
| await fetch("/feedback", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ feedback, email }) |
| }); |
| |
| alert("Thank you for your feedback! 💙"); |
| document.getElementById("feedback").value = ""; |
| document.getElementById("email").value = ""; |
| closeFeedback(); |
| } |
| </script> |
|
|
| </body> |
| </html> |
|
|