diff --git a/dashboard/components/ChatMessages.js b/dashboard/components/ChatMessages.js index 2147418..6a75935 100644 --- a/dashboard/components/ChatMessages.js +++ b/dashboard/components/ChatMessages.js @@ -1,8 +1,9 @@ import { html, useRef, useEffect } from '../lib/preact.js'; -import { getUserMessageBg } from '../utils/status.js'; +import { getUserMessageBg, getStatusMeta } from '../utils/status.js'; import { MessageBubble, filterDisplayMessages } from './MessageBubble.js'; export function ChatMessages({ messages, status }) { + const statusMeta = getStatusMeta(status); const containerRef = useRef(null); const userBgClass = getUserMessageBg(status); const wasAtBottomRef = useRef(true); @@ -56,15 +57,25 @@ export function ChatMessages({ messages, status }) { const displayMessages = filterDisplayMessages(messages).slice(-20); return html` -
- ${displayMessages.map((msg, i) => html` - <${MessageBubble} - key=${i} - msg=${msg} - userBg=${userBgClass} - compact=${true} - /> - `)} +
+
+ ${displayMessages.map((msg, i) => html` + <${MessageBubble} + key=${i} + msg=${msg} + userBg=${userBgClass} + compact=${true} + /> + `)} +
+ ${statusMeta.spinning && html` +
+ + + ... + +
+ `}
`; } diff --git a/dashboard/components/Modal.js b/dashboard/components/Modal.js index a8f8361..5547168 100644 --- a/dashboard/components/Modal.js +++ b/dashboard/components/Modal.js @@ -132,7 +132,7 @@ export function Modal({ session, conversations, conversationLoading, onClose, on

${session.project || session.name || session.session_id}

- + ${status.label}
@@ -178,6 +178,15 @@ export function Modal({ session, conversations, conversationLoading, onClose, on

No conversation messages

`}
+ ${status.spinning && html` +
+ + Agent is working + + ... + +
+ `}
diff --git a/dashboard/components/SessionCard.js b/dashboard/components/SessionCard.js index 40a09d5..69061a2 100644 --- a/dashboard/components/SessionCard.js +++ b/dashboard/components/SessionCard.js @@ -35,7 +35,7 @@ export function SessionCard({ session, onClick, conversation, onFetchConversatio
- + ${session.project || session.name || 'Session'}
diff --git a/dashboard/styles.css b/dashboard/styles.css index 24644b6..5fbac34 100644 --- a/dashboard/styles.css +++ b/dashboard/styles.css @@ -67,6 +67,36 @@ body { animation: pulse-attention 2s ease-in-out infinite; } +/* Active session spinner */ +@keyframes spin-ring { + to { transform: rotate(360deg); } +} + +.spinner-dot { + position: relative; + display: inline-block; +} + +.spinner-dot::after { + content: ''; + position: absolute; + inset: -2px; + border-radius: 50%; + border: 1.5px solid transparent; + border-top-color: currentColor; + animation: spin-ring 0.8s linear infinite; +} + +/* Working indicator at bottom of chat */ +@keyframes bounce-dot { + 0%, 80%, 100% { transform: translateY(0); } + 40% { transform: translateY(-4px); } +} + +.working-dots span:nth-child(1) { animation: bounce-dot 1.2s ease-in-out infinite; } +.working-dots span:nth-child(2) { animation: bounce-dot 1.2s ease-in-out 0.15s infinite; } +.working-dots span:nth-child(3) { animation: bounce-dot 1.2s ease-in-out 0.3s infinite; } + /* Glass panel effect */ .glass-panel { backdrop-filter: blur(10px); diff --git a/dashboard/utils/status.js b/dashboard/utils/status.js index a517bef..1e3d56b 100644 --- a/dashboard/utils/status.js +++ b/dashboard/utils/status.js @@ -22,6 +22,7 @@ export function getStatusMeta(status) { dot: 'bg-active', badge: 'bg-active/18 text-active border-active/40', borderColor: '#5fd0a4', + spinning: true, }; case 'starting': return { @@ -29,6 +30,7 @@ export function getStatusMeta(status) { dot: 'bg-starting', badge: 'bg-starting/18 text-starting border-starting/40', borderColor: '#7cb2ff', + spinning: true, }; case 'done': return {