From 39ede2e8ddf335c747158451a96ea51dd18a8fcb Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Tue, 14 Apr 2026 19:43:09 -0300 Subject: [PATCH] fix: improve GPT-oSS thinking indicator with animated dots --- ui/suite/chat/chat.css | 38 +++++++++++++++++++++++++++++--------- ui/suite/chat/chat.html | 15 +++++++++------ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/ui/suite/chat/chat.css b/ui/suite/chat/chat.css index 216451a..405c350 100644 --- a/ui/suite/chat/chat.css +++ b/ui/suite/chat/chat.css @@ -1504,17 +1504,37 @@ form.input-container { /* Thinking indicator - shown while LLM is reasoning */ .thinking-text { - display: inline-flex; - align-items: center; - gap: 8px; - color: var(--text-secondary, #888); - font-style: italic; - animation: thinkingPulse 1.5s ease-in-out infinite; + display: inline-flex; + align-items: center; + gap: 8px; + color: var(--text-secondary, #888); + font-style: italic; } -@keyframes thinkingPulse { - 0%, 100% { opacity: 0.6; } - 50% { opacity: 1; } +.thinking-text::after { + content: ""; + display: inline-flex; + gap: 4px; + width: 24px; + height: 8px; + background-image: radial-gradient(circle, var(--accent, #3b82f6) 2px, transparent 2px), + radial-gradient(circle, var(--accent, #3b82f6) 2px, transparent 2px), + radial-gradient(circle, var(--accent, #3b82f6) 2px, transparent 2px); + background-size: 8px 8px; + background-position: 0 50%, 50% 50%, 100% 50%; + background-repeat: no-repeat; + animation: thinkingDots 1.4s infinite ease-in-out both; +} + +@keyframes thinkingDots { + 0%, 80%, 100% { + opacity: 0.3; + transform: scale(0.8); + } + 40% { + opacity: 1; + transform: scale(1); + } } /* Hide default quick action suggestions - only show bot suggestions */ diff --git a/ui/suite/chat/chat.html b/ui/suite/chat/chat.html index faa2867..b29080c 100644 --- a/ui/suite/chat/chat.html +++ b/ui/suite/chat/chat.html @@ -826,18 +826,21 @@ function processMessage(data) { // Extract ALL thinking content and thinking_clear signals var thinkingContent = []; var hasThinkingClear = false; - - // Match all thinking objects including those with content - var thinkingRegex = /\{"content":"[^"]*","type":"thinking"\}/g; + + // Match all thinking objects - support both formats: + // {"content":"...","type":"thinking"} or {"type":"thinking","content":"..."} + // Also handle concatenated JSON objects without separators + var thinkingRegex = /\{"content":"([^"]*)"\s*,\s*"type":"thinking"\}|\{"type":"thinking"\s*,\s*"content":"([^"]*)"\}/g; var clearRegex = /\{"type":"thinking_clear"\}/g; // Find all thinking signals var match; while ((match = thinkingRegex.exec(content)) !== null) { try { - var obj = JSON.parse(match[0]); - if (obj.content) { - thinkingContent.push(obj.content); + // match[1] is content from first pattern, match[2] from second pattern + var thinkingText = match[1] || match[2]; + if (thinkingText) { + thinkingContent.push(thinkingText); } // Remove from content content = content.replace(match[0], "");