- 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.
93 lines
4.1 KiB
TypeScript
93 lines
4.1 KiB
TypeScript
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>
|
||
);
|
||
}
|