design: neobrutalist_theme into main

This commit is contained in:
Per Stark
2025-09-17 10:00:55 +02:00
parent 62d909bb7e
commit 6ea51095e8
57 changed files with 1791 additions and 951 deletions

View File

@@ -9,94 +9,40 @@
{% block main %}
<div class="flex grow relative justify-center mt-2 sm:mt-4">
<div class="container">
<div class="overflow-auto hide-scrollbar">
<section class="mb-3">
<div class="nb-panel p-3 flex items-center justify-between">
<h1 class="text-xl font-extrabold tracking-tight">Chat</h1>
<div class="text-xs opacity-70">Converse with your knowledge</div>
</div>
</section>
<div id="chat-scroll-container" class="overflow-auto hide-scrollbar">
{% include "chat/history.html" %}
{% include "chat/new_message_form.html" %}
</div>
</div>
</div>
<style>
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.markdown-content p {
margin-bottom: 0.75em;
}
.markdown-content p:last-child {
margin-bottom: 0;
}
.markdown-content ul,
.markdown-content ol {
margin-top: 0.5em;
margin-bottom: 0.75em;
padding-left: 2em;
}
.markdown-content li {
margin-bottom: 0.25em;
}
.markdown-content pre {
background-color: rgba(0, 0, 0, 0.05);
padding: 0.5em;
border-radius: 4px;
overflow-x: auto;
}
.markdown-content code {
background-color: rgba(0, 0, 0, 0.05);
padding: 0.2em 0.4em;
border-radius: 3px;
font-size: 0.9em;
}
.markdown-content {
line-height: 1.5;
word-wrap: break-word;
}
.markdown-content table {
border-collapse: collapse;
margin: 0.75em 0;
width: 100%;
}
.markdown-content th,
.markdown-content td {
border: 1px solid #ddd;
padding: 6px 12px;
text-align: left;
}
.markdown-content blockquote {
border-left: 4px solid #ddd;
padding-left: 10px;
margin: 0.5em 0 0.5em 0.5em;
color: #666;
}
.markdown-content hr {
border: none;
border-top: 1px solid #ddd;
margin: 0.75em 0;
}
</style>
<script>
function scrollChatToBottom() {
const chatContainer = document.getElementById('chat_container');
if (chatContainer) chatContainer.scrollTop = chatContainer.scrollHeight;
requestAnimationFrame(() => {
const mainScroll = document.querySelector('main');
if (mainScroll) mainScroll.scrollTop = mainScroll.scrollHeight;
const chatScroll = document.getElementById('chat-scroll-container');
if (chatScroll) chatScroll.scrollTop = chatScroll.scrollHeight;
const chatContainer = document.getElementById('chat_container');
if (chatContainer) chatContainer.scrollTop = chatContainer.scrollHeight;
window.scrollTo(0, document.body.scrollHeight);
});
}
window.scrollChatToBottom = scrollChatToBottom;
document.addEventListener('DOMContentLoaded', scrollChatToBottom);
document.body.addEventListener('htmx:afterSwap', scrollChatToBottom);
document.body.addEventListener('htmx:afterSettle', scrollChatToBottom);
</script>
{% endblock %}
{% endblock %}

View File

@@ -1,4 +1,4 @@
<div id="chat_container" class="pl-3 overflow-y-auto h-[calc(100vh-170px)] sm:h-[calc(100vh-190px)] hide-scrollbar">
<div id="chat_container" class="px-3 pb-44 space-y-3">
{% for message in history %}
{% if message.role == "AI" %}
<div class="chat chat-start">

View File

@@ -1,11 +1,9 @@
{% include "chat/streaming_response.html" %}
<!-- OOB swap targeting the form element directly -->
<form id="chat-form" hx-post="/chat/{{conversation.id}}" hx-target="#chat_container" hx-swap="beforeend"
class="relative flex gap-2" hx-swap-oob="true">
<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>
class="nb-input h-24 pr-8 pl-2 pt-2 pb-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-1">
{% include "icons/send_icon.html" %}
</button>

View File

