diff --git a/app/static/public/css/chat.css b/app/static/public/css/chat.css
index c3ec762..6cabe3b 100644
--- a/app/static/public/css/chat.css
+++ b/app/static/public/css/chat.css
@@ -685,16 +685,6 @@ body {
animation-delay: var(--think-spin-delay, 0s);
}
-.think-block[data-thinking="true"] .think-agent-avatar {
- animation: thinkSpinSlow 2.2s linear infinite;
- animation-delay: var(--think-spin-delay, 0s);
-}
-
-.think-block[data-thinking="true"] .think-rollout-title::before {
- animation: thinkSpinSlow 2.2s linear infinite;
- animation-delay: var(--think-spin-delay, 0s);
-}
-
.think-summary::after {
content: "";
width: 6px;
@@ -971,16 +961,25 @@ body {
gap: 8px;
}
-.think-rollout-title::before {
- content: "";
+.think-rollout-avatar {
width: 18px;
height: 18px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ flex: 0 0 18px;
border-radius: 50%;
background: conic-gradient(from 180deg, #f59e0b, #f97316, #22c55e, #0ea5e9, #f59e0b);
border: 1px solid #fff;
box-shadow: 0 2px 8px rgba(17, 17, 17, 0.15);
}
+.think-rollout-label {
+ display: inline-flex;
+ align-items: center;
+ min-width: 0;
+}
+
.think-rollout-body {
margin-top: 8px;
display: flex;
diff --git a/app/static/public/js/chat.js b/app/static/public/js/chat.js
index bef11d7..6434720 100644
--- a/app/static/public/js/chat.js
+++ b/app/static/public/js/chat.js
@@ -38,6 +38,8 @@
let followStreamScroll = true;
let suppressScrollTracking = false;
let userLockedStreamScroll = false;
+ const activeThinkSpinEntries = new Set();
+ let thinkSpinRafId = 0;
const feedbackUrl = 'https://github.com/chenyme/grok2api/issues/new';
const STORAGE_KEY = 'grok2api_chat_sessions';
const SIDEBAR_STATE_KEY = 'grok2api_chat_sidebar_collapsed';
@@ -1168,7 +1170,7 @@
}).join('');
const title = escapeHtml(group.id);
const openAttr = openAllGroups ? ' open' : '';
- return `${title}
${items}
`;
+ return `${title}
${items}
`;
}).join('');
};
@@ -1619,19 +1621,43 @@
if (!entry.thinkingActive) {
block.removeAttribute('data-thinking');
node.style.removeProperty('--think-spin-delay');
- block.querySelectorAll('.think-agent-avatar').forEach((avatar) => {
- avatar.style.removeProperty('--think-spin-delay');
+ activeThinkSpinEntries.delete(entry);
+ block.querySelectorAll('.think-agent-avatar, .think-rollout-avatar').forEach((avatar) => {
+ avatar.style.removeProperty('transform');
});
} else {
block.setAttribute('data-thinking', 'true');
node.style.setProperty('--think-spin-delay', spinOffset);
- block.querySelectorAll('.think-agent-avatar').forEach((avatar) => {
- avatar.style.setProperty('--think-spin-delay', spinOffset);
- });
+ activeThinkSpinEntries.add(entry);
+ ensureThinkSpinLoop();
}
});
}
+ function ensureThinkSpinLoop() {
+ if (thinkSpinRafId) return;
+ const tick = () => {
+ thinkSpinRafId = 0;
+ if (!activeThinkSpinEntries.size) return;
+ const now = Date.now();
+ activeThinkSpinEntries.forEach((entry) => {
+ if (!entry || !entry.contentNode || !entry.thinkingActive || !entry.contentNode.isConnected) {
+ activeThinkSpinEntries.delete(entry);
+ return;
+ }
+ const elapsedMs = Math.max(0, now - (entry.startedAt || now));
+ const angle = ((elapsedMs % 2200) / 2200) * 360;
+ entry.contentNode.querySelectorAll('.think-block[data-thinking="true"] .think-agent-avatar, .think-block[data-thinking="true"] .think-rollout-avatar').forEach((avatar) => {
+ avatar.style.transform = `rotate(${angle}deg)`;
+ });
+ });
+ if (activeThinkSpinEntries.size) {
+ thinkSpinRafId = requestAnimationFrame(tick);
+ }
+ };
+ thinkSpinRafId = requestAnimationFrame(tick);
+ }
+
function normalizeSourceText(value) {
return String(value || '').replace(/\s+/g, ' ').trim();
}