feat: MetonaAI Desktop 初始项目
- Electron + React + TypeScript 架构 - 三栏布局: Sidebar | ChatPanel | DetailPanel - 9 个内置工具 (文件系统/网络/记忆/命令) - SQLite 持久化 (better-sqlite3) - MUI 暗色/亮色主题系统 - Agent Loop ReAct 状态机引擎 - DeepSeek / Agnes AI / Ollama Provider 适配器 - MCP 协议集成 - 系统托盘 + 全局快捷键 - Tailwind CSS v4 + Tailwind Merge - 修复: Sidebar 缺失 TextField 导入导致黑屏
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* MessageList — 消息列表容器
|
||||
*/
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { useAgentStore } from '@renderer/stores/agent-store';
|
||||
import { MessageItem } from './MessageItem';
|
||||
import { StreamingIndicator } from './StreamingIndicator';
|
||||
|
||||
export function MessageList(): React.JSX.Element {
|
||||
const messages = useAgentStore((s) => s.messages);
|
||||
const isStreaming = useAgentStore((s) => s.isStreaming);
|
||||
const streamingContent = useAgentStore((s) => s.streamingContent);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages, streamingContent]);
|
||||
|
||||
if (messages.length === 0 && !isStreaming) {
|
||||
return (
|
||||
<Box sx={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Box sx={{ textAlign: 'center', animation: 'fadeIn 200ms ease-out' }}>
|
||||
<Box component="img" src="./assets/logo.png" alt="Metona" sx={{ width: 64, height: 64, mx: 'auto', mb: 2, borderRadius: 2 }} />
|
||||
<Typography variant="h6" sx={{ color: 'text.primary', mb: 0.5 }}>MetonaAI Desktop</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>生产级通用 AI Agent 智能体桌面应用</Typography>
|
||||
<Typography variant="caption" sx={{ mt: 2, display: 'block', opacity: 0.6 }}>Agent 就绪 · 选择一个 Provider 开始对话</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ flex: 1, overflowY: 'auto', px: 2, py: 3 }}>
|
||||
<Box sx={{ maxWidth: 768, mx: 'auto', display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{messages.map((msg, i) => (
|
||||
<MessageItem key={msg.id} message={msg} isLast={i === messages.length - 1} isStreaming={isStreaming} streamContent={streamingContent} />
|
||||
))}
|
||||
<StreamingIndicator />
|
||||
<div ref={messagesEndRef} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user