@@ -1,13 +1,14 @@
<div class="absolute w-full mx-auto max-w-3xl p-0 pb-0 sm:pb-4 left-0 right-0 bottom-0 z-10">
<form hx-post="{% if conversation %} /chat/{{conversation.id}} {% else %} /chat {% endif %}"
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 focus:outline-none focus:bg-base-200"
id="chat-input"></textarea>
<button type="submit" class="absolute p-2 cursor-pointer right-0.5 btn-ghost btn-sm top-6">{% include
"icons/send_icon.html" %}
</button>
</form>
<div class="fixed bottom-0 left-0 right-0 lg:left-72 z-20">
<div class="mx-auto max-w-3xl px-4 pb-3">
<div class="nb-panel p-2">
<form hx-post="{% if conversation %} /chat/{{conversation.id}} {% else %} /chat {% endif %}"
hx-target="#chat_container" hx-swap="beforeend" class="relative flex gap-2 items-end" id="chat-form">
<textarea autofocus required name="content" placeholder="Type your message…" rows="3"
class="nb-input flex-grow min-h-24 pr-10 pl-2 pt-2 pb-2 resize-none" id="chat-input"></textarea>
<button type="submit" class="nb-btn nb-cta h-10 px-3">{% include "icons/send_icon.html" %}</button>
</form>
</div>
</div>
</div>
<script>
@@ -23,4 +24,4 @@
document.getElementById('chat-input').value = ''; // Clear the textarea
}
});
</script>
</script>

View File

@@ -1,8 +1,8 @@
<div class="relative my-2">
<button id="references-toggle-{{message.id}}"
class="text-xs text-blue-500 hover:text-blue-700 hover:underline focus:outline-none flex items-center"
class="nb-btn btn-xs bg-base-100 hover:bg-base-200 flex items-center"
onclick="toggleReferences('{{message.id}}')">
References
REFERENCES
{% include "icons/chevron_icon.html" %}
</button>
<div id="references-content-{{message.id}}" class="hidden max-w-full mt-1">
@@ -10,7 +10,7 @@
{% for reference in message.references %}
<div class="reference-badge-container" data-reference="{{reference}}" data-message-id="{{message.id}}"
data-index="{{loop.index}}">
<span class="badge badge-xs badge-neutral truncate max-w-[20ch] overflow-hidden text-left block cursor-pointer">
<span class="nb-badge truncate max-w-[20ch] overflow-hidden text-left block cursor-pointer">
{{reference}}
</span>
</div>
@@ -80,7 +80,7 @@
function createTooltip() {
const tooltip = document.createElement('div');
tooltip.id = tooltipId;
tooltip.className = 'fixed z-[9999] bg-neutral-800 text-white p-3 rounded-md shadow-lg text-sm w-72 max-w-xs border border-neutral-700 hidden';
tooltip.className = 'reference-tooltip hidden';
tooltip.innerHTML = '<div class="animate-pulse">Loading...</div>';
document.body.appendChild(tooltip);
return tooltip;
@@ -135,15 +135,3 @@
});
}
</script>
<style>
#references-toggle- {
{
message.id
}
}
svg {
transition: transform 0.2s ease;
}
</style>

View File

@@ -25,7 +25,7 @@
e.preventDefault();
window.markdownBuffer[msgId] = (window.markdownBuffer[msgId] || '') + (e.detail.data || '');
el.innerHTML = marked.parse(window.markdownBuffer[msgId].replace(/\\n/g, '\n'));
if (typeof scrollChatToBottom === "function") scrollChatToBottom();
if (typeof window.scrollChatToBottom === "function") window.scrollChatToBottom();
});
document.body.addEventListener('htmx:sseClose', function () {
const msgId = '{{ user_message.id }}';
@@ -33,7 +33,7 @@
if (el && window.markdownBuffer[msgId]) {
el.innerHTML = marked.parse(window.markdownBuffer[msgId].replace(/\\n/g, '\n'));
delete window.markdownBuffer[msgId];
if (typeof scrollChatToBottom === "function") scrollChatToBottom();
if (typeof window.scrollChatToBottom === "function") window.scrollChatToBottom();
}
});
</script>
</script>