|
'use client' |
|
import type { FC } from 'react' |
|
import React, { useState } from 'react' |
|
import { useTranslation } from 'react-i18next' |
|
|
|
import { |
|
RiArrowDownSLine, |
|
RiLoader2Line, |
|
} from '@remixicon/react' |
|
import type { ToolInfoInThought } from '../type' |
|
import Panel from './panel' |
|
import cn from '@/utils/classnames' |
|
import { CheckCircle } from '@/app/components/base/icons/src/vender/solid/general' |
|
import { DataSet as DataSetIcon } from '@/app/components/base/icons/src/public/thought' |
|
import type { Emoji } from '@/app/components/tools/types' |
|
import AppIcon from '@/app/components/base/app-icon' |
|
|
|
type Props = { |
|
payload: ToolInfoInThought |
|
allToolIcons?: Record<string, string | Emoji> |
|
} |
|
|
|
const getIcon = (toolName: string, allToolIcons: Record<string, string | Emoji>) => { |
|
if (toolName.startsWith('dataset_')) |
|
return <DataSetIcon className='shrink-0'></DataSetIcon> |
|
const icon = allToolIcons[toolName] |
|
if (!icon) |
|
return null |
|
return ( |
|
typeof icon === 'string' |
|
? ( |
|
<div |
|
className='w-3 h-3 bg-cover bg-center rounded-[3px] shrink-0' |
|
style={{ |
|
backgroundImage: `url(${icon})`, |
|
}} |
|
></div> |
|
) |
|
: ( |
|
<AppIcon |
|
className='rounded-[3px] shrink-0' |
|
size='xs' |
|
icon={icon?.content} |
|
background={icon?.background} |
|
/> |
|
)) |
|
} |
|
|
|
const Tool: FC<Props> = ({ |
|
payload, |
|
allToolIcons = {}, |
|
}) => { |
|
const { t } = useTranslation() |
|
const { name, label, input, isFinished, output } = payload |
|
const toolName = name.startsWith('dataset_') ? t('dataset.knowledge') : name |
|
const toolLabel = name.startsWith('dataset_') ? t('dataset.knowledge') : label |
|
const [isShowDetail, setIsShowDetail] = useState(false) |
|
const icon = getIcon(name, allToolIcons) as any |
|
return ( |
|
<div> |
|
<div className={cn(!isShowDetail && 'shadow-sm', !isShowDetail && 'inline-block', 'max-w-full overflow-x-auto bg-white rounded-md')}> |
|
<div |
|
className={cn('flex items-center h-7 px-2 cursor-pointer')} |
|
onClick={() => setIsShowDetail(!isShowDetail)} |
|
> |
|
{!isFinished && ( |
|
<RiLoader2Line className='w-3 h-3 text-gray-500 animate-spin shrink-0' /> |
|
)} |
|
{isFinished && !isShowDetail && ( |
|
<CheckCircle className='w-3 h-3 text-[#12B76A] shrink-0' /> |
|
)} |
|
{isFinished && isShowDetail && ( |
|
icon |
|
)} |
|
<span className='mx-1 text-xs font-medium text-gray-500 shrink-0'> |
|
{t(`tools.thought.${isFinished ? 'used' : 'using'}`)} |
|
</span> |
|
<span |
|
className='text-xs font-medium text-gray-700 truncate' |
|
title={toolLabel} |
|
> |
|
{toolLabel} |
|
</span> |
|
<RiArrowDownSLine |
|
className={cn(isShowDetail && 'rotate-180', 'ml-1 w-3 h-3 text-gray-500 select-none cursor-pointer shrink-0')} |
|
/> |
|
</div> |
|
{isShowDetail && ( |
|
<div className='border-t border-black/5 p-2 space-y-2 '> |
|
<Panel |
|
isRequest={true} |
|
toolName={toolName} |
|
content={input} /> |
|
{output && ( |
|
<Panel |
|
isRequest={false} |
|
toolName={toolName} |
|
content={output as string} /> |
|
)} |
|
</div> |
|
)} |
|
</div> |
|
</div> |
|
) |
|
} |
|
export default React.memo(Tool) |
|
|