feat(T07): KI-Copilot with OpenRouter chat, voice input, action system, chat history

This commit is contained in:
2026-07-17 02:28:41 +02:00
parent cb89e3ff1e
commit 5cc9f8e9a9
24 changed files with 2878 additions and 188 deletions
@@ -0,0 +1,57 @@
'use client';
import { type ChatMessage } from '@/lib/copilot';
interface MessageListProps {
messages: ChatMessage[];
loading?: boolean;
}
export function MessageList({ messages, loading }: MessageListProps) {
return (
<div
className="flex-1 overflow-y-auto space-y-3 p-2"
data-testid="message-list"
>
{messages.length === 0 && !loading && (
<div className="flex-1 flex items-center justify-center text-gray-400">
<p>Starte eine Konversation mit dem KI-Copilot...</p>
</div>
)}
{messages.map((msg) => (
<div
key={msg.id}
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
data-testid={`message-${msg.role}`}
>
<div
className={`max-w-[80%] rounded-lg px-4 py-2 ${
msg.role === 'user'
? 'bg-blue-600 text-white'
: 'bg-gray-100 text-gray-900'
}`}
>
<p className="whitespace-pre-wrap">{msg.content}</p>
{msg.actions && msg.actions.length > 0 && (
<div className="mt-2 text-sm opacity-75">
<p className="font-semibold">Vorgeschlagene Aktionen:</p>
<ul className="list-disc list-inside">
{msg.actions.map((action, idx) => (
<li key={idx}>{action.type}</li>
))}
</ul>
</div>
)}
</div>
</div>
))}
{loading && (
<div className="flex justify-start" data-testid="loading-indicator">
<div className="bg-gray-100 rounded-lg px-4 py-2 text-gray-500">
<span className="animate-pulse">Copilot denkt nach...</span>
</div>
</div>
)}
</div>
);
}