wip: sse implementation chat

This commit is contained in:
Per Stark
2025-02-24 12:23:58 +01:00
parent e43b63de9f
commit c41e370b81
16 changed files with 361 additions and 63 deletions

View File

@@ -9,9 +9,7 @@
<div class="relative w-full">
{% include "chat/history.html" %}
<div class="fixed w-full mx-auto max-w-3xl left-0 right-0 bottom-0">
{% include "chat/input_field.html" %}
</div>
{% include "chat/new_message_form.html" %}
</div>
</main>
</div>

View File

@@ -16,4 +16,24 @@
</div>
{% endif %}
{% endfor %}
</div>
</div>
<script>
// Scroll to latest message after HTMX swap
document.body.addEventListener('htmx:afterSwap', function (evt) {
const chatContainer = document.getElementById('chat_container');
if (chatContainer) {
chatContainer.scrollTop = chatContainer.scrollHeight;
}
});
</script>
<style>
#chat_container {
max-height: 70vh;
/* Adjust as needed */
overflow-y: auto;
/* Enable scrolling */
padding: 1rem;
}
</style>

View File

@@ -1,22 +0,0 @@
<form hx-post="/chat/{{conversation_id}}" hx-target="#chat_container" hx-swap="afterend" class="relative flex gap-2"
id="chat-form">
<textarea name="content" placeholder="Type your message..." rows="2"
class="textarea rounded-t-2xl rounded-b-none border-2 flex-grow resize-none" id="chat-input"></textarea>
<button type="submit" class="absolute p-2 cursor-pointer right-0.5 btn-ghost btn-sm top-2">{% include
"icons/send_icon.html" %}
</button>
<label for="my-drawer-2" class="absolute cursor-pointer top-10 right-0.5 p-2 drawer-button xl:hidden z-20 ">
{% include "icons/hamburger_icon.html" %}
</label>
</form>
<script>
document.getElementById('chat-input').addEventListener('keydown', function (e) {
// Check if Enter is pressed without Shift
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault(); // Prevent default Enter behavior (new line)
document.getElementById('chat-form').submit(); // Submit the form
}
// Shift + Enter will naturally create a new line due to browser default behavior
});
</script>

View File

@@ -0,0 +1,29 @@
<div class="fixed w-full mx-auto max-w-3xl p-4 pb-0 sm:pb-4 left-0 right-0 bottom-0">
<form hx-post="/chat/{{conversation_id}}" hx-target="#chat_container" hx-swap="beforeend" class="relative flex gap-2"
id="chat-form">
<textarea autofocus required name="content" placeholder="Type your message..." rows="2"
class="textarea textarea-ghost rounded-2xl rounded-b-none h-24 sm:rounded-b-2xl pr-8 bg-base-200 flex-grow resize-none"
id="chat-input"></textarea>
<button type="submit" class="absolute p-2 cursor-pointer right-0.5 btn-ghost btn-sm top-1">{% include
"icons/send_icon.html" %}
</button>
<label for="my-drawer-2" class="absolute cursor-pointer top-9 right-0.5 p-2 drawer-button xl:hidden z-20 ">
{% include "icons/hamburger_icon.html" %}
</label>
</form>
</div>
<script>
document.getElementById('chat-input').addEventListener('keydown', function (e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
htmx.trigger('#chat-form', 'submit');
}
});
// Clear textarea after successful submission
document.getElementById('chat-form').addEventListener('htmx:afterRequest', function (e) {
if (e.detail.successful) { // Check if the request was successful
document.getElementById('chat-input').value = ''; // Clear the textarea
}
});
</script>

View File

@@ -0,0 +1,25 @@
<div class="chat chat-end">
<div class="chat-header">User</div>
<div class="chat-bubble">
{{user_message}}
</div>
</div>
<div class="chat chat-start">
<div class="chat-header">AI</div>
<div class="chat-bubble" hx-ext="sse" sse-connect="/chat/response-stream?query_id={{query_id}}"
sse-swap="chat_message" sse-close="close_stream" hx-swap="beforeend">
<span class="loading loading-dots loading-sm loading-id-{{query_id}}"></span>
</div>
</div>
<script>
document.body.addEventListener('htmx:sseBeforeMessage', (e) => {
const targetElement = e.detail.elt;
const loadingSpinner = targetElement.querySelector('.loading-id-{{query_id}}');
// Hiding the loading spinner before data is swapped in
if (loadingSpinner) {
loadingSpinner.style.display = 'none';
}
}
)
</script>

View File

@@ -10,6 +10,7 @@
<!-- Preload critical assets -->
<link rel="preload" href="/assets/htmx.min.js" as="script">
<link rel="preload" href="/assets/htmx-ext-sse.js" as="script">
<link rel="preload" href="/assets/style.css" as="style">
<!-- Core styles -->
@@ -17,6 +18,7 @@
<!-- Scripts -->
<script src="/assets/htmx.min.js" defer></script>
<script src="/assets/htmx-ext-sse.js" defer></script>
<script src="/assets/theme-toggle.js" defer></script>
<!-- Icons -->

View File

@@ -1,6 +1,7 @@
<div class="flex gap-4 flex-col sm:flex-row">
<a class="btn btn-secondary" href="/knowledge" hx-boost="true">View Knowledge</a>
<a class="btn btn-accent" href="/content" hx-boost="true">View Content</a>
<a class="btn btn-accent" href="/chat" hx-boost="true">Chat</a>
<button class="btn btn-primary" hx-get="/ingress-form" hx-target="#modal" hx-swap="innerHTML">Add
Content</button>
</div>