// Handles key down event to send message on Enter function handleKeyDown(event) { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); sendMessage(); } } // Sends a message to the chat API async function sendMessage() { const messageInput = document.getElementById('messageInput'); let message = messageInput.value.trim(); if (message === '') return; // Encode the message to avoid XSS attacks message = he.encode(message); // Display the user's message in the chat displayMessage(message, 'user'); messageInput.value = ''; // Clear the input toggleLoading(true); // Show loading indicator try { const response = await fetch('https://infer.x64.world/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message }) }); if (response.ok) { const data = await response.json(); displayMessage(data.content, 'assistant'); } else { handleErrorResponse(response.status); } } catch (error) { displayMessage('Error: ' + error.message, 'assistant'); } finally { toggleLoading(false); // Hide loading indicator } } // Toggles the loading indicator function toggleLoading(show) { const loadingElement = document.getElementById('loading'); loadingElement.style.display = show ? 'block' : 'none'; } // Displays a message in the chat window function displayMessage(content, sender) { const messages = document.getElementById('messages'); const messageElement = document.createElement('div'); messageElement.classList.add('message', sender); // Decode HTML entities and render Markdown const decodedContent = he.decode(content); const htmlContent = marked(decodedContent); messageElement.innerHTML = htmlContent; messages.appendChild(messageElement); messages.scrollTop = messages.scrollHeight; // Scroll to the bottom of the chat // Highlight code blocks if any document.querySelectorAll('pre code').forEach((block) => { hljs.highlightElement(block); if (sender === 'assistant') { addCopyButton(block); // Add copy button to code blocks } }); // Add "Copy Full Response" button after each assistant response if (sender === 'assistant') { addCopyFullResponseButton(messages, messageElement); } } // Adds a copy button to a code block function addCopyButton(block) { const button = document.createElement('button'); button.classList.add('copy-button'); button.textContent = 'Copy'; button.addEventListener('click', () => copyToClipboard(block)); block.parentNode.appendChild(button); } // Adds "Copy Full Response" button below the assistant response function addCopyFullResponseButton(messagesContainer, messageElement) { const copyFullResponseButton = document.createElement('button'); copyFullResponseButton.classList.add('copy-button'); copyFullResponseButton.textContent = 'Copy Full Response'; copyFullResponseButton.addEventListener('click', () => copyFullResponse(messageElement)); messagesContainer.appendChild(copyFullResponseButton); } // Copies code block content to the clipboard function copyToClipboard(block) { const text = block.innerText; navigator.clipboard.writeText(text).then(() => { displayAlert('success', 'The code block was copied to the clipboard!'); }).catch((err) => { displayAlert('error', 'Failed to copy code: ' + err); }); } // Copies the full response content to the clipboard in Markdown format function copyFullResponse(messageElement) { const markdownContent = convertToMarkdown(messageElement); navigator.clipboard.writeText(markdownContent).then(() => { displayAlert('success', 'Full response copied to clipboard!'); }).catch((err) => { displayAlert('error', 'Failed to copy response: ' + err); }); } // Converts the HTML content of the response to Markdown, including language identifier function convertToMarkdown(element) { let markdown = ''; const nodes = element.childNodes; nodes.forEach(node => { if (node.nodeName === 'P') { markdown += `${node.innerText}\n\n`; } else if (node.nodeName === 'PRE') { const codeBlock = node.querySelector('code'); const languageClass = codeBlock.className.match(/language-(\w+)/); // Extract language from class if available const language = languageClass ? languageClass[1] : ''; // Default to empty if no language found const codeText = codeBlock.innerText; // Add language identifier to the Markdown code block markdown += `\`\`\`${language}\n${codeText}\n\`\`\`\n\n`; } }); return markdown; } // Displays an alert message with animation function displayAlert(type, message) { const alertElement = document.getElementById(`${type}-alert`); alertElement.textContent = message; alertElement.style.display = 'flex'; // Show the alert alertElement.classList.remove('fade-out'); // Remove fade-out class if present alertElement.style.opacity = '1'; // Ensure it's fully visible // Automatically hide the alert after 3 seconds with animation setTimeout(() => { alertElement.classList.add('fade-out'); // Add fade-out class to trigger animation setTimeout(() => { alertElement.style.display = 'none'; // Hide after animation finishes }, 500); // Match this time to the animation duration }, 3000); // Show the alert for 3 seconds before hiding } // Handles error responses based on status code function handleErrorResponse(status) { if (status === 429) { displayAlert('error', 'Sorry, I am currently too busy at the moment!'); } else { displayMessage('Error: ' + status, 'assistant'); } } // Helper function to reset the conversation on the server async function sendResetRequest() { const response = await fetch('https://infer.x64.world/reset-conversation', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); return response; } // Resets the chat messages and optionally displays a success message async function resetChat(displaySuccessMessage = true) { try { const response = await sendResetRequest(); if (response.ok) { const messagesContainer = document.getElementById('messages'); messagesContainer.innerHTML = ''; // Clear all messages if (displaySuccessMessage) { displayAlert('success', 'Messages Cleared!'); } } else { displayAlert('error', 'Failed to reset conversation. Please try again.'); } } catch (error) { displayAlert('error', 'An error occurred while resetting the conversation.'); } } // Resets the chat on page load without displaying the success message document.addEventListener('DOMContentLoaded', function () { resetChat(false); }); function openWindow(url, windowName, width, height) { window.open(url, windowName, `width=${width},height=${height},menubar=no,toolbar=no,location=no,status=no,scrollbars=yes,resizable=yes`); } function openGpuStats() { openWindow('https://raven-scott.fyi/smi.html', 'gpuStatsWindow', 800, 600); } function openLiveLog() { openWindow('https://llama-live-log.x64.world/', 'liveLogWindow', 600, 600); } function openTop() { openWindow('https://ai-top.x64.world/', 'liveLogGtopWindow', 800, 600); } function openAbout() { openWindow('https://raven-scott.fyi/about-rayai', 'aboutRAIWindow', 800, 650); }