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 = `
Hi, I’m AskRoss šŸ‘‹
Simple mortgage advice — even if the bank said no.
`; 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,'&') .replace(//g,'>'); } function normalizeMarkdownLinks(md) { let normalized = md; normalized = normalized.replace(/]+)"[^>]*>([^<]+)<\/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(); } }