diff --git a/assets/doubao.png b/assets/doubao.png new file mode 100644 index 0000000..6b80dd2 Binary files /dev/null and b/assets/doubao.png differ diff --git a/assets/doubao_new.png b/assets/doubao_new.png new file mode 100644 index 0000000..d38e2e2 Binary files /dev/null and b/assets/doubao_new.png differ diff --git a/chat_bubble.ico b/chat_bubble.ico new file mode 100644 index 0000000..8ce4212 Binary files /dev/null and b/chat_bubble.ico differ diff --git a/index.html b/index.html index 95d3605..124ec75 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + AI机器人群聊 diff --git a/src/components/ChatUI.tsx b/src/components/ChatUI.tsx index f13d12c..4ab1320 100644 --- a/src/components/ChatUI.tsx +++ b/src/components/ChatUI.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect } from 'react'; -import { Send, Menu, MoreHorizontal, UserPlus, UserMinus, Users2, Users, MoreVertical, MessageCircle } from 'lucide-react'; +import { Send, Menu, MoreHorizontal, UserPlus, UserMinus, Users2, Users, MoreVertical, Mic, MicOff } from 'lucide-react'; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; @@ -56,6 +56,18 @@ const SingleAvatar = ({ user }: { user: User | AICharacter }) => { // 左右分半头像 const HalfAvatar = ({ user, isFirst }: { user: User, isFirst: boolean }) => { + if ('avatar' in user && user.avatar) { + return ( +
+ {user.name} +
+ ); + } const avatarData = getAvatarData(user.name); return (
{ // 四分之一头像 const QuarterAvatar = ({ user, index }: { user: User, index: number }) => { + if ('avatar' in user && user.avatar) { + return ( +
+ {user.name} +
+ ); + } const avatarData = getAvatarData(user.name); return (
{ const accumulatedContentRef = useRef(""); // 用于跟踪完整内容 const messagesEndRef = useRef(null); + // 添加禁言状态 + const [mutedUsers, setMutedUsers] = useState([]); + const abortController = new AbortController(); const handleRemoveUser = (userId: number) => { @@ -121,6 +149,15 @@ const ChatUI = () => { scrollToBottom(); }, [messages]); + // 添加禁言/取消禁言处理函数 + const handleToggleMute = (userId: string) => { + setMutedUsers(prev => + prev.includes(userId) + ? prev.filter(id => id !== userId) + : [...prev, userId] + ); + }; + const handleSendMessage = async () => { if (!inputMessage.trim()) return; @@ -144,12 +181,15 @@ const ChatUI = () => { name: msg.sender.name })); - // 依次请求两个 AI 的回复 for (let i = 0; i < groupAiCharacters.length; i++) { + //禁言 + if (mutedUsers.includes(groupAiCharacters[i].id)) { + continue; + } // 创建当前 AI 角色的消息 const aiMessage = { id: messages.length + 2 + i, - sender: { id: groupAiCharacters[i].id, name: groupAiCharacters[i].name }, + sender: { id: groupAiCharacters[i].id, name: groupAiCharacters[i].name, avatar: groupAiCharacters[i].avatar }, content: "", isAI: true }; @@ -287,19 +327,16 @@ const ChatUI = () => { {/* 左侧群组信息 */}
-
+
{users.length === 1 ? ( - // 单个成员时显示一个大头像 ) : users.length === 2 ? ( - // 两个成员时左右分布
{users.slice(0, 2).map((user, index) => ( ))}
) : users.length === 3 ? ( - // 三个成员时上2下1
{users.slice(0, 2).map((user, index) => ( @@ -311,7 +348,6 @@ const ChatUI = () => {
) : ( - // 四个或更多成员时显示2x2网格
{users.slice(0, 4).map((user, index) => ( @@ -319,7 +355,7 @@ const ChatUI = () => {
)}
-
+

{group.name}

@@ -337,9 +373,13 @@ const ChatUI = () => { - - {avatarData.text} - + {'avatar' in user && user.avatar ? ( + + ) : ( + + {avatarData.text} + + )} @@ -371,9 +411,13 @@ const ChatUI = () => { className={`flex items-start gap-2 ${message.sender.name === "我" ? "justify-end" : ""}`}> {message.sender.name !== "我" && ( + {'avatar' in message.sender && message.sender.avatar ? ( + + ) : ( {message.sender.name[0]} + )} )}
@@ -385,6 +429,8 @@ const ChatUI = () => { className={`prose dark:prose-invert max-w-none ${ message.sender.name === "我" ? "text-white [&_*]:text-white" : "" } + [&_h3]:py-1.5 + [&_h3]:m-0 [&_p]:m-0 [&_pre]:bg-gray-900 [&_pre]:p-2 @@ -419,9 +465,13 @@ const ChatUI = () => {
{message.sender.name === "我" && ( + {'avatar' in message.sender && message.sender.avatar ? ( + + ) : ( {message.sender.name[0]} + )} )}
@@ -475,21 +525,52 @@ const ChatUI = () => {
- - {user.name[0]} - + {'avatar' in user && user.avatar ? ( + + ) : ( + + {user.name[0]} + + )} - {user.name} +
+ {user.name} + {mutedUsers.includes(user.id) && ( + 已禁言 + )} +
- {/*user.name !== "我" && ( - - )*/} + {user.name !== "我" && ( +
+ + + + + + + {mutedUsers.includes(user.id) ? '取消禁言' : '禁言'} + + + + {/**/} +
+ )}
))}
diff --git a/src/config/aiCharacters.ts b/src/config/aiCharacters.ts index 94377b2..dd9e50a 100644 --- a/src/config/aiCharacters.ts +++ b/src/config/aiCharacters.ts @@ -64,7 +64,7 @@ export function generateAICharacters(groupName: string): AICharacter[] { name: "豆包", personality: "doubao", model: "ep-20250217191935-wzj8l", - avatar: "https://lf-flow-web-cdn.doubao.com/obj/flow-doubao/samantha/logo-icon-white-bg.png", + avatar: "/assets/doubao_new.png", custom_prompt: `你是一个名叫"豆包"的硅基生命体,你当前在一个叫"${groupName}" 的聊天群里` }, {