| @@ -141,7 +141,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => { | |||
| if (!appDetail) { | |||
| 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 /> | |||
| </div> | |||
| ) | |||
| @@ -152,7 +152,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => { | |||
| {appDetail && ( | |||
| <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} | |||
| </div> | |||
| </div> | |||
| @@ -2,7 +2,7 @@ | |||
| import type { FC } from 'react' | |||
| import React from 'react' | |||
| 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' | |||
| export enum EditItemType { | |||
| @@ -31,12 +31,10 @@ const EditItem: FC<Props> = ({ | |||
| {avatar} | |||
| </div> | |||
| <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 | |||
| className='mt-1 block w-full leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none' | |||
| value={content} | |||
| onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => onChange(e.target.value)} | |||
| autoSize={{ minRows: 3 }} | |||
| placeholder={placeholder} | |||
| autoFocus | |||
| /> | |||
| @@ -4,6 +4,7 @@ import React, { useState } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import type { AnnotationItemBasic } from '../type' | |||
| import EditItem, { EditItemType } from './edit-item' | |||
| import Checkbox from '@/app/components/base/checkbox' | |||
| import Drawer from '@/app/components/base/drawer-plus' | |||
| import Button from '@/app/components/base/button' | |||
| import Toast from '@/app/components/base/toast' | |||
| @@ -96,11 +97,11 @@ const AddAnnotationModal: FC<Props> = ({ | |||
| <AnnotationFull /> | |||
| </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 | |||
| 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> | |||
| <div className='mt-2 flex space-x-2'> | |||
| @@ -33,19 +33,19 @@ const CSVDownload: FC = () => { | |||
| return ( | |||
| <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'> | |||
| <table className='table-fixed w-full border-separate border-spacing-0 border border-gray-200 rounded-lg text-xs'> | |||
| <thead className='text-gray-500'> | |||
| <table className='table-fixed w-full border-separate border-spacing-0 border border-divider-regular rounded-lg text-xs'> | |||
| <thead className='text-text-tertiary'> | |||
| <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-gray-200'>{t('appAnnotation.batchModal.answer')}</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-divider-regular'>{t('appAnnotation.batchModal.answer')}</td> | |||
| </tr> | |||
| </thead> | |||
| <tbody className='text-gray-700'> | |||
| <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-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.question')} 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> | |||
| <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} | |||
| 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' /> | |||
| {t('appAnnotation.batchModal.template')} | |||
| </div> | |||
| @@ -91,29 +91,29 @@ const CSVUploader: FC<Props> = ({ | |||
| /> | |||
| <div ref={dropRef}> | |||
| {!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'> | |||
| <CSVIcon className="shrink-0" /> | |||
| <div className='text-gray-500'> | |||
| <div className='text-text-tertiary'> | |||
| {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> | |||
| {dragging && <div ref={dragRef} className='absolute w-full h-full top-0 left-0' />} | |||
| </div> | |||
| )} | |||
| {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" /> | |||
| <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='shrink-0 text-gray-500'>.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-text-tertiary'>.csv</span> | |||
| </div> | |||
| <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> | |||
| <div className='mx-2 w-px h-4 bg-gray-200' /> | |||
| <Button variant='secondary' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button> | |||
| <div className='mx-2 w-px h-4 bg-divider-regular' /> | |||
| <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> | |||
| @@ -88,9 +88,9 @@ const BatchModal: FC<IBatchModalProps> = ({ | |||
| return ( | |||
| <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}> | |||
| <RiCloseLine className='w-4 h-4 text-gray-500' /> | |||
| <RiCloseLine className='w-4 h-4 text-text-tertiary' /> | |||
| </div> | |||
| <CSVUploader | |||
| file={currentCSV} | |||
| @@ -105,11 +105,10 @@ const BatchModal: FC<IBatchModalProps> = ({ | |||
| )} | |||
| <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')} | |||
| </Button> | |||
| <Button | |||
| className='text-sm font-medium' | |||
| variant="primary" | |||
| onClick={handleSend} | |||
| disabled={isAnnotationFull || !currentCSV} | |||
| @@ -2,13 +2,11 @@ | |||
| import type { FC } from 'react' | |||
| import React, { useState } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import Textarea from 'rc-textarea' | |||
| import { RiDeleteBinLine } from '@remixicon/react' | |||
| import cn from '@/utils/classnames' | |||
| import { RiDeleteBinLine, RiEditFill, RiEditLine } from '@remixicon/react' | |||
| import { Robot, User } from '@/app/components/base/icons/src/public/avatar' | |||
| import { Edit04 } from '@/app/components/base/icons/src/vender/line/general' | |||
| import { Edit04 as EditSolid } from '@/app/components/base/icons/src/vender/solid/general' | |||
| import Textarea from '@/app/components/base/textarea' | |||
| import Button from '@/app/components/base/button' | |||
| import cn from '@/utils/classnames' | |||
| export enum EditItemType { | |||
| Query = 'query', | |||
| @@ -22,8 +20,8 @@ type Props = { | |||
| } | |||
| 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')}> | |||
| <EditSolid className='mr-1 w-3.5 h-3.5' /> | |||
| <div className={cn(className, 'flex items-center h-[18px] system-xs-medium text-text-tertiary')}> | |||
| <RiEditFill className='mr-1 w-3.5 h-3.5' /> | |||
| <div>{title}</div> | |||
| <div | |||
| className='ml-2 grow h-[1px]' | |||
| @@ -64,32 +62,32 @@ const EditItem: FC<Props> = ({ | |||
| {avatar} | |||
| </div> | |||
| <div className='grow'> | |||
| <div className='mb-1 leading-[18px] text-xs font-semibold text-gray-900'>{name}</div> | |||
| <div className='leading-5 text-sm font-normal text-gray-900'>{content}</div> | |||
| <div className='mb-1 system-xs-semibold text-text-primary'>{name}</div> | |||
| <div className='system-sm-regular text-text-primary'>{content}</div> | |||
| {!isEdit | |||
| ? ( | |||
| <div> | |||
| {showNewContent && ( | |||
| <div className='mt-3'> | |||
| <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 className='mt-2 flex items-center'> | |||
| {!readonly && ( | |||
| <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={() => { | |||
| 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> | |||
| )} | |||
| {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='flex items-center space-x-1 cursor-pointer' | |||
| @@ -112,10 +110,8 @@ const EditItem: FC<Props> = ({ | |||
| <div className='mt-3'> | |||
| <EditTitle title={editTitle} /> | |||
| <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} | |||
| onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setNewContent(e.target.value)} | |||
| autoSize={{ minRows: 3 }} | |||
| placeholder={placeholder} | |||
| autoFocus | |||
| /> | |||
| @@ -123,7 +123,7 @@ const EditAnnotationModal: FC<Props> = ({ | |||
| { | |||
| 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 | |||
| className='flex items-center pl-3 space-x-2 cursor-pointer' | |||
| onClick={() => setShowModal(true)} | |||
| @@ -4,6 +4,7 @@ import React, { Fragment, useEffect, useState } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { | |||
| RiAddLine, | |||
| RiMoreFill, | |||
| } from '@remixicon/react' | |||
| import { useContext } from 'use-context-selector' | |||
| import { | |||
| @@ -14,7 +15,6 @@ import Button from '../../../base/button' | |||
| import AddAnnotationModal from '../add-annotation-modal' | |||
| import type { AnnotationItemBasic } from '../type' | |||
| import BatchAddModal from '../batch-add-annotation-modal' | |||
| import s from './style.module.css' | |||
| import cn from '@/utils/classnames' | |||
| import CustomPopover from '@/app/components/base/popover' | |||
| import { FileDownload02, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files' | |||
| @@ -80,17 +80,17 @@ const HeaderOptions: FC<Props> = ({ | |||
| const Operations = () => { | |||
| return ( | |||
| <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) | |||
| }}> | |||
| <FilePlus02 className={s.actionItemIcon} /> | |||
| <span className={s.actionName}>{t('appAnnotation.table.header.bulkImport')}</span> | |||
| <FilePlus02 className='w-4 h-4 text-text-tertiary' /> | |||
| <span className='grow text-text-secondary system-sm-regular text-left'>{t('appAnnotation.table.header.bulkImport')}</span> | |||
| </button> | |||
| <Menu as="div" className="relative w-full h-full"> | |||
| <Menu.Button className={s.actionItem}> | |||
| <FileDownload02 className={s.actionItemIcon} /> | |||
| <span className={s.actionName}>{t('appAnnotation.table.header.bulkExport')}</span> | |||
| <ChevronRight className='shrink-0 w-[14px] h-[14px] text-gray-500' /> | |||
| <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='w-4 h-4 text-text-tertiary' /> | |||
| <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-text-tertiary' /> | |||
| </Menu.Button> | |||
| <Transition | |||
| as={Fragment} | |||
| @@ -103,11 +103,7 @@ const HeaderOptions: FC<Props> = ({ | |||
| > | |||
| <Menu.Items | |||
| className={cn( | |||
| ` | |||
| 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, | |||
| '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', | |||
| )} | |||
| > | |||
| <CSVDownloader | |||
| @@ -119,12 +115,12 @@ const HeaderOptions: FC<Props> = ({ | |||
| ...list.map(item => [item.question, item.answer]), | |||
| ]} | |||
| > | |||
| <button disabled={annotationUnavailable} className={s.actionItem}> | |||
| <span className={s.actionName}>CSV</span> | |||
| <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='grow text-text-secondary system-sm-regular text-left'>CSV</span> | |||
| </button> | |||
| </CSVDownloader> | |||
| <button disabled={annotationUnavailable} className={cn(s.actionItem, '!border-0')} onClick={JSONLOutput}> | |||
| <span className={s.actionName}>JSONL</span> | |||
| <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='grow text-text-secondary system-sm-regular text-left'>JSONL</span> | |||
| </button> | |||
| </Menu.Items> | |||
| </Transition> | |||
| @@ -137,21 +133,20 @@ const HeaderOptions: FC<Props> = ({ | |||
| return ( | |||
| <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'> | |||
| <RiAddLine className='w-4 h-4' /> | |||
| <Button variant='primary' onClick={() => setShowAddModal(true)}> | |||
| <RiAddLine className='w-4 h-4 mr-0.5' /> | |||
| <div>{t('appAnnotation.table.header.addAnnotation')}</div> | |||
| </Button> | |||
| <CustomPopover | |||
| htmlContent={<Operations />} | |||
| position="br" | |||
| trigger="click" | |||
| btnElement={<div className={cn(s.actionIcon, s.commonIcon)} />} | |||
| btnClassName={open => | |||
| cn( | |||
| open ? 'border-gray-300 !bg-gray-100 !shadow-none' : 'border-gray-200', | |||
| s.actionIconWrapper, | |||
| ) | |||
| btnElement={ | |||
| <Button variant='secondary' className='w-8 p-0'> | |||
| <RiMoreFill className='w-4 h-4' /> | |||
| </Button> | |||
| } | |||
| btnClassName='p-0 border-0' | |||
| className={'!w-[155px] h-fit !z-20'} | |||
| popupClassName='!w-full !overflow-visible' | |||
| 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 { useTranslation } from 'react-i18next' | |||
| import { useDebounce } from 'ahooks' | |||
| import { RiEqualizer2Line } from '@remixicon/react' | |||
| import Toast from '../../base/toast' | |||
| import Filter from './filter' | |||
| import type { QueryParam } from './filter' | |||
| @@ -11,7 +12,8 @@ import EmptyElement from './empty-element' | |||
| import HeaderOpts from './header-opts' | |||
| import { AnnotationEnableStatus, type AnnotationItem, type AnnotationItemBasic, JobStatus } from './type' | |||
| 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 Switch from '@/app/components/base/switch' | |||
| 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 { useProviderContext } from '@/context/provider-context' | |||
| 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 cn from '@/utils/classnames' | |||
| type Props = { | |||
| appDetail: App | |||
| @@ -157,8 +159,9 @@ const Annotation: FC<Props> = ({ | |||
| <div className='flex items-center space-x-2'> | |||
| {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='leading-[18px] text-[13px] font-medium text-gray-900'>{t('appAnnotation.name')}</div> | |||
| <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')}> | |||
| <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 | |||
| key={controlRefreshSwitch} | |||
| defaultValue={annotationConfig?.enabled} | |||
| @@ -185,22 +188,14 @@ const Annotation: FC<Props> = ({ | |||
| ></Switch> | |||
| {annotationConfig?.enabled && ( | |||
| <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 h-7 w-7 flex items-center justify-center | |||
| 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 className='shrink-0 mr-1 w-[1px] h-3.5 bg-divider-subtle'></div> | |||
| <ActionButton onClick={() => setIsShowEdit(true)}> | |||
| <RiEqualizer2Line className='w-4 h-4 text-text-tertiary' /> | |||
| </ActionButton> | |||
| </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 React from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { RiDeleteBinLine } from '@remixicon/react' | |||
| import { Edit02 } from '../../base/icons/src/vender/line/general' | |||
| import { RiDeleteBinLine, RiEditLine } from '@remixicon/react' | |||
| import type { AnnotationItem } from './type' | |||
| 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 cn from '@/utils/classnames' | |||
| type Props = { | |||
| list: AnnotationItem[] | |||
| @@ -59,26 +59,18 @@ const List: FC<Props> = ({ | |||
| <td className='p-3 pr-2'>{item.hit_count}</td> | |||
| <td className='w-[96px] p-3 pr-2' onClick={e => e.stopPropagation()}> | |||
| {/* Actions */} | |||
| <div className='flex space-x-2 text-gray-500'> | |||
| <div | |||
| className='p-1 cursor-pointer rounded-md hover:bg-black/5' | |||
| onClick={ | |||
| () => { | |||
| onView(item) | |||
| } | |||
| } | |||
| > | |||
| <Edit02 className='w-4 h-4' /> | |||
| </div> | |||
| <div | |||
| className='p-1 cursor-pointer rounded-md hover:bg-black/5' | |||
| <div className='flex space-x-1 text-text-tertiary'> | |||
| <ActionButton onClick={() => onView(item)}> | |||
| <RiEditLine className='w-4 h-4' /> | |||
| </ActionButton> | |||
| <ActionButton | |||
| onClick={() => { | |||
| setCurrId(item.id) | |||
| setShowConfirmDelete(true) | |||
| }} | |||
| > | |||
| <RiDeleteBinLine className='w-4 h-4' /> | |||
| </div> | |||
| </ActionButton> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| @@ -7,11 +7,11 @@ import { ClockFastForward } from '@/app/components/base/icons/src/vender/line/ti | |||
| const HitHistoryNoData: FC = () => { | |||
| const { t } = useTranslation() | |||
| return ( | |||
| <div className='mx-auto mt-20 w-[480px] p-5 rounded-2xl bg-gray-50 space-y-2'> | |||
| <div className='inline-block p-3 rounded-lg border border-gray-200'> | |||
| <ClockFastForward className='w-5 h-5 text-gray-500' /> | |||
| <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-divider-subtle'> | |||
| <ClockFastForward className='w-5 h-5 text-text-tertiary' /> | |||
| </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> | |||
| ) | |||
| } | |||
| @@ -4,17 +4,17 @@ import React, { useEffect, useState } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import EditItem, { EditItemType } from '../edit-annotation-modal/edit-item' | |||
| import type { AnnotationItem, HitHistoryItem } from '../type' | |||
| import s from './style.module.css' | |||
| import HitHistoryNoData from './hit-history-no-data' | |||
| import cn from '@/utils/classnames' | |||
| import Pagination from '@/app/components/base/pagination' | |||
| import Badge from '@/app/components/base/badge' | |||
| 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 Confirm from '@/app/components/base/confirm' | |||
| import TabSlider from '@/app/components/base/tab-slider-plain' | |||
| import { fetchHitHistoryList } from '@/service/annotation' | |||
| import { APP_PAGE_LIMIT } from '@/config' | |||
| import useTimestamp from '@/hooks/use-timestamp' | |||
| import cn from '@/utils/classnames' | |||
| type Props = { | |||
| appId: string | |||
| @@ -72,7 +72,9 @@ const ViewAnnotationModal: FC<Props> = ({ | |||
| ? ( | |||
| <div className='flex items-center space-x-1'> | |||
| <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> | |||
| ) | |||
| : t('appAnnotation.viewModal.hitHistory') | |||
| @@ -111,44 +113,45 @@ const ViewAnnotationModal: FC<Props> = ({ | |||
| ? (<HitHistoryNoData />) | |||
| : ( | |||
| <div> | |||
| <table className={cn(s.table, 'w-full min-w-[440px] border-collapse border-0 text-sm')} > | |||
| <thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-bold"> | |||
| <tr className='uppercase'> | |||
| <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.query')}</td> | |||
| <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.match')}</td> | |||
| <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.response')}</td> | |||
| <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.source')}</td> | |||
| <td className='whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.score')}</td> | |||
| <td className='whitespace-nowrap w-[160px]'>{t('appAnnotation.hitHistoryTable.time')}</td> | |||
| <table className={cn('w-full min-w-[440px] border-collapse border-0')} > | |||
| <thead className="system-xs-medium-uppercase text-text-tertiary"> | |||
| <tr> | |||
| <td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.query')}</td> | |||
| <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.match')}</td> | |||
| <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.response')}</td> | |||
| <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.source')}</td> | |||
| <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.score')}</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> | |||
| </thead> | |||
| <tbody className="text-gray-500"> | |||
| <tbody className="text-text-secondary system-sm-regular"> | |||
| {hitHistoryList.map(item => ( | |||
| <tr | |||
| 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 | |||
| 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} | |||
| >{item.question}</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} | |||
| >{item.match}</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} | |||
| >{item.response}</td> | |||
| <td>{item.source}</td> | |||
| <td>{item.score ? item.score.toFixed(2) : '-'}</td> | |||
| <td>{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td> | |||
| <td className='p-3 pr-2'>{item.source}</td> | |||
| <td className='p-3 pr-2'>{item.score ? item.score.toFixed(2) : '-'}</td> | |||
| <td className='p-3 pr-2'>{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td> | |||
| </tr> | |||
| ))} | |||
| </tbody> | |||
| </table> | |||
| {(total && total > APP_PAGE_LIMIT) | |||
| ? <Pagination | |||
| className='px-0' | |||
| current={currPage} | |||
| onChange={setCurrPage} | |||
| total={total} | |||
| @@ -163,7 +166,6 @@ const ViewAnnotationModal: FC<Props> = ({ | |||
| isShow={isShow} | |||
| onHide={onHide} | |||
| maxWidthClassName='!max-w-[800px]' | |||
| // t('appAnnotation.editModal.title') as string | |||
| title={ | |||
| <TabSlider | |||
| className='shrink-0 relative top-[9px]' | |||
| @@ -193,7 +195,7 @@ const ViewAnnotationModal: FC<Props> = ({ | |||
| )} | |||
| 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 | |||
| className='flex items-center pl-3 space-x-2 cursor-pointer' | |||
| 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) { | |||
| 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 /> | |||
| </div> | |||
| ) | |||
| @@ -5,9 +5,8 @@ import useSWR from 'swr' | |||
| import { | |||
| HandThumbDownIcon, | |||
| HandThumbUpIcon, | |||
| XMarkIcon, | |||
| } from '@heroicons/react/24/outline' | |||
| import { RiEditFill, RiQuestionLine } from '@remixicon/react' | |||
| import { RiCloseLine, RiEditFill } from '@remixicon/react' | |||
| import { get } from 'lodash-es' | |||
| import InfiniteScroll from 'react-infinite-scroll-component' | |||
| import dayjs from 'dayjs' | |||
| @@ -18,20 +17,16 @@ import { useShallow } from 'zustand/react/shallow' | |||
| import { useTranslation } from 'react-i18next' | |||
| import type { ChatItemInTree } from '../../base/chat/types' | |||
| 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 { Annotation, ChatConversationGeneralDetail, ChatConversationsResponse, ChatMessage, ChatMessagesRequest, CompletionConversationGeneralDetail, CompletionConversationsResponse, LogAnnotation } from '@/models/log' | |||
| import type { App } from '@/types/app' | |||
| import ActionButton from '@/app/components/base/action-button' | |||
| import Loading from '@/app/components/base/loading' | |||
| import Drawer from '@/app/components/base/drawer' | |||
| import Popover from '@/app/components/base/popover' | |||
| import Chat from '@/app/components/base/chat/chat' | |||
| import { ToastContext } from '@/app/components/base/toast' | |||
| import { fetchChatConversationDetail, fetchChatMessages, fetchCompletionConversationDetail, updateLogMessageAnnotations, updateLogMessageFeedbacks } from '@/service/log' | |||
| import { TONE_LIST } from '@/config' | |||
| 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 ModelInfo from '@/app/components/app/log/model-info' | |||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | |||
| import TextGeneration from '@/app/components/app/text-generate/item' | |||
| 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 { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils' | |||
| import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' | |||
| import cn from '@/utils/classnames' | |||
| dayjs.extend(utc) | |||
| dayjs.extend(timezone) | |||
| @@ -75,15 +71,6 @@ const HandThumbIconWithCount: FC<{ count: number; iconType: 'up' | 'down' }> = ( | |||
| </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 newChatList: IChatItem[] = [] | |||
| messages.forEach((item: ChatMessage) => { | |||
| @@ -156,9 +143,6 @@ const getFormattedChatList = (messages: ChatMessage[], conversationId: string, t | |||
| return newChatList | |||
| } | |||
| // const displayedParams = CompletionParams.slice(0, -2) | |||
| const validatedParams = ['temperature', 'top_p', 'presence_penalty', 'frequency_penalty'] | |||
| type IDetailPanel = { | |||
| detail: any | |||
| onFeedback: FeedbackFunc | |||
| @@ -315,22 +299,6 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) { | |||
| const isChatMode = appDetail?.mode !== 'completion' | |||
| 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 itemContent = item[Object.keys(item)[0]] | |||
| return { | |||
| @@ -342,18 +310,6 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) { | |||
| ? 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 ref = useRef<HTMLDivElement>(null) | |||
| @@ -367,162 +323,71 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) { | |||
| }, []) | |||
| 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 */} | |||
| <div className='border-b border-gray-100 py-4 px-6 flex items-center justify-between bg-components-panel-bg'> | |||
| <div> | |||
| <div className='text-gray-500 text-[10px] leading-[14px]'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div> | |||
| <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 className='shrink-0'> | |||
| <div className='mb-0.5 text-text-primary system-xs-semibold-uppercase'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div> | |||
| {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 | |||
| popupContent={detail.id} | |||
| > | |||
| <div className='max-w-[105px] truncate'>{detail.id}</div> | |||
| <div className='truncate'>{detail.id}</div> | |||
| </Tooltip> | |||
| <CopyIcon content={detail.id} /> | |||
| </div> | |||
| )} | |||
| {!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 className='flex items-center flex-wrap gap-y-1 justify-end'> | |||
| {!isAdvanced && ( | |||
| <> | |||
| <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 className='grow flex items-center flex-wrap gap-y-1 justify-end'> | |||
| {!isAdvanced && <ModelInfo model={detail.model_config.model} />} | |||
| </div> | |||
| <ActionButton size='l' onClick={onClose}> | |||
| <RiCloseLine className='w-4 h-4 text-text-tertiary' /> | |||
| </ActionButton> | |||
| </div> | |||
| {/* Panel Body */} | |||
| {(varList.length > 0 || (!isChatMode && message_files.length > 0)) && ( | |||
| <div className='px-6 pt-4 pb-2'> | |||
| <VarPanel | |||
| varList={varList} | |||
| message_files={message_files} | |||
| /> | |||
| </div> | |||
| )} | |||
| {!isChatMode | |||
| ? <div className="px-6 py-4"> | |||
| <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='grow h-[1px]' style={{ | |||
| background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, rgb(243, 244, 246) 100%)', | |||
| }}></div> | |||
| </div> | |||
| <TextGeneration | |||
| className='mt-2' | |||
| content={detail.message.answer} | |||
| messageId={detail.message.id} | |||
| isError={false} | |||
| onRetry={() => { }} | |||
| isInstalledApp={false} | |||
| supportFeedback | |||
| feedback={detail.message.feedbacks.find((item: any) => item.from_source === 'admin')} | |||
| onFeedback={feedback => onFeedback(detail.message.id, feedback)} | |||
| supportAnnotation | |||
| isShowTextToSpeech | |||
| appId={appDetail?.id} | |||
| varList={varList} | |||
| siteInfo={null} | |||
| /> | |||
| <div className='shrink-0 pt-1 px-1'> | |||
| <div className='p-3 pb-2 rounded-t-xl bg-background-section-burn'> | |||
| {(varList.length > 0 || (!isChatMode && message_files.length > 0)) && ( | |||
| <VarPanel | |||
| varList={varList} | |||
| message_files={message_files} | |||
| /> | |||
| )} | |||
| </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 className='grow mx-1 mb-1 bg-background-section-burn rounded-b-xl overflow-auto'> | |||
| {!isChatMode | |||
| ? <div className="px-6 py-4"> | |||
| <div className='flex h-[18px] items-center space-x-3'> | |||
| <div className='text-text-tertiary system-xs-semibold-uppercase'>{t('appLog.table.header.output')}</div> | |||
| <div className='grow h-[1px]' style={{ | |||
| background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, rgb(243, 244, 246) 100%)', | |||
| }}></div> | |||
| </div> | |||
| <TextGeneration | |||
| className='mt-2' | |||
| content={detail.message.answer} | |||
| messageId={detail.message.id} | |||
| isError={false} | |||
| onRetry={() => { }} | |||
| isInstalledApp={false} | |||
| supportFeedback | |||
| feedback={detail.message.feedbacks.find((item: any) => item.from_source === 'admin')} | |||
| onFeedback={feedback => onFeedback(detail.message.id, feedback)} | |||
| supportAnnotation | |||
| isShowTextToSpeech | |||
| appId={appDetail?.id} | |||
| varList={varList} | |||
| siteInfo={null} | |||
| /> | |||
| </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-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} | |||
| > | |||
| : threadChatItems.length < 8 | |||
| ? <div className="pt-4 mb-4"> | |||
| <Chat | |||
| config={{ | |||
| appId: appDetail?.id, | |||
| @@ -543,12 +408,68 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) { | |||
| noChatInput | |||
| showPromptLog | |||
| hideProcessDetail | |||
| chatContainerInnerClassName='px-6' | |||
| chatContainerInnerClassName='px-3' | |||
| 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 && ( | |||
| <MessageLogModal | |||
| width={width} | |||
| @@ -780,7 +701,7 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh }) | |||
| onClose={onCloseDrawer} | |||
| mask={isMobile} | |||
| 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={{ | |||
| onClose: onCloseDrawer, | |||
| @@ -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, | |||
| RiArrowRightSLine, | |||
| } 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 cn from '@/utils/classnames' | |||
| type Props = { | |||
| varList: { label: string; value: string }[] | |||
| @@ -23,34 +25,35 @@ const VarPanel: FC<Props> = ({ | |||
| const [imagePreviewUrl, setImagePreviewUrl] = useState('') | |||
| 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 | |||
| 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} | |||
| > | |||
| <Variable02 className='w-4 h-4' /> | |||
| <div className='grow system-md-medium'>{t('appLog.detail.variables')}</div> | |||
| { | |||
| isCollapse | |||
| ? <RiArrowRightSLine className='w-3 h-3 text-gray-300' /> | |||
| : <RiArrowDownSLine className='w-3 h-3 text-gray-300' /> | |||
| ? <RiArrowRightSLine className='w-4 h-4' /> | |||
| : <RiArrowDownSLine className='w-4 h-4' /> | |||
| } | |||
| <div className='text-sm font-semibold text-indigo-800 uppercase'>{t('appLog.detail.variables')}</div> | |||
| </div> | |||
| {!isCollapse && ( | |||
| <div className='px-6 pb-3'> | |||
| <div className='p-3 flex flex-col gap-2'> | |||
| {varList.map(({ label, value }, index) => ( | |||
| <div key={index} className='flex py-2 leading-[18px] text-[13px]'> | |||
| <div className='shrink-0 w-[128px] flex text-primary-600'> | |||
| <div key={index} className='flex py-2 system-xs-medium'> | |||
| <div className='shrink-0 w-[128px] flex text-text-accent'> | |||
| <span className='shrink-0 opacity-60'>{'{{'}</span> | |||
| <span className='truncate'>{label}</span> | |||
| <span className='shrink-0 opacity-60'>{'}}'}</span> | |||
| </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> | |||
| ))} | |||
| {message_files.length > 0 && ( | |||
| <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"> | |||
| {message_files.map((url, index) => ( | |||
| <div | |||
| @@ -69,6 +72,7 @@ const VarPanel: FC<Props> = ({ | |||
| imagePreviewUrl && ( | |||
| <ImagePreview | |||
| url={imagePreviewUrl} | |||
| title={imagePreviewUrl} | |||
| onCancel={() => setImagePreviewUrl('')} | |||
| /> | |||
| ) | |||
| @@ -282,7 +282,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({ | |||
| const [currentTab, setCurrentTab] = useState<string>('DETAIL') | |||
| 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 | |||
| ? { | |||
| 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 | |||
| 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 && ( | |||
| @@ -212,15 +212,15 @@ const Answer: FC<AnswerProps> = ({ | |||
| disabled={!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> | |||
| <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 | |||
| className={`${item.nextSibling ? 'opacity-100' : 'opacity-65'}`} | |||
| disabled={!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> | |||
| </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> | |||
| @@ -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' | |||
| type CheckboxProps = { | |||
| @@ -9,13 +9,27 @@ type 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 ( | |||
| <div | |||
| className={cn( | |||
| s.wrapper, | |||
| checked && s.checked, | |||
| disabled && s.disabled, | |||
| 'w-4 h-4 border rounded border-gray-300', | |||
| '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', | |||
| disabled && 'bg-components-checkbox-bg-disabled-checked hover:bg-components-checkbox-bg-disabled-checked text-components-checkbox-icon-disabled cursor-not-allowed', | |||
| className, | |||
| )} | |||
| onClick={() => { | |||
| @@ -24,7 +38,9 @@ const Checkbox = ({ checked, onCheck, className, disabled }: CheckboxProps) => { | |||
| onCheck?.() | |||
| }} | |||
| /> | |||
| > | |||
| <RiCheckLine className={cn('w-3 h-3')} /> | |||
| </div> | |||
| ) | |||
| } | |||
| @@ -39,10 +39,10 @@ export const CopyIcon = ({ content }: Props) => { | |||
| <div onMouseLeave={onMouseLeave}> | |||
| {!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> | |||
| @@ -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)} | |||
| > | |||
| <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={{ | |||
| height, | |||
| }} | |||
| 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='text-base font-semibold text-gray-900'> | |||
| <div className='system-xl-semibold text-text-primary'> | |||
| {title} | |||
| </div> | |||
| <div className='flex items-center'> | |||
| @@ -74,12 +74,12 @@ const DrawerPlus: FC<Props> = ({ | |||
| onClick={onHide} | |||
| 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> | |||
| {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} | |||
| </div> | |||
| )} | |||
| @@ -49,7 +49,7 @@ export default function Drawer({ | |||
| <Dialog.Overlay | |||
| 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 | |||
| as="h3" | |||
| @@ -77,9 +77,9 @@ const ConfigParamModal: FC<Props> = ({ | |||
| <Modal | |||
| isShow={isShow} | |||
| 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'}`)} | |||
| </div> | |||
| @@ -10,11 +10,11 @@ export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }> | |||
| }) => { | |||
| return ( | |||
| <div> | |||
| <div className='flex items-center space-x-1'> | |||
| <div>{title}</div> | |||
| <div className='flex items-center space-x-1 mb-1'> | |||
| <div className='py-1 system-sm-semibold text-text-secondary'>{title}</div> | |||
| <Tooltip | |||
| 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> | |||
| @@ -26,7 +26,7 @@ const Slider: React.FC<ISliderProps> = ({ className, max, min, step, value, disa | |||
| renderThumb={(props, state) => ( | |||
| <div {...props}> | |||
| <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)} | |||
| </div> | |||
| </div> | |||
| @@ -28,13 +28,13 @@ const ScoreSlider: FC<Props> = ({ | |||
| onChange={onChange} | |||
| /> | |||
| </div> | |||
| <div className='mt-[10px] flex justify-between items-center leading-4 text-xs font-normal '> | |||
| <div className='flex space-x-1 text-[#00A286]'> | |||
| <div className='mt-[10px] flex justify-between items-center system-xs-semibold-uppercase'> | |||
| <div className='flex space-x-1 text-util-colors-cyan-cyan-500'> | |||
| <div>0.8</div> | |||
| <div>·</div> | |||
| <div>{t('appDebug.feature.annotation.scoreThreshold.easyMatch')}</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>·</div> | |||
| <div>{t('appDebug.feature.annotation.scoreThreshold.accurateMatch')}</div> | |||
| @@ -3,5 +3,5 @@ | |||
| } | |||
| .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) { | |||
| return ( | |||
| <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 | |||
| as={Fragment} | |||
| enter="ease-out duration-300" | |||
| @@ -60,22 +60,22 @@ export default function Modal({ | |||
| leaveTo="opacity-0 scale-95" | |||
| > | |||
| <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', | |||
| className, | |||
| )}> | |||
| {title && <Dialog.Title | |||
| as="h3" | |||
| className="text-lg font-medium leading-6 text-gray-900" | |||
| className="text-lg font-medium leading-6 text-text-primary" | |||
| > | |||
| {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} | |||
| </Dialog.Description>} | |||
| {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'> | |||
| <XMarkIcon className='w-4 h-4 text-gray-500' onClick={ | |||
| && <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-text-tertiary' onClick={ | |||
| (e) => { | |||
| e.stopPropagation() | |||
| onClose() | |||
| @@ -1,6 +1,5 @@ | |||
| import { Popover, Transition } from '@headlessui/react' | |||
| import { Fragment, cloneElement, useRef } from 'react' | |||
| import s from './style.module.css' | |||
| import cn from '@/utils/classnames' | |||
| export type HtmlContentProps = { | |||
| @@ -63,19 +62,19 @@ export default function CustomPopover({ | |||
| <Popover.Button | |||
| ref={buttonRef} | |||
| disabled={disabled} | |||
| className={`group ${s.popupBtn} ${open ? '' : 'bg-gray-100'} ${!btnClassName | |||
| ? '' | |||
| : typeof btnClassName === 'string' | |||
| ? btnClassName | |||
| : btnClassName?.(open) | |||
| }`} | |||
| 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', | |||
| open && 'bg-components-button-secondary-bg-hover border-components-button-secondary-border', | |||
| (btnClassName && typeof btnClassName === 'string') && btnClassName, | |||
| (btnClassName && typeof btnClassName !== 'string') && btnClassName?.(open), | |||
| )} | |||
| > | |||
| {btnElement} | |||
| </Popover.Button> | |||
| <Transition as={Fragment}> | |||
| <Popover.Panel | |||
| 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 === 'bl' && 'left-0', | |||
| position === 'br' && 'right-0', | |||
| @@ -91,7 +90,7 @@ export default function CustomPopover({ | |||
| > | |||
| {({ close }) => ( | |||
| <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' | |||
| ? {} | |||
| : { | |||
| @@ -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 ( | |||
| <div | |||
| 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)} | |||
| > | |||
| <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 && ( | |||
| <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> | |||
| ) | |||
| @@ -52,7 +56,7 @@ const TabSlider: FC<Props> = ({ | |||
| itemClassName, | |||
| }) => { | |||
| 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 => ( | |||
| <Item | |||
| isActive={option.value === value} | |||
| @@ -96,7 +96,7 @@ const Tooltip: FC<TooltipProps> = ({ | |||
| > | |||
| {popupContent && (<div | |||
| 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, | |||
| )} | |||
| onMouseEnter={() => triggerMethod === 'hover' && setHoverPopup()} | |||
| @@ -11,7 +11,7 @@ const ModelBadge: FC<ModelBadgeProps> = ({ | |||
| }) => { | |||
| return ( | |||
| <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, | |||
| )}> | |||
| {children} | |||
| @@ -7,7 +7,7 @@ import { useLanguage } from '../hooks' | |||
| import type { ModelItem } from '../declarations' | |||
| import ModelBadge from '../model-badge' | |||
| import FeatureIcon from '../model-selector/feature-icon' | |||
| import classNames from '@/utils/classnames' | |||
| import cn from '@/utils/classnames' | |||
| type ModelNameProps = PropsWithChildren<{ | |||
| modelItem: ModelItem | |||
| @@ -37,12 +37,7 @@ const ModelName: FC<ModelNameProps> = ({ | |||
| if (!modelItem) | |||
| return null | |||
| return ( | |||
| <div | |||
| className={` | |||
| flex items-center truncate text-[13px] font-medium text-gray-800 | |||
| ${className} | |||
| `} | |||
| > | |||
| <div className={cn('flex items-center truncate text-components-input-text-filled system-sm-regular', className)}> | |||
| <div | |||
| className='truncate' | |||
| title={modelItem.label[language] || modelItem.label.en_US} | |||
| @@ -51,14 +46,14 @@ const ModelName: FC<ModelNameProps> = ({ | |||
| </div> | |||
| { | |||
| showModelType && modelItem.model_type && ( | |||
| <ModelBadge className={classNames('ml-1', modelTypeClassName)}> | |||
| <ModelBadge className={cn('ml-1', modelTypeClassName)}> | |||
| {modelTypeFormat(modelItem.model_type)} | |||
| </ModelBadge> | |||
| ) | |||
| } | |||
| { | |||
| modelItem.model_properties.mode && showMode && ( | |||
| <ModelBadge className={classNames('ml-1', modeClassName)}> | |||
| <ModelBadge className={cn('ml-1', modeClassName)}> | |||
| {(modelItem.model_properties.mode as string).toLocaleUpperCase()} | |||
| </ModelBadge> | |||
| ) | |||
| @@ -21,9 +21,10 @@ const ToolsNav = ({ | |||
| return ( | |||
| <Link href="/tools" className={classNames( | |||
| className, 'group', | |||
| activated && 'bg-white shadow-md', | |||
| activated ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200', | |||
| 'group text-sm font-medium', | |||
| 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-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 | |||
| @@ -75,9 +75,9 @@ const Base: FC<Props> = ({ | |||
| return ( | |||
| <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='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) => { | |||
| e.nativeEvent.stopImmediatePropagation() | |||
| e.stopPropagation() | |||
| @@ -90,10 +90,10 @@ const Base: FC<Props> = ({ | |||
| )} | |||
| {!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', | |||
| uploadImages: 'Uploaded Images', | |||
| modelParams: 'Model parameters', | |||
| }, | |||
| filter: { | |||
| period: { | |||
| @@ -52,6 +52,7 @@ const translation = { | |||
| }, | |||
| variables: '变量', | |||
| uploadImages: '上传的图片', | |||
| modelParams: '模型参数', | |||
| }, | |||
| filter: { | |||
| period: { | |||