diff --git a/src/ChatItem/demos/Alert.tsx b/src/ChatItem/demos/Alert.tsx index 150ef6f2..4045fc33 100644 --- a/src/ChatItem/demos/Alert.tsx +++ b/src/ChatItem/demos/Alert.tsx @@ -1,5 +1,7 @@ import { ChatItem, ChatItemProps, StroyBook, useControls, useCreateStore } from '@lobehub/ui'; +import { avatar } from './data'; + export default () => { const store = useCreateStore(); const control: ChatItemProps['alert'] | any = useControls( @@ -16,7 +18,7 @@ export default () => { return ( - + ); }; diff --git a/src/ChatItem/demos/data.ts b/src/ChatItem/demos/data.ts new file mode 100644 index 00000000..f7914dd9 --- /dev/null +++ b/src/ChatItem/demos/data.ts @@ -0,0 +1,7 @@ +import { MetaData } from '@/types/meta'; + +export const avatar: MetaData = { + avatar: '😎', + title: 'Advertiser', + backgroundColor: '#E8DA5A', +}; diff --git a/src/ChatItem/demos/index.tsx b/src/ChatItem/demos/index.tsx index 04dd2f8b..b5d358e6 100644 --- a/src/ChatItem/demos/index.tsx +++ b/src/ChatItem/demos/index.tsx @@ -1,16 +1,18 @@ import { ChatItem, ChatItemProps, StroyBook, useControls, useCreateStore } from '@lobehub/ui'; +import { avatar } from './data'; + export default () => { const store = useCreateStore(); const control: ChatItemProps | any = useControls( { - avatar: '😅', - name: '', + showTitle: false, message: { value: "要使用 dayjs 的 fromNow 函数,需要先安装 dayjs 库并在代码中引入它。然后,可以使用以下语法来获取当前时间与给定时间之间的相对时间:\n\n```javascript\ndayjs().fromNow();\ndayjs('2021-05-01').fromNow();\n```", rows: true, }, + time: 1686538950084, primary: false, placement: { value: 'left', @@ -28,7 +30,7 @@ export default () => { return ( - + ); }; diff --git a/src/ChatItem/index.tsx b/src/ChatItem/index.tsx index f899d5c1..2078a117 100644 --- a/src/ChatItem/index.tsx +++ b/src/ChatItem/index.tsx @@ -4,6 +4,8 @@ import { memo } from 'react'; import { Avatar, Icon, Markdown } from '@/index'; import type { DivProps } from '@/types'; +import { MetaData } from '@/types/meta'; +import { formatTime } from '@/utils/formatTime'; import { useStyles } from './style'; @@ -11,13 +13,13 @@ const AVATAR_SIZE = 40; export interface ChatItemProps extends DivProps { /** - * @description Weather to show alert and alert config + * @description Whether to show alert and alert config */ alert?: AlertProps; /** - * @description URL of the avatar image + * @description Avatar config */ - avatar?: string; + avatar: MetaData; /** * @description Whether to add spacing between chat items * @default true @@ -32,10 +34,6 @@ export interface ChatItemProps extends DivProps { * @description The message to be displayed */ message?: string; - /** - * @description The name of the chat item - */ - name?: string; /** * @description The placement of the chat item * @default 'left' @@ -46,6 +44,15 @@ export interface ChatItemProps extends DivProps { * @default false */ primary?: boolean; + /** + * @description Whether to show name of the chat item + * @default false + */ + showTitle?: boolean; + /** + * @description Time of chat message + */ + time?: number; /** * @description The type of chat item * @default 'block' @@ -56,7 +63,7 @@ export interface ChatItemProps extends DivProps { const ChatItem = memo( ({ className, - name, + title, primary, borderSpacing = true, loading, @@ -65,13 +72,27 @@ const ChatItem = memo( type = 'block', avatar, alert, + showTitle, + time, ...props }) => { - const { cx, styles } = useStyles({ placement, type, name, primary, avatarSize: AVATAR_SIZE }); + const { cx, styles } = useStyles({ + placement, + type, + title, + primary, + avatarSize: AVATAR_SIZE, + showTitle, + }); return (
- + {loading && (
@@ -79,7 +100,14 @@ const ChatItem = memo( )}
- {name &&
{name}
} +
+ {showTitle ? avatar.title || 'untitled' : null} + {time && ( + + {formatTime(time)} + + )} +
{alert ? ( ) : ( diff --git a/src/ChatItem/style.ts b/src/ChatItem/style.ts index 71b54f3d..6d546c58 100644 --- a/src/ChatItem/style.ts +++ b/src/ChatItem/style.ts @@ -2,18 +2,20 @@ import { createStyles } from 'antd-style'; export const useStyles = createStyles( ( - { cx, css, token }, + { cx, css, token, stylish }, { placement, type, - name, + title, primary, avatarSize, + showTitle, }: { avatarSize: number; - name?: string; placement?: 'left' | 'right'; primary?: boolean; + showTitle?: boolean; + title?: string; type?: 'block' | 'pure'; }, ) => { @@ -29,7 +31,7 @@ export const useStyles = createStyles( `; const pureStylish = css` - padding-top: ${name ? 0 : '6px'}; + padding-top: ${title ? 0 : '6px'}; `; const pureContainerStylish = css` @@ -61,6 +63,16 @@ export const useStyles = createStyles( width: 100%; padding: 12px; + + .chat-item-time { + display: none; + } + + &:hover { + .chat-item-time { + display: inline-block; + } + } `, ), loading: css` @@ -96,6 +108,15 @@ export const useStyles = createStyles( } `, name: css` + position: ${showTitle ? 'relative' : 'absolute'}; + top: ${showTitle ? 'unset' : '-16px'}; + right: ${placement === 'right' ? '0' : 'unset'}; + left: ${placement === 'left' ? '0' : 'unset'}; + + display: flex; + flex-direction: ${placement === 'left' ? 'row' : 'row-reverse'}; + gap: 4px; + margin-bottom: 6px; font-size: 12px; @@ -103,6 +124,21 @@ export const useStyles = createStyles( color: ${token.colorTextDescription}; text-align: ${placement === 'left' ? 'left' : 'right'}; `, + time: cx( + stylish.blur, + css` + display: flex; + align-items: center; + justify-content: center; + + padding: 4px 6px; + + line-height: 1; + + background: ${token.colorFillQuaternary}; + border-radius: ${token.borderRadius}px; + `, + ), message: cx( typeStylish, css` diff --git a/src/ChatList/demos/data.ts b/src/ChatList/demos/data.ts index d07173cd..49f96ba1 100644 --- a/src/ChatList/demos/data.ts +++ b/src/ChatList/demos/data.ts @@ -1,13 +1,28 @@ -import { ChatMessage } from '@/Chat'; +import { ChatMessage } from '@/types/chatMessage'; export const data: ChatMessage[] = [ { + id: '1', + meta: { + avatar: 'https://avatars.githubusercontent.com/u/17870709?v=4', + title: 'CanisMinor', + }, role: 'user', content: 'dayjs 如何使用 fromNow', + createAt: 1686437950084, + updateAt: 1686437950084, }, { + id: '2', + meta: { + avatar: '😎', + title: 'Advertiser', + backgroundColor: '#E8DA5A', + }, role: 'assistant', content: '要使用 dayjs 的 fromNow 函数,需要先安装 dayjs 库并在代码中引入它。然后,可以使用以下语法来获取当前时间与给定时间之间的相对时间:\n\n```javascript\ndayjs().fromNow(); // 获取当前时间的相对时间\ndayjs(\'2021-05-01\').fromNow(); // 获取给定时间的相对时间\n```\n\n第一个示例将返回类似于 "几秒前"、"一分钟前"、"2 天前" 的相对时间字符串,表示当前时间与调用 fromNow 方法时的时间差。第二个示例将返回给定时间与当前时间的相对时间字符串。', + createAt: 1686538950084, + updateAt: 1686538950084, }, ]; diff --git a/src/ChatList/demos/index.tsx b/src/ChatList/demos/index.tsx index fcbee0b7..77678d44 100644 --- a/src/ChatList/demos/index.tsx +++ b/src/ChatList/demos/index.tsx @@ -10,6 +10,7 @@ export default () => { value: 'chat', options: ['doc', 'chat'], }, + showTitle: false, }, { store }, ); diff --git a/src/ChatList/index.tsx b/src/ChatList/index.tsx index 75b6f6bf..55a6daa5 100644 --- a/src/ChatList/index.tsx +++ b/src/ChatList/index.tsx @@ -1,8 +1,8 @@ import { memo } from 'react'; -import { ChatMessage } from '@/Chat'; -import { ChatItem } from '@/index'; +import { ChatItem, ChatItemProps } from '@/index'; import type { DivProps } from '@/types'; +import { ChatMessage } from '@/types/chatMessage'; import { useStyles } from './style'; @@ -11,6 +11,11 @@ export interface ChatListProps extends DivProps { * @description Data of chat messages to be displayed */ data: ChatMessage[]; + /** + * @description Whether to show name of the chat item + * @default false + */ + showTitle?: ChatItemProps['showTitle']; /** * @description Type of chat list * @default 'chat' @@ -18,18 +23,21 @@ export interface ChatListProps extends DivProps { type?: 'docs' | 'chat'; } -const ChatList = memo(({ className, data, type = 'chat', ...props }) => { +const ChatList = memo(({ className, data, type = 'chat', showTitle, ...props }) => { const { cx, styles } = useStyles(); return (
- {data.map((item, index) => ( + {data.map((item) => ( ))} diff --git a/src/types/chatMessage.ts b/src/types/chatMessage.ts new file mode 100644 index 00000000..f21c034e --- /dev/null +++ b/src/types/chatMessage.ts @@ -0,0 +1,41 @@ +import { LLMRoleType } from './llm'; +import { BaseDataModel } from './meta'; + +/** + * 聊天消息错误对象 + */ +export interface ChatMessageError { + /** + * 错误信息 + */ + message: string; + status: number; + type: 'general' | 'llm'; +} + +export interface ChatMessage extends BaseDataModel { + /** + * @title 内容 + * @description 消息内容 + */ + content: string; + + // 扩展字段 + extra?: { + // 翻译 + translate: { + target: string; + to: string; + }; + // 语音 + } & Record; + + parentId?: string; + // 引用 + quotaId?: string; + /** + * 角色 + * @description 消息发送者的角色 + */ + role: LLMRoleType; +} diff --git a/src/types/llm.ts b/src/types/llm.ts new file mode 100644 index 00000000..af643a77 --- /dev/null +++ b/src/types/llm.ts @@ -0,0 +1,47 @@ +/** + * LLM 模型 + */ +export enum LanguageModel { + /** + * GPT 3.5 Turbo + */ + GPT3_5 = 'gpt-3.5-turbo', + /** + * GPT 4 + */ + GPT4 = 'gpt-4', +} + +// 语言模型的设置参数 +export interface LLMParams { + /** + * 控制生成文本中的惩罚系数,用于减少重复性 + */ + frequency_penalty?: number; + /** + * 生成文本的最大长度 + */ + max_tokens?: number; + /** + * 控制生成文本中的惩罚系数,用于减少主题的变化 + */ + presence_penalty?: number; + /** + * 生成文本的随机度量,用于控制文本的创造性和多样性 + * @default 0.8 + */ + temperature: number; + /** + * 控制生成文本中最高概率的单个 token + */ + top_p?: number; +} + +export type LLMRoleType = 'user' | 'system' | 'assistant'; + +export interface LLMMessage { + content: string; + role: LLMRoleType; +} + +export type LLMExample = LLMMessage[]; diff --git a/src/types/meta.ts b/src/types/meta.ts new file mode 100644 index 00000000..4d1e33d9 --- /dev/null +++ b/src/types/meta.ts @@ -0,0 +1,26 @@ +export interface MetaData { + /** + * 角色头像 + * @description 可选参数,如果不传则使用默认头像 + */ + avatar?: string; + /** + * 背景色 + * @description 可选参数,如果不传则使用默认背景色 + */ + backgroundColor?: string; + description?: string; + tag?: string[]; + /** + * 名称 + * @description 可选参数,如果不传则使用默认名称 + */ + title?: string; +} + +export interface BaseDataModel { + createAt?: number; + id: string; + meta: MetaData; + updateAt?: number; +} diff --git a/src/utils/formatTime.ts b/src/utils/formatTime.ts new file mode 100644 index 00000000..3e09bfa9 --- /dev/null +++ b/src/utils/formatTime.ts @@ -0,0 +1,14 @@ +import dayjs from 'dayjs'; + +export const formatTime = (time: number): string => { + const now = dayjs(); + const target = dayjs(time); + + if (target.isSame(now, 'day')) { + return target.format('HH:mm'); + } else if (target.isSame(now, 'year')) { + return target.format('MM-DD HH:mm'); + } else { + return target.format('YYYY-MM-DD HH:mm'); + } +};