新增模型调度功能&增加全员讨论开关
This commit is contained in:
2
package-lock.json
generated
2
package-lock.json
generated
@@ -941,7 +941,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-dialog": {
|
"node_modules/@radix-ui/react-dialog": {
|
||||||
"version": "1.1.6",
|
"version": "1.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz",
|
"resolved": "https://registry.npmmirror.com/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz",
|
||||||
"integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==",
|
"integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/primitive": "1.1.1",
|
"@radix-ui/primitive": "1.1.1",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.3",
|
"@radix-ui/react-scroll-area": "^1.2.3",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
|
"@radix-ui/react-switch": "^1.1.3",
|
||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.1.8",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { Send, Menu, MoreHorizontal, UserPlus, UserMinus, Users2, Users, MoreVertical, Share2, Mic, MicOff } from 'lucide-react';
|
import { Send, Menu, MoreHorizontal, UserPlus, UserMinus, Users2, Users, MoreVertical, Share2, Mic, MicOff, Settings2 } from 'lucide-react';
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
@@ -12,11 +12,11 @@ import {
|
|||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Sheet,
|
||||||
DialogContent,
|
SheetContent,
|
||||||
DialogHeader,
|
SheetHeader,
|
||||||
DialogTitle,
|
SheetTitle,
|
||||||
} from "@/components/ui/dialog"
|
} from "@/components/ui/sheet"
|
||||||
|
|
||||||
import {generateAICharacters} from "@/config/aiCharacters";
|
import {generateAICharacters} from "@/config/aiCharacters";
|
||||||
import { groups } from "@/config/groups";
|
import { groups } from "@/config/groups";
|
||||||
@@ -27,6 +27,7 @@ import remarkMath from 'remark-math'
|
|||||||
import rehypeKatex from 'rehype-katex'
|
import rehypeKatex from 'rehype-katex'
|
||||||
import html2canvas from 'html2canvas';
|
import html2canvas from 'html2canvas';
|
||||||
import { SharePoster } from '@/components/SharePoster';
|
import { SharePoster } from '@/components/SharePoster';
|
||||||
|
import { MembersManagement } from '@/components/MembersManagement';
|
||||||
|
|
||||||
// 使用本地头像数据,避免外部依赖
|
// 使用本地头像数据,避免外部依赖
|
||||||
const getAvatarData = (name: string) => {
|
const getAvatarData = (name: string) => {
|
||||||
@@ -117,9 +118,9 @@ const QuarterAvatar = ({ user, index }: { user: User, index: number }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 动态导入 KaTeX 样式
|
// 修改 KaTeXStyle 组件
|
||||||
const KaTeXStyle = () => (
|
const KaTeXStyle = () => (
|
||||||
<style jsx global>{`
|
<style dangerouslySetInnerHTML={{ __html: `
|
||||||
/* 只在聊天消息内应用 KaTeX 样式 */
|
/* 只在聊天消息内应用 KaTeX 样式 */
|
||||||
.chat-message .katex-html {
|
.chat-message .katex-html {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -141,11 +142,12 @@ const KaTeXStyle = () => (
|
|||||||
|
|
||||||
/* 其他必要的 KaTeX 样式 */
|
/* 其他必要的 KaTeX 样式 */
|
||||||
@import "katex/dist/katex.min.css";
|
@import "katex/dist/katex.min.css";
|
||||||
`}</style>
|
`}} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const ChatUI = () => {
|
const ChatUI = () => {
|
||||||
const [group, setGroup] = useState(groups[1]);
|
const [group, setGroup] = useState(groups[1]);
|
||||||
|
const [isGroupDiscussionMode, setIsGroupDiscussionMode] = useState(false);
|
||||||
const groupAiCharacters = generateAICharacters(group.name).filter(character => group.members.includes(character.id));
|
const groupAiCharacters = generateAICharacters(group.name).filter(character => group.members.includes(character.id));
|
||||||
const [users, setUsers] = useState([
|
const [users, setUsers] = useState([
|
||||||
{ id: 1, name: "我" },
|
{ id: 1, name: "我" },
|
||||||
@@ -215,17 +217,18 @@ const ChatUI = () => {
|
|||||||
name: msg.sender.name
|
name: msg.sender.name
|
||||||
}));
|
}));
|
||||||
let selectedGroupAiCharacters = groupAiCharacters;
|
let selectedGroupAiCharacters = groupAiCharacters;
|
||||||
// 调度器api请求
|
if (!isGroupDiscussionMode) {
|
||||||
const shedulerResponse = await fetch('/api/scheduler', {
|
const shedulerResponse = await fetch('/api/scheduler', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ message: inputMessage, history: messageHistory, availableAIs: groupAiCharacters })
|
body: JSON.stringify({ message: inputMessage, history: messageHistory, availableAIs: groupAiCharacters })
|
||||||
});
|
});
|
||||||
const shedulerData = await shedulerResponse.json();
|
const shedulerData = await shedulerResponse.json();
|
||||||
const selectedAIs = shedulerData.selectedAIs;
|
const selectedAIs = shedulerData.selectedAIs;
|
||||||
selectedGroupAiCharacters = selectedAIs.map(ai => groupAiCharacters.find(c => c.id === ai));
|
selectedGroupAiCharacters = selectedAIs.map(ai => groupAiCharacters.find(c => c.id === ai));
|
||||||
|
}
|
||||||
for (let i = 0; i < selectedGroupAiCharacters.length; i++) {
|
for (let i = 0; i < selectedGroupAiCharacters.length; i++) {
|
||||||
//禁言
|
//禁言
|
||||||
if (mutedUsers.includes(selectedGroupAiCharacters[i].id)) {
|
if (mutedUsers.includes(selectedGroupAiCharacters[i].id)) {
|
||||||
@@ -453,7 +456,7 @@ const ChatUI = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Button variant="ghost" size="icon" onClick={() => setShowMembers(true)}>
|
<Button variant="ghost" size="icon" onClick={() => setShowMembers(true)}>
|
||||||
<Users className="w-5 h-5" />
|
<Settings2 className="w-5 h-5" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -590,78 +593,16 @@ const ChatUI = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Members Management Dialog */}
|
{/* Members Management Dialog */}
|
||||||
<Dialog open={showMembers} onOpenChange={setShowMembers}>
|
<MembersManagement
|
||||||
<DialogContent className="max-w-md">
|
showMembers={showMembers}
|
||||||
<DialogHeader>
|
setShowMembers={setShowMembers}
|
||||||
<DialogTitle>群成员管理</DialogTitle>
|
users={users}
|
||||||
</DialogHeader>
|
mutedUsers={mutedUsers}
|
||||||
<div className="mt-4">
|
handleToggleMute={handleToggleMute}
|
||||||
<div className="flex justify-between items-center mb-4">
|
isGroupDiscussionMode={isGroupDiscussionMode}
|
||||||
<span className="text-sm text-gray-500">当前成员({users.length})</span>
|
onToggleGroupDiscussion={() => setIsGroupDiscussionMode(!isGroupDiscussionMode)}
|
||||||
<Button variant="outline" size="sm">
|
getAvatarData={getAvatarData}
|
||||||
<UserPlus className="w-4 h-4 mr-2" />
|
/>
|
||||||
添加成员
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<ScrollArea className="h-[300px]">
|
|
||||||
<div className="space-y-2">
|
|
||||||
{users.map((user) => (
|
|
||||||
<div key={user.id} className="flex items-center justify-between p-2 hover:bg-gray-100 rounded-lg">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Avatar>
|
|
||||||
{'avatar' in user && user.avatar ? (
|
|
||||||
<AvatarImage src={user.avatar} className="w-10 h-10" />
|
|
||||||
) : (
|
|
||||||
<AvatarFallback style={{ backgroundColor: getAvatarData(user.name).backgroundColor, color: 'white' }}>
|
|
||||||
{user.name[0]}
|
|
||||||
</AvatarFallback>
|
|
||||||
)}
|
|
||||||
</Avatar>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span>{user.name}</span>
|
|
||||||
{mutedUsers.includes(user.id) && (
|
|
||||||
<span className="text-xs text-red-500">已禁言</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{user.name !== "我" && (
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onClick={() => handleToggleMute(user.id)}
|
|
||||||
>
|
|
||||||
{mutedUsers.includes(user.id) ? (
|
|
||||||
<MicOff className="w-4 h-4 text-red-500" />
|
|
||||||
) : (
|
|
||||||
<Mic className="w-4 h-4 text-green-500" />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
{mutedUsers.includes(user.id) ? '取消禁言' : '禁言'}
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
{/*<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onClick={() => handleRemoveUser(user.id)}
|
|
||||||
>
|
|
||||||
<UserMinus className="w-4 h-4 text-red-500" />
|
|
||||||
</Button>*/}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</ScrollArea>
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 添加 SharePoster 组件 */}
|
{/* 添加 SharePoster 组件 */}
|
||||||
|
|||||||
116
src/components/MembersManagement.tsx
Normal file
116
src/components/MembersManagement.tsx
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
|
import { UserPlus, Mic, MicOff } from 'lucide-react';
|
||||||
|
import { type AICharacter } from "@/config/aiCharacters";
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: number | string;
|
||||||
|
name: string;
|
||||||
|
avatar?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MembersManagementProps {
|
||||||
|
showMembers: boolean;
|
||||||
|
setShowMembers: (show: boolean) => void;
|
||||||
|
users: (User | AICharacter)[];
|
||||||
|
mutedUsers: string[];
|
||||||
|
handleToggleMute: (userId: string) => void;
|
||||||
|
getAvatarData: (name: string) => { backgroundColor: string; text: string };
|
||||||
|
isGroupDiscussionMode: boolean;
|
||||||
|
onToggleGroupDiscussion: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MembersManagement = ({
|
||||||
|
showMembers,
|
||||||
|
setShowMembers,
|
||||||
|
users,
|
||||||
|
mutedUsers,
|
||||||
|
handleToggleMute,
|
||||||
|
getAvatarData,
|
||||||
|
isGroupDiscussionMode,
|
||||||
|
onToggleGroupDiscussion
|
||||||
|
}: MembersManagementProps) => {
|
||||||
|
return (
|
||||||
|
<Sheet open={showMembers} onOpenChange={setShowMembers}>
|
||||||
|
<SheetContent side="right" className="w-[300px] sm:w-[400px]">
|
||||||
|
<SheetHeader>
|
||||||
|
<SheetTitle>群聊配置</SheetTitle>
|
||||||
|
</SheetHeader>
|
||||||
|
<div className="mt-4">
|
||||||
|
<div className="mb-6 p-4 bg-gray-50 rounded-lg">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="text-sm">全员讨论模式</div>
|
||||||
|
<div className="text-xs text-gray-500">开启后全员回复讨论</div>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
checked={isGroupDiscussionMode}
|
||||||
|
onCheckedChange={onToggleGroupDiscussion}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<span className="text-sm text-gray-500">当前成员({users.length})</span>
|
||||||
|
<Button variant="outline" size="sm">
|
||||||
|
<UserPlus className="w-4 h-4 mr-2" />
|
||||||
|
添加成员
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<ScrollArea className="h-[calc(100vh-150px)]">
|
||||||
|
<div className="space-y-2 pr-4">
|
||||||
|
{users.map((user) => (
|
||||||
|
<div key={user.id} className="flex items-center justify-between p-2 hover:bg-gray-100 rounded-lg">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Avatar>
|
||||||
|
{'avatar' in user && user.avatar ? (
|
||||||
|
<AvatarImage src={user.avatar} className="w-10 h-10" />
|
||||||
|
) : (
|
||||||
|
<AvatarFallback style={{ backgroundColor: getAvatarData(user.name).backgroundColor, color: 'white' }}>
|
||||||
|
{user.name[0]}
|
||||||
|
</AvatarFallback>
|
||||||
|
)}
|
||||||
|
</Avatar>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>{user.name}</span>
|
||||||
|
{mutedUsers.includes(user.id as string) && (
|
||||||
|
<span className="text-xs text-red-500">已禁言</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{user.name !== "我" && (
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => handleToggleMute(user.id as string)}
|
||||||
|
>
|
||||||
|
{mutedUsers.includes(user.id as string) ? (
|
||||||
|
<MicOff className="w-4 h-4 text-red-500" />
|
||||||
|
) : (
|
||||||
|
<Mic className="w-4 h-4 text-green-500" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
{mutedUsers.includes(user.id as string) ? '取消禁言' : '禁言'}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
);
|
||||||
|
};
|
||||||
140
src/components/ui/sheet.tsx
Normal file
140
src/components/ui/sheet.tsx
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
import { X } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Sheet = SheetPrimitive.Root
|
||||||
|
|
||||||
|
const SheetTrigger = SheetPrimitive.Trigger
|
||||||
|
|
||||||
|
const SheetClose = SheetPrimitive.Close
|
||||||
|
|
||||||
|
const SheetPortal = SheetPrimitive.Portal
|
||||||
|
|
||||||
|
const SheetOverlay = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SheetPrimitive.Overlay
|
||||||
|
className={cn(
|
||||||
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
|
||||||
|
|
||||||
|
const sheetVariants = cva(
|
||||||
|
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
side: {
|
||||||
|
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||||
|
bottom:
|
||||||
|
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||||
|
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||||
|
right:
|
||||||
|
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
side: "right",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
interface SheetContentProps
|
||||||
|
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||||
|
VariantProps<typeof sheetVariants> {}
|
||||||
|
|
||||||
|
const SheetContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||||
|
SheetContentProps
|
||||||
|
>(({ side = "right", className, children, ...props }, ref) => (
|
||||||
|
<SheetPortal>
|
||||||
|
<SheetOverlay />
|
||||||
|
<SheetPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(sheetVariants({ side }), className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
<span className="sr-only">关闭</span>
|
||||||
|
</SheetPrimitive.Close>
|
||||||
|
</SheetPrimitive.Content>
|
||||||
|
</SheetPortal>
|
||||||
|
))
|
||||||
|
SheetContent.displayName = SheetPrimitive.Content.displayName
|
||||||
|
|
||||||
|
const SheetHeader = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col space-y-2 text-center sm:text-left",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
SheetHeader.displayName = "SheetHeader"
|
||||||
|
|
||||||
|
const SheetFooter = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
SheetFooter.displayName = "SheetFooter"
|
||||||
|
|
||||||
|
const SheetTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SheetPrimitive.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-lg font-semibold text-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
SheetTitle.displayName = SheetPrimitive.Title.displayName
|
||||||
|
|
||||||
|
const SheetDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SheetPrimitive.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
SheetDescription.displayName = SheetPrimitive.Description.displayName
|
||||||
|
|
||||||
|
export {
|
||||||
|
Sheet,
|
||||||
|
SheetPortal,
|
||||||
|
SheetOverlay,
|
||||||
|
SheetTrigger,
|
||||||
|
SheetClose,
|
||||||
|
SheetContent,
|
||||||
|
SheetHeader,
|
||||||
|
SheetFooter,
|
||||||
|
SheetTitle,
|
||||||
|
SheetDescription,
|
||||||
|
}
|
||||||
27
src/components/ui/switch.tsx
Normal file
27
src/components/ui/switch.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as SwitchPrimitives from "@radix-ui/react-switch"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Switch = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SwitchPrimitives.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SwitchPrimitives.Root
|
||||||
|
className={cn(
|
||||||
|
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<SwitchPrimitives.Thumb
|
||||||
|
className={cn(
|
||||||
|
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SwitchPrimitives.Root>
|
||||||
|
))
|
||||||
|
Switch.displayName = SwitchPrimitives.Root.displayName
|
||||||
|
|
||||||
|
export { Switch }
|
||||||
@@ -48,8 +48,8 @@ export function shedulerAICharacter(message: string, allTags: string[]): AIChara
|
|||||||
avatar: "",
|
avatar: "",
|
||||||
custom_prompt: `你是一个群聊总结分析专家,你在一个聊天群里,请分析群用户消息和上文群聊内容
|
custom_prompt: `你是一个群聊总结分析专家,你在一个聊天群里,请分析群用户消息和上文群聊内容
|
||||||
1、只能从给定的标签列表中选择最相关的标签,可选标签:${allTags.join(', ')}。
|
1、只能从给定的标签列表中选择最相关的标签,可选标签:${allTags.join(', ')}。
|
||||||
2、请只返回标签列表,用逗号分隔,不要有其他解释。
|
2、请只返回标签列表,用逗号分隔,不要有其他解释, 不要有任何前缀。
|
||||||
3、回复格式示例:标签1,标签2,标签3`
|
3、回复格式示例:文字游戏, 生活助手, 娱乐`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ export function generateAICharacters(groupName: string): AICharacter[] {
|
|||||||
model: modelConfigs[2].model,
|
model: modelConfigs[2].model,
|
||||||
avatar: "/img/yuanbao.png",
|
avatar: "/img/yuanbao.png",
|
||||||
custom_prompt: `你是一个名叫"元宝"的硅基生命体,你当前在一个叫"${groupName}" 的聊天群里`,
|
custom_prompt: `你是一个名叫"元宝"的硅基生命体,你当前在一个叫"${groupName}" 的聊天群里`,
|
||||||
tags: ["微信生态", "新闻搜索", "文字游戏", "生活助手", "娱乐", "信息总结"]
|
tags: ["微信生态", "新闻报道", "文字游戏", "生活助手", "娱乐", "信息总结"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ai5',
|
id: 'ai5',
|
||||||
@@ -122,16 +122,16 @@ export function generateAICharacters(groupName: string): AICharacter[] {
|
|||||||
model: modelConfigs[0].model,
|
model: modelConfigs[0].model,
|
||||||
avatar: "/img/qwen.jpg",
|
avatar: "/img/qwen.jpg",
|
||||||
custom_prompt: `你是一个名叫"千问"的硅基生命体,你当前在一个叫"${groupName}" 的聊天群里`,
|
custom_prompt: `你是一个名叫"千问"的硅基生命体,你当前在一个叫"${groupName}" 的聊天群里`,
|
||||||
tags: ["广告文案","分析数据","文字游戏", "阿里"]
|
tags: ["广告文案","分析数据","文字游戏","信息总结", "阿里"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ai7',
|
id: 'ai7',
|
||||||
name: "DeepSeek",
|
name: "DeepSeek",
|
||||||
personality: "deepseek-v3",
|
personality: "deepseek-r1",
|
||||||
model: modelConfigs[1].model,
|
model: modelConfigs[3].model,
|
||||||
avatar: "/img/ds.svg",
|
avatar: "/img/ds.svg",
|
||||||
custom_prompt: `你是一个名叫"DeepSeek"的硅基生命体,你当前在一个叫"${groupName}" 的聊天群里`,
|
custom_prompt: `你是一个名叫"DeepSeek"的硅基生命体,你当前在一个叫"${groupName}" 的聊天群里`,
|
||||||
tags: ["深度推理", "编程", "文字游戏", "数学"]
|
tags: ["深度推理", "编程", "文字游戏", "数学", "信息总结"]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,3 +126,14 @@
|
|||||||
@keyframes blink {
|
@keyframes blink {
|
||||||
50% { opacity: 0; }
|
50% { opacity: 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user