WebashalarForML's picture
Update static/frontend.html
f389b10 verified
raw
history blame
12.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Code Assistant Agent</title>
<style>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f0f2f5; color: #333; }
.container { max-width: 850px; margin: auto; background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1); }
h1 { color: #1f7cff; text-align: center; margin-bottom: 25px; font-weight: 600; }
#chat-window {
height: 400px;
overflow-y: scroll;
border: 1px solid #ddd;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
background-color: #e9eff4;
}
/* NEW STRUCTURE FOR CHAT BUBBLES */
.message-row {
display: flex;
margin-bottom: 15px;
}
.message {
padding: 10px 15px;
border-radius: 18px;
max-width: 75%;
position: relative;
word-wrap: break-word;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.user {
background-color: #1f7cff; /* Bright blue for user */
color: white;
margin-left: auto; /* Pushes to the right */
border-bottom-right-radius: 4px; /* Sharper corner on user's side */
}
.assistant {
background-color: #ffffff;
border: 1px solid #c9d6de;
color: #333;
margin-right: auto; /* Keeps it on the left */
border-bottom-left-radius: 4px;
text-align: left;
}
#input-container { display: flex; gap: 10px; margin-bottom: 20px; }
#user-input { flex-grow: 1; padding: 12px; border: 1px solid #ccc; border-radius: 6px; }
#send-button { padding: 10px 20px; background-color: #1f7cff; color: white; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.3s; }
#send-button:hover { background-color: #165bb0; }
#state-info { padding: 15px; background-color: #e6f0ff; border-left: 5px solid #1f7cff; border-radius: 4px; font-size: 0.9em; margin-bottom: 25px; display: flex; justify-content: space-between; align-items: center; }
pre {
background-color: #272822;
color: #f8f8f2;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
margin-top: 10px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.9em;
}
.tag-area {
margin-top: 10px;
padding-top: 5px;
border-top: 1px dashed #e0e0e0;
font-size: 0.8em;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.tag-area input[type="text"] {
flex-grow: 1;
padding: 4px;
border: 1px solid #ccc;
border-radius: 4px;
margin-right: 5px;
min-width: 150px;
}
.tag-chip { display: inline-block; background-color: #6c757d; color: white; padding: 3px 10px; border-radius: 16px; font-size: 0.75em; margin-right: 5px; margin-bottom: 5px; }
.bookmark-btn { background-color: #28a745; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; transition: background-color 0.3s; font-size: 0.8em; }
.bookmark-btn:hover { background-color: #1e7e34; }
/* Bookmarked Replies Styling */
h2 { border-bottom: 2px solid #ddd; padding-bottom: 5px; margin-top: 30px; }
#tag-search { padding: 10px; border: 1px solid #ccc; border-radius: 4px; width: 100%; margin-bottom: 15px; }
.tagged-replies-list { list-style: none; padding: 0; }
.tagged-replies-list li { border: 1px solid #f0f0f0; background: #fafafa; padding: 15px; border-radius: 6px; margin-bottom: 10px; }
.tagged-replies-list li p { margin: 0 0 5px 0; }
</style>
</head>
<body>
<div class="container">
<h1>Code Assistant Agent 🤖</h1>
<div id="state-info">
Current Language: <span id="current-language">Python</span>
| Total Bookmarked Replies: <span id="tagged-count">0</span>
</div>
<div id="chat-window">
</div>
<div id="input-container">
<input type="text" id="user-input" placeholder="Ask about code, concepts, or bugs..." onkeypress="if(event.key === 'Enter') sendMessage()">
<button id="send-button" onclick="sendMessage()">Send</button>
</div>
<h2>Bookmarked Replies</h2>
<input type="text" id="tag-search" placeholder="Search by Tag (e.g., Python, Debugging)" oninput="filterReplies()">
<ul id="tagged-replies-display" class="tagged-replies-list">
</ul>
</div>
<script>
// Store the full conversation history (Crucial for context)
let chatHistory = [];
// Initial state structure matching the backend
let assistantState = {
conversationSummary: "",
language: "Python",
taggedReplies: []
};
const apiUrl = '/chat';
const tagUrl = '/tag_reply';
document.addEventListener('DOMContentLoaded', () => {
updateStateInfo();
// Initial greeting (display only, not pushed to chatHistory)
appendMessage({ role: 'assistant', content: "Hello! I'm your Code Assistant. What programming language are you working in today?" });
});
// --- Core Chat Logic ---
async function sendMessage() {
const inputElement = document.getElementById('user-input');
const userMessage = inputElement.value.trim();
if (!userMessage) return;
// 1. Capture and add the user message to history
const userMsgObj = { role: 'user', content: userMessage };
chatHistory.push(userMsgObj);
appendMessage(userMsgObj);
inputElement.value = '';
try {
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
// CRUCIAL FIX: Send the complete chatHistory array
chat_history: chatHistory,
assistant_state: assistantState
})
});
if (!response.ok) {
// Remove the user message from history if the request failed
chatHistory.pop();
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// 2. Update state
assistantState = data.updated_state;
// 3. Capture and add the assistant reply to history
const assistantMsgObj = {
role: 'assistant',
content: data.assistant_reply
};
chatHistory.push(assistantMsgObj); // <-- Add assistant's reply to history
// 4. Display the reply
appendMessage({
role: 'assistant',
content: data.assistant_reply,
suggestedTags: data.suggested_tags
});
updateStateInfo();
} catch (error) {
console.error('Chat failed:', error);
appendMessage({ role: 'assistant', content: `Error: Could not connect to the assistant. (${error.message})` });
}
}
// --- Display and State Management ---
function appendMessage(msg) {
const chatWindow = document.getElementById('chat-window');
const messageRow = document.createElement('div');
messageRow.className = 'message-row';
const msgDiv = document.createElement('div');
msgDiv.className = `message ${msg.role}`;
let content = msg.content;
// Basic Markdown for code blocks in the frontend
content = content.replace(/```(\w+)?\n([\s\S]*?)\n```/g, (match, lang, code) => {
const language = lang || 'code';
return `<pre><code class="language-${language}">${code.trim()}</code></pre>`;
});
msgDiv.innerHTML = content;
messageRow.appendChild(msgDiv);
chatWindow.appendChild(messageRow);
// Add Tagging UI for assistant replies
if (msg.role === 'assistant') {
const tagArea = document.createElement('div');
tagArea.className = 'tag-area';
tagArea.innerHTML = 'Tags: ';
const tagInput = document.createElement('input');
tagInput.type = 'text';
tagInput.placeholder = 'Add your tags (comma-separated)';
tagInput.id = 'tag-input-' + Date.now();
tagInput.style.marginRight = '10px';
tagInput.value = (msg.suggestedTags || []).join(', '); // Pre-populate with suggested tags
const bookmarkBtn = document.createElement('button');
bookmarkBtn.className = 'bookmark-btn';
bookmarkBtn.textContent = 'Save/Bookmark';
bookmarkBtn.onclick = () => saveReply(msg.content, tagInput.value, bookmarkBtn);
tagArea.appendChild(tagInput);
tagArea.appendChild(bookmarkBtn);
msgDiv.appendChild(tagArea);
}
chatWindow.scrollTop = chatWindow.scrollHeight;
}
function updateStateInfo() {
document.getElementById('current-language').textContent = assistantState.language;
document.getElementById('tagged-count').textContent = assistantState.taggedReplies.length;
filterReplies(); // Refresh the bookmark list
}
// --- Bookmarking and Tagging Logic ---
async function saveReply(replyContent, tagsString, buttonElement) {
const tags = tagsString.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0);
if (tags.length === 0) {
alert("Please enter at least one tag.");
return;
}
try {
const response = await fetch(tagUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
reply: replyContent,
tags: tags,
assistant_state: assistantState
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
assistantState = data.updated_state; // Update state with the new tagged reply
alert(data.message);
buttonElement.textContent = 'Saved!';
buttonElement.disabled = true;
updateStateInfo();
} catch (error) {
console.error('Tagging failed:', error);
alert(`Failed to save reply: ${error.message}`);
}
}
function filterReplies() {
const query = document.getElementById('tag-search').value.toLowerCase();
const displayList = document.getElementById('tagged-replies-display');
displayList.innerHTML = '';
const filtered = assistantState.taggedReplies.filter(item => {
if (!query) return true;
return item.tags.some(tag => tag.toLowerCase().includes(query));
});
if (filtered.length === 0 && query) {
displayList.innerHTML = '<li>No bookmarked replies match your search tag.</li>';
return;
}
filtered.forEach(item => {
const li = document.createElement('li');
let tagChips = item.tags.map(tag => `<span class="tag-chip">${tag}</span>`).join('');
// Show a truncated reply and tags
li.innerHTML = `
<p style="font-weight: bold;">${item.reply.substring(0, 150)}...</p>
<p style="font-size: 0.9em;">${tagChips}</p>
`;
displayList.appendChild(li);
});
}
</script>
</body>
</html>