diff --git a/RETRIEX_SSE_FINAL_THINK_CLEANUP_FIX_README.md b/RETRIEX_SSE_FINAL_THINK_CLEANUP_FIX_README.md
new file mode 100644
index 0000000..653edc9
--- /dev/null
+++ b/RETRIEX_SSE_FINAL_THINK_CLEANUP_FIX_README.md
@@ -0,0 +1,16 @@
+# RetrieX SSE final think cleanup fix
+
+This patch fixes a frontend finalization issue where the SSE stream could receive
+`event: done` / `[DONE]`, but the last transient `.think` status such as
+"Denke nach..." stayed visible.
+
+The streaming renderer still keeps the latest `.think` block while the answer is
+running. On final `done`, it now removes all transient `.think` blocks and also
+removes the loader CSS class. This is a UI-only fix and does not change retrieval,
+prompting, shop search, scoring, or SSE job logic.
+
+Changed files:
+
+- public/assets/js/base.js
+
+After applying the patch, clear Symfony/browser asset cache as appropriate.
diff --git a/public/assets/js/base.js b/public/assets/js/base.js
index 53aba97..6ce80e6 100644
--- a/public/assets/js/base.js
+++ b/public/assets/js/base.js
@@ -232,11 +232,16 @@ document.addEventListener('DOMContentLoaded', () => {
return false;
}
- function cleanupThinkSpans(container) {
+ function cleanupThinkSpans(container, final = false) {
if (!container) {
return;
}
+ if (final) {
+ removeThinkSpansOnly(container);
+ return;
+ }
+
const thinkSpans = Array.from(container.querySelectorAll('.think'));
if (thinkSpans.length === 0) {
@@ -254,9 +259,9 @@ document.addEventListener('DOMContentLoaded', () => {
removeThinkSpansOnly(container);
}
- function renderBubbleContent(bubble, raw) {
+ function renderBubbleContent(bubble, raw, final = false) {
bubble.innerHTML = renderMarkdown(raw);
- cleanupThinkSpans(bubble);
+ cleanupThinkSpans(bubble, final);
enhanceChatLinks(bubble);
scrollChatToBottom();
}
@@ -333,7 +338,8 @@ document.addEventListener('DOMContentLoaded', () => {
function finalizeStream(bubble, raw) {
clearScheduledRender();
- renderBubbleContent(bubble, raw);
+ bubble.classList.remove('loader');
+ renderBubbleContent(bubble, raw, true);
}
async function releaseStreamResources() {
@@ -567,7 +573,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (raw.trim() !== '') {
const formattedMessage = `${userMessage}`;
raw += raw.trim() === '' ? formattedMessage : `\n\n${formattedMessage}`;
- renderBubbleContent(bubble, raw);
+ renderBubbleContent(bubble, raw, true);
} else {
bubble.innerHTML = `${userMessage}`;
enhanceChatLinks(bubble);