Commit 7a1b139f authored by Joel's avatar Joel

feat: add ai thinking anim

parent caf5b31c
...@@ -11,6 +11,7 @@ import Tooltip from '@/app/components/base/tooltip' ...@@ -11,6 +11,7 @@ import Tooltip from '@/app/components/base/tooltip'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea' import AutoHeightTextarea from '@/app/components/base/auto-height-textarea'
import { Markdown } from '@/app/components/base/markdown' import { Markdown } from '@/app/components/base/markdown'
import LoadingAnim from './loading-anim'
export type FeedbackFunc = (messageId: string, feedback: Feedbacktype) => Promise<any> export type FeedbackFunc = (messageId: string, feedback: Feedbacktype) => Promise<any>
...@@ -166,7 +167,13 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback, ...@@ -166,7 +167,13 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback,
return ( return (
<div key={id}> <div key={id}>
<div className='flex items-start'> <div className='flex items-start'>
<div className={`${s.answerIcon} ${isResponsing ? s.typeingIcon : ''} w-10 h-10 shrink-0`}></div> <div className={`${s.answerIcon} w-10 h-10 shrink-0`}>
{isResponsing &&
<div className={s.typeingIcon}>
<LoadingAnim type='avatar' />
</div>
}
</div>
<div className={`${s.answerWrap}`}> <div className={`${s.answerWrap}`}>
<div className={`${s.answer} relative text-sm text-gray-900`}> <div className={`${s.answer} relative text-sm text-gray-900`}>
<div className={'ml-2 py-3 px-4 bg-gray-100 rounded-tr-2xl rounded-b-2xl'}> <div className={'ml-2 py-3 px-4 bg-gray-100 rounded-tr-2xl rounded-b-2xl'}>
...@@ -176,7 +183,13 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback, ...@@ -176,7 +183,13 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback,
<div className='text-xs text-gray-500'>{t('app.chat.openingStatementTitle')}</div> <div className='text-xs text-gray-500'>{t('app.chat.openingStatementTitle')}</div>
</div> </div>
)} )}
{(isResponsing && !content) ? (
<div className='flex items-center justify-center w-6 h-5'>
<LoadingAnim type='text' />
</div>
) : (
<Markdown content={content} /> <Markdown content={content} />
)}
</div> </div>
<div className='absolute top-[-14px] right-[-14px] flex flex-row justify-end gap-1'> <div className='absolute top-[-14px] right-[-14px] flex flex-row justify-end gap-1'>
{!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()} {!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()}
......
'use client'
import React, { FC } from 'react'
import s from './style.module.css'
export interface ILoaidingAnimProps {
type: 'text' | 'avatar'
}
const LoaidingAnim: FC<ILoaidingAnimProps> = ({
type
}) => {
return (
<div className={`${s['dot-flashing']} ${s[type]}`}></div>
)
}
export default React.memo(LoaidingAnim)
.dot-flashing {
position: relative;
animation: 1s infinite linear alternate;
animation-delay: 0.5s;
}
.dot-flashing::before,
.dot-flashing::after {
content: "";
display: inline-block;
position: absolute;
top: 0;
animation: 1s infinite linear alternate;
}
.dot-flashing::before {
animation-delay: 0s;
}
.dot-flashing::after {
animation-delay: 1s;
}
@keyframes dot-flashing {
0% {
background-color: #667085;
}
50%,
100% {
background-color: rgba(102, 112, 133, 0.3);
}
}
@keyframes dot-flashing-avatar {
0% {
background-color: #155EEF;
}
50%,
100% {
background-color: rgba(21, 94, 239, 0.3);
}
}
.text,
.text::before,
.text::after {
width: 4px;
height: 4px;
border-radius: 50%;
background-color: #667085;
color: #667085;
animation-name: dot-flashing;
}
.text::before {
left: -7px;
}
.text::after {
left: 7px;
}
.avatar,
.avatar::before,
.avatar::after {
width: 2px;
height: 2px;
border-radius: 50%;
background-color: #155EEF;
color: #155EEF;
animation-name: dot-flashing-avatar;
}
.avatar::before {
left: -5px;
}
.avatar::after {
left: 5px;
}
\ No newline at end of file
.answerIcon { .answerIcon {
position: relative;
background: url(./icons/robot.svg); background: url(./icons/robot.svg);
} }
.typeingIcon { .typeingIcon {
position: relative;
}
.typeingIcon::after {
content: '';
position: absolute; position: absolute;
top: -3px; top: 0px;
left: -3px; left: 0px;
display: flex;
justify-content: center;
align-items: center;
width: 16px; width: 16px;
height: 16px; height: 16px;
background: url(./icons/typing.svg) no-repeat; background: #FFFFFF;
background-size: contain; box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
border-radius: 16px;
} }
.questionIcon { .questionIcon {
background: url(./icons/default-avatar.jpg); background: url(./icons/default-avatar.jpg);
background-size: contain; background-size: contain;
......
...@@ -282,7 +282,7 @@ const Main: FC = () => { ...@@ -282,7 +282,7 @@ const Main: FC = () => {
const placeholderAnswerId = `answer-placeholder-${Date.now()}` const placeholderAnswerId = `answer-placeholder-${Date.now()}`
const placeholderAnswerItem = { const placeholderAnswerItem = {
id: placeholderAnswerId, id: placeholderAnswerId,
content: '...', content: '',
isAnswer: true, isAnswer: true,
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment