mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-13 09:49:02 +08:00
Feat: dark mode for logs and annotations (#11575)
This commit is contained in:
parent
0d04cdc323
commit
f96fdc2970
@ -141,7 +141,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||||||
|
|
||||||
if (!appDetail) {
|
if (!appDetail) {
|
||||||
return (
|
return (
|
||||||
<div className='flex h-full items-center justify-center bg-white'>
|
<div className='flex h-full items-center justify-center bg-background-body'>
|
||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -152,7 +152,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||||||
{appDetail && (
|
{appDetail && (
|
||||||
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background} desc={appDetail.mode} navigation={navigation} />
|
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background} desc={appDetail.mode} navigation={navigation} />
|
||||||
)}
|
)}
|
||||||
<div className="bg-white grow overflow-hidden">
|
<div className="bg-components-panel-bg grow overflow-hidden">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Textarea from 'rc-textarea'
|
import Textarea from '@/app/components/base/textarea'
|
||||||
import { Robot, User } from '@/app/components/base/icons/src/public/avatar'
|
import { Robot, User } from '@/app/components/base/icons/src/public/avatar'
|
||||||
|
|
||||||
export enum EditItemType {
|
export enum EditItemType {
|
||||||
@ -31,12 +31,10 @@ const EditItem: FC<Props> = ({
|
|||||||
{avatar}
|
{avatar}
|
||||||
</div>
|
</div>
|
||||||
<div className='grow'>
|
<div className='grow'>
|
||||||
<div className='mb-1 leading-[18px] text-xs font-semibold text-gray-900'>{name}</div>
|
<div className='mb-1 system-xs-semibold text-text-primary'>{name}</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
className='mt-1 block w-full leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none'
|
|
||||||
value={content}
|
value={content}
|
||||||
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => onChange(e.target.value)}
|
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => onChange(e.target.value)}
|
||||||
autoSize={{ minRows: 3 }}
|
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
|
@ -4,6 +4,7 @@ import React, { useState } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type { AnnotationItemBasic } from '../type'
|
import type { AnnotationItemBasic } from '../type'
|
||||||
import EditItem, { EditItemType } from './edit-item'
|
import EditItem, { EditItemType } from './edit-item'
|
||||||
|
import Checkbox from '@/app/components/base/checkbox'
|
||||||
import Drawer from '@/app/components/base/drawer-plus'
|
import Drawer from '@/app/components/base/drawer-plus'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
@ -96,11 +97,11 @@ const AddAnnotationModal: FC<Props> = ({
|
|||||||
<AnnotationFull />
|
<AnnotationFull />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className='px-6 flex h-16 items-center justify-between border-t border-black/5 bg-gray-50 rounded-bl-xl rounded-br-xl leading-[18px] text-[13px] font-medium text-gray-500'>
|
<div className='px-4 flex h-16 items-center justify-between border-t border-divider-subtle bg-background-section-burn rounded-bl-xl rounded-br-xl system-sm-medium text-text-tertiary'>
|
||||||
<div
|
<div
|
||||||
className='flex items-center space-x-2'
|
className='flex items-center space-x-2'
|
||||||
>
|
>
|
||||||
<input type="checkbox" checked={isCreateNext} onChange={() => setIsCreateNext(!isCreateNext)} className="w-4 h-4 rounded border-gray-300 text-blue-700 focus:ring-blue-700" />
|
<Checkbox checked={isCreateNext} onCheck={() => setIsCreateNext(!isCreateNext)} />
|
||||||
<div>{t('appAnnotation.addModal.createNext')}</div>
|
<div>{t('appAnnotation.addModal.createNext')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-2 flex space-x-2'>
|
<div className='mt-2 flex space-x-2'>
|
||||||
|
@ -33,19 +33,19 @@ const CSVDownload: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-6'>
|
<div className='mt-6'>
|
||||||
<div className='text-sm text-gray-900 font-medium'>{t('share.generation.csvStructureTitle')}</div>
|
<div className='system-sm-medium text-text-primary'>{t('share.generation.csvStructureTitle')}</div>
|
||||||
<div className='mt-2 max-h-[500px] overflow-auto'>
|
<div className='mt-2 max-h-[500px] overflow-auto'>
|
||||||
<table className='table-fixed w-full border-separate border-spacing-0 border border-gray-200 rounded-lg text-xs'>
|
<table className='table-fixed w-full border-separate border-spacing-0 border border-divider-regular rounded-lg text-xs'>
|
||||||
<thead className='text-gray-500'>
|
<thead className='text-text-tertiary'>
|
||||||
<tr>
|
<tr>
|
||||||
<td className='h-9 pl-3 pr-2 border-b border-gray-200'>{t('appAnnotation.batchModal.question')}</td>
|
<td className='h-9 pl-3 pr-2 border-b border-divider-regular'>{t('appAnnotation.batchModal.question')}</td>
|
||||||
<td className='h-9 pl-3 pr-2 border-b border-gray-200'>{t('appAnnotation.batchModal.answer')}</td>
|
<td className='h-9 pl-3 pr-2 border-b border-divider-regular'>{t('appAnnotation.batchModal.answer')}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className='text-gray-700'>
|
<tbody className='text-gray-700'>
|
||||||
<tr>
|
<tr>
|
||||||
<td className='h-9 pl-3 pr-2 border-b border-gray-100 text-[13px]'>{t('appAnnotation.batchModal.question')} 1</td>
|
<td className='h-9 pl-3 pr-2 border-b border-divider-subtle text-[13px]'>{t('appAnnotation.batchModal.question')} 1</td>
|
||||||
<td className='h-9 pl-3 pr-2 border-b border-gray-100 text-[13px]'>{t('appAnnotation.batchModal.answer')} 1</td>
|
<td className='h-9 pl-3 pr-2 border-b border-divider-subtle text-[13px]'>{t('appAnnotation.batchModal.answer')} 1</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td className='h-9 pl-3 pr-2 text-[13px]'>{t('appAnnotation.batchModal.question')} 2</td>
|
<td className='h-9 pl-3 pr-2 text-[13px]'>{t('appAnnotation.batchModal.question')} 2</td>
|
||||||
@ -61,7 +61,7 @@ const CSVDownload: FC = () => {
|
|||||||
bom={true}
|
bom={true}
|
||||||
data={getTemplate()}
|
data={getTemplate()}
|
||||||
>
|
>
|
||||||
<div className='flex items-center h-[18px] space-x-1 text-[#155EEF] text-xs font-medium'>
|
<div className='flex items-center h-[18px] space-x-1 text-text-accent system-xs-medium'>
|
||||||
<DownloadIcon className='w-3 h-3 mr-1' />
|
<DownloadIcon className='w-3 h-3 mr-1' />
|
||||||
{t('appAnnotation.batchModal.template')}
|
{t('appAnnotation.batchModal.template')}
|
||||||
</div>
|
</div>
|
||||||
|
@ -91,29 +91,29 @@ const CSVUploader: FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
<div ref={dropRef}>
|
<div ref={dropRef}>
|
||||||
{!file && (
|
{!file && (
|
||||||
<div className={cn('flex items-center h-20 rounded-xl bg-gray-50 border border-dashed border-gray-200 text-sm font-normal', dragging && 'bg-[#F5F8FF] border border-[#B2CCFF]')}>
|
<div className={cn('flex items-center h-20 rounded-xl bg-components-dropzone-bg border border-dashed border-components-dropzone-border system-sm-regular', dragging && 'bg-components-dropzone-bg-accent border border-components-dropzone-border-accent')}>
|
||||||
<div className='w-full flex items-center justify-center space-x-2'>
|
<div className='w-full flex items-center justify-center space-x-2'>
|
||||||
<CSVIcon className="shrink-0" />
|
<CSVIcon className="shrink-0" />
|
||||||
<div className='text-gray-500'>
|
<div className='text-text-tertiary'>
|
||||||
{t('appAnnotation.batchModal.csvUploadTitle')}
|
{t('appAnnotation.batchModal.csvUploadTitle')}
|
||||||
<span className='text-primary-400 cursor-pointer' onClick={selectHandle}>{t('appAnnotation.batchModal.browse')}</span>
|
<span className='text-text-accent cursor-pointer' onClick={selectHandle}>{t('appAnnotation.batchModal.browse')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{dragging && <div ref={dragRef} className='absolute w-full h-full top-0 left-0' />}
|
{dragging && <div ref={dragRef} className='absolute w-full h-full top-0 left-0' />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{file && (
|
{file && (
|
||||||
<div className={cn('flex items-center h-20 px-6 rounded-xl bg-gray-50 border border-gray-200 text-sm font-normal group', 'hover:bg-[#F5F8FF] hover:border-[#B2CCFF]')}>
|
<div className={cn('flex items-center h-20 px-6 rounded-xl bg-components-panel-bg border border-components-panel-border text-sm font-normal group', 'hover:bg-components-panel-bg-blur hover:border-components-panel-bg-blur')}>
|
||||||
<CSVIcon className="shrink-0" />
|
<CSVIcon className="shrink-0" />
|
||||||
<div className='flex ml-2 w-0 grow'>
|
<div className='flex ml-2 w-0 grow'>
|
||||||
<span className='max-w-[calc(100%_-_30px)] text-ellipsis whitespace-nowrap overflow-hidden text-gray-800'>{file.name.replace(/.csv$/, '')}</span>
|
<span className='max-w-[calc(100%_-_30px)] text-ellipsis whitespace-nowrap overflow-hidden text-text-primary'>{file.name.replace(/.csv$/, '')}</span>
|
||||||
<span className='shrink-0 text-gray-500'>.csv</span>
|
<span className='shrink-0 text-text-tertiary'>.csv</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='hidden group-hover:flex items-center'>
|
<div className='hidden group-hover:flex items-center'>
|
||||||
<Button className='!h-8 !px-3 !py-[6px] bg-white !text-[13px] !leading-[18px] text-gray-700' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
|
<Button variant='secondary' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
|
||||||
<div className='mx-2 w-px h-4 bg-gray-200' />
|
<div className='mx-2 w-px h-4 bg-divider-regular' />
|
||||||
<div className='p-2 cursor-pointer' onClick={removeFile}>
|
<div className='p-2 cursor-pointer' onClick={removeFile}>
|
||||||
<RiDeleteBinLine className='w-4 h-4 text-gray-500' />
|
<RiDeleteBinLine className='w-4 h-4 text-text-tertiary' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -88,9 +88,9 @@ const BatchModal: FC<IBatchModalProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isShow={isShow} onClose={() => { }} className='px-8 py-6 !max-w-[520px] !rounded-xl'>
|
<Modal isShow={isShow} onClose={() => { }} className='px-8 py-6 !max-w-[520px] !rounded-xl'>
|
||||||
<div className='relative pb-1 text-xl font-medium leading-[30px] text-gray-900'>{t('appAnnotation.batchModal.title')}</div>
|
<div className='relative pb-1 system-xl-medium text-text-primary'>{t('appAnnotation.batchModal.title')}</div>
|
||||||
<div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onCancel}>
|
<div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onCancel}>
|
||||||
<RiCloseLine className='w-4 h-4 text-gray-500' />
|
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
|
||||||
</div>
|
</div>
|
||||||
<CSVUploader
|
<CSVUploader
|
||||||
file={currentCSV}
|
file={currentCSV}
|
||||||
@ -105,11 +105,10 @@ const BatchModal: FC<IBatchModalProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='mt-[28px] pt-6 flex justify-end'>
|
<div className='mt-[28px] pt-6 flex justify-end'>
|
||||||
<Button className='mr-2 text-gray-700 text-sm font-medium' onClick={onCancel}>
|
<Button className='mr-2 text-text-tertiary system-sm-medium' onClick={onCancel}>
|
||||||
{t('appAnnotation.batchModal.cancel')}
|
{t('appAnnotation.batchModal.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className='text-sm font-medium'
|
|
||||||
variant="primary"
|
variant="primary"
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
disabled={isAnnotationFull || !currentCSV}
|
disabled={isAnnotationFull || !currentCSV}
|
||||||
|
@ -2,13 +2,11 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Textarea from 'rc-textarea'
|
import { RiDeleteBinLine, RiEditFill, RiEditLine } from '@remixicon/react'
|
||||||
import { RiDeleteBinLine } from '@remixicon/react'
|
|
||||||
import cn from '@/utils/classnames'
|
|
||||||
import { Robot, User } from '@/app/components/base/icons/src/public/avatar'
|
import { Robot, User } from '@/app/components/base/icons/src/public/avatar'
|
||||||
import { Edit04 } from '@/app/components/base/icons/src/vender/line/general'
|
import Textarea from '@/app/components/base/textarea'
|
||||||
import { Edit04 as EditSolid } from '@/app/components/base/icons/src/vender/solid/general'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
export enum EditItemType {
|
export enum EditItemType {
|
||||||
Query = 'query',
|
Query = 'query',
|
||||||
@ -22,8 +20,8 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const EditTitle: FC<{ className?: string; title: string }> = ({ className, title }) => (
|
export const EditTitle: FC<{ className?: string; title: string }> = ({ className, title }) => (
|
||||||
<div className={cn(className, 'flex items-center height-[18px] text-xs font-medium text-gray-500')}>
|
<div className={cn(className, 'flex items-center h-[18px] system-xs-medium text-text-tertiary')}>
|
||||||
<EditSolid className='mr-1 w-3.5 h-3.5' />
|
<RiEditFill className='mr-1 w-3.5 h-3.5' />
|
||||||
<div>{title}</div>
|
<div>{title}</div>
|
||||||
<div
|
<div
|
||||||
className='ml-2 grow h-[1px]'
|
className='ml-2 grow h-[1px]'
|
||||||
@ -64,32 +62,32 @@ const EditItem: FC<Props> = ({
|
|||||||
{avatar}
|
{avatar}
|
||||||
</div>
|
</div>
|
||||||
<div className='grow'>
|
<div className='grow'>
|
||||||
<div className='mb-1 leading-[18px] text-xs font-semibold text-gray-900'>{name}</div>
|
<div className='mb-1 system-xs-semibold text-text-primary'>{name}</div>
|
||||||
<div className='leading-5 text-sm font-normal text-gray-900'>{content}</div>
|
<div className='system-sm-regular text-text-primary'>{content}</div>
|
||||||
{!isEdit
|
{!isEdit
|
||||||
? (
|
? (
|
||||||
<div>
|
<div>
|
||||||
{showNewContent && (
|
{showNewContent && (
|
||||||
<div className='mt-3'>
|
<div className='mt-3'>
|
||||||
<EditTitle title={editTitle} />
|
<EditTitle title={editTitle} />
|
||||||
<div className='mt-1 leading-5 text-sm font-normal text-gray-900'>{newContent}</div>
|
<div className='mt-1 system-sm-regular text-text-primary'>{newContent}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className='mt-2 flex items-center'>
|
<div className='mt-2 flex items-center'>
|
||||||
{!readonly && (
|
{!readonly && (
|
||||||
<div
|
<div
|
||||||
className='flex items-center space-x-1 leading-[18px] text-xs font-medium text-[#155EEF] cursor-pointer'
|
className='flex items-center space-x-1 system-xs-medium text-text-accent cursor-pointer'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsEdit(true)
|
setIsEdit(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Edit04 className='mr-1 w-3.5 h-3.5' />
|
<RiEditLine className='mr-1 w-3.5 h-3.5' />
|
||||||
<div>{t('common.operation.edit')}</div>
|
<div>{t('common.operation.edit')}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showNewContent && (
|
{showNewContent && (
|
||||||
<div className='ml-2 flex items-center leading-[18px] text-xs font-medium text-gray-500'>
|
<div className='ml-2 flex items-center system-xs-medium text-text-tertiary'>
|
||||||
<div className='mr-2'>·</div>
|
<div className='mr-2'>·</div>
|
||||||
<div
|
<div
|
||||||
className='flex items-center space-x-1 cursor-pointer'
|
className='flex items-center space-x-1 cursor-pointer'
|
||||||
@ -112,10 +110,8 @@ const EditItem: FC<Props> = ({
|
|||||||
<div className='mt-3'>
|
<div className='mt-3'>
|
||||||
<EditTitle title={editTitle} />
|
<EditTitle title={editTitle} />
|
||||||
<Textarea
|
<Textarea
|
||||||
className='mt-1 block w-full leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none'
|
|
||||||
value={newContent}
|
value={newContent}
|
||||||
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setNewContent(e.target.value)}
|
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setNewContent(e.target.value)}
|
||||||
autoSize={{ minRows: 3 }}
|
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
|
@ -123,7 +123,7 @@ const EditAnnotationModal: FC<Props> = ({
|
|||||||
{
|
{
|
||||||
annotationId
|
annotationId
|
||||||
? (
|
? (
|
||||||
<div className='px-4 flex h-16 items-center justify-between border-t border-black/5 bg-gray-50 rounded-bl-xl rounded-br-xl leading-[18px] text-[13px] font-medium text-gray-500'>
|
<div className='px-4 flex h-16 items-center justify-between border-t border-divider-subtle bg-background-section-burn rounded-bl-xl rounded-br-xl system-sm-medium text-text-tertiary'>
|
||||||
<div
|
<div
|
||||||
className='flex items-center pl-3 space-x-2 cursor-pointer'
|
className='flex items-center pl-3 space-x-2 cursor-pointer'
|
||||||
onClick={() => setShowModal(true)}
|
onClick={() => setShowModal(true)}
|
||||||
|
@ -4,6 +4,7 @@ import React, { Fragment, useEffect, useState } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import {
|
import {
|
||||||
RiAddLine,
|
RiAddLine,
|
||||||
|
RiMoreFill,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import {
|
import {
|
||||||
@ -14,7 +15,6 @@ import Button from '../../../base/button'
|
|||||||
import AddAnnotationModal from '../add-annotation-modal'
|
import AddAnnotationModal from '../add-annotation-modal'
|
||||||
import type { AnnotationItemBasic } from '../type'
|
import type { AnnotationItemBasic } from '../type'
|
||||||
import BatchAddModal from '../batch-add-annotation-modal'
|
import BatchAddModal from '../batch-add-annotation-modal'
|
||||||
import s from './style.module.css'
|
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import CustomPopover from '@/app/components/base/popover'
|
import CustomPopover from '@/app/components/base/popover'
|
||||||
import { FileDownload02, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files'
|
import { FileDownload02, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files'
|
||||||
@ -80,17 +80,17 @@ const HeaderOptions: FC<Props> = ({
|
|||||||
const Operations = () => {
|
const Operations = () => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full py-1">
|
<div className="w-full py-1">
|
||||||
<button className={s.actionItem} onClick={() => {
|
<button className='h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]' onClick={() => {
|
||||||
setShowBulkImportModal(true)
|
setShowBulkImportModal(true)
|
||||||
}}>
|
}}>
|
||||||
<FilePlus02 className={s.actionItemIcon} />
|
<FilePlus02 className='w-4 h-4 text-text-tertiary' />
|
||||||
<span className={s.actionName}>{t('appAnnotation.table.header.bulkImport')}</span>
|
<span className='grow text-text-secondary system-sm-regular text-left'>{t('appAnnotation.table.header.bulkImport')}</span>
|
||||||
</button>
|
</button>
|
||||||
<Menu as="div" className="relative w-full h-full">
|
<Menu as="div" className="relative w-full h-full">
|
||||||
<Menu.Button className={s.actionItem}>
|
<Menu.Button className='h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]'>
|
||||||
<FileDownload02 className={s.actionItemIcon} />
|
<FileDownload02 className='w-4 h-4 text-text-tertiary' />
|
||||||
<span className={s.actionName}>{t('appAnnotation.table.header.bulkExport')}</span>
|
<span className='grow text-text-secondary system-sm-regular text-left'>{t('appAnnotation.table.header.bulkExport')}</span>
|
||||||
<ChevronRight className='shrink-0 w-[14px] h-[14px] text-gray-500' />
|
<ChevronRight className='shrink-0 w-[14px] h-[14px] text-text-tertiary' />
|
||||||
</Menu.Button>
|
</Menu.Button>
|
||||||
<Transition
|
<Transition
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
@ -103,11 +103,7 @@ const HeaderOptions: FC<Props> = ({
|
|||||||
>
|
>
|
||||||
<Menu.Items
|
<Menu.Items
|
||||||
className={cn(
|
className={cn(
|
||||||
`
|
'absolute top-[1px] left-1 -translate-x-full py-1 min-w-[100px] z-10 bg-components-panel-bg border-[0.5px] border-components-panel-on-panel-item-bg origin-top-right rounded-xl shadow-xs',
|
||||||
absolute top-[1px] py-1 min-w-[100px] z-10 bg-white border-[0.5px] border-gray-200
|
|
||||||
divide-y divide-gray-100 origin-top-right rounded-xl
|
|
||||||
`,
|
|
||||||
s.popup,
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CSVDownloader
|
<CSVDownloader
|
||||||
@ -119,12 +115,12 @@ const HeaderOptions: FC<Props> = ({
|
|||||||
...list.map(item => [item.question, item.answer]),
|
...list.map(item => [item.question, item.answer]),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<button disabled={annotationUnavailable} className={s.actionItem}>
|
<button disabled={annotationUnavailable} className='h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]'>
|
||||||
<span className={s.actionName}>CSV</span>
|
<span className='grow text-text-secondary system-sm-regular text-left'>CSV</span>
|
||||||
</button>
|
</button>
|
||||||
</CSVDownloader>
|
</CSVDownloader>
|
||||||
<button disabled={annotationUnavailable} className={cn(s.actionItem, '!border-0')} onClick={JSONLOutput}>
|
<button disabled={annotationUnavailable} className={cn('h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]', '!border-0')} onClick={JSONLOutput}>
|
||||||
<span className={s.actionName}>JSONL</span>
|
<span className='grow text-text-secondary system-sm-regular text-left'>JSONL</span>
|
||||||
</button>
|
</button>
|
||||||
</Menu.Items>
|
</Menu.Items>
|
||||||
</Transition>
|
</Transition>
|
||||||
@ -137,21 +133,20 @@ const HeaderOptions: FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex space-x-2'>
|
<div className='flex space-x-2'>
|
||||||
<Button variant='primary' onClick={() => setShowAddModal(true)} className='flex items-center !h-8 !px-3 !text-[13px] space-x-2'>
|
<Button variant='primary' onClick={() => setShowAddModal(true)}>
|
||||||
<RiAddLine className='w-4 h-4' />
|
<RiAddLine className='w-4 h-4 mr-0.5' />
|
||||||
<div>{t('appAnnotation.table.header.addAnnotation')}</div>
|
<div>{t('appAnnotation.table.header.addAnnotation')}</div>
|
||||||
</Button>
|
</Button>
|
||||||
<CustomPopover
|
<CustomPopover
|
||||||
htmlContent={<Operations />}
|
htmlContent={<Operations />}
|
||||||
position="br"
|
position="br"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
btnElement={<div className={cn(s.actionIcon, s.commonIcon)} />}
|
btnElement={
|
||||||
btnClassName={open =>
|
<Button variant='secondary' className='w-8 p-0'>
|
||||||
cn(
|
<RiMoreFill className='w-4 h-4' />
|
||||||
open ? 'border-gray-300 !bg-gray-100 !shadow-none' : 'border-gray-200',
|
</Button>
|
||||||
s.actionIconWrapper,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
btnClassName='p-0 border-0'
|
||||||
className={'!w-[155px] h-fit !z-20'}
|
className={'!w-[155px] h-fit !z-20'}
|
||||||
popupClassName='!w-full !overflow-visible'
|
popupClassName='!w-full !overflow-visible'
|
||||||
manualClose
|
manualClose
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
.actionIconWrapper {
|
|
||||||
@apply h-8 w-8 p-2 rounded-md hover:bg-gray-100 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.commonIcon {
|
|
||||||
@apply w-4 h-4 inline-block align-middle;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center center;
|
|
||||||
background-size: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actionIcon {
|
|
||||||
@apply bg-gray-500;
|
|
||||||
mask-image: url(~@/assets/action.svg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actionItemIcon {
|
|
||||||
@apply w-4 h-4 text-gray-500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actionItem {
|
|
||||||
@apply h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-gray-100 rounded-lg cursor-pointer disabled:opacity-50;
|
|
||||||
width: calc(100% - 0.5rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.deleteActionItem {
|
|
||||||
@apply hover:bg-red-50 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actionName {
|
|
||||||
@apply grow text-gray-700 text-sm text-left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup {
|
|
||||||
left: 4px;
|
|
||||||
transform: translateX(-100%);
|
|
||||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ import type { FC } from 'react'
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useDebounce } from 'ahooks'
|
import { useDebounce } from 'ahooks'
|
||||||
|
import { RiEqualizer2Line } from '@remixicon/react'
|
||||||
import Toast from '../../base/toast'
|
import Toast from '../../base/toast'
|
||||||
import Filter from './filter'
|
import Filter from './filter'
|
||||||
import type { QueryParam } from './filter'
|
import type { QueryParam } from './filter'
|
||||||
@ -11,7 +12,8 @@ import EmptyElement from './empty-element'
|
|||||||
import HeaderOpts from './header-opts'
|
import HeaderOpts from './header-opts'
|
||||||
import { AnnotationEnableStatus, type AnnotationItem, type AnnotationItemBasic, JobStatus } from './type'
|
import { AnnotationEnableStatus, type AnnotationItem, type AnnotationItemBasic, JobStatus } from './type'
|
||||||
import ViewAnnotationModal from './view-annotation-modal'
|
import ViewAnnotationModal from './view-annotation-modal'
|
||||||
import cn from '@/utils/classnames'
|
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||||
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
import Pagination from '@/app/components/base/pagination'
|
import Pagination from '@/app/components/base/pagination'
|
||||||
import Switch from '@/app/components/base/switch'
|
import Switch from '@/app/components/base/switch'
|
||||||
import { addAnnotation, delAnnotation, fetchAnnotationConfig as doFetchAnnotationConfig, editAnnotation, fetchAnnotationList, queryAnnotationJobStatus, updateAnnotationScore, updateAnnotationStatus } from '@/service/annotation'
|
import { addAnnotation, delAnnotation, fetchAnnotationConfig as doFetchAnnotationConfig, editAnnotation, fetchAnnotationList, queryAnnotationJobStatus, updateAnnotationScore, updateAnnotationStatus } from '@/service/annotation'
|
||||||
@ -22,8 +24,8 @@ import type { AnnotationReplyConfig } from '@/models/debug'
|
|||||||
import { sleep } from '@/utils'
|
import { sleep } from '@/utils'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
import AnnotationFullModal from '@/app/components/billing/annotation-full/modal'
|
import AnnotationFullModal from '@/app/components/billing/annotation-full/modal'
|
||||||
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
|
|
||||||
import type { App } from '@/types/app'
|
import type { App } from '@/types/app'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
appDetail: App
|
appDetail: App
|
||||||
@ -157,8 +159,9 @@ const Annotation: FC<Props> = ({
|
|||||||
<div className='flex items-center space-x-2'>
|
<div className='flex items-center space-x-2'>
|
||||||
{isChatApp && (
|
{isChatApp && (
|
||||||
<>
|
<>
|
||||||
<div className={cn(!annotationConfig?.enabled && 'pr-2', 'flex items-center h-7 rounded-lg border border-gray-200 pl-2 space-x-1')}>
|
<div className={cn(!annotationConfig?.enabled && 'pr-2', 'flex items-center h-7 rounded-lg bg-components-panel-bg-blur border border-components-panel-border pl-2 space-x-1')}>
|
||||||
<div className='leading-[18px] text-[13px] font-medium text-gray-900'>{t('appAnnotation.name')}</div>
|
<MessageFast className='w-4 h-4 text-util-colors-indigo-indigo-600' />
|
||||||
|
<div className='system-sm-medium text-text-primary'>{t('appAnnotation.name')}</div>
|
||||||
<Switch
|
<Switch
|
||||||
key={controlRefreshSwitch}
|
key={controlRefreshSwitch}
|
||||||
defaultValue={annotationConfig?.enabled}
|
defaultValue={annotationConfig?.enabled}
|
||||||
@ -185,22 +188,14 @@ const Annotation: FC<Props> = ({
|
|||||||
></Switch>
|
></Switch>
|
||||||
{annotationConfig?.enabled && (
|
{annotationConfig?.enabled && (
|
||||||
<div className='flex items-center pl-1.5'>
|
<div className='flex items-center pl-1.5'>
|
||||||
<div className='shrink-0 mr-1 w-[1px] h-3.5 bg-gray-200'></div>
|
<div className='shrink-0 mr-1 w-[1px] h-3.5 bg-divider-subtle'></div>
|
||||||
<div
|
<ActionButton onClick={() => setIsShowEdit(true)}>
|
||||||
className={`
|
<RiEqualizer2Line className='w-4 h-4 text-text-tertiary' />
|
||||||
shrink-0 h-7 w-7 flex items-center justify-center
|
</ActionButton>
|
||||||
text-xs text-gray-700 font-medium
|
|
||||||
`}
|
|
||||||
onClick={() => { setIsShowEdit(true) }}
|
|
||||||
>
|
|
||||||
<div className='flex h-6 w-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-200'>
|
|
||||||
<Settings04 className='w-4 h-4' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className='shrink-0 mx-3 w-[1px] h-3.5 bg-gray-200'></div>
|
<div className='shrink-0 mx-3 w-[1px] h-3.5 bg-divider-regular'></div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { RiDeleteBinLine } from '@remixicon/react'
|
import { RiDeleteBinLine, RiEditLine } from '@remixicon/react'
|
||||||
import { Edit02 } from '../../base/icons/src/vender/line/general'
|
|
||||||
import type { AnnotationItem } from './type'
|
import type { AnnotationItem } from './type'
|
||||||
import RemoveAnnotationConfirmModal from './remove-annotation-confirm-modal'
|
import RemoveAnnotationConfirmModal from './remove-annotation-confirm-modal'
|
||||||
import cn from '@/utils/classnames'
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
import useTimestamp from '@/hooks/use-timestamp'
|
import useTimestamp from '@/hooks/use-timestamp'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
list: AnnotationItem[]
|
list: AnnotationItem[]
|
||||||
@ -59,26 +59,18 @@ const List: FC<Props> = ({
|
|||||||
<td className='p-3 pr-2'>{item.hit_count}</td>
|
<td className='p-3 pr-2'>{item.hit_count}</td>
|
||||||
<td className='w-[96px] p-3 pr-2' onClick={e => e.stopPropagation()}>
|
<td className='w-[96px] p-3 pr-2' onClick={e => e.stopPropagation()}>
|
||||||
{/* Actions */}
|
{/* Actions */}
|
||||||
<div className='flex space-x-2 text-gray-500'>
|
<div className='flex space-x-1 text-text-tertiary'>
|
||||||
<div
|
<ActionButton onClick={() => onView(item)}>
|
||||||
className='p-1 cursor-pointer rounded-md hover:bg-black/5'
|
<RiEditLine className='w-4 h-4' />
|
||||||
onClick={
|
</ActionButton>
|
||||||
() => {
|
<ActionButton
|
||||||
onView(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Edit02 className='w-4 h-4' />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='p-1 cursor-pointer rounded-md hover:bg-black/5'
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrId(item.id)
|
setCurrId(item.id)
|
||||||
setShowConfirmDelete(true)
|
setShowConfirmDelete(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RiDeleteBinLine className='w-4 h-4' />
|
<RiDeleteBinLine className='w-4 h-4' />
|
||||||
</div>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -7,11 +7,11 @@ import { ClockFastForward } from '@/app/components/base/icons/src/vender/line/ti
|
|||||||
const HitHistoryNoData: FC = () => {
|
const HitHistoryNoData: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<div className='mx-auto mt-20 w-[480px] p-5 rounded-2xl bg-gray-50 space-y-2'>
|
<div className='mx-auto mt-20 w-[480px] p-5 rounded-2xl bg-background-section-burn space-y-2'>
|
||||||
<div className='inline-block p-3 rounded-lg border border-gray-200'>
|
<div className='inline-block p-3 rounded-lg border border-divider-subtle'>
|
||||||
<ClockFastForward className='w-5 h-5 text-gray-500' />
|
<ClockFastForward className='w-5 h-5 text-text-tertiary' />
|
||||||
</div>
|
</div>
|
||||||
<div className='leading-5 text-sm font-normal text-gray-500'>{t('appAnnotation.viewModal.noHitHistory')}</div>
|
<div className='system-sm-regular text-text-tertiary'>{t('appAnnotation.viewModal.noHitHistory')}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,17 +4,17 @@ import React, { useEffect, useState } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import EditItem, { EditItemType } from '../edit-annotation-modal/edit-item'
|
import EditItem, { EditItemType } from '../edit-annotation-modal/edit-item'
|
||||||
import type { AnnotationItem, HitHistoryItem } from '../type'
|
import type { AnnotationItem, HitHistoryItem } from '../type'
|
||||||
import s from './style.module.css'
|
|
||||||
import HitHistoryNoData from './hit-history-no-data'
|
import HitHistoryNoData from './hit-history-no-data'
|
||||||
import cn from '@/utils/classnames'
|
import Badge from '@/app/components/base/badge'
|
||||||
import Pagination from '@/app/components/base/pagination'
|
|
||||||
import Drawer from '@/app/components/base/drawer-plus'
|
import Drawer from '@/app/components/base/drawer-plus'
|
||||||
|
import Pagination from '@/app/components/base/pagination'
|
||||||
import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication'
|
import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication'
|
||||||
import Confirm from '@/app/components/base/confirm'
|
import Confirm from '@/app/components/base/confirm'
|
||||||
import TabSlider from '@/app/components/base/tab-slider-plain'
|
import TabSlider from '@/app/components/base/tab-slider-plain'
|
||||||
import { fetchHitHistoryList } from '@/service/annotation'
|
import { fetchHitHistoryList } from '@/service/annotation'
|
||||||
import { APP_PAGE_LIMIT } from '@/config'
|
import { APP_PAGE_LIMIT } from '@/config'
|
||||||
import useTimestamp from '@/hooks/use-timestamp'
|
import useTimestamp from '@/hooks/use-timestamp'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
appId: string
|
appId: string
|
||||||
@ -72,7 +72,9 @@ const ViewAnnotationModal: FC<Props> = ({
|
|||||||
? (
|
? (
|
||||||
<div className='flex items-center space-x-1'>
|
<div className='flex items-center space-x-1'>
|
||||||
<div>{t('appAnnotation.viewModal.hitHistory')}</div>
|
<div>{t('appAnnotation.viewModal.hitHistory')}</div>
|
||||||
<div className='flex px-1.5 item-center rounded-md border border-black/[8%] h-5 text-xs font-medium text-gray-500'>{total} {t(`appAnnotation.viewModal.hit${hitHistoryList.length > 1 ? 's' : ''}`)}</div>
|
<Badge
|
||||||
|
text={`${total} ${t(`appAnnotation.viewModal.hit${hitHistoryList.length > 1 ? 's' : ''}`)}`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
: t('appAnnotation.viewModal.hitHistory')
|
: t('appAnnotation.viewModal.hitHistory')
|
||||||
@ -111,44 +113,45 @@ const ViewAnnotationModal: FC<Props> = ({
|
|||||||
? (<HitHistoryNoData />)
|
? (<HitHistoryNoData />)
|
||||||
: (
|
: (
|
||||||
<div>
|
<div>
|
||||||
<table className={cn(s.table, 'w-full min-w-[440px] border-collapse border-0 text-sm')} >
|
<table className={cn('w-full min-w-[440px] border-collapse border-0')} >
|
||||||
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-bold">
|
<thead className="system-xs-medium-uppercase text-text-tertiary">
|
||||||
<tr className='uppercase'>
|
<tr>
|
||||||
<td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.query')}</td>
|
<td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.query')}</td>
|
||||||
<td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.match')}</td>
|
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.match')}</td>
|
||||||
<td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.response')}</td>
|
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.response')}</td>
|
||||||
<td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.source')}</td>
|
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.source')}</td>
|
||||||
<td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.score')}</td>
|
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.score')}</td>
|
||||||
<td className='whitespace-nowrap w-[160px]'>{t('appAnnotation.hitHistoryTable.time')}</td>
|
<td className='pl-3 py-1.5 rounded-r-lg bg-background-section-burn whitespace-nowrap w-[160px]'>{t('appAnnotation.hitHistoryTable.time')}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="text-gray-500">
|
<tbody className="text-text-secondary system-sm-regular">
|
||||||
{hitHistoryList.map(item => (
|
{hitHistoryList.map(item => (
|
||||||
<tr
|
<tr
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={'border-b border-gray-200 h-8 hover:bg-gray-50 cursor-pointer'}
|
className={'border-b border-divider-subtle hover:bg-background-default-hover cursor-pointer'}
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
|
className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
|
||||||
title={item.question}
|
title={item.question}
|
||||||
>{item.question}</td>
|
>{item.question}</td>
|
||||||
<td
|
<td
|
||||||
className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
|
className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
|
||||||
title={item.match}
|
title={item.match}
|
||||||
>{item.match}</td>
|
>{item.match}</td>
|
||||||
<td
|
<td
|
||||||
className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
|
className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
|
||||||
title={item.response}
|
title={item.response}
|
||||||
>{item.response}</td>
|
>{item.response}</td>
|
||||||
<td>{item.source}</td>
|
<td className='p-3 pr-2'>{item.source}</td>
|
||||||
<td>{item.score ? item.score.toFixed(2) : '-'}</td>
|
<td className='p-3 pr-2'>{item.score ? item.score.toFixed(2) : '-'}</td>
|
||||||
<td>{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td>
|
<td className='p-3 pr-2'>{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{(total && total > APP_PAGE_LIMIT)
|
{(total && total > APP_PAGE_LIMIT)
|
||||||
? <Pagination
|
? <Pagination
|
||||||
|
className='px-0'
|
||||||
current={currPage}
|
current={currPage}
|
||||||
onChange={setCurrPage}
|
onChange={setCurrPage}
|
||||||
total={total}
|
total={total}
|
||||||
@ -163,7 +166,6 @@ const ViewAnnotationModal: FC<Props> = ({
|
|||||||
isShow={isShow}
|
isShow={isShow}
|
||||||
onHide={onHide}
|
onHide={onHide}
|
||||||
maxWidthClassName='!max-w-[800px]'
|
maxWidthClassName='!max-w-[800px]'
|
||||||
// t('appAnnotation.editModal.title') as string
|
|
||||||
title={
|
title={
|
||||||
<TabSlider
|
<TabSlider
|
||||||
className='shrink-0 relative top-[9px]'
|
className='shrink-0 relative top-[9px]'
|
||||||
@ -193,7 +195,7 @@ const ViewAnnotationModal: FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
foot={id
|
foot={id
|
||||||
? (
|
? (
|
||||||
<div className='px-4 flex h-16 items-center justify-between border-t border-black/5 bg-gray-50 rounded-bl-xl rounded-br-xl leading-[18px] text-[13px] font-medium text-gray-500'>
|
<div className='px-4 flex h-16 items-center justify-between border-t border-divider-subtle bg-background-section-burn rounded-bl-xl rounded-br-xl system-sm-medium text-text-tertiary'>
|
||||||
<div
|
<div
|
||||||
className='flex items-center pl-3 space-x-2 cursor-pointer'
|
className='flex items-center pl-3 space-x-2 cursor-pointer'
|
||||||
onClick={() => setShowModal(true)}
|
onClick={() => setShowModal(true)}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
.table td {
|
|
||||||
padding: 7px 8px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
max-width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination li {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
@ -34,7 +34,7 @@ const LogAnnotation: FC<Props> = ({
|
|||||||
|
|
||||||
if (!appDetail) {
|
if (!appDetail) {
|
||||||
return (
|
return (
|
||||||
<div className='flex h-full items-center justify-center bg-white'>
|
<div className='flex h-full items-center justify-center bg-background-body'>
|
||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -5,9 +5,8 @@ import useSWR from 'swr'
|
|||||||
import {
|
import {
|
||||||
HandThumbDownIcon,
|
HandThumbDownIcon,
|
||||||
HandThumbUpIcon,
|
HandThumbUpIcon,
|
||||||
XMarkIcon,
|
|
||||||
} from '@heroicons/react/24/outline'
|
} from '@heroicons/react/24/outline'
|
||||||
import { RiEditFill, RiQuestionLine } from '@remixicon/react'
|
import { RiCloseLine, RiEditFill } from '@remixicon/react'
|
||||||
import { get } from 'lodash-es'
|
import { get } from 'lodash-es'
|
||||||
import InfiniteScroll from 'react-infinite-scroll-component'
|
import InfiniteScroll from 'react-infinite-scroll-component'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@ -18,20 +17,16 @@ import { useShallow } from 'zustand/react/shallow'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type { ChatItemInTree } from '../../base/chat/types'
|
import type { ChatItemInTree } from '../../base/chat/types'
|
||||||
import VarPanel from './var-panel'
|
import VarPanel from './var-panel'
|
||||||
import cn from '@/utils/classnames'
|
|
||||||
import type { FeedbackFunc, FeedbackType, IChatItem, SubmitAnnotationFunc } from '@/app/components/base/chat/chat/type'
|
import type { FeedbackFunc, FeedbackType, IChatItem, SubmitAnnotationFunc } from '@/app/components/base/chat/chat/type'
|
||||||
import type { Annotation, ChatConversationGeneralDetail, ChatConversationsResponse, ChatMessage, ChatMessagesRequest, CompletionConversationGeneralDetail, CompletionConversationsResponse, LogAnnotation } from '@/models/log'
|
import type { Annotation, ChatConversationGeneralDetail, ChatConversationsResponse, ChatMessage, ChatMessagesRequest, CompletionConversationGeneralDetail, CompletionConversationsResponse, LogAnnotation } from '@/models/log'
|
||||||
import type { App } from '@/types/app'
|
import type { App } from '@/types/app'
|
||||||
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import Drawer from '@/app/components/base/drawer'
|
import Drawer from '@/app/components/base/drawer'
|
||||||
import Popover from '@/app/components/base/popover'
|
|
||||||
import Chat from '@/app/components/base/chat/chat'
|
import Chat from '@/app/components/base/chat/chat'
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import { fetchChatConversationDetail, fetchChatMessages, fetchCompletionConversationDetail, updateLogMessageAnnotations, updateLogMessageFeedbacks } from '@/service/log'
|
import { fetchChatConversationDetail, fetchChatMessages, fetchCompletionConversationDetail, updateLogMessageAnnotations, updateLogMessageFeedbacks } from '@/service/log'
|
||||||
import { TONE_LIST } from '@/config'
|
import ModelInfo from '@/app/components/app/log/model-info'
|
||||||
import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
|
|
||||||
import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
|
||||||
import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name'
|
|
||||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
import TextGeneration from '@/app/components/app/text-generate/item'
|
import TextGeneration from '@/app/components/app/text-generate/item'
|
||||||
import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils'
|
import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils'
|
||||||
@ -44,6 +39,7 @@ import Tooltip from '@/app/components/base/tooltip'
|
|||||||
import { CopyIcon } from '@/app/components/base/copy-icon'
|
import { CopyIcon } from '@/app/components/base/copy-icon'
|
||||||
import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils'
|
import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils'
|
||||||
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
|
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
@ -75,15 +71,6 @@ const HandThumbIconWithCount: FC<{ count: number; iconType: 'up' | 'down' }> = (
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
const PARAM_MAP = {
|
|
||||||
temperature: 'Temperature',
|
|
||||||
top_p: 'Top P',
|
|
||||||
presence_penalty: 'Presence Penalty',
|
|
||||||
max_tokens: 'Max Token',
|
|
||||||
stop: 'Stop',
|
|
||||||
frequency_penalty: 'Frequency Penalty',
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFormattedChatList = (messages: ChatMessage[], conversationId: string, timezone: string, format: string) => {
|
const getFormattedChatList = (messages: ChatMessage[], conversationId: string, timezone: string, format: string) => {
|
||||||
const newChatList: IChatItem[] = []
|
const newChatList: IChatItem[] = []
|
||||||
messages.forEach((item: ChatMessage) => {
|
messages.forEach((item: ChatMessage) => {
|
||||||
@ -156,9 +143,6 @@ const getFormattedChatList = (messages: ChatMessage[], conversationId: string, t
|
|||||||
return newChatList
|
return newChatList
|
||||||
}
|
}
|
||||||
|
|
||||||
// const displayedParams = CompletionParams.slice(0, -2)
|
|
||||||
const validatedParams = ['temperature', 'top_p', 'presence_penalty', 'frequency_penalty']
|
|
||||||
|
|
||||||
type IDetailPanel = {
|
type IDetailPanel = {
|
||||||
detail: any
|
detail: any
|
||||||
onFeedback: FeedbackFunc
|
onFeedback: FeedbackFunc
|
||||||
@ -315,22 +299,6 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
|||||||
const isChatMode = appDetail?.mode !== 'completion'
|
const isChatMode = appDetail?.mode !== 'completion'
|
||||||
const isAdvanced = appDetail?.mode === 'advanced-chat'
|
const isAdvanced = appDetail?.mode === 'advanced-chat'
|
||||||
|
|
||||||
const targetTone = TONE_LIST.find((item: any) => {
|
|
||||||
let res = true
|
|
||||||
validatedParams.forEach((param) => {
|
|
||||||
res = item.config?.[param] === detail?.model_config.model?.completion_params?.[param]
|
|
||||||
})
|
|
||||||
return res
|
|
||||||
})?.name ?? 'custom'
|
|
||||||
|
|
||||||
const modelName = (detail.model_config as any).model?.name
|
|
||||||
const provideName = (detail.model_config as any).model?.provider as any
|
|
||||||
const {
|
|
||||||
currentModel,
|
|
||||||
currentProvider,
|
|
||||||
} = useTextGenerationCurrentProviderAndModelAndModelList(
|
|
||||||
{ provider: provideName, model: modelName },
|
|
||||||
)
|
|
||||||
const varList = (detail.model_config as any).user_input_form?.map((item: any) => {
|
const varList = (detail.model_config as any).user_input_form?.map((item: any) => {
|
||||||
const itemContent = item[Object.keys(item)[0]]
|
const itemContent = item[Object.keys(item)[0]]
|
||||||
return {
|
return {
|
||||||
@ -342,18 +310,6 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
|||||||
? detail.message.message_files.map((item: any) => item.url)
|
? detail.message.message_files.map((item: any) => item.url)
|
||||||
: []
|
: []
|
||||||
|
|
||||||
const getParamValue = (param: string) => {
|
|
||||||
const value = detail?.model_config.model?.completion_params?.[param] || '-'
|
|
||||||
if (param === 'stop') {
|
|
||||||
if (Array.isArray(value))
|
|
||||||
return value.join(',')
|
|
||||||
else
|
|
||||||
return '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
const [width, setWidth] = useState(0)
|
const [width, setWidth] = useState(0)
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
@ -367,162 +323,71 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className='rounded-xl border-[0.5px] border-gray-200 h-full flex flex-col overflow-auto'>
|
<div ref={ref} className='rounded-xl border-[0.5px] border-components-panel-border h-full flex flex-col'>
|
||||||
{/* Panel Header */}
|
{/* Panel Header */}
|
||||||
<div className='border-b border-gray-100 py-4 px-6 flex items-center justify-between bg-components-panel-bg'>
|
<div className='shrink-0 pl-4 pt-3 pr-3 pb-2 flex items-center gap-2 bg-components-panel-bg rounded-t-xl'>
|
||||||
<div>
|
<div className='shrink-0'>
|
||||||
<div className='text-gray-500 text-[10px] leading-[14px]'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div>
|
<div className='mb-0.5 text-text-primary system-xs-semibold-uppercase'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div>
|
||||||
{isChatMode && (
|
{isChatMode && (
|
||||||
<div className='flex items-center text-gray-700 text-[13px] leading-[18px]'>
|
<div className='flex items-center text-text-secondary system-2xs-regular-uppercase'>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
popupContent={detail.id}
|
popupContent={detail.id}
|
||||||
>
|
>
|
||||||
<div className='max-w-[105px] truncate'>{detail.id}</div>
|
<div className='truncate'>{detail.id}</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<CopyIcon content={detail.id} />
|
<CopyIcon content={detail.id} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isChatMode && (
|
{!isChatMode && (
|
||||||
<div className='text-gray-700 text-[13px] leading-[18px]'>{formatTime(detail.created_at, t('appLog.dateTimeFormat') as string)}</div>
|
<div className='text-text-secondary system-2xs-regular-uppercase'>{formatTime(detail.created_at, t('appLog.dateTimeFormat') as string)}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center flex-wrap gap-y-1 justify-end'>
|
<div className='grow flex items-center flex-wrap gap-y-1 justify-end'>
|
||||||
{!isAdvanced && (
|
{!isAdvanced && <ModelInfo model={detail.model_config.model} />}
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className={cn('mr-2 flex items-center border h-8 px-2 space-x-2 rounded-lg bg-indigo-25 border-[#2A87F5]')}
|
|
||||||
>
|
|
||||||
<ModelIcon
|
|
||||||
className='!w-5 !h-5'
|
|
||||||
provider={currentProvider}
|
|
||||||
modelName={currentModel?.model}
|
|
||||||
/>
|
|
||||||
<ModelName
|
|
||||||
modelItem={currentModel!}
|
|
||||||
showMode
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Popover
|
|
||||||
position='br'
|
|
||||||
className='!w-[280px]'
|
|
||||||
btnClassName='mr-4 !bg-gray-50 !py-1.5 !px-2.5 border-none font-normal'
|
|
||||||
btnElement={<>
|
|
||||||
<span className='text-[13px]'>{targetTone}</span>
|
|
||||||
<RiQuestionLine className='h-4 w-4 text-gray-800 ml-1.5' />
|
|
||||||
</>}
|
|
||||||
htmlContent={<div className='w-[280px]'>
|
|
||||||
<div className='flex justify-between py-2 px-4 font-medium text-sm text-gray-700'>
|
|
||||||
<span>Tone of responses</span>
|
|
||||||
<div>{targetTone}</div>
|
|
||||||
</div>
|
|
||||||
{['temperature', 'top_p', 'presence_penalty', 'max_tokens', 'stop'].map((param: string, index: number) => {
|
|
||||||
return <div className='flex justify-between py-2 px-4 bg-gray-50' key={index}>
|
|
||||||
<span className='text-xs text-gray-700'>{PARAM_MAP[param as keyof typeof PARAM_MAP]}</span>
|
|
||||||
<span className='text-gray-800 font-medium text-xs'>{getParamValue(param)}</span>
|
|
||||||
</div>
|
|
||||||
})}
|
|
||||||
</div>}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div className='w-6 h-6 rounded-lg flex items-center justify-center hover:cursor-pointer hover:bg-gray-100'>
|
|
||||||
<XMarkIcon className='w-4 h-4 text-gray-500' onClick={onClose} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<ActionButton size='l' onClick={onClose}>
|
||||||
|
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
|
||||||
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
{/* Panel Body */}
|
{/* Panel Body */}
|
||||||
{(varList.length > 0 || (!isChatMode && message_files.length > 0)) && (
|
<div className='shrink-0 pt-1 px-1'>
|
||||||
<div className='px-6 pt-4 pb-2'>
|
<div className='p-3 pb-2 rounded-t-xl bg-background-section-burn'>
|
||||||
<VarPanel
|
{(varList.length > 0 || (!isChatMode && message_files.length > 0)) && (
|
||||||
varList={varList}
|
<VarPanel
|
||||||
message_files={message_files}
|
varList={varList}
|
||||||
/>
|
message_files={message_files}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
<div className='grow mx-1 mb-1 bg-background-section-burn rounded-b-xl overflow-auto'>
|
||||||
{!isChatMode
|
{!isChatMode
|
||||||
? <div className="px-6 py-4">
|
? <div className="px-6 py-4">
|
||||||
<div className='flex h-[18px] items-center space-x-3'>
|
<div className='flex h-[18px] items-center space-x-3'>
|
||||||
<div className='leading-[18px] text-xs font-semibold text-gray-500 uppercase'>{t('appLog.table.header.output')}</div>
|
<div className='text-text-tertiary system-xs-semibold-uppercase'>{t('appLog.table.header.output')}</div>
|
||||||
<div className='grow h-[1px]' style={{
|
<div className='grow h-[1px]' style={{
|
||||||
background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, rgb(243, 244, 246) 100%)',
|
background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, rgb(243, 244, 246) 100%)',
|
||||||
}}></div>
|
}}></div>
|
||||||
</div>
|
</div>
|
||||||
<TextGeneration
|
<TextGeneration
|
||||||
className='mt-2'
|
className='mt-2'
|
||||||
content={detail.message.answer}
|
content={detail.message.answer}
|
||||||
messageId={detail.message.id}
|
messageId={detail.message.id}
|
||||||
isError={false}
|
isError={false}
|
||||||
onRetry={() => { }}
|
onRetry={() => { }}
|
||||||
isInstalledApp={false}
|
isInstalledApp={false}
|
||||||
supportFeedback
|
supportFeedback
|
||||||
feedback={detail.message.feedbacks.find((item: any) => item.from_source === 'admin')}
|
feedback={detail.message.feedbacks.find((item: any) => item.from_source === 'admin')}
|
||||||
onFeedback={feedback => onFeedback(detail.message.id, feedback)}
|
onFeedback={feedback => onFeedback(detail.message.id, feedback)}
|
||||||
supportAnnotation
|
supportAnnotation
|
||||||
isShowTextToSpeech
|
isShowTextToSpeech
|
||||||
appId={appDetail?.id}
|
appId={appDetail?.id}
|
||||||
varList={varList}
|
varList={varList}
|
||||||
siteInfo={null}
|
siteInfo={null}
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
: threadChatItems.length < 8
|
|
||||||
? <div className="pt-4 mb-4">
|
|
||||||
<Chat
|
|
||||||
config={{
|
|
||||||
appId: appDetail?.id,
|
|
||||||
text_to_speech: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
supportAnnotation: true,
|
|
||||||
annotation_reply: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
supportFeedback: true,
|
|
||||||
} as any}
|
|
||||||
chatList={threadChatItems}
|
|
||||||
onAnnotationAdded={handleAnnotationAdded}
|
|
||||||
onAnnotationEdited={handleAnnotationEdited}
|
|
||||||
onAnnotationRemoved={handleAnnotationRemoved}
|
|
||||||
onFeedback={onFeedback}
|
|
||||||
noChatInput
|
|
||||||
showPromptLog
|
|
||||||
hideProcessDetail
|
|
||||||
chatContainerInnerClassName='px-6'
|
|
||||||
switchSibling={switchSibling}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
: <div
|
: threadChatItems.length < 8
|
||||||
className="py-4"
|
? <div className="pt-4 mb-4">
|
||||||
id="scrollableDiv"
|
|
||||||
style={{
|
|
||||||
height: 1000, // Specify a value
|
|
||||||
overflow: 'auto',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column-reverse',
|
|
||||||
}}>
|
|
||||||
{/* Put the scroll bar always on the bottom */}
|
|
||||||
<InfiniteScroll
|
|
||||||
scrollableTarget="scrollableDiv"
|
|
||||||
dataLength={threadChatItems.length}
|
|
||||||
next={fetchData}
|
|
||||||
hasMore={hasMore}
|
|
||||||
loader={<div className='text-center text-gray-400 text-xs'>{t('appLog.detail.loading')}...</div>}
|
|
||||||
// endMessage={<div className='text-center'>Nothing more to show</div>}
|
|
||||||
// below props only if you need pull down functionality
|
|
||||||
refreshFunction={fetchData}
|
|
||||||
pullDownToRefresh
|
|
||||||
pullDownToRefreshThreshold={50}
|
|
||||||
// pullDownToRefreshContent={
|
|
||||||
// <div className='text-center'>Pull down to refresh</div>
|
|
||||||
// }
|
|
||||||
// releaseToRefreshContent={
|
|
||||||
// <div className='text-center'>Release to refresh</div>
|
|
||||||
// }
|
|
||||||
// To put endMessage and loader to the top.
|
|
||||||
style={{ display: 'flex', flexDirection: 'column-reverse' }}
|
|
||||||
inverse={true}
|
|
||||||
>
|
|
||||||
<Chat
|
<Chat
|
||||||
config={{
|
config={{
|
||||||
appId: appDetail?.id,
|
appId: appDetail?.id,
|
||||||
@ -543,12 +408,68 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
|||||||
noChatInput
|
noChatInput
|
||||||
showPromptLog
|
showPromptLog
|
||||||
hideProcessDetail
|
hideProcessDetail
|
||||||
chatContainerInnerClassName='px-6'
|
chatContainerInnerClassName='px-3'
|
||||||
switchSibling={switchSibling}
|
switchSibling={switchSibling}
|
||||||
/>
|
/>
|
||||||
</InfiniteScroll>
|
</div>
|
||||||
</div>
|
: <div
|
||||||
}
|
className="py-4"
|
||||||
|
id="scrollableDiv"
|
||||||
|
style={{
|
||||||
|
height: 1000, // Specify a value
|
||||||
|
overflow: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column-reverse',
|
||||||
|
}}>
|
||||||
|
{/* Put the scroll bar always on the bottom */}
|
||||||
|
<InfiniteScroll
|
||||||
|
scrollableTarget="scrollableDiv"
|
||||||
|
dataLength={threadChatItems.length}
|
||||||
|
next={fetchData}
|
||||||
|
hasMore={hasMore}
|
||||||
|
loader={<div className='text-center text-text-tertiary system-xs-regular'>{t('appLog.detail.loading')}...</div>}
|
||||||
|
// endMessage={<div className='text-center'>Nothing more to show</div>}
|
||||||
|
// below props only if you need pull down functionality
|
||||||
|
refreshFunction={fetchData}
|
||||||
|
pullDownToRefresh
|
||||||
|
pullDownToRefreshThreshold={50}
|
||||||
|
// pullDownToRefreshContent={
|
||||||
|
// <div className='text-center'>Pull down to refresh</div>
|
||||||
|
// }
|
||||||
|
// releaseToRefreshContent={
|
||||||
|
// <div className='text-center'>Release to refresh</div>
|
||||||
|
// }
|
||||||
|
// To put endMessage and loader to the top.
|
||||||
|
style={{ display: 'flex', flexDirection: 'column-reverse' }}
|
||||||
|
inverse={true}
|
||||||
|
>
|
||||||
|
<Chat
|
||||||
|
config={{
|
||||||
|
appId: appDetail?.id,
|
||||||
|
text_to_speech: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
supportAnnotation: true,
|
||||||
|
annotation_reply: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
supportFeedback: true,
|
||||||
|
} as any}
|
||||||
|
chatList={threadChatItems}
|
||||||
|
onAnnotationAdded={handleAnnotationAdded}
|
||||||
|
onAnnotationEdited={handleAnnotationEdited}
|
||||||
|
onAnnotationRemoved={handleAnnotationRemoved}
|
||||||
|
onFeedback={onFeedback}
|
||||||
|
noChatInput
|
||||||
|
showPromptLog
|
||||||
|
hideProcessDetail
|
||||||
|
chatContainerInnerClassName='px-3'
|
||||||
|
switchSibling={switchSibling}
|
||||||
|
/>
|
||||||
|
</InfiniteScroll>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
{showMessageLogModal && (
|
{showMessageLogModal && (
|
||||||
<MessageLogModal
|
<MessageLogModal
|
||||||
width={width}
|
width={width}
|
||||||
@ -780,7 +701,7 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
|
|||||||
onClose={onCloseDrawer}
|
onClose={onCloseDrawer}
|
||||||
mask={isMobile}
|
mask={isMobile}
|
||||||
footer={null}
|
footer={null}
|
||||||
panelClassname='mt-16 mx-2 sm:mr-2 mb-4 !p-0 !max-w-[640px] rounded-xl bg-background-gradient-bg-fill-chat-bg-1'
|
panelClassname='mt-16 mx-2 sm:mr-2 mb-4 !p-0 !max-w-[640px] rounded-xl bg-components-panel-bg'
|
||||||
>
|
>
|
||||||
<DrawerContext.Provider value={{
|
<DrawerContext.Provider value={{
|
||||||
onClose: onCloseDrawer,
|
onClose: onCloseDrawer,
|
||||||
|
107
web/app/components/app/log/model-info.tsx
Normal file
107
web/app/components/app/log/model-info.tsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
RiInformation2Line,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
|
||||||
|
import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
const PARAM_MAP = {
|
||||||
|
temperature: 'Temperature',
|
||||||
|
top_p: 'Top P',
|
||||||
|
presence_penalty: 'Presence Penalty',
|
||||||
|
max_tokens: 'Max Token',
|
||||||
|
stop: 'Stop',
|
||||||
|
frequency_penalty: 'Frequency Penalty',
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
model: any
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModelInfo: FC<Props> = ({
|
||||||
|
model,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const modelName = model.name
|
||||||
|
const provideName = model.provider as any
|
||||||
|
const {
|
||||||
|
currentModel,
|
||||||
|
currentProvider,
|
||||||
|
} = useTextGenerationCurrentProviderAndModelAndModelList(
|
||||||
|
{ provider: provideName, model: modelName },
|
||||||
|
)
|
||||||
|
|
||||||
|
const [open, setOpen] = React.useState(false)
|
||||||
|
|
||||||
|
const getParamValue = (param: string) => {
|
||||||
|
const value = model.completion_params?.[param] || '-'
|
||||||
|
if (param === 'stop') {
|
||||||
|
if (Array.isArray(value))
|
||||||
|
return value.join(',')
|
||||||
|
else
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn('flex items-center rounded-lg')}>
|
||||||
|
<div className='shrink-0 flex items-center gap-1 mr-px h-8 pl-1.5 pr-2 rounded-l-lg bg-components-input-bg-normal'>
|
||||||
|
<ModelIcon
|
||||||
|
className='!w-5 !h-5'
|
||||||
|
provider={currentProvider}
|
||||||
|
modelName={currentModel?.model}
|
||||||
|
/>
|
||||||
|
<ModelName
|
||||||
|
modelItem={currentModel!}
|
||||||
|
showMode
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<PortalToFollowElem
|
||||||
|
open={open}
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
placement='bottom-end'
|
||||||
|
offset={4}
|
||||||
|
>
|
||||||
|
<div className='relative'>
|
||||||
|
<PortalToFollowElemTrigger
|
||||||
|
onClick={() => setOpen(v => !v)}
|
||||||
|
className='block'
|
||||||
|
>
|
||||||
|
<div className={cn(
|
||||||
|
'p-2 rounded-r-lg bg-components-button-tertiary-bg hover:bg-components-button-tertiary-bg-hover cursor-pointer',
|
||||||
|
open && 'bg-components-button-tertiary-bg-hover',
|
||||||
|
)}>
|
||||||
|
<RiInformation2Line className='h-4 w-4 text-text-tertiary' />
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent className='z-[1002]'>
|
||||||
|
<div className='relative w-[280px] pt-3 px-4 pb-2 bg-components-panel-bg rounded-2xl border-[0.5px] border-components-panel-border shadow-xl overflow-hidden'>
|
||||||
|
<div className='mb-1 h-6 text-text-secondary system-sm-semibold-uppercase'>{t('appLog.detail.modelParams')}</div>
|
||||||
|
<div className='py-1'>
|
||||||
|
{['temperature', 'top_p', 'presence_penalty', 'max_tokens', 'stop'].map((param: string, index: number) => {
|
||||||
|
return <div className='flex justify-between py-1.5' key={index}>
|
||||||
|
<span className='text-text-tertiary system-xs-medium-uppercase'>{PARAM_MAP[param as keyof typeof PARAM_MAP]}</span>
|
||||||
|
<span className='text-text-secondary system-xs-medium-uppercase'>{getParamValue(param)}</span>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(ModelInfo)
|
@ -7,7 +7,9 @@ import {
|
|||||||
RiArrowDownSLine,
|
RiArrowDownSLine,
|
||||||
RiArrowRightSLine,
|
RiArrowRightSLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
|
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||||
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
|
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
varList: { label: string; value: string }[]
|
varList: { label: string; value: string }[]
|
||||||
@ -23,34 +25,35 @@ const VarPanel: FC<Props> = ({
|
|||||||
const [imagePreviewUrl, setImagePreviewUrl] = useState('')
|
const [imagePreviewUrl, setImagePreviewUrl] = useState('')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='rounded-xl border border-color-indigo-100 bg-indigo-25'>
|
<div className='rounded-[10px] border border-divider-subtle bg-chat-bubble-bg'>
|
||||||
<div
|
<div
|
||||||
className='flex items-center h-6 pl-2 py-6 space-x-1 cursor-pointer'
|
className={cn('flex items-center gap-1 px-3 pt-2.5 pb-2 border-b border-divider-subtle text-text-secondary cursor-pointer', isCollapse && 'pb-2.5 border-0')}
|
||||||
onClick={toggleCollapse}
|
onClick={toggleCollapse}
|
||||||
>
|
>
|
||||||
|
<Variable02 className='w-4 h-4' />
|
||||||
|
<div className='grow system-md-medium'>{t('appLog.detail.variables')}</div>
|
||||||
{
|
{
|
||||||
isCollapse
|
isCollapse
|
||||||
? <RiArrowRightSLine className='w-3 h-3 text-gray-300' />
|
? <RiArrowRightSLine className='w-4 h-4' />
|
||||||
: <RiArrowDownSLine className='w-3 h-3 text-gray-300' />
|
: <RiArrowDownSLine className='w-4 h-4' />
|
||||||
}
|
}
|
||||||
<div className='text-sm font-semibold text-indigo-800 uppercase'>{t('appLog.detail.variables')}</div>
|
|
||||||
</div>
|
</div>
|
||||||
{!isCollapse && (
|
{!isCollapse && (
|
||||||
<div className='px-6 pb-3'>
|
<div className='p-3 flex flex-col gap-2'>
|
||||||
{varList.map(({ label, value }, index) => (
|
{varList.map(({ label, value }, index) => (
|
||||||
<div key={index} className='flex py-2 leading-[18px] text-[13px]'>
|
<div key={index} className='flex py-2 system-xs-medium'>
|
||||||
<div className='shrink-0 w-[128px] flex text-primary-600'>
|
<div className='shrink-0 w-[128px] flex text-text-accent'>
|
||||||
<span className='shrink-0 opacity-60'>{'{{'}</span>
|
<span className='shrink-0 opacity-60'>{'{{'}</span>
|
||||||
<span className='truncate'>{label}</span>
|
<span className='truncate'>{label}</span>
|
||||||
<span className='shrink-0 opacity-60'>{'}}'}</span>
|
<span className='shrink-0 opacity-60'>{'}}'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='pl-2.5 whitespace-pre-wrap'>{value}</div>
|
<div className='pl-2.5 whitespace-pre-wrap text-text-secondary'>{value}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{message_files.length > 0 && (
|
{message_files.length > 0 && (
|
||||||
<div className='mt-1 flex py-2'>
|
<div className='mt-1 flex py-2'>
|
||||||
<div className='shrink-0 w-[128px] leading-[18px] text-[13px] font-medium text-gray-700'>{t('appLog.detail.uploadImages')}</div>
|
<div className='shrink-0 w-[128px] system-xs-medium text-text-tertiary'>{t('appLog.detail.uploadImages')}</div>
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
{message_files.map((url, index) => (
|
{message_files.map((url, index) => (
|
||||||
<div
|
<div
|
||||||
@ -69,6 +72,7 @@ const VarPanel: FC<Props> = ({
|
|||||||
imagePreviewUrl && (
|
imagePreviewUrl && (
|
||||||
<ImagePreview
|
<ImagePreview
|
||||||
url={imagePreviewUrl}
|
url={imagePreviewUrl}
|
||||||
|
title={imagePreviewUrl}
|
||||||
onCancel={() => setImagePreviewUrl('')}
|
onCancel={() => setImagePreviewUrl('')}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -282,7 +282,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
|
|||||||
const [currentTab, setCurrentTab] = useState<string>('DETAIL')
|
const [currentTab, setCurrentTab] = useState<string>('DETAIL')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className={cn(isTop ? `rounded-xl border ${!isError ? 'border-gray-200 bg-white' : 'border-[#FECDCA] bg-[#FEF3F2]'} ` : 'rounded-br-xl !mt-0', className)}
|
<div ref={ref} className={cn(isTop ? `rounded-xl border ${!isError ? 'border-gray-200 bg-chat-bubble-bg' : 'border-[#FECDCA] bg-[#FEF3F2]'} ` : 'rounded-br-xl !mt-0', className)}
|
||||||
style={isTop
|
style={isTop
|
||||||
? {
|
? {
|
||||||
boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)',
|
boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)',
|
||||||
|
@ -114,7 +114,7 @@ const Answer: FC<AnswerProps> = ({
|
|||||||
<div className={cn('group relative pr-10', chatAnswerContainerInner)}>
|
<div className={cn('group relative pr-10', chatAnswerContainerInner)}>
|
||||||
<div
|
<div
|
||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
className={cn('relative inline-block px-4 py-3 max-w-full bg-gray-100 rounded-2xl text-sm text-gray-900', workflowProcess && 'w-full')}
|
className={cn('relative inline-block px-4 py-3 max-w-full bg-chat-bubble-bg rounded-2xl body-lg-regular text-text-primary', workflowProcess && 'w-full')}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
!responding && (
|
!responding && (
|
||||||
@ -212,15 +212,15 @@ const Answer: FC<AnswerProps> = ({
|
|||||||
disabled={!item.prevSibling}
|
disabled={!item.prevSibling}
|
||||||
onClick={() => item.prevSibling && switchSibling?.(item.prevSibling)}
|
onClick={() => item.prevSibling && switchSibling?.(item.prevSibling)}
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-[14px] h-[14px] rotate-180 text-gray-500" />
|
<ChevronRight className="w-[14px] h-[14px] rotate-180 text-text-tertiary" />
|
||||||
</button>
|
</button>
|
||||||
<span className="px-2 text-xs text-gray-700">{item.siblingIndex + 1} / {item.siblingCount}</span>
|
<span className="px-2 text-xs text-text-quaternary">{item.siblingIndex + 1} / {item.siblingCount}</span>
|
||||||
<button
|
<button
|
||||||
className={`${item.nextSibling ? 'opacity-100' : 'opacity-65'}`}
|
className={`${item.nextSibling ? 'opacity-100' : 'opacity-65'}`}
|
||||||
disabled={!item.nextSibling}
|
disabled={!item.nextSibling}
|
||||||
onClick={() => item.nextSibling && switchSibling?.(item.nextSibling)}
|
onClick={() => item.nextSibling && switchSibling?.(item.nextSibling)}
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-[14px] h-[14px] text-gray-500" />
|
<ChevronRight className="w-[14px] h-[14px] text-text-tertiary" />
|
||||||
</button>
|
</button>
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 212 B |
@ -1,14 +0,0 @@
|
|||||||
.wrapper {
|
|
||||||
border-color: #d0d5dd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checked {
|
|
||||||
background: #155eef url(./assets/check.svg) center center no-repeat;
|
|
||||||
background-size: 12px 12px;
|
|
||||||
border-color: #155eef;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checked.disabled {
|
|
||||||
background-color: #d0d5dd;
|
|
||||||
border-color: #d0d5dd;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import s from './index.module.css'
|
import { RiCheckLine } from '@remixicon/react'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type CheckboxProps = {
|
type CheckboxProps = {
|
||||||
@ -9,13 +9,27 @@ type CheckboxProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Checkbox = ({ checked, onCheck, className, disabled }: CheckboxProps) => {
|
const Checkbox = ({ checked, onCheck, className, disabled }: CheckboxProps) => {
|
||||||
|
if (!checked) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'w-4 h-4 rounded-[4px] bg-components-checkbox-bg-unchecked border border-components-checkbox-border hover:bg-components-checkbox-bg-unchecked-hover hover:border-components-checkbox-border-hover shadow-xs cursor-pointer',
|
||||||
|
disabled && 'border-components-checkbox-border-disabled bg-components-checkbox-bg-disabled hover:border-components-checkbox-border-disabled hover:bg-components-checkbox-bg-disabled cursor-not-allowed',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
if (disabled)
|
||||||
|
return
|
||||||
|
onCheck?.()
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
s.wrapper,
|
'w-4 h-4 flex items-center justify-center rounded-[4px] bg-components-checkbox-bg hover:bg-components-checkbox-bg-hover text-components-checkbox-icon shadow-xs cursor-pointer',
|
||||||
checked && s.checked,
|
disabled && 'bg-components-checkbox-bg-disabled-checked hover:bg-components-checkbox-bg-disabled-checked text-components-checkbox-icon-disabled cursor-not-allowed',
|
||||||
disabled && s.disabled,
|
|
||||||
'w-4 h-4 border rounded border-gray-300',
|
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -24,7 +38,9 @@ const Checkbox = ({ checked, onCheck, className, disabled }: CheckboxProps) => {
|
|||||||
|
|
||||||
onCheck?.()
|
onCheck?.()
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
<RiCheckLine className={cn('w-3 h-3')} />
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +39,10 @@ export const CopyIcon = ({ content }: Props) => {
|
|||||||
<div onMouseLeave={onMouseLeave}>
|
<div onMouseLeave={onMouseLeave}>
|
||||||
{!isCopied
|
{!isCopied
|
||||||
? (
|
? (
|
||||||
<Clipboard className='mx-1 w-3 h-3 text-gray-500 cursor-pointer' onClick={onClickCopy} />
|
<Clipboard className='mx-1 w-3.5 h-3.5 text-text-tertiary cursor-pointer' onClick={onClickCopy} />
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<ClipboardCheck className='mx-1 w-3 h-3 text-gray-500' />
|
<ClipboardCheck className='mx-1 w-3.5 h-3.5 text-text-tertiary' />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,15 +58,15 @@ const DrawerPlus: FC<Props> = ({
|
|||||||
panelClassname={cn('mt-16 mx-2 sm:mr-2 mb-3 !p-0 rounded-xl', panelClassName, maxWidthClassName)}
|
panelClassname={cn('mt-16 mx-2 sm:mr-2 mb-3 !p-0 rounded-xl', panelClassName, maxWidthClassName)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(contentClassName, 'w-full flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl')}
|
className={cn(contentClassName, 'w-full flex flex-col bg-components-panel-bg border-[0.5px] border-divider-subtle rounded-xl shadow-xl')}
|
||||||
style={{
|
style={{
|
||||||
height,
|
height,
|
||||||
}}
|
}}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<div className={cn(headerClassName, 'shrink-0 border-b border-b-gray-100 py-4')}>
|
<div className={cn(headerClassName, 'shrink-0 border-b border-divider-subtle py-4')}>
|
||||||
<div className='flex justify-between items-center pl-6 pr-5 h-6'>
|
<div className='flex justify-between items-center pl-6 pr-5 h-6'>
|
||||||
<div className='text-base font-semibold text-gray-900'>
|
<div className='system-xl-semibold text-text-primary'>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
@ -74,12 +74,12 @@ const DrawerPlus: FC<Props> = ({
|
|||||||
onClick={onHide}
|
onClick={onHide}
|
||||||
className='flex justify-center items-center w-6 h-6 cursor-pointer'
|
className='flex justify-center items-center w-6 h-6 cursor-pointer'
|
||||||
>
|
>
|
||||||
<RiCloseLine className='w-4 h-4 text-gray-500' />
|
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{titleDescription && (
|
{titleDescription && (
|
||||||
<div className='pl-6 pr-10 leading-[18px] text-xs font-normal text-gray-500'>
|
<div className='pl-6 pr-10 system-xs-regular text-text-tertiary'>
|
||||||
{titleDescription}
|
{titleDescription}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -49,7 +49,7 @@ export default function Drawer({
|
|||||||
<Dialog.Overlay
|
<Dialog.Overlay
|
||||||
className={cn('z-40 fixed inset-0', mask && 'bg-black bg-opacity-30')}
|
className={cn('z-40 fixed inset-0', mask && 'bg-black bg-opacity-30')}
|
||||||
/>
|
/>
|
||||||
<div className={cn('relative z-50 flex flex-col justify-between bg-white w-full max-w-sm p-6 overflow-hidden text-left align-middle shadow-xl', panelClassname)}>
|
<div className={cn('relative z-50 flex flex-col justify-between bg-background-body w-full max-w-sm p-6 overflow-hidden text-left align-middle shadow-xl', panelClassname)}>
|
||||||
<>
|
<>
|
||||||
{title && <Dialog.Title
|
{title && <Dialog.Title
|
||||||
as="h3"
|
as="h3"
|
||||||
|
@ -77,9 +77,9 @@ const ConfigParamModal: FC<Props> = ({
|
|||||||
<Modal
|
<Modal
|
||||||
isShow={isShow}
|
isShow={isShow}
|
||||||
onClose={onHide}
|
onClose={onHide}
|
||||||
className='!p-8 !pb-6 !mt-14 !max-w-none !w-[640px]'
|
className='!p-6 !mt-14 !max-w-none !w-[640px]'
|
||||||
>
|
>
|
||||||
<div className='mb-2 text-xl font-semibold text-[#1D2939]'>
|
<div className='mb-2 title-2xl-semibold text-text-primary'>
|
||||||
{t(`appAnnotation.initSetup.${isInit ? 'title' : 'configTitle'}`)}
|
{t(`appAnnotation.initSetup.${isInit ? 'title' : 'configTitle'}`)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -10,11 +10,11 @@ export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }>
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='flex items-center space-x-1'>
|
<div className='flex items-center space-x-1 mb-1'>
|
||||||
<div>{title}</div>
|
<div className='py-1 system-sm-semibold text-text-secondary'>{title}</div>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
popupContent={
|
popupContent={
|
||||||
<div className='max-w-[200px] leading-[18px] text-[13px] font-medium text-gray-800'>{tooltip}</div>
|
<div className='max-w-[200px] system-sm-regular text-text-secondary'>{tooltip}</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +26,7 @@ const Slider: React.FC<ISliderProps> = ({ className, max, min, step, value, disa
|
|||||||
renderThumb={(props, state) => (
|
renderThumb={(props, state) => (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<div className='relative w-full h-full'>
|
<div className='relative w-full h-full'>
|
||||||
<div className='absolute top-[-16px] left-[50%] translate-x-[-50%] leading-[18px] text-xs font-medium text-gray-900'>
|
<div className='absolute top-[-16px] left-[50%] translate-x-[-50%] system-sm-semibold text-text-primary'>
|
||||||
{(state.valueNow / 100).toFixed(2)}
|
{(state.valueNow / 100).toFixed(2)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,13 +28,13 @@ const ScoreSlider: FC<Props> = ({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-[10px] flex justify-between items-center leading-4 text-xs font-normal '>
|
<div className='mt-[10px] flex justify-between items-center system-xs-semibold-uppercase'>
|
||||||
<div className='flex space-x-1 text-[#00A286]'>
|
<div className='flex space-x-1 text-util-colors-cyan-cyan-500'>
|
||||||
<div>0.8</div>
|
<div>0.8</div>
|
||||||
<div>·</div>
|
<div>·</div>
|
||||||
<div>{t('appDebug.feature.annotation.scoreThreshold.easyMatch')}</div>
|
<div>{t('appDebug.feature.annotation.scoreThreshold.easyMatch')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex space-x-1 text-[#0057D8]'>
|
<div className='flex space-x-1 text-util-colors-blue-blue-500'>
|
||||||
<div>1.0</div>
|
<div>1.0</div>
|
||||||
<div>·</div>
|
<div>·</div>
|
||||||
<div>{t('appDebug.feature.annotation.scoreThreshold.accurateMatch')}</div>
|
<div>{t('appDebug.feature.annotation.scoreThreshold.accurateMatch')}</div>
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal-panel {
|
.modal-panel {
|
||||||
@apply w-full max-w-[480px] transform rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all;
|
@apply w-full max-w-[480px] transform rounded-2xl bg-components-panel-bg p-6 text-left align-middle shadow-xl transition-all;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ export default function Modal({
|
|||||||
}: IModal) {
|
}: IModal) {
|
||||||
return (
|
return (
|
||||||
<Transition appear show={isShow} as={Fragment}>
|
<Transition appear show={isShow} as={Fragment}>
|
||||||
<Dialog as="div" className={classNames('modal-dialog', wrapperClassName)} onClose={onClose}>
|
<Dialog as="div" className={classNames('relative z-50', wrapperClassName)} onClose={onClose}>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter="ease-out duration-300"
|
enter="ease-out duration-300"
|
||||||
@ -60,22 +60,22 @@ export default function Modal({
|
|||||||
leaveTo="opacity-0 scale-95"
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className={classNames(
|
<Dialog.Panel className={classNames(
|
||||||
'modal-panel',
|
'w-full max-w-[480px] transform rounded-2xl bg-components-panel-bg p-6 text-left align-middle shadow-xl transition-all',
|
||||||
overflowVisible ? 'overflow-visible' : 'overflow-hidden',
|
overflowVisible ? 'overflow-visible' : 'overflow-hidden',
|
||||||
className,
|
className,
|
||||||
)}>
|
)}>
|
||||||
{title && <Dialog.Title
|
{title && <Dialog.Title
|
||||||
as="h3"
|
as="h3"
|
||||||
className="text-lg font-medium leading-6 text-gray-900"
|
className="text-lg font-medium leading-6 text-text-primary"
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Dialog.Title>}
|
</Dialog.Title>}
|
||||||
{description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'>
|
{description && <Dialog.Description className='text-text-tertiary text-xs font-normal mt-2'>
|
||||||
{description}
|
{description}
|
||||||
</Dialog.Description>}
|
</Dialog.Description>}
|
||||||
{closable
|
{closable
|
||||||
&& <div className='absolute z-10 top-6 right-6 w-5 h-5 rounded-2xl flex items-center justify-center hover:cursor-pointer hover:bg-gray-100'>
|
&& <div className='absolute z-10 top-6 right-6 w-5 h-5 rounded-2xl flex items-center justify-center hover:cursor-pointer hover:bg-components-panel-on-panel-item-bg-hover'>
|
||||||
<XMarkIcon className='w-4 h-4 text-gray-500' onClick={
|
<XMarkIcon className='w-4 h-4 text-text-tertiary' onClick={
|
||||||
(e) => {
|
(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
onClose()
|
onClose()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Popover, Transition } from '@headlessui/react'
|
import { Popover, Transition } from '@headlessui/react'
|
||||||
import { Fragment, cloneElement, useRef } from 'react'
|
import { Fragment, cloneElement, useRef } from 'react'
|
||||||
import s from './style.module.css'
|
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
export type HtmlContentProps = {
|
export type HtmlContentProps = {
|
||||||
@ -63,19 +62,19 @@ export default function CustomPopover({
|
|||||||
<Popover.Button
|
<Popover.Button
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={`group ${s.popupBtn} ${open ? '' : 'bg-gray-100'} ${!btnClassName
|
className={cn(
|
||||||
? ''
|
'group inline-flex items-center bg-components-button-secondary-bg px-3 py-2 rounded-lg text-base border border-components-button-secondary-border font-medium hover:bg-components-button-secondary-bg-hover hover:border-components-button-secondary-border-hover focus:outline-none',
|
||||||
: typeof btnClassName === 'string'
|
open && 'bg-components-button-secondary-bg-hover border-components-button-secondary-border',
|
||||||
? btnClassName
|
(btnClassName && typeof btnClassName === 'string') && btnClassName,
|
||||||
: btnClassName?.(open)
|
(btnClassName && typeof btnClassName !== 'string') && btnClassName?.(open),
|
||||||
}`}
|
)}
|
||||||
>
|
>
|
||||||
{btnElement}
|
{btnElement}
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
<Transition as={Fragment}>
|
<Transition as={Fragment}>
|
||||||
<Popover.Panel
|
<Popover.Panel
|
||||||
className={cn(
|
className={cn(
|
||||||
s.popupPanel,
|
'absolute z-10 w-full max-w-sm px-4 mt-1 sm:px-0 lg:max-w-3xl',
|
||||||
position === 'bottom' && '-translate-x-1/2 left-1/2',
|
position === 'bottom' && '-translate-x-1/2 left-1/2',
|
||||||
position === 'bl' && 'left-0',
|
position === 'bl' && 'left-0',
|
||||||
position === 'br' && 'right-0',
|
position === 'br' && 'right-0',
|
||||||
@ -91,7 +90,7 @@ export default function CustomPopover({
|
|||||||
>
|
>
|
||||||
{({ close }) => (
|
{({ close }) => (
|
||||||
<div
|
<div
|
||||||
className={cn(s.panelContainer, popupClassName)}
|
className={cn('overflow-hidden bg-components-panel-bg w-fit min-w-[130px] rounded-lg shadow-lg ring-1 ring-black ring-opacity-5', popupClassName)}
|
||||||
{...(trigger !== 'hover'
|
{...(trigger !== 'hover'
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
.popupBtn {
|
|
||||||
@apply inline-flex items-center bg-white px-3 py-2 rounded-lg text-base border border-gray-200 font-medium hover:bg-gray-100 focus:outline-none
|
|
||||||
}
|
|
||||||
.popupPanel {
|
|
||||||
@apply absolute z-10 w-full max-w-sm px-4 mt-1 sm:px-0 lg:max-w-3xl
|
|
||||||
}
|
|
||||||
.panelContainer {
|
|
||||||
@apply overflow-hidden bg-white w-fit min-w-[130px] rounded-lg shadow-lg ring-1 ring-black ring-opacity-5
|
|
||||||
}
|
|
@ -23,12 +23,16 @@ const Item: FC<ItemProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={option.value}
|
key={option.value}
|
||||||
className={cn(className, !isActive && 'cursor-pointer', 'relative pb-2.5 leading-6 text-base font-semibold')}
|
className={cn(
|
||||||
|
'relative pb-2.5 system-xl-semibold',
|
||||||
|
!isActive && 'cursor-pointer',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
onClick={() => !isActive && onClick(option.value)}
|
onClick={() => !isActive && onClick(option.value)}
|
||||||
>
|
>
|
||||||
<div className={cn(isActive ? 'text-gray-900' : 'text-gray-600')}>{option.text}</div>
|
<div className={cn(isActive ? 'text-text-primary' : 'text-text-tertiary')}>{option.text}</div>
|
||||||
{isActive && (
|
{isActive && (
|
||||||
<div className='absolute bottom-0 left-0 right-0 h-0.5 bg-[#155EEF]'></div>
|
<div className='absolute bottom-0 left-0 right-0 h-0.5 bg-util-colors-blue-blue-500'></div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -52,7 +56,7 @@ const TabSlider: FC<Props> = ({
|
|||||||
itemClassName,
|
itemClassName,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={cn(className, !noBorderBottom && 'border-b border-[#EAECF0]', 'flex space-x-6')}>
|
<div className={cn(className, !noBorderBottom && 'border-b border-divider-subtle', 'flex space-x-6')}>
|
||||||
{options.map(option => (
|
{options.map(option => (
|
||||||
<Item
|
<Item
|
||||||
isActive={option.value === value}
|
isActive={option.value === value}
|
||||||
|
@ -96,7 +96,7 @@ const Tooltip: FC<TooltipProps> = ({
|
|||||||
>
|
>
|
||||||
{popupContent && (<div
|
{popupContent && (<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative px-3 py-2 text-xs font-normal text-gray-700 bg-white rounded-md shadow-lg break-words',
|
'relative px-3 py-2 system-xs-regular text-text-tertiary bg-components-panel-bg rounded-md shadow-lg break-words',
|
||||||
popupClassName,
|
popupClassName,
|
||||||
)}
|
)}
|
||||||
onMouseEnter={() => triggerMethod === 'hover' && setHoverPopup()}
|
onMouseEnter={() => triggerMethod === 'hover' && setHoverPopup()}
|
||||||
|
@ -11,7 +11,7 @@ const ModelBadge: FC<ModelBadgeProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(
|
<div className={classNames(
|
||||||
'flex items-center px-1 h-[18px] rounded-[5px] border border-black/8 bg-white/[0.48] text-[10px] font-medium text-gray-500 cursor-default',
|
'flex items-center px-1 h-[18px] rounded-[5px] border border-divider-deep system-2xs-medium-uppercase text-text-tertiary cursor-default',
|
||||||
className,
|
className,
|
||||||
)}>
|
)}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -7,7 +7,7 @@ import { useLanguage } from '../hooks'
|
|||||||
import type { ModelItem } from '../declarations'
|
import type { ModelItem } from '../declarations'
|
||||||
import ModelBadge from '../model-badge'
|
import ModelBadge from '../model-badge'
|
||||||
import FeatureIcon from '../model-selector/feature-icon'
|
import FeatureIcon from '../model-selector/feature-icon'
|
||||||
import classNames from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type ModelNameProps = PropsWithChildren<{
|
type ModelNameProps = PropsWithChildren<{
|
||||||
modelItem: ModelItem
|
modelItem: ModelItem
|
||||||
@ -37,12 +37,7 @@ const ModelName: FC<ModelNameProps> = ({
|
|||||||
if (!modelItem)
|
if (!modelItem)
|
||||||
return null
|
return null
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cn('flex items-center truncate text-components-input-text-filled system-sm-regular', className)}>
|
||||||
className={`
|
|
||||||
flex items-center truncate text-[13px] font-medium text-gray-800
|
|
||||||
${className}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className='truncate'
|
className='truncate'
|
||||||
title={modelItem.label[language] || modelItem.label.en_US}
|
title={modelItem.label[language] || modelItem.label.en_US}
|
||||||
@ -51,14 +46,14 @@ const ModelName: FC<ModelNameProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
showModelType && modelItem.model_type && (
|
showModelType && modelItem.model_type && (
|
||||||
<ModelBadge className={classNames('ml-1', modelTypeClassName)}>
|
<ModelBadge className={cn('ml-1', modelTypeClassName)}>
|
||||||
{modelTypeFormat(modelItem.model_type)}
|
{modelTypeFormat(modelItem.model_type)}
|
||||||
</ModelBadge>
|
</ModelBadge>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
modelItem.model_properties.mode && showMode && (
|
modelItem.model_properties.mode && showMode && (
|
||||||
<ModelBadge className={classNames('ml-1', modeClassName)}>
|
<ModelBadge className={cn('ml-1', modeClassName)}>
|
||||||
{(modelItem.model_properties.mode as string).toLocaleUpperCase()}
|
{(modelItem.model_properties.mode as string).toLocaleUpperCase()}
|
||||||
</ModelBadge>
|
</ModelBadge>
|
||||||
)
|
)
|
||||||
|
@ -21,9 +21,10 @@ const ToolsNav = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href="/tools" className={classNames(
|
<Link href="/tools" className={classNames(
|
||||||
className, 'group',
|
'group text-sm font-medium',
|
||||||
activated && 'bg-white shadow-md',
|
activated && 'font-semibold bg-components-main-nav-nav-button-bg-active hover:bg-components-main-nav-nav-button-bg-active-hover shadow-md',
|
||||||
activated ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200',
|
activated ? 'text-components-main-nav-nav-button-text-active' : 'text-components-main-nav-nav-button-text hover:bg-components-main-nav-nav-button-bg-hover',
|
||||||
|
className,
|
||||||
)}>
|
)}>
|
||||||
{
|
{
|
||||||
activated
|
activated
|
||||||
|
@ -75,9 +75,9 @@ const Base: FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrap className={cn(wrapClassName)} style={wrapStyle} isInNode={isInNode} isExpand={isExpand}>
|
<Wrap className={cn(wrapClassName)} style={wrapStyle} isInNode={isInNode} isExpand={isExpand}>
|
||||||
<div ref={ref} className={cn(className, isExpand && 'h-full', 'rounded-lg border', isFocus ? 'bg-white border-gray-200' : 'bg-gray-100 border-gray-100 overflow-hidden')}>
|
<div ref={ref} className={cn(className, isExpand && 'h-full', 'rounded-lg border', isFocus ? 'bg-components-input-bg-normal border-transparent' : 'bg-components-input-bg-hover border-components-input-border-hover overflow-hidden')}>
|
||||||
<div className='flex justify-between items-center h-7 pt-1 pl-3 pr-2'>
|
<div className='flex justify-between items-center h-7 pt-1 pl-3 pr-2'>
|
||||||
<div className='text-xs font-semibold text-gray-700'>{title}</div>
|
<div className='system-xs-semibold-uppercase text-text-secondary'>{title}</div>
|
||||||
<div className='flex items-center' onClick={(e) => {
|
<div className='flex items-center' onClick={(e) => {
|
||||||
e.nativeEvent.stopImmediatePropagation()
|
e.nativeEvent.stopImmediatePropagation()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@ -90,10 +90,10 @@ const Base: FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
{!isCopied
|
{!isCopied
|
||||||
? (
|
? (
|
||||||
<Clipboard className='mx-1 w-3.5 h-3.5 text-gray-500 cursor-pointer' onClick={handleCopy} />
|
<Clipboard className='mx-1 w-3.5 h-3.5 text-text-tertiary cursor-pointer' onClick={handleCopy} />
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<ClipboardCheck className='mx-1 w-3.5 h-3.5 text-gray-500' />
|
<ClipboardCheck className='mx-1 w-3.5 h-3.5 text-text-tertiary' />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ const translation = {
|
|||||||
},
|
},
|
||||||
variables: 'Variables',
|
variables: 'Variables',
|
||||||
uploadImages: 'Uploaded Images',
|
uploadImages: 'Uploaded Images',
|
||||||
|
modelParams: 'Model parameters',
|
||||||
},
|
},
|
||||||
filter: {
|
filter: {
|
||||||
period: {
|
period: {
|
||||||
|
@ -52,6 +52,7 @@ const translation = {
|
|||||||
},
|
},
|
||||||
variables: '变量',
|
variables: '变量',
|
||||||
uploadImages: '上传的图片',
|
uploadImages: '上传的图片',
|
||||||
|
modelParams: '模型参数',
|
||||||
},
|
},
|
||||||
filter: {
|
filter: {
|
||||||
period: {
|
period: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user