feat(pitch-deck): Waiting-Indicator im Investor Agent Chat
All checks were successful
CI / test-go-consent (push) Successful in 27s
CI / test-bqas (push) Successful in 29s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-python-voice (push) Successful in 31s
All checks were successful
CI / test-go-consent (push) Successful in 27s
CI / test-bqas (push) Successful in 29s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-python-voice (push) Successful in 31s
Drei animierte Punkte (iMessage-Style) erscheinen sofort nach dem Absenden und verschwinden wenn der erste Token eintrifft. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ export default function ChatInterface({ lang }: ChatInterfaceProps) {
|
||||
const [messages, setMessages] = useState<ChatMessage[]>([])
|
||||
const [input, setInput] = useState('')
|
||||
const [isStreaming, setIsStreaming] = useState(false)
|
||||
const [isWaiting, setIsWaiting] = useState(false)
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
@@ -29,6 +30,7 @@ export default function ChatInterface({ lang }: ChatInterfaceProps) {
|
||||
setInput('')
|
||||
setMessages(prev => [...prev, { role: 'user', content: message }])
|
||||
setIsStreaming(true)
|
||||
setIsWaiting(true)
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/chat', {
|
||||
@@ -47,21 +49,28 @@ export default function ChatInterface({ lang }: ChatInterfaceProps) {
|
||||
const decoder = new TextDecoder()
|
||||
let content = ''
|
||||
|
||||
setMessages(prev => [...prev, { role: 'assistant', content: '' }])
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
|
||||
content += decoder.decode(value, { stream: true })
|
||||
setMessages(prev => {
|
||||
const updated = [...prev]
|
||||
updated[updated.length - 1] = { role: 'assistant', content }
|
||||
return updated
|
||||
})
|
||||
const chunk = decoder.decode(value, { stream: true })
|
||||
content += chunk
|
||||
|
||||
if (isWaiting || content.length === chunk.length) {
|
||||
// First chunk arrived — replace waiting indicator with real content
|
||||
setIsWaiting(false)
|
||||
setMessages(prev => [...prev, { role: 'assistant', content }])
|
||||
} else {
|
||||
setMessages(prev => {
|
||||
const updated = [...prev]
|
||||
updated[updated.length - 1] = { role: 'assistant', content }
|
||||
return updated
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Chat error:', err)
|
||||
setIsWaiting(false)
|
||||
setMessages(prev => [
|
||||
...prev,
|
||||
{ role: 'assistant', content: lang === 'de'
|
||||
@@ -71,6 +80,7 @@ export default function ChatInterface({ lang }: ChatInterfaceProps) {
|
||||
])
|
||||
} finally {
|
||||
setIsStreaming(false)
|
||||
setIsWaiting(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +145,33 @@ export default function ChatInterface({ lang }: ChatInterfaceProps) {
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Waiting indicator — shown between send and first token */}
|
||||
<AnimatePresence>
|
||||
{isWaiting && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 8 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -4 }}
|
||||
className="flex gap-3"
|
||||
>
|
||||
<div className="w-8 h-8 rounded-full bg-indigo-500/20 flex items-center justify-center shrink-0">
|
||||
<Bot className="w-4 h-4 text-indigo-400" />
|
||||
</div>
|
||||
<div className="bg-white/[0.06] rounded-2xl px-4 py-3 flex items-center gap-1">
|
||||
{[0, 1, 2].map(i => (
|
||||
<motion.span
|
||||
key={i}
|
||||
className="block w-2 h-2 rounded-full bg-indigo-400/70"
|
||||
animate={{ opacity: [0.3, 1, 0.3], y: [0, -4, 0] }}
|
||||
transition={{ duration: 0.8, repeat: Infinity, delay: i * 0.18 }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user