mirror of
https://github.com/langgenius/webapp-conversation.git
synced 2025-12-08 17:32:27 +08:00
feat: add ai thinking anim
This commit is contained in:
@ -11,6 +11,7 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea'
|
||||
import { Markdown } from '@/app/components/base/markdown'
|
||||
import LoadingAnim from './loading-anim'
|
||||
|
||||
export type FeedbackFunc = (messageId: string, feedback: Feedbacktype) => Promise<any>
|
||||
|
||||
@ -166,7 +167,13 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback,
|
||||
return (
|
||||
<div key={id}>
|
||||
<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.answer} relative text-sm text-gray-900`}>
|
||||
<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,
|
||||
<div className='text-xs text-gray-500'>{t('app.chat.openingStatementTitle')}</div>
|
||||
</div>
|
||||
)}
|
||||
{(isResponsing && !content) ? (
|
||||
<div className='flex items-center justify-center w-6 h-5'>
|
||||
<LoadingAnim type='text' />
|
||||
</div>
|
||||
) : (
|
||||
<Markdown content={content} />
|
||||
)}
|
||||
</div>
|
||||
<div className='absolute top-[-14px] right-[-14px] flex flex-row justify-end gap-1'>
|
||||
{!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()}
|
||||
|
||||
16
app/components/chat/loading-anim/index.tsx
Normal file
16
app/components/chat/loading-anim/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
'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)
|
||||
82
app/components/chat/loading-anim/style.module.css
Normal file
82
app/components/chat/loading-anim/style.module.css
Normal file
@ -0,0 +1,82 @@
|
||||
.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;
|
||||
}
|
||||
@ -1,22 +1,23 @@
|
||||
.answerIcon {
|
||||
position: relative;
|
||||
background: url(./icons/robot.svg);
|
||||
}
|
||||
|
||||
.typeingIcon {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.typeingIcon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -3px;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url(./icons/typing.svg) no-repeat;
|
||||
background-size: contain;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
|
||||
.questionIcon {
|
||||
background: url(./icons/default-avatar.jpg);
|
||||
background-size: contain;
|
||||
|
||||
@ -282,7 +282,7 @@ const Main: FC = () => {
|
||||
const placeholderAnswerId = `answer-placeholder-${Date.now()}`
|
||||
const placeholderAnswerItem = {
|
||||
id: placeholderAnswerId,
|
||||
content: '...',
|
||||
content: '',
|
||||
isAnswer: true,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user