synapse-coder-studio / components /chat-interface.js
00Boobs00's picture
import gradio as gr
afe04f9 verified
class CustomChatInterface extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
:host {
display: flex;
flex-direction: column;
height: 100%;
position: relative;
}
.chat-history {
flex: 1;
overflow-y: auto;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.message {
display: flex;
gap: 1rem;
max-width: 85%;
animation: fadeIn 0.3s ease-out;
}
.message.user {
align-self: flex-end;
flex-direction: row-reverse;
}
.message.ai {
align-self: flex-start;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 0.5rem;
flex-shrink: 0;
}
.msg-bubble {
padding: 1rem;
border-radius: 1rem;
font-size: 0.95rem;
line-height: 1.5;
position: relative;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.user .msg-bubble {
background: #6366f1;
color: white;
border-bottom-right-radius: 0.25rem;
}
.ai .msg-bubble {
background: #1e293b;
color: #e2e8f0;
border: 1px solid #334155;
border-bottom-left-radius: 0.25rem;
}
.input-area {
padding: 1.5rem;
background: #0f172a;
border-top: 1px solid #334155;
display: flex;
gap: 1rem;
align-items: flex-end;
}
textarea {
flex: 1;
background: #1e293b;
border: 1px solid #334155;
color: white;
padding: 0.75rem;
border-radius: 0.5rem;
resize: none;
height: 50px;
min-height: 50px;
max-height: 150px;
font-family: inherit;
}
textarea:focus {
outline: none;
border-color: #6366f1;
}
.send-btn {
background: #6366f1;
color: white;
border: none;
width: 50px;
height: 50px;
border-radius: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background 0.2s;
}
.send-btn:hover {
background: #4f46e5;
}
.send-btn:disabled {
background: #334155;
cursor: not-allowed;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.typing-indicator span {
display: inline-block;
width: 6px;
height: 6px;
background-color: #94a3b8;
border-radius: 50%;
animation: typing 0.6s infinite;
margin: 0 2px;
}
@keyframes typing {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-3px); }
}
</style>
<div class="chat-history" id="chat-history">
<!-- Messages will be injected here -->
<div class="message ai">
<img src="http://static.photos/technology/200x200/5" class="avatar" alt="AI">
<div class="msg-bubble">
Hello! I am Synapse, your AI coding assistant. I can help you search the web, analyze documents, or generate code. What's on your mind today?
</div>
</div>
</div>
<div class="input-area">
<textarea id="msg-input" placeholder="Type a message... (Shift+Enter for new line)"></textarea>
<button id="send-btn" class="send-btn">
<i data-feather="send"></i>
</button>
</div>
`;
const chatHistory = this.shadowRoot.getElementById('chat-history');
const msgInput = this.shadowRoot.getElementById('msg-input');
const sendBtn = this.shadowRoot.getElementById('send-btn');
let isProcessing = false;
const addMessage = (role, text) => {
const msgDiv = document.createElement('div');
msgDiv.className = `message ${role}`;
const avatarUrl = role === 'user'
? 'http://static.photos/people/200x200/12'
: 'http://static.photos/technology/200x200/5';
// Simple markdown-like parsing for bold/newlines
const formattedText = text
.replace(/\n/g, '<br>')
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>');
msgDiv.innerHTML = `
<img src="${avatarUrl}" class="avatar" alt="${role}">
<div class="msg-bubble message-content">${formattedText}</div>
`;
chatHistory.appendChild(msgDiv);
chatHistory.scrollTop = chatHistory.scrollHeight;
};
const showTyping = () => {
const typingDiv = document.createElement('div');
typingDiv.className = 'message ai typing-msg';
typingDiv.innerHTML = `
<img src="http://static.photos/technology/200x200/5" class="avatar" alt="AI">
<div class="msg-bubble typing-indicator">
<span></span><span></span><span></span>
</div>
`;
chatHistory.appendChild(typingDiv);
chatHistory.scrollTop = chatHistory.scrollHeight;
return typingDiv;
};
const handleSend = async () => {
const text = msgInput.value.trim();
if (!text || isProcessing) return;
addMessage('user', text);
msgInput.value = '';
msgInput.style.height = '50px'; // Reset height
isProcessing = true;
sendBtn.disabled = true;
// Save to Memory
if(window.MemoryDB) window.MemoryDB.save('user', text);
// Show typing indicator
const typingEl = showTyping();
// Get Response
try {
const response = await window.Agent.respond(text);
typingEl.remove();
addMessage('ai', response);
if(window.MemoryDB) window.MemoryDB.save('assistant', response);
} catch (error) {
typingEl.remove();
addMessage('ai', "Sorry, I encountered an error connecting to the neural net.");
}
isProcessing = false;
sendBtn.disabled = false;
msgInput.focus();
};
sendBtn.addEventListener('click', handleSend);
msgInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSend();
}
});
// Auto-resize textarea
msgInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
});
// Listen for clear memory event
window.appEvents.addEventListener('memory-cleared', () => {
chatHistory.innerHTML = '';
addMessage('ai', 'Session memory cleared. Ready for a new topic.');
});
feather.replace();
}
}
customElements.define('custom-chat-interface', CustomChatInterface);