Files
zn-ai/src-react/components/chat/ChatMessageList.tsx
duanshuwen b1dea9a5c2 feat: implement task management store with IPC integration
- Added a new task store in `src-react/stores/task.ts` to manage tasks and their statuses.
- Implemented functions for creating, executing, and retrying tasks, along with handling task progress and completion.
- Introduced persistence for tasks using IPC.
- Created utility functions for normalizing room types and building subtasks.
- Added a new CSS file for global styles in `src-react/styles.css`.
- Created runtime types in `src-react/types/runtime.ts` and exported them.
- Updated the main entry points for Vue and React applications to support dynamic framework loading.
- Refactored chat model interfaces and utility functions into `src/shared/chat-model.ts`.
- Updated TypeScript configuration to include paths for React components and types.
- Enhanced Vite configuration to support both Vue and React frameworks.
2026-04-17 07:09:56 +08:00

93 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useRef } from 'react';
import type { ChatMessageItem } from './types';
type ChatMessageListProps = {
messages: ChatMessageItem[];
loading?: boolean;
};
export default function ChatMessageList({ messages, loading }: ChatMessageListProps) {
const containerRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const container = containerRef.current;
if (!container) return;
container.scrollTop = container.scrollHeight;
}, [messages]);
return (
<div className="flex min-h-0 flex-1 flex-col overflow-hidden px-6 py-6">
<div ref={containerRef} className="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto pr-1">
{loading ? (
<div className="rounded-[18px] border border-dashed border-[#BEDBFF] bg-[#EFF6FF] px-4 py-3 text-sm text-[#525866] dark:border-[#2a2a2d] dark:bg-[#1f1f22] dark:text-gray-400">
...
</div>
) : null}
{!loading && messages.length === 0 ? (
<div className="rounded-[18px] border border-dashed border-[#BEDBFF] bg-[#EFF6FF] px-4 py-6 text-sm leading-7 text-[#525866] dark:border-[#2a2a2d] dark:bg-[#1f1f22] dark:text-gray-400">
</div>
) : null}
{messages.map((message) => (
<article
key={message.id}
className={[
'flex gap-3 rounded-[18px] border p-4',
message.role === 'assistant'
? 'border-[#E5E8EE] bg-[#f8fbff] dark:border-[#2a2a2d] dark:bg-[#1f1f22]'
: 'border-[#dfeaf6] bg-white dark:border-[#2a2a2d] dark:bg-[#232327]',
].join(' ')}
>
<div className="flex h-10 w-10 flex-none items-center justify-center rounded-full bg-[#eff6ff] text-sm font-bold text-[#2B7FFF] dark:bg-[#222225]">
{message.role === 'assistant' ? 'AI' : 'ME'}
</div>
<div className="min-w-0 flex-1">
<div className="flex items-center justify-between gap-3">
<div className="text-sm font-semibold text-[#171717] dark:text-gray-100">{message.name}</div>
<div className="text-xs text-[#99A0AE] dark:text-gray-500">{message.time}</div>
</div>
<p className="mt-2 whitespace-pre-wrap text-sm leading-7 text-[#525866] dark:text-gray-300">{message.content}</p>
{message.attachments && message.attachments.length > 0 ? (
<div className="mt-3 flex flex-wrap gap-2">
{message.attachments.map((attachment, index) => {
const attachmentKey = attachment.filePath || `${attachment.fileName}-${index}`;
const isImage = attachment.mimeType.startsWith('image/') && Boolean(attachment.preview);
if (isImage && attachment.preview) {
return (
<div
key={attachmentKey}
className="overflow-hidden rounded-xl border border-[#E5E8EE] bg-white dark:border-[#2a2a2d] dark:bg-[#232327]"
>
<img
alt={attachment.fileName}
className="h-24 w-24 object-cover"
src={attachment.preview}
/>
</div>
);
}
return (
<div
key={attachmentKey}
className="rounded-full border border-[#E5E8EE] px-3 py-1.5 text-xs text-[#525866] dark:border-[#2a2a2d] dark:text-gray-300"
>
{attachment.fileName}
</div>
);
})}
</div>
) : null}
{message.isStreaming ? (
<div className="mt-3 text-xs text-[#2B7FFF]">...</div>
) : null}
</div>
</article>
))}
</div>
</div>
);
}