Created
August 21, 2025 18:55
-
-
Save lukemilby/e29114ead04df3e4aa002f39ede1b07a to your computer and use it in GitHub Desktop.
htmx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Chat with AI</title> | |
| <!-- 1. Loading external libraries --> | |
| <!-- htmx for AJAX and WebSocket functionality --> | |
| <script src="https://unpkg.com/htmx.org@1.9.6"></script> | |
| <!-- htmx WebSocket extension --> | |
| <script src="https://unpkg.com/htmx.org/dist/ext/ws.js"></script> | |
| <!-- marked for Markdown parsing --> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <!-- Tailwind CSS for styling --> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <!-- highlight.js for code syntax highlighting --> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github.min.css"> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen flex flex-col"> | |
| <!-- 2. Main container with htmx WebSocket connection --> | |
| <div class="container mx-auto p-4 flex-grow flex flex-col" hx-ext="ws" ws-connect="ws://10.10.1.215:8090/ws"> | |
| <!-- 3. Chat messages container --> | |
| <div id="chat" class="flex-grow overflow-y-auto bg-white rounded-lg shadow-md p-4 space-y-4"></div> | |
| <form ws-send class="flex space-x-2"> | |
| <input type="text" name="text" placeholder="Type a message..." class="flex-grow px-4 py-2 boarder rounded-ld focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <button type="submit" | |
| class="px-4 py-2 bg-blue-500 text-white rounded-ld hover:bg-blue-600 focus:outline-none focue:ring-2 focus:ring-blue-500">Send</button> | |
| </form> | |
| </div> | |
| <!-- 4. Message input form with htmx WebSocket send attribute --> | |
| <script> | |
| // 5. Variables to track current AI message | |
| let currentAIMessage = null; | |
| let aiMessageContent = ''; | |
| // 6. Configure marked library for Markdown parsing | |
| console.log("script is running") | |
| marked.setOptions({ | |
| break:true, | |
| gfm: true, | |
| highlight: function (code, lang) { | |
| const language=hljs.getLanguage(lang) ? lang: 'plaintext'; | |
| return hljs.highlight(code, {language}).value; | |
| } | |
| }); | |
| //https://www.youtube.com/watch?v=bjlVqw7ALls&t=518s | |
| // 7. Function to render Markdown content | |
| function renderMarkdown(content) { | |
| return marked.parse(content); | |
| } | |
| // 8. htmx WebSocket message handler | |
| htmx.on("htmx:wsAfterMessage", (event) => { | |
| console.log("htmx message") | |
| console.log(JSON.parse(event.detail.message).text) | |
| message = JSON.parse(event.detail.message).text | |
| if (message.startsWith("AI: ")) { | |
| if (currentAIMessage) { | |
| const contentDiv = currentAIMessage.querySelector('.markdown-content'); | |
| contentDiv.innerHTML = renderMarkdown(aiMessageContent); | |
| } | |
| currentAIMessage = document.createElement('div'); | |
| currentAIMessage.className = 'message ai bg-gree-100 rounded-lg p-4'; | |
| currentAIMessage.innerHTML = '<strong class="text-green-700">AI:</strong> '; | |
| const contentDiv = document.createElement('div'); | |
| contentDiv.className = 'markdown-content mt-2 text-gray-800'; | |
| currentAIMessage.appendChild(contentDiv); | |
| chat.appendChild(currentAIMessage); | |
| aiMessageContent = message.substring(4); | |
| } else if (currentAIMessage) { | |
| chat.scrollTop = chat.scrollHeight; | |
| aiMessageContent += message; | |
| } | |
| if (currentAIMessage) { | |
| const contentDiv = currentAIMessage.querySelector('.markdown-content'); | |
| contentDiv.innerHTML = renderMarkdown(aiMessageContent); | |
| } | |
| window.scrollTo(0, document.body.scrollHeight); | |
| }); | |
| // Sending message tot he screen | |
| document.querySelector('form').addEventListener('submit', (e)=> { | |
| console.log("sending") | |
| const input = e.target.querySelector('input'); | |
| const div = document.createElement('div'); | |
| div.className = 'message user bg-blue-100 rounded-lg p-4'; | |
| div.innerHTML = '<strong class="text-blue-700">User:</strong> ' + | |
| '<span class="text-gray-800">' + input.value + '</span>'; | |
| document.getElementById('chat').appendChild(div); | |
| if (currentAIMessage) { | |
| const contentDiv = currentAIMessage.querySelector('.markdown-content'); | |
| contentDiv.innerHTML = renderMarkdown(aiMessageContent); | |
| } | |
| currentAIMessage = null; | |
| aiMessageContent = ''; | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment