AskRoss-Chatbot / static /chatbot.js
Shakeel401's picture
Update static/chatbot.js
0b974ec verified
const launcher = document.getElementById('askross-launcher');
const openChat = document.getElementById('open-chat');
const closeChat = document.getElementById('close-chat');
const chatbotContainer = document.getElementById('chatbot-container');
const chatForm = document.getElementById('chat-form');
const userInput = document.getElementById('user-input');
const chatMessages = document.getElementById('chat-messages');
const typingIndicator = document.getElementById('typing-indicator');
const scrollBottom = document.getElementById('scroll-bottom');
let thread_id = localStorage.getItem('askross_thread_id');
if (!thread_id) {
thread_id = crypto.randomUUID();
localStorage.setItem('askross_thread_id', thread_id);
}
const pageContext = {
page_id: window.location.pathname === '/' ? 'home' : 'page',
page_title: document.title || 'AskRoss',
};
let isFirstMessage = true;
const homepageQuestions = [
"How can I get a mortgage if the bank said no?",
"What mortgage services do you offer?",
"Can I refinance my home?",
"How do I improve my credit score?"
];
// ✅ Welcome + Suggestions (clean spacing)
function showSuggestions() {
const wrapper = document.createElement('div');
wrapper.className = 'suggestions-wrapper';
const welcome = document.createElement('div');
welcome.className = 'welcome-message';
welcome.innerHTML = `<div class="bubble"><strong>Hi, I’m AskRoss 👋</strong><br>Simple mortgage advice — even if the bank said no.</div>`;
const divider = document.createElement('div');
divider.className = 'suggestion-divider';
divider.textContent = 'Popular questions';
wrapper.appendChild(welcome);
wrapper.appendChild(divider);
homepageQuestions.forEach(q => {
const btn = document.createElement('button');
btn.className = 'suggestion-btn';
btn.textContent = q;
btn.onclick = () => sendMessage(q);
wrapper.appendChild(btn);
});
chatMessages.appendChild(wrapper);
}
// OPEN
openChat.addEventListener('click', () => {
chatbotContainer.classList.add('open');
launcher.style.display = 'none';
if (chatMessages.children.length === 0) {
showSuggestions();
}
});
// CLOSE
closeChat.addEventListener('click', () => {
chatbotContainer.classList.remove('open');
launcher.style.display = 'flex';
});
// Initialize closed by default
chatbotContainer.classList.remove('open');
launcher.style.display = 'flex';
// SEND
async function sendMessage(message) {
if (!message) return;
if (isFirstMessage) {
chatMessages.innerHTML = '';
isFirstMessage = false;
}
appendMessage('user', message);
typingIndicator.classList.remove('hidden');
scrollToBottom();
try {
const response = await fetch('/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
message,
thread_id,
page_id: pageContext.page_id,
page_title: pageContext.page_title
})
});
const data = await response.json();
typingIndicator.classList.add('hidden');
if (data.message) {
appendMessage('bot', data.message);
} else {
appendMessage('bot', "Sorry, something went wrong");
}
updateScrollButton();
scrollToBotMessage();
} catch {
typingIndicator.classList.add('hidden');
appendMessage('bot', "Network error. Please try again.");
updateScrollButton();
scrollToBottom();
}
updateScrollButton();
}
function updateScrollButton() {
const nearBottom = chatMessages.scrollHeight - chatMessages.scrollTop - chatMessages.clientHeight < 80;
scrollBottom.classList.toggle('hidden', nearBottom);
}
chatMessages.addEventListener('scroll', updateScrollButton);
scrollBottom.addEventListener('click', () => {
scrollToBottom();
updateScrollButton();
});
chatForm.addEventListener('submit', (e) => {
e.preventDefault();
const message = userInput.value.trim();
if (!message) return;
userInput.value = '';
sendMessage(message);
});
// Markdown
function escapeHtml(text) {
return text
.replace(/&/g,'&amp;')
.replace(/</g,'&lt;')
.replace(/>/g,'&gt;');
}
function normalizeMarkdownLinks(md) {
let normalized = md;
normalized = normalized.replace(/<a\s+href="(https?:\/\/[^\s"<>]+)"[^>]*>([^<]+)<\/a>/gi, '[$2]($1)');
normalized = normalized.replace(/(https?:\/\/[^\s"<>]+)(?:"\s*target="[^"]*"\s*rel="[^"]*"\s*>|\s*target="[^"]*"\s*rel="[^"]*"\s*>|>)([^\n<>]+)/gi, '[$2]($1)');
return normalized;
}
function markdownToHtml(md) {
const normalized = normalizeMarkdownLinks(md);
const html = marked.parse(normalized);
return DOMPurify.sanitize(html, {ADD_ATTR: ['target', 'rel']});
}
// ✅ Messages (NO avatars)
function appendMessage(sender, text) {
const wrapper = document.createElement('div');
wrapper.className = `message ${sender}`;
const bubble = document.createElement('div');
bubble.className = 'bubble';
if (sender === 'bot') {
bubble.innerHTML = markdownToHtml(text);
} else {
bubble.textContent = text;
}
wrapper.appendChild(bubble);
chatMessages.appendChild(wrapper);
}
function scrollToBottom() {
chatMessages.scrollTo({
top: chatMessages.scrollHeight,
behavior: 'smooth'
});
}
function scrollToBotMessage() {
const lastBot = chatMessages.querySelector('.message.bot:last-child');
if (lastBot) {
lastBot.scrollIntoView({ behavior: 'smooth', block: 'start' });
} else {
scrollToBottom();
}
}