mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-19 16:39:13 +08:00
feat: fe mobile responsive next (#1609)
This commit is contained in:
parent
3cc697832a
commit
a9c1c7d239
@ -39,10 +39,10 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||||||
const navigation = useMemo(() => {
|
const navigation = useMemo(() => {
|
||||||
const navs = [
|
const navs = [
|
||||||
{ name: t('common.appMenus.overview'), href: `/app/${appId}/overview`, icon: ChartBarSquareIcon, selectedIcon: ChartBarSquareSolidIcon },
|
{ name: t('common.appMenus.overview'), href: `/app/${appId}/overview`, icon: ChartBarSquareIcon, selectedIcon: ChartBarSquareSolidIcon },
|
||||||
isCurrentWorkspaceManager ? { name: t('common.appMenus.promptEng'), href: `/app/${appId}/configuration`, icon: Cog8ToothIcon, selectedIcon: Cog8ToothSolidIcon } : false,
|
...(isCurrentWorkspaceManager ? [{ name: t('common.appMenus.promptEng'), href: `/app/${appId}/configuration`, icon: Cog8ToothIcon, selectedIcon: Cog8ToothSolidIcon }] : []),
|
||||||
{ name: t('common.appMenus.apiAccess'), href: `/app/${appId}/develop`, icon: CommandLineIcon, selectedIcon: CommandLineSolidIcon },
|
{ name: t('common.appMenus.apiAccess'), href: `/app/${appId}/develop`, icon: CommandLineIcon, selectedIcon: CommandLineSolidIcon },
|
||||||
{ name: t('common.appMenus.logAndAnn'), href: `/app/${appId}/logs`, icon: DocumentTextIcon, selectedIcon: DocumentTextSolidIcon },
|
{ name: t('common.appMenus.logAndAnn'), href: `/app/${appId}/logs`, icon: DocumentTextIcon, selectedIcon: DocumentTextSolidIcon },
|
||||||
].filter(nav => !!nav)
|
]
|
||||||
return navs
|
return navs
|
||||||
}, [appId, isCurrentWorkspaceManager, t])
|
}, [appId, isCurrentWorkspaceManager, t])
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div className={cn(s.app, 'flex', 'overflow-hidden')}>
|
<div className={cn(s.app, 'flex', 'overflow-hidden')}>
|
||||||
<AppSideBar title={response.name} icon={response.icon} icon_background={response.icon_background} desc={appModeName} navigation={navigation} />
|
<AppSideBar title={response.name} icon={response.icon} icon_background={response.icon_background} desc={appModeName} navigation={navigation} />
|
||||||
<div className="bg-white grow">{children}</div>
|
<div className="bg-white grow overflow-hidden">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-w-max grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6">
|
<div className="grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6">
|
||||||
<AppCard
|
<AppCard
|
||||||
appInfo={response}
|
appInfo={response}
|
||||||
cardType="webapp"
|
cardType="webapp"
|
||||||
|
@ -19,7 +19,7 @@ const Overview = async ({
|
|||||||
*/
|
*/
|
||||||
const { t } = await translate(locale, 'app-overview')
|
const { t } = await translate(locale, 'app-overview')
|
||||||
return (
|
return (
|
||||||
<div className="h-full px-16 py-6 overflow-scroll">
|
<div className="h-full px-4 sm:px-16 py-6 overflow-scroll">
|
||||||
<ApikeyInfoPanel />
|
<ApikeyInfoPanel />
|
||||||
<div className='flex flex-row items-center justify-between mb-4 text-xl text-gray-900'>
|
<div className='flex flex-row items-center justify-between mb-4 text-xl text-gray-900'>
|
||||||
{t('overview.title')}
|
{t('overview.title')}
|
||||||
|
@ -122,7 +122,7 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
|
|||||||
<input ref={nameInputRef} className='h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' placeholder={t('app.appNamePlaceholder') || ''}/>
|
<input ref={nameInputRef} className='h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' placeholder={t('app.appNamePlaceholder') || ''}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='h-[247px] overflow-y-auto'>
|
<div className='overflow-y-auto'>
|
||||||
<div className={style.newItemCaption}>
|
<div className={style.newItemCaption}>
|
||||||
<h3 className='inline'>{t('app.newApp.captionAppType')}</h3>
|
<h3 className='inline'>{t('app.newApp.captionAppType')}</h3>
|
||||||
{isWithTemplate && (
|
{isWithTemplate && (
|
||||||
@ -139,7 +139,7 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
|
|||||||
</div>
|
</div>
|
||||||
{isWithTemplate
|
{isWithTemplate
|
||||||
? (
|
? (
|
||||||
<ul className='grid grid-cols-2 gap-4'>
|
<ul className='grid grid-cols-1 md:grid-cols-2 gap-4'>
|
||||||
{templates?.data?.map((template, index) => (
|
{templates?.data?.map((template, index) => (
|
||||||
<li
|
<li
|
||||||
key={index}
|
key={index}
|
||||||
@ -161,7 +161,7 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
|
|||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<>
|
<>
|
||||||
<ul className='grid grid-cols-2 gap-4'>
|
<ul className='grid grid-cols-1 md:grid-cols-2 gap-4'>
|
||||||
<li
|
<li
|
||||||
className={classNames(style.listItem, style.selectable, newAppMode === 'chat' && style.selected)}
|
className={classNames(style.listItem, style.selectable, newAppMode === 'chat' && style.selected)}
|
||||||
onClick={() => setNewAppMode('chat')}
|
onClick={() => setNewAppMode('chat')}
|
||||||
|
@ -4,6 +4,8 @@ import React, { useEffect } from 'react'
|
|||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import { useBoolean } from 'ahooks'
|
||||||
import {
|
import {
|
||||||
Cog8ToothIcon,
|
Cog8ToothIcon,
|
||||||
// CommandLineIcon,
|
// CommandLineIcon,
|
||||||
@ -11,6 +13,8 @@ import {
|
|||||||
// eslint-disable-next-line sort-imports
|
// eslint-disable-next-line sort-imports
|
||||||
PuzzlePieceIcon,
|
PuzzlePieceIcon,
|
||||||
DocumentTextIcon,
|
DocumentTextIcon,
|
||||||
|
PaperClipIcon,
|
||||||
|
QuestionMarkCircleIcon,
|
||||||
} from '@heroicons/react/24/outline'
|
} from '@heroicons/react/24/outline'
|
||||||
import {
|
import {
|
||||||
Cog8ToothIcon as Cog8ToothSolidIcon,
|
Cog8ToothIcon as Cog8ToothSolidIcon,
|
||||||
@ -20,29 +24,39 @@ import {
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
|
import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
|
||||||
import type { RelatedApp } from '@/models/datasets'
|
import type { RelatedApp, RelatedAppResponse } from '@/models/datasets'
|
||||||
import { getLocaleOnClient } from '@/i18n/client'
|
import { getLocaleOnClient } from '@/i18n/client'
|
||||||
import AppSideBar from '@/app/components/app-sidebar'
|
import AppSideBar from '@/app/components/app-sidebar'
|
||||||
import Divider from '@/app/components/base/divider'
|
import Divider from '@/app/components/base/divider'
|
||||||
import Indicator from '@/app/components/header/indicator'
|
import Indicator from '@/app/components/header/indicator'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
|
import FloatPopoverContainer from '@/app/components/base/float-popover-container'
|
||||||
import DatasetDetailContext from '@/context/dataset-detail'
|
import DatasetDetailContext from '@/context/dataset-detail'
|
||||||
import { DataSourceType } from '@/models/datasets'
|
import { DataSourceType } from '@/models/datasets'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
export type IAppDetailLayoutProps = {
|
export type IAppDetailLayoutProps = {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
params: { datasetId: string }
|
params: { datasetId: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
const LikedItem: FC<{ type?: 'plugin' | 'app'; appStatus?: boolean; detail: RelatedApp }> = ({
|
type ILikedItemProps = {
|
||||||
|
type?: 'plugin' | 'app'
|
||||||
|
appStatus?: boolean
|
||||||
|
detail: RelatedApp
|
||||||
|
isMobile: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const LikedItem = ({
|
||||||
type = 'app',
|
type = 'app',
|
||||||
appStatus = true,
|
appStatus = true,
|
||||||
detail,
|
detail,
|
||||||
}) => {
|
isMobile,
|
||||||
|
}: ILikedItemProps) => {
|
||||||
return (
|
return (
|
||||||
<Link className={s.itemWrapper} href={`/app/${detail?.id}/overview`}>
|
<Link className={classNames(s.itemWrapper, 'px-0 sm:px-3 justify-center sm:justify-start')} href={`/app/${detail?.id}/overview`}>
|
||||||
<div className={s.iconWrapper}>
|
<div className={classNames(s.iconWrapper, 'mr-0 sm:mr-2')}>
|
||||||
<AppIcon size='tiny' icon={detail?.icon} background={detail?.icon_background}/>
|
<AppIcon size='tiny' icon={detail?.icon} background={detail?.icon_background}/>
|
||||||
{type === 'app' && (
|
{type === 'app' && (
|
||||||
<div className={s.statusPoint}>
|
<div className={s.statusPoint}>
|
||||||
@ -50,7 +64,7 @@ const LikedItem: FC<{ type?: 'plugin' | 'app'; appStatus?: boolean; detail: Rela
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={s.appInfo}>{detail?.name || '--'}</div>
|
{!isMobile && <div className={s.appInfo}>{detail?.name || '--'}</div>}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -83,6 +97,68 @@ const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
|
|||||||
</svg>
|
</svg>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IExtraInfoProps = {
|
||||||
|
isMobile: boolean
|
||||||
|
relatedApps?: RelatedAppResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExtraInfo = ({ isMobile, relatedApps }: IExtraInfoProps) => {
|
||||||
|
const locale = getLocaleOnClient()
|
||||||
|
const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setShowTips(!isMobile)
|
||||||
|
}, [isMobile, setShowTips])
|
||||||
|
|
||||||
|
return <div className='w-full flex flex-col items-center'>
|
||||||
|
<Divider className='mt-5' />
|
||||||
|
{(relatedApps?.data && relatedApps?.data?.length > 0) && (
|
||||||
|
<>
|
||||||
|
{!isMobile && <div className={s.subTitle}>{relatedApps?.total || '--'} {t('common.datasetMenus.relatedApp')}</div>}
|
||||||
|
{isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
|
||||||
|
{relatedApps?.total || '--'}
|
||||||
|
<PaperClipIcon className='h-4 w-4 text-gray-700' />
|
||||||
|
</div>}
|
||||||
|
{relatedApps?.data?.map((item, index) => (<LikedItem key={index} isMobile={isMobile} detail={item} />))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!relatedApps?.data?.length && (
|
||||||
|
<FloatPopoverContainer
|
||||||
|
placement='bottom-start'
|
||||||
|
open={isShowTips}
|
||||||
|
toggle={toggleTips}
|
||||||
|
isMobile={isMobile}
|
||||||
|
triggerElement={
|
||||||
|
<div className={classNames('h-7 w-7 inline-flex justify-center items-center rounded-lg bg-transparent', isShowTips && '!bg-gray-50')}>
|
||||||
|
<QuestionMarkCircleIcon className='h-4 w-4 flex-shrink-0 text-gray-500' />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={classNames('mt-5 p-3', isMobile && 'border-[0.5px] border-gray-200 shadow-lg rounded-lg bg-white w-[150px]')}>
|
||||||
|
<div className='flex items-center justify-start gap-2'>
|
||||||
|
<div className={s.emptyIconDiv}>
|
||||||
|
<Squares2X2Icon className='w-3 h-3 text-gray-500' />
|
||||||
|
</div>
|
||||||
|
<div className={s.emptyIconDiv}>
|
||||||
|
<PuzzlePieceIcon className='w-3 h-3 text-gray-500' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='text-xs text-gray-500 mt-2'>{t('common.datasetMenus.emptyTip')}</div>
|
||||||
|
<a
|
||||||
|
className='inline-flex items-center text-xs text-primary-600 mt-2 cursor-pointer'
|
||||||
|
href={`https://docs.dify.ai/${locale === 'zh-Hans' ? 'v/zh-hans' : ''}/application/prompt-engineering`}
|
||||||
|
target='_blank'
|
||||||
|
>
|
||||||
|
<BookOpenIcon className='mr-1' />
|
||||||
|
{t('common.datasetMenus.viewDoc')}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</FloatPopoverContainer>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
@ -91,6 +167,10 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const hideSideBar = /documents\/create$/.test(pathname)
|
const hideSideBar = /documents\/create$/.test(pathname)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const { data: datasetRes, error, mutate: mutateDatasetRes } = useSWR({
|
const { data: datasetRes, error, mutate: mutateDatasetRes } = useSWR({
|
||||||
url: 'fetchDatasetDetail',
|
url: 'fetchDatasetDetail',
|
||||||
datasetId,
|
datasetId,
|
||||||
@ -113,54 +193,18 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||||||
document.title = `${datasetRes.name || 'Dataset'} - Dify`
|
document.title = `${datasetRes.name || 'Dataset'} - Dify`
|
||||||
}, [datasetRes])
|
}, [datasetRes])
|
||||||
|
|
||||||
const ExtraInfo: FC = () => {
|
|
||||||
const locale = getLocaleOnClient()
|
|
||||||
|
|
||||||
return <div className='w-full'>
|
|
||||||
<Divider className='mt-5' />
|
|
||||||
{relatedApps?.data?.length
|
|
||||||
? (
|
|
||||||
<>
|
|
||||||
<div className={s.subTitle}>{relatedApps?.total || '--'} {t('common.datasetMenus.relatedApp')}</div>
|
|
||||||
{relatedApps?.data?.map((item, index) => (<LikedItem key={index} detail={item} />))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<div className='mt-5 p-3'>
|
|
||||||
<div className='flex items-center justify-start gap-2'>
|
|
||||||
<div className={s.emptyIconDiv}>
|
|
||||||
<Squares2X2Icon className='w-3 h-3 text-gray-500' />
|
|
||||||
</div>
|
|
||||||
<div className={s.emptyIconDiv}>
|
|
||||||
<PuzzlePieceIcon className='w-3 h-3 text-gray-500' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='text-xs text-gray-500 mt-2'>{t('common.datasetMenus.emptyTip')}</div>
|
|
||||||
<a
|
|
||||||
className='inline-flex items-center text-xs text-primary-600 mt-2 cursor-pointer'
|
|
||||||
href={`https://docs.dify.ai/${locale === 'zh-Hans' ? 'v/zh-hans' : ''}/application/prompt-engineering`}
|
|
||||||
target='_blank'
|
|
||||||
>
|
|
||||||
<BookOpenIcon className='mr-1' />
|
|
||||||
{t('common.datasetMenus.viewDoc')}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!datasetRes && !error)
|
if (!datasetRes && !error)
|
||||||
return <Loading />
|
return <Loading />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex'>
|
<div className='flex overflow-hidden'>
|
||||||
{!hideSideBar && <AppSideBar
|
{!hideSideBar && <AppSideBar
|
||||||
title={datasetRes?.name || '--'}
|
title={datasetRes?.name || '--'}
|
||||||
icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
|
icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
|
||||||
icon_background={datasetRes?.icon_background || '#F5F5F5'}
|
icon_background={datasetRes?.icon_background || '#F5F5F5'}
|
||||||
desc={datasetRes?.description || '--'}
|
desc={datasetRes?.description || '--'}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
extraInfo={<ExtraInfo />}
|
extraInfo={<ExtraInfo isMobile={isMobile} relatedApps={relatedApps} />}
|
||||||
iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
|
iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
|
||||||
/>}
|
/>}
|
||||||
<DatasetDetailContext.Provider value={{
|
<DatasetDetailContext.Provider value={{
|
||||||
@ -168,7 +212,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||||||
dataset: datasetRes,
|
dataset: datasetRes,
|
||||||
mutateDatasetRes: () => mutateDatasetRes(),
|
mutateDatasetRes: () => mutateDatasetRes(),
|
||||||
}}>
|
}}>
|
||||||
<div className="bg-white grow" style={{ minHeight: 'calc(100vh - 56px)' }}>{children}</div>
|
<div className="bg-white grow overflow-hidden">{children}</div>
|
||||||
</DatasetDetailContext.Provider>
|
</DatasetDetailContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -14,15 +14,13 @@ const Settings = async ({
|
|||||||
const { t } = await useTranslation(locale, 'dataset-settings')
|
const { t } = await useTranslation(locale, 'dataset-settings')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-white h-full'>
|
<div className='bg-white h-full overflow-y-auto'>
|
||||||
<div className='px-6 py-3'>
|
<div className='px-6 py-3'>
|
||||||
<div className='mb-1 text-lg font-semibold text-gray-900'>{t('title')}</div>
|
<div className='mb-1 text-lg font-semibold text-gray-900'>{t('title')}</div>
|
||||||
<div className='text-sm text-gray-500'>{t('desc')}</div>
|
<div className='text-sm text-gray-500'>{t('desc')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<Form datasetId={datasetId} />
|
<Form datasetId={datasetId} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
.itemWrapper {
|
.itemWrapper {
|
||||||
@apply flex items-center w-full h-10 px-3 rounded-lg hover:bg-gray-50 cursor-pointer;
|
@apply flex items-center w-full h-10 rounded-lg hover:bg-gray-50 cursor-pointer;
|
||||||
}
|
}
|
||||||
.appInfo {
|
.appInfo {
|
||||||
@apply truncate text-gray-700 text-sm font-normal;
|
@apply truncate text-gray-700 text-sm font-normal;
|
||||||
}
|
}
|
||||||
.iconWrapper {
|
.iconWrapper {
|
||||||
@apply relative w-6 h-6 mr-2 bg-[#D5F5F6] rounded-md;
|
@apply relative w-6 h-6 bg-[#D5F5F6] rounded-md;
|
||||||
}
|
}
|
||||||
.statusPoint {
|
.statusPoint {
|
||||||
@apply flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded;
|
@apply flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded;
|
||||||
|
@ -15,10 +15,10 @@ const ApiServer: FC<ApiServerProps> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center flex-wrap gap-y-2'>
|
||||||
<div className='flex items-center mr-2 pl-1.5 pr-1 h-8 bg-white/80 border-[0.5px] border-white rounded-lg'>
|
<div className='flex items-center mr-2 pl-1.5 pr-1 h-8 bg-white/80 border-[0.5px] border-white rounded-lg leading-5'>
|
||||||
<div className='mr-0.5 px-1.5 h-5 border border-gray-200 text-[11px] text-gray-500 rounded-md'>{t('appApi.apiServer')}</div>
|
<div className='mr-0.5 px-1.5 h-5 border border-gray-200 text-[11px] text-gray-500 rounded-md shrink-0'>{t('appApi.apiServer')}</div>
|
||||||
<div className='px-1 w-[248px] text-[13px] font-medium text-gray-800'>{apiBaseUrl}</div>
|
<div className='px-1 truncate w-fit sm:w-[248px] text-[13px] font-medium text-gray-800'>{apiBaseUrl}</div>
|
||||||
<div className='mx-1 w-[1px] h-[14px] bg-gray-200'></div>
|
<div className='mx-1 w-[1px] h-[14px] bg-gray-200'></div>
|
||||||
<CopyFeedback
|
<CopyFeedback
|
||||||
content={apiBaseUrl}
|
content={apiBaseUrl}
|
||||||
|
@ -29,7 +29,7 @@ const Container = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className='grow relative flex flex-col bg-gray-100 overflow-y-auto'>
|
<div ref={containerRef} className='grow relative flex flex-col bg-gray-100 overflow-y-auto'>
|
||||||
<div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 h-14 bg-gray-100 z-10'>
|
<div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-10 flex-wrap gap-y-2'>
|
||||||
<TabSlider
|
<TabSlider
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onChange={newActiveTab => setActiveTab(newActiveTab)}
|
onChange={newActiveTab => setActiveTab(newActiveTab)}
|
||||||
@ -38,16 +38,14 @@ const Container = () => {
|
|||||||
{activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />}
|
{activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activeTab === 'dataset'
|
{activeTab === 'dataset' && (
|
||||||
? (
|
|
||||||
<>
|
<>
|
||||||
<Datasets containerRef={containerRef} />
|
<Datasets containerRef={containerRef} />
|
||||||
<DatasetFooter />
|
<DatasetFooter />
|
||||||
</>
|
</>
|
||||||
)
|
|
||||||
: (
|
|
||||||
activeTab === 'api' && data && <Doc apiBaseUrl={data.api_base_url || ''} />
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{activeTab === 'api' && data && <Doc apiBaseUrl={data.api_base_url || ''} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ const Doc: FC<DocProps> = ({
|
|||||||
const { locale } = useContext(I18n)
|
const { locale } = useContext(I18n)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className='mx-12 pt-16 bg-white rounded-t-xl prose prose-xl'>
|
<article className='mx-1 px-4 sm:mx-12 pt-16 bg-white rounded-t-xl prose prose-xl'>
|
||||||
{
|
{
|
||||||
locale === 'en'
|
locale === 'en'
|
||||||
? <TemplateEn apiBaseUrl={apiBaseUrl} />
|
? <TemplateEn apiBaseUrl={apiBaseUrl} />
|
||||||
|
@ -15,6 +15,7 @@ export type IAppBasicProps = {
|
|||||||
hoverTip?: string
|
hoverTip?: string
|
||||||
textStyle?: { main?: string; extra?: string }
|
textStyle?: { main?: string; extra?: string }
|
||||||
isExtraInLine?: boolean
|
isExtraInLine?: boolean
|
||||||
|
mode?: 'expand' | 'collapse'
|
||||||
}
|
}
|
||||||
|
|
||||||
const ApiSvg = <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
const ApiSvg = <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -55,7 +56,7 @@ const ICON_MAP = {
|
|||||||
notion: <AppIcon innerIcon={NotionSvg} className='!border-[0.5px] !border-indigo-100 !bg-white' />,
|
notion: <AppIcon innerIcon={NotionSvg} className='!border-[0.5px] !border-indigo-100 !bg-white' />,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AppBasic({ icon, icon_background, name, type, hoverTip, textStyle, iconType = 'app', isExtraInLine }: IAppBasicProps) {
|
export default function AppBasic({ icon, icon_background, name, type, hoverTip, textStyle, mode = 'expand', iconType = 'app', isExtraInLine }: IAppBasicProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start">
|
<div className="flex items-start">
|
||||||
{icon && icon_background && iconType === 'app' && (
|
{icon && icon_background && iconType === 'app' && (
|
||||||
@ -69,7 +70,7 @@ export default function AppBasic({ icon, icon_background, name, type, hoverTip,
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
<div className="group">
|
{mode === 'expand' && <div className="group">
|
||||||
<div className={`flex flex-row items-center text-sm font-semibold text-gray-700 group-hover:text-gray-900 break-all ${textStyle?.main ?? ''}`}>
|
<div className={`flex flex-row items-center text-sm font-semibold text-gray-700 group-hover:text-gray-900 break-all ${textStyle?.main ?? ''}`}>
|
||||||
{name}
|
{name}
|
||||||
{hoverTip
|
{hoverTip
|
||||||
@ -78,7 +79,7 @@ export default function AppBasic({ icon, icon_background, name, type, hoverTip,
|
|||||||
</Tooltip>}
|
</Tooltip>}
|
||||||
</div>
|
</div>
|
||||||
<div className={`text-xs font-normal text-gray-500 group-hover:text-gray-700 break-all ${textStyle?.extra ?? ''}`}>{type}</div>
|
<div className={`text-xs font-normal text-gray-500 group-hover:text-gray-700 break-all ${textStyle?.extra ?? ''}`}>{type}</div>
|
||||||
</div>
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import NavLink from './navLink'
|
import NavLink from './navLink'
|
||||||
import AppBasic from './basic'
|
|
||||||
|
|
||||||
import type { NavIcon } from './navLink'
|
import type { NavIcon } from './navLink'
|
||||||
|
import AppBasic from './basic'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
export type IAppDetailNavProps = {
|
export type IAppDetailNavProps = {
|
||||||
iconType?: 'app' | 'dataset' | 'notion'
|
iconType?: 'app' | 'dataset' | 'notion'
|
||||||
@ -20,15 +20,19 @@ export type IAppDetailNavProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInfo, iconType = 'app' }: IAppDetailNavProps) => {
|
const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInfo, iconType = 'app' }: IAppDetailNavProps) => {
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
const mode = isMobile ? 'collapse' : 'expand'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
|
<div className="flex flex-col sm:w-56 w-16 overflow-y-auto bg-white border-r border-gray-200 shrink-0 mobile:h-screen">
|
||||||
<div className="flex flex-shrink-0 p-4">
|
<div className="flex flex-shrink-0 p-4">
|
||||||
<AppBasic iconType={iconType} icon={icon} icon_background={icon_background} name={title} type={desc} />
|
<AppBasic mode={mode} iconType={iconType} icon={icon} icon_background={icon_background} name={title} type={desc} />
|
||||||
</div>
|
</div>
|
||||||
<nav className="flex-1 p-4 space-y-1 bg-white">
|
<nav className="flex-1 p-4 space-y-1 bg-white">
|
||||||
{navigation.map((item, index) => {
|
{navigation.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<NavLink key={index} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
|
<NavLink key={index} mode={mode} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
{extraInfo ?? null}
|
{extraInfo ?? null}
|
||||||
|
@ -18,12 +18,14 @@ export type NavLinkProps = {
|
|||||||
selected: NavIcon
|
selected: NavIcon
|
||||||
normal: NavIcon
|
normal: NavIcon
|
||||||
}
|
}
|
||||||
|
mode?: 'expand' | 'collapse'
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavLink({
|
export default function NavLink({
|
||||||
name,
|
name,
|
||||||
href,
|
href,
|
||||||
iconMap,
|
iconMap,
|
||||||
|
mode = 'expand',
|
||||||
}: NavLinkProps) {
|
}: NavLinkProps) {
|
||||||
const segment = useSelectedLayoutSegment()
|
const segment = useSelectedLayoutSegment()
|
||||||
const isActive = href.toLowerCase().split('/')?.pop() === segment?.toLowerCase()
|
const isActive = href.toLowerCase().split('/')?.pop() === segment?.toLowerCase()
|
||||||
@ -45,7 +47,7 @@ export default function NavLink({
|
|||||||
)}
|
)}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
{name}
|
{mode === 'expand' && name}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import Loading from '@/app/components/base/loading'
|
|||||||
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
|
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
|
||||||
import { ModelType, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
import { ModelType, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
import type { ModelModeType } from '@/types/app'
|
import type { ModelModeType } from '@/types/app'
|
||||||
export type IConfigModelProps = {
|
export type IConfigModelProps = {
|
||||||
isAdvancedMode: boolean
|
isAdvancedMode: boolean
|
||||||
@ -54,6 +55,10 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||||||
const [maxTokenSettingTipVisible, setMaxTokenSettingTipVisible] = useState(false)
|
const [maxTokenSettingTipVisible, setMaxTokenSettingTipVisible] = useState(false)
|
||||||
const configContentRef = React.useRef(null)
|
const configContentRef = React.useRef(null)
|
||||||
const currModel = textGenerationModelList.find(item => item.model_name === modelId)
|
const currModel = textGenerationModelList.find(item => item.model_name === modelId)
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
// Cache loaded model param
|
// Cache loaded model param
|
||||||
const [allParams, setAllParams, getAllParams] = useGetState<Record<string, Record<string, any>>>({})
|
const [allParams, setAllParams, getAllParams] = useGetState<Record<string, Record<string, any>>>({})
|
||||||
const currParams = allParams[provider]?.[modelId]
|
const currParams = allParams[provider]?.[modelId]
|
||||||
@ -288,7 +293,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
{isShowConfig && (
|
{isShowConfig && (
|
||||||
<Panel
|
<Panel
|
||||||
className='absolute z-20 top-8 right-0 !w-[496px] bg-white !overflow-visible shadow-md'
|
className='absolute z-20 top-8 left-0 sm:left-[unset] sm:right-0 !w-fit sm:!w-[496px] bg-white !overflow-visible shadow-md'
|
||||||
keepUnFold
|
keepUnFold
|
||||||
headerIcon={
|
headerIcon={
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -340,7 +345,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||||||
<div className='grow flex items-center' key={tone.id}>
|
<div className='grow flex items-center' key={tone.id}>
|
||||||
<Radio
|
<Radio
|
||||||
value={tone.id}
|
value={tone.id}
|
||||||
className={cn(tone.id === toneId && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-2 !justify-center text-[13px] font-medium')}
|
className={cn(tone.id === toneId && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-1 sm:!px-2 !justify-center text-[13px] font-medium')}
|
||||||
labelClassName={cn(tone.id === toneId
|
labelClassName={cn(tone.id === toneId
|
||||||
? ({
|
? ({
|
||||||
1: 'text-[#6938EF]',
|
1: 'text-[#6938EF]',
|
||||||
@ -351,7 +356,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{getToneIcon(tone.id)}
|
{getToneIcon(tone.id)}
|
||||||
<div>{t(`common.model.tone.${tone.name}`) as string}</div>
|
{!isMobile && <div>{t(`common.model.tone.${tone.name}`) as string}</div>}
|
||||||
<div className=""></div>
|
<div className=""></div>
|
||||||
</>
|
</>
|
||||||
</Radio>
|
</Radio>
|
||||||
@ -361,12 +366,12 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||||||
</>
|
</>
|
||||||
<Radio
|
<Radio
|
||||||
value={TONE_LIST[3].id}
|
value={TONE_LIST[3].id}
|
||||||
className={cn(toneId === 4 && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-2 !justify-center text-[13px] font-medium')}
|
className={cn(toneId === 4 && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-1 sm:!px-2 !justify-center text-[13px] font-medium')}
|
||||||
labelClassName={cn('flex items-center space-x-2 ', toneId === 4 ? 'text-[#155EEF]' : 'text-[#667085]')}
|
labelClassName={cn('flex items-center space-x-2 ', toneId === 4 ? 'text-[#155EEF]' : 'text-[#667085]')}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{getToneIcon(TONE_LIST[3].id)}
|
{getToneIcon(TONE_LIST[3].id)}
|
||||||
<div>{t(`common.model.tone.${TONE_LIST[3].name}`) as string}</div>
|
{!isMobile && <div>{t(`common.model.tone.${TONE_LIST[3].name}`) as string}</div>}
|
||||||
</>
|
</>
|
||||||
</Radio>
|
</Radio>
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
|
@ -20,7 +20,7 @@ const ModelModeTypeLabel: FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(className, isHighlight ? 'border-indigo-300 text-indigo-600' : 'border-gray-300 text-gray-500', 'flex items-center h-4 px-1 border rounded text-xs font-semibold uppercase')}
|
className={cn(className, isHighlight ? 'border-indigo-300 text-indigo-600' : 'border-gray-300 text-gray-500', 'flex items-center h-4 px-1 border rounded text-xs font-semibold uppercase text-ellipsis overflow-hidden whitespace-nowrap')}
|
||||||
>
|
>
|
||||||
{t(`appDebug.modelConfig.modeType.${type}`)}
|
{t(`appDebug.modelConfig.modeType.${type}`)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,7 +18,7 @@ const ModelName: FC<IModelNameProps> = ({
|
|||||||
modelDisplayName,
|
modelDisplayName,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<span title={modelDisplayName}>
|
<span className='text-ellipsis overflow-hidden whitespace-nowrap' title={modelDisplayName}>
|
||||||
{modelDisplayName}
|
{modelDisplayName}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
@ -49,7 +49,7 @@ const ParamItem: FC<IParamIteProps> = ({ id, name, tip, step = 0.1, min = 0, max
|
|||||||
onChange(id, getFitPrecisionValue(value, precision))
|
onChange(id, getFitPrecisionValue(value, precision))
|
||||||
}, [value, precision])
|
}, [value, precision])
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between flex-wrap gap-y-2">
|
||||||
<div className="flex flex-col flex-shrink-0">
|
<div className="flex flex-col flex-shrink-0">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="mr-[6px] text-gray-500 text-[13px] font-medium">{name}</span>
|
<span className="mr-[6px] text-gray-500 text-[13px] font-medium">{name}</span>
|
||||||
|
@ -185,8 +185,8 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
|
|||||||
<div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.notSetVar')}</div>
|
<div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.notSetVar')}</div>
|
||||||
)}
|
)}
|
||||||
{hasVar && (
|
{hasVar && (
|
||||||
<div className='rounded-lg border border-gray-200 bg-white'>
|
<div className='rounded-lg border border-gray-200 bg-white overflow-x-auto'>
|
||||||
<table className={`${s.table} w-full border-collapse border-0 rounded-lg text-sm`}>
|
<table className={`${s.table} min-w-[440px] max-w-full border-collapse border-0 rounded-lg text-sm`}>
|
||||||
<thead className="border-b border-gray-200 text-gray-500 text-xs font-medium">
|
<thead className="border-b border-gray-200 text-gray-500 text-xs font-medium">
|
||||||
<tr className='uppercase'>
|
<tr className='uppercase'>
|
||||||
<td>{t('appDebug.variableTable.key')}</td>
|
<td>{t('appDebug.variableTable.key')}</td>
|
||||||
|
@ -31,7 +31,7 @@ const ParamsConfig: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent style={{ zIndex: 50 }}>
|
<PortalToFollowElemContent style={{ zIndex: 50 }}>
|
||||||
<div className='w-[412px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
|
<div className='w-80 sm:w-[412px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
|
||||||
<ParamConfigContent />
|
<ParamConfigContent />
|
||||||
</div>
|
</div>
|
||||||
</PortalToFollowElemContent>
|
</PortalToFollowElemContent>
|
||||||
|
@ -16,6 +16,7 @@ import Loading from '@/app/components/base/loading'
|
|||||||
import Confirm from '@/app/components/base/confirm'
|
import Confirm from '@/app/components/base/confirm'
|
||||||
// type
|
// type
|
||||||
import type { AutomaticRes } from '@/service/debug'
|
import type { AutomaticRes } from '@/service/debug'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
const noDataIcon = (
|
const noDataIcon = (
|
||||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -47,6 +48,9 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const [audiences, setAudiences] = React.useState<string>('')
|
const [audiences, setAudiences] = React.useState<string>('')
|
||||||
const [hopingToSolve, setHopingToSolve] = React.useState<string>('')
|
const [hopingToSolve, setHopingToSolve] = React.useState<string>('')
|
||||||
const isValid = () => {
|
const isValid = () => {
|
||||||
@ -103,15 +107,36 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
|
|||||||
|
|
||||||
const [showConfirmOverwrite, setShowConfirmOverwrite] = React.useState(false)
|
const [showConfirmOverwrite, setShowConfirmOverwrite] = React.useState(false)
|
||||||
|
|
||||||
|
const isShowAutoPromptInput = () => {
|
||||||
|
if (isMobile) {
|
||||||
|
// hide prompt panel on mobile if it is loading or has had result
|
||||||
|
if (isLoading || res)
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// alway display prompt panel on desktop mode
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const isShowAutoPromptResPlaceholder = () => {
|
||||||
|
if (isMobile) {
|
||||||
|
// hide placeholder panel on mobile
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isLoading && !res
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isShow={isShow}
|
isShow={isShow}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
className='min-w-[1120px] !p-0'
|
className='!p-0 sm:min-w-[768px] xl:min-w-[1120px]'
|
||||||
closable
|
closable
|
||||||
>
|
>
|
||||||
<div className='flex h-[680px]'>
|
<div className='flex h-[680px] flex-wrap gap-y-4 overflow-y-auto'>
|
||||||
<div className='w-[480px] shrink-0 px-8 py-6 h-full overflow-y-auto border-r border-gray-100'>
|
{isShowAutoPromptInput() && <div className='w-full sm:w-[360px] xl:w-[480px] shrink-0 px-8 py-6 h-full overflow-y-auto border-r border-gray-100'>
|
||||||
<div>
|
<div>
|
||||||
<div className='mb-1 text-xl font-semibold text-primary-600'>{t('appDebug.automatic.title')}</div>
|
<div className='mb-1 text-xl font-semibold text-primary-600'>{t('appDebug.automatic.title')}</div>
|
||||||
<div className='text-[13px] font-normal text-gray-500'>{t('appDebug.automatic.description')}</div>
|
<div className='text-[13px] font-normal text-gray-500'>{t('appDebug.automatic.description')}</div>
|
||||||
@ -139,7 +164,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>}
|
||||||
|
|
||||||
{(!isLoading && res) && (
|
{(!isLoading && res) && (
|
||||||
<div className='grow px-8 pt-6 h-full overflow-y-auto'>
|
<div className='grow px-8 pt-6 h-full overflow-y-auto'>
|
||||||
@ -180,7 +205,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isLoading && renderLoading}
|
{isLoading && renderLoading}
|
||||||
{(!isLoading && !res) && renderNoData}
|
{isShowAutoPromptResPlaceholder() && renderNoData}
|
||||||
{showConfirmOverwrite && (
|
{showConfirmOverwrite && (
|
||||||
<Confirm
|
<Confirm
|
||||||
title={t('appDebug.automatic.overwriteTitle')}
|
title={t('appDebug.automatic.overwriteTitle')}
|
||||||
|
@ -9,6 +9,8 @@ import { formatNumber } from '@/utils/format'
|
|||||||
import FileIcon from '@/app/components/base/file-icon'
|
import FileIcon from '@/app/components/base/file-icon'
|
||||||
import { Settings01, Trash03 } from '@/app/components/base/icons/src/vender/line/general'
|
import { Settings01, Trash03 } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
|
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
|
||||||
|
import Drawer from '@/app/components/base/drawer'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
type ItemProps = {
|
type ItemProps = {
|
||||||
className?: string
|
className?: string
|
||||||
@ -24,6 +26,10 @@ const Item: FC<ItemProps> = ({
|
|||||||
onRemove,
|
onRemove,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const [showSettingsModal, setShowSettingsModal] = useState(false)
|
const [showSettingsModal, setShowSettingsModal] = useState(false)
|
||||||
|
|
||||||
const handleSave = (newDataset: DataSet) => {
|
const handleSave = (newDataset: DataSet) => {
|
||||||
@ -74,15 +80,13 @@ const Item: FC<ItemProps> = ({
|
|||||||
<Trash03 className='w-4 h-4 text-gray-500 group-hover/action:text-[#D92D20]' />
|
<Trash03 className='w-4 h-4 text-gray-500 group-hover/action:text-[#D92D20]' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{
|
<Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
|
||||||
showSettingsModal && (
|
|
||||||
<SettingsModal
|
<SettingsModal
|
||||||
currentDataset={config}
|
currentDataset={config}
|
||||||
onCancel={() => setShowSettingsModal(false)}
|
onCancel={() => setShowSettingsModal(false)}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
/>
|
/>
|
||||||
)
|
</Drawer>
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ const ParamsConfig: FC = () => {
|
|||||||
onClose={() => {
|
onClose={() => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}}
|
}}
|
||||||
className='min-w-[528px]'
|
className='sm:min-w-[528px]'
|
||||||
wrapperClassName='z-50'
|
wrapperClassName='z-50'
|
||||||
title={t('appDebug.datasetConfig.settingTitle')}
|
title={t('appDebug.datasetConfig.settingTitle')}
|
||||||
>
|
>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { useRef, useState } from 'react'
|
import { useRef, useState } from 'react'
|
||||||
import { useClickAway } from 'ahooks'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { isEqual } from 'lodash-es'
|
import { isEqual } from 'lodash-es'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
@ -30,7 +29,7 @@ type SettingsModalProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const rowClass = `
|
const rowClass = `
|
||||||
flex justify-between py-4
|
flex justify-between py-4 flex-wrap gap-y-2
|
||||||
`
|
`
|
||||||
|
|
||||||
const labelClass = `
|
const labelClass = `
|
||||||
@ -45,10 +44,6 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useToastContext()
|
const { notify } = useToastContext()
|
||||||
const ref = useRef(null)
|
const ref = useRef(null)
|
||||||
useClickAway(() => {
|
|
||||||
if (ref)
|
|
||||||
onCancel()
|
|
||||||
}, ref)
|
|
||||||
|
|
||||||
const { setShowAccountSettingModal } = useModalContext()
|
const { setShowAccountSettingModal } = useModalContext()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@ -122,10 +117,8 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='fixed top-16 right-2 flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl z-10'
|
className='overflow-hidden w-full flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl'
|
||||||
style={{
|
style={{
|
||||||
zIndex: 11,
|
|
||||||
width: 700,
|
|
||||||
height: 'calc(100vh - 72px)',
|
height: 'calc(100vh - 72px)',
|
||||||
}}
|
}}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -179,12 +172,12 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||||||
<div className={labelClass}>
|
<div className={labelClass}>
|
||||||
<div>{t('datasetSettings.form.permissions')}</div>
|
<div>{t('datasetSettings.form.permissions')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-[480px]'>
|
<div className='w-full sm:w-[480px]'>
|
||||||
<PermissionsRadio
|
<PermissionsRadio
|
||||||
disable={!localeCurrentDataset?.embedding_available}
|
disable={!localeCurrentDataset?.embedding_available}
|
||||||
value={localeCurrentDataset.permission}
|
value={localeCurrentDataset.permission}
|
||||||
onChange={v => handleValueChange('permission', v!)}
|
onChange={v => handleValueChange('permission', v!)}
|
||||||
itemClassName='!w-[227px]'
|
itemClassName='sm:!w-[227px]'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -198,7 +191,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||||||
disable={!localeCurrentDataset?.embedding_available}
|
disable={!localeCurrentDataset?.embedding_available}
|
||||||
value={indexMethod}
|
value={indexMethod}
|
||||||
onChange={v => setIndexMethod(v!)}
|
onChange={v => setIndexMethod(v!)}
|
||||||
itemClassName='!w-[227px]'
|
itemClassName='sm:!w-[227px]'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -272,7 +265,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className='absolute z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t bg-white '
|
className='sticky z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t bg-white '
|
||||||
style={{
|
style={{
|
||||||
borderColor: 'rgba(0, 0, 0, 0.05)',
|
borderColor: 'rgba(0, 0, 0, 0.05)',
|
||||||
}}
|
}}
|
||||||
|
@ -8,6 +8,7 @@ import produce from 'immer'
|
|||||||
import { useBoolean, useGetState } from 'ahooks'
|
import { useBoolean, useGetState } from 'ahooks'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import { clone, isEqual } from 'lodash-es'
|
import { clone, isEqual } from 'lodash-es'
|
||||||
|
import { CodeBracketIcon } from '@heroicons/react/20/solid'
|
||||||
import Button from '../../base/button'
|
import Button from '../../base/button'
|
||||||
import Loading from '../../base/loading'
|
import Loading from '../../base/loading'
|
||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
@ -44,6 +45,8 @@ import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/
|
|||||||
import SelectDataSet from '@/app/components/app/configuration/dataset-config/select-dataset'
|
import SelectDataSet from '@/app/components/app/configuration/dataset-config/select-dataset'
|
||||||
import I18n from '@/context/i18n'
|
import I18n from '@/context/i18n'
|
||||||
import { useModalContext } from '@/context/modal-context'
|
import { useModalContext } from '@/context/modal-context'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
import Drawer from '@/app/components/base/drawer'
|
||||||
|
|
||||||
type PublichConfig = {
|
type PublichConfig = {
|
||||||
modelConfig: ModelConfig
|
modelConfig: ModelConfig
|
||||||
@ -64,6 +67,10 @@ const Configuration: FC = () => {
|
|||||||
|
|
||||||
const [conversationId, setConversationId] = useState<string | null>('')
|
const [conversationId, setConversationId] = useState<string | null>('')
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
const [isShowDebugPanel, { setTrue: showDebugPanel, setFalse: hideDebugPanel }] = useBoolean(false)
|
||||||
|
|
||||||
const [introduction, setIntroduction] = useState<string>('')
|
const [introduction, setIntroduction] = useState<string>('')
|
||||||
const [controlClearChatMessage, setControlClearChatMessage] = useState(0)
|
const [controlClearChatMessage, setControlClearChatMessage] = useState(0)
|
||||||
const [prevPromptConfig, setPrevPromptConfig] = useState<PromptConfig>({
|
const [prevPromptConfig, setPrevPromptConfig] = useState<PromptConfig>({
|
||||||
@ -600,7 +607,7 @@ const Configuration: FC = () => {
|
|||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
<div className='flex items-center justify-between px-6 shrink-0 h-14'>
|
<div className='flex items-center justify-between px-6 shrink-0 py-3 flex-wrap gap-y-2'>
|
||||||
<div className='flex items-end'>
|
<div className='flex items-end'>
|
||||||
<div className={s.promptTitle}></div>
|
<div className={s.promptTitle}></div>
|
||||||
<div className='flex items-center h-[14px] space-x-1 text-xs'>
|
<div className='flex items-center h-[14px] space-x-1 text-xs'>
|
||||||
@ -630,7 +637,7 @@ const Configuration: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center flex-wrap gap-y-2 gap-x-2'>
|
||||||
{/* Model and Parameters */}
|
{/* Model and Parameters */}
|
||||||
<ConfigModel
|
<ConfigModel
|
||||||
isAdvancedMode={isAdvancedMode}
|
isAdvancedMode={isAdvancedMode}
|
||||||
@ -644,22 +651,28 @@ const Configuration: FC = () => {
|
|||||||
}}
|
}}
|
||||||
disabled={!hasSetAPIKEY}
|
disabled={!hasSetAPIKEY}
|
||||||
/>
|
/>
|
||||||
<div className='mx-3 w-[1px] h-[14px] bg-gray-200'></div>
|
<div className='w-[1px] h-[14px] bg-gray-200'></div>
|
||||||
<Button onClick={() => setShowConfirm(true)} className='shrink-0 mr-2 w-[70px] !h-8 !text-[13px] font-medium'>{t('appDebug.operation.resetConfig')}</Button>
|
<Button onClick={() => setShowConfirm(true)} className='shrink-0 mr-2 w-[70px] !h-8 !text-[13px] font-medium'>{t('appDebug.operation.resetConfig')}</Button>
|
||||||
|
{isMobile && (
|
||||||
|
<Button className='!h-8 !text-[13px] font-medium' onClick={showDebugPanel}>
|
||||||
|
<span className='mr-1'>{t('appDebug.operation.debugConfig')}</span>
|
||||||
|
<CodeBracketIcon className="h-4 w-4 text-gray-500" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button type='primary' onClick={() => handlePublish(false)} className={cn(cannotPublish && '!bg-primary-200 !cursor-not-allowed', 'shrink-0 w-[70px] !h-8 !text-[13px] font-medium')}>{t('appDebug.operation.applyConfig')}</Button>
|
<Button type='primary' onClick={() => handlePublish(false)} className={cn(cannotPublish && '!bg-primary-200 !cursor-not-allowed', 'shrink-0 w-[70px] !h-8 !text-[13px] font-medium')}>{t('appDebug.operation.applyConfig')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex grow h-[200px]'>
|
<div className='flex grow h-[200px]'>
|
||||||
<div className="w-1/2 min-w-[560px] shrink-0">
|
<div className="w-full sm:w-1/2 shrink-0">
|
||||||
<Config />
|
<Config />
|
||||||
</div>
|
</div>
|
||||||
<div className="relative w-1/2 grow h-full overflow-y-auto py-4 px-6 bg-gray-50 flex flex-col rounded-tl-2xl border-t border-l" style={{ borderColor: 'rgba(0, 0, 0, 0.02)' }}>
|
{!isMobile && <div className="relative w-1/2 grow h-full overflow-y-auto py-4 px-6 bg-gray-50 flex flex-col rounded-tl-2xl border-t border-l" style={{ borderColor: 'rgba(0, 0, 0, 0.02)' }}>
|
||||||
<Debug
|
<Debug
|
||||||
hasSetAPIKEY={hasSetAPIKEY}
|
hasSetAPIKEY={hasSetAPIKEY}
|
||||||
onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
|
onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
|
||||||
inputs={inputs}
|
inputs={inputs}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{showConfirm && (
|
{showConfirm && (
|
||||||
@ -707,6 +720,15 @@ const Configuration: FC = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{isMobile && (
|
||||||
|
<Drawer showClose isOpen={isShowDebugPanel} onClose={hideDebugPanel} mask footer={null} panelClassname='!bg-gray-50'>
|
||||||
|
<Debug
|
||||||
|
hasSetAPIKEY={hasSetAPIKEY}
|
||||||
|
onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
|
||||||
|
inputs={inputs}
|
||||||
|
/>
|
||||||
|
</Drawer>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
</ConfigContext.Provider>
|
</ConfigContext.Provider>
|
||||||
)
|
)
|
||||||
|
@ -39,7 +39,7 @@ const Filter: FC<IFilterProps> = ({ appId, queryParams, setQueryParams }: IFilte
|
|||||||
if (!data)
|
if (!data)
|
||||||
return null
|
return null
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-row items-center mb-4 text-gray-900 text-base'>
|
<div className='flex flex-row flex-wrap gap-y-2 gap-x-4 items-center mb-4 text-gray-900 text-base'>
|
||||||
<SimpleSelect
|
<SimpleSelect
|
||||||
items={TIME_PERIOD_LIST.map(item => ({ value: item.value, name: t(`appLog.filter.period.${item.name}`) }))}
|
items={TIME_PERIOD_LIST.map(item => ({ value: item.value, name: t(`appLog.filter.period.${item.name}`) }))}
|
||||||
className='mt-0 !w-40'
|
className='mt-0 !w-40'
|
||||||
@ -47,7 +47,7 @@ const Filter: FC<IFilterProps> = ({ appId, queryParams, setQueryParams }: IFilte
|
|||||||
setQueryParams({ ...queryParams, period: item.value })
|
setQueryParams({ ...queryParams, period: item.value })
|
||||||
}}
|
}}
|
||||||
defaultValue={queryParams.period} />
|
defaultValue={queryParams.period} />
|
||||||
<div className="relative ml-4 rounded-md mr-4">
|
<div className="relative rounded-md">
|
||||||
<SimpleSelect
|
<SimpleSelect
|
||||||
defaultValue={'all'}
|
defaultValue={'all'}
|
||||||
className='!w-[300px]'
|
className='!w-[300px]'
|
||||||
|
@ -33,6 +33,7 @@ import { TONE_LIST } from '@/config'
|
|||||||
import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
|
import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
|
||||||
import ModelName from '@/app/components/app/configuration/config-model/model-name'
|
import ModelName from '@/app/components/app/configuration/config-model/model-name'
|
||||||
import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label'
|
import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
type IConversationList = {
|
type IConversationList = {
|
||||||
logs?: ChatConversationsResponse | CompletionConversationsResponse
|
logs?: ChatConversationsResponse | CompletionConversationsResponse
|
||||||
@ -200,7 +201,7 @@ function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionCo
|
|||||||
<div className='text-gray-500 text-[10px] leading-[14px]'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div>
|
<div className='text-gray-500 text-[10px] leading-[14px]'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div>
|
||||||
<div className='text-gray-700 text-[13px] leading-[18px]'>{isChatMode ? detail.id?.split('-').slice(-1)[0] : dayjs.unix(detail.created_at).format(t('appLog.dateTimeFormat') as string)}</div>
|
<div className='text-gray-700 text-[13px] leading-[18px]'>{isChatMode ? detail.id?.split('-').slice(-1)[0] : dayjs.unix(detail.created_at).format(t('appLog.dateTimeFormat') as string)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center flex-wrap gap-y-1 justify-end'>
|
||||||
<div
|
<div
|
||||||
className={cn('mr-2 flex items-center border h-8 px-2 space-x-2 rounded-lg bg-indigo-25 border-[#2A87F5]')}
|
className={cn('mr-2 flex items-center border h-8 px-2 space-x-2 rounded-lg bg-indigo-25 border-[#2A87F5]')}
|
||||||
>
|
>
|
||||||
@ -412,6 +413,10 @@ const ChatConversationDetailComp: FC<{ appId?: string; conversationId?: string }
|
|||||||
*/
|
*/
|
||||||
const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh }) => {
|
const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const [showDrawer, setShowDrawer] = useState<boolean>(false) // Whether to display the chat details drawer
|
const [showDrawer, setShowDrawer] = useState<boolean>(false) // Whether to display the chat details drawer
|
||||||
const [currentConversation, setCurrentConversation] = useState<ChatConversationGeneralDetail | CompletionConversationGeneralDetail | undefined>() // Currently selected conversation
|
const [currentConversation, setCurrentConversation] = useState<ChatConversationGeneralDetail | CompletionConversationGeneralDetail | undefined>() // Currently selected conversation
|
||||||
const isChatMode = appDetail?.mode === 'chat' // Whether the app is a chat app
|
const isChatMode = appDetail?.mode === 'chat' // Whether the app is a chat app
|
||||||
@ -445,17 +450,17 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
|
|||||||
return <Loading />
|
return <Loading />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className='overflow-x-auto'>
|
||||||
<table className={`w-full border-collapse border-0 text-sm mt-3 ${s.logTable}`}>
|
<table className={`w-full min-w-[440px] border-collapse border-0 text-sm mt-3 ${s.logTable}`}>
|
||||||
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-bold">
|
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-bold">
|
||||||
<tr>
|
<tr>
|
||||||
<td className='w-[1.375rem]'></td>
|
<td className='w-[1.375rem] whitespace-nowrap'></td>
|
||||||
<td>{t('appLog.table.header.time')}</td>
|
<td className='whitespace-nowrap'>{t('appLog.table.header.time')}</td>
|
||||||
<td>{t('appLog.table.header.endUser')}</td>
|
<td className='whitespace-nowrap'>{t('appLog.table.header.endUser')}</td>
|
||||||
<td>{isChatMode ? t('appLog.table.header.summary') : t('appLog.table.header.input')}</td>
|
<td className='whitespace-nowrap'>{isChatMode ? t('appLog.table.header.summary') : t('appLog.table.header.input')}</td>
|
||||||
<td>{isChatMode ? t('appLog.table.header.messageCount') : t('appLog.table.header.output')}</td>
|
<td className='whitespace-nowrap'>{isChatMode ? t('appLog.table.header.messageCount') : t('appLog.table.header.output')}</td>
|
||||||
<td>{t('appLog.table.header.userRate')}</td>
|
<td className='whitespace-nowrap'>{t('appLog.table.header.userRate')}</td>
|
||||||
<td>{t('appLog.table.header.adminRate')}</td>
|
<td className='whitespace-nowrap'>{t('appLog.table.header.adminRate')}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="text-gray-500">
|
<tbody className="text-gray-500">
|
||||||
@ -504,9 +509,9 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
|
|||||||
<Drawer
|
<Drawer
|
||||||
isOpen={showDrawer}
|
isOpen={showDrawer}
|
||||||
onClose={onCloseDrawer}
|
onClose={onCloseDrawer}
|
||||||
mask={false}
|
mask={isMobile}
|
||||||
footer={null}
|
footer={null}
|
||||||
panelClassname='mt-16 mr-2 mb-3 !p-0 !max-w-[640px] rounded-b-xl'
|
panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'
|
||||||
>
|
>
|
||||||
<DrawerContext.Provider value={{
|
<DrawerContext.Provider value={{
|
||||||
onClose: onCloseDrawer,
|
onClose: onCloseDrawer,
|
||||||
@ -518,7 +523,7 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
|
|||||||
}
|
}
|
||||||
</DrawerContext.Provider>
|
</DrawerContext.Provider>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ function AppCard({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`min-w-max shadow-xs border-[0.5px] rounded-lg border-gray-200 ${
|
className={`shadow-xs border-[0.5px] rounded-lg border-gray-200 ${
|
||||||
className ?? ''
|
className ?? ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -163,8 +163,8 @@ function AppCard({
|
|||||||
: t('appOverview.overview.apiInfo.accessibleAddress')}
|
: t('appOverview.overview.apiInfo.accessibleAddress')}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full h-9 pl-2 pr-0.5 py-0.5 bg-black bg-opacity-[0.02] rounded-lg border border-black border-opacity-5 justify-start items-center inline-flex">
|
<div className="w-full h-9 pl-2 pr-0.5 py-0.5 bg-black bg-opacity-[0.02] rounded-lg border border-black border-opacity-5 justify-start items-center inline-flex">
|
||||||
<div className="h-4 px-2 justify-start items-start gap-2 flex flex-1">
|
<div className="h-4 px-2 justify-start items-start gap-2 flex flex-1 min-w-0">
|
||||||
<div className="text-gray-700 text-xs font-medium">
|
<div className="text-gray-700 text-xs font-medium text-ellipsis overflow-hidden whitespace-nowrap">
|
||||||
{isApp ? appUrl : apiUrl}
|
{isApp ? appUrl : apiUrl}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -196,7 +196,7 @@ function AppCard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={'pt-2 flex flex-row items-center'}>
|
<div className={'pt-2 flex flex-row items-center flex-wrap gap-y-2'}>
|
||||||
{!isApp && <SecretKeyButton className='flex-shrink-0 !h-8 bg-white mr-2' textCls='!text-gray-700 font-medium' iconCls='stroke-[1.2px]' appId={appInfo.id} />}
|
{!isApp && <SecretKeyButton className='flex-shrink-0 !h-8 bg-white mr-2' textCls='!text-gray-700 font-medium' iconCls='stroke-[1.2px]' appId={appInfo.id} />}
|
||||||
{OPERATIONS_MAP[cardType].map((op) => {
|
{OPERATIONS_MAP[cardType].map((op) => {
|
||||||
const disabled
|
const disabled
|
||||||
|
@ -81,10 +81,10 @@ const CustomizeModal: FC<IShareLinkProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className='flex py-4'>
|
<div className='flex py-4'>
|
||||||
<StepNum>3</StepNum>
|
<StepNum>3</StepNum>
|
||||||
<div className='flex flex-col w-full'>
|
<div className='flex flex-col w-full overflow-hidden'>
|
||||||
<div className='text-gray-900'>{t(`${prefixCustomize}.way1.step3`)}</div>
|
<div className='text-gray-900'>{t(`${prefixCustomize}.way1.step3`)}</div>
|
||||||
<div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step3Tip`)}</div>
|
<div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step3Tip`)}</div>
|
||||||
<pre className='box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg select-text'>
|
<pre className='overflow-x-scroll box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg select-text'>
|
||||||
NEXT_PUBLIC_APP_ID={`'${appId}'`} <br />
|
NEXT_PUBLIC_APP_ID={`'${appId}'`} <br />
|
||||||
NEXT_PUBLIC_APP_KEY={'\'<Web API Key From Dify>\''} <br />
|
NEXT_PUBLIC_APP_KEY={'\'<Web API Key From Dify>\''} <br />
|
||||||
NEXT_PUBLIC_API_URL={`'${api_base_url}'`}
|
NEXT_PUBLIC_API_URL={`'${api_base_url}'`}
|
||||||
|
@ -106,7 +106,7 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
|
|||||||
<div className="mb-4 mt-8 text-gray-900 text-[14px] font-medium leading-tight">
|
<div className="mb-4 mt-8 text-gray-900 text-[14px] font-medium leading-tight">
|
||||||
{t(`${prefixEmbedded}.explanation`)}
|
{t(`${prefixEmbedded}.explanation`)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between flex-wrap gap-y-2">
|
||||||
{Object.keys(OPTION_MAP).map((v, index) => {
|
{Object.keys(OPTION_MAP).map((v, index) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -150,7 +150,7 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="self-stretch p-3 justify-start items-start gap-2 inline-flex">
|
<div className="p-3 justify-start items-start gap-2 flex overflow-x-auto w-full">
|
||||||
<div className="grow shrink basis-0 text-slate-700 text-[13px] leading-tight font-mono">
|
<div className="grow shrink basis-0 text-slate-700 text-[13px] leading-tight font-mono">
|
||||||
<pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, isTestEnv)}</pre>
|
<pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, isTestEnv)}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Dialog } from '@headlessui/react'
|
import { Dialog } from '@headlessui/react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { XMarkIcon } from '@heroicons/react/24/outline'
|
||||||
import Button from '../button'
|
import Button from '../button'
|
||||||
|
|
||||||
type DrawerProps = {
|
export type IDrawerProps = {
|
||||||
title?: string
|
title?: string
|
||||||
description?: string
|
description?: string
|
||||||
panelClassname?: string
|
panelClassname?: string
|
||||||
@ -12,6 +13,7 @@ type DrawerProps = {
|
|||||||
mask?: boolean
|
mask?: boolean
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
// closable: boolean
|
// closable: boolean
|
||||||
|
showClose?: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onCancel?: () => void
|
onCancel?: () => void
|
||||||
onOk?: () => void
|
onOk?: () => void
|
||||||
@ -24,11 +26,12 @@ export default function Drawer({
|
|||||||
children,
|
children,
|
||||||
footer,
|
footer,
|
||||||
mask = true,
|
mask = true,
|
||||||
|
showClose = false,
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
onCancel,
|
onCancel,
|
||||||
onOk,
|
onOk,
|
||||||
}: DrawerProps) {
|
}: IDrawerProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@ -52,6 +55,9 @@ export default function Drawer({
|
|||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Dialog.Title>}
|
</Dialog.Title>}
|
||||||
|
{showClose && <Dialog.Title className="flex items-center mb-4" as="div">
|
||||||
|
<XMarkIcon className='w-4 h-4 text-gray-500' onClick={onClose} />
|
||||||
|
</Dialog.Title>}
|
||||||
{description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'>{description}</Dialog.Description>}
|
{description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'>{description}</Dialog.Description>}
|
||||||
{children}
|
{children}
|
||||||
</>
|
</>
|
||||||
|
37
web/app/components/base/float-popover-container/index.tsx
Normal file
37
web/app/components/base/float-popover-container/index.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
'use client'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import type { PortalToFollowElemOptions } from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
|
||||||
|
type IFloatRightContainerProps = {
|
||||||
|
isMobile: boolean
|
||||||
|
open: boolean
|
||||||
|
toggle: () => void
|
||||||
|
triggerElement?: React.ReactNode
|
||||||
|
children?: React.ReactNode
|
||||||
|
} & PortalToFollowElemOptions
|
||||||
|
|
||||||
|
const FloatRightContainer = ({ open, toggle, triggerElement, isMobile, children, ...portalProps }: IFloatRightContainerProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isMobile && (
|
||||||
|
<PortalToFollowElem open={open} {...portalProps}>
|
||||||
|
<PortalToFollowElemTrigger onClick={toggle}>
|
||||||
|
{triggerElement}
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent>
|
||||||
|
{children}
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
)}
|
||||||
|
{!isMobile && open && (
|
||||||
|
<>{children}</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FloatRightContainer
|
23
web/app/components/base/float-right-container/index.tsx
Normal file
23
web/app/components/base/float-right-container/index.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
'use client'
|
||||||
|
import Drawer from '@/app/components/base/drawer'
|
||||||
|
import type { IDrawerProps } from '@/app/components/base/drawer'
|
||||||
|
|
||||||
|
type IFloatRightContainerProps = {
|
||||||
|
isMobile: boolean
|
||||||
|
children?: React.ReactNode
|
||||||
|
} & IDrawerProps
|
||||||
|
|
||||||
|
const FloatRightContainer = ({ isMobile, children, isOpen, ...drawerProps }: IFloatRightContainerProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isMobile && (
|
||||||
|
<Drawer isOpen={isOpen} {...drawerProps}>{children}</Drawer>
|
||||||
|
)}
|
||||||
|
{(!isMobile && isOpen) && (
|
||||||
|
<>{children}</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FloatRightContainer
|
@ -5,5 +5,5 @@
|
|||||||
@apply absolute z-10 w-full max-w-sm px-4 mt-1 sm:px-0 lg:max-w-3xl
|
@apply absolute z-10 w-full max-w-sm px-4 mt-1 sm:px-0 lg:max-w-3xl
|
||||||
}
|
}
|
||||||
.panelContainer {
|
.panelContainer {
|
||||||
@apply overflow-hidden bg-white w-full rounded-lg shadow-lg ring-1 ring-black ring-opacity-5
|
@apply overflow-hidden bg-white w-fit min-w-[130px] rounded-lg shadow-lg ring-1 ring-black ring-opacity-5
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
|
|
||||||
import type { OffsetOptions, Placement } from '@floating-ui/react'
|
import type { OffsetOptions, Placement } from '@floating-ui/react'
|
||||||
|
|
||||||
type PortalToFollowElemOptions = {
|
export type PortalToFollowElemOptions = {
|
||||||
/*
|
/*
|
||||||
* top, bottom, left, right
|
* top, bottom, left, right
|
||||||
* start, end. Default is middle
|
* start, end. Default is middle
|
||||||
|
@ -148,7 +148,7 @@ const Select: FC<ISelectProps> = ({
|
|||||||
|
|
||||||
const SimpleSelect: FC<ISelectProps> = ({
|
const SimpleSelect: FC<ISelectProps> = ({
|
||||||
className,
|
className,
|
||||||
wrapperClassName,
|
wrapperClassName = '',
|
||||||
items = defaultItems,
|
items = defaultItems,
|
||||||
defaultValue = 1,
|
defaultValue = 1,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
color: #667085;
|
color: #667085;
|
||||||
}
|
}
|
||||||
.uploader {
|
.uploader {
|
||||||
@apply relative box-border flex justify-center items-center mb-2;
|
@apply relative box-border flex justify-center items-center mb-2 p-3;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
height: 80px;
|
min-height: 80px;
|
||||||
background: #F9FAFB;
|
background: #F9FAFB;
|
||||||
border: 1px dashed #EAECF0;
|
border: 1px dashed #EAECF0;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
@ -234,10 +234,12 @@ const FileUploader = ({
|
|||||||
/>
|
/>
|
||||||
<div className={cn(s.title, titleClassName)}>{t('datasetCreation.stepOne.uploader.title')}</div>
|
<div className={cn(s.title, titleClassName)}>{t('datasetCreation.stepOne.uploader.title')}</div>
|
||||||
<div ref={dropRef} className={cn(s.uploader, dragging && s.dragging)}>
|
<div ref={dropRef} className={cn(s.uploader, dragging && s.dragging)}>
|
||||||
<div className='flex justify-center items-center h-6 mb-2'>
|
<div className='flex justify-center items-center min-h-6 mb-2'>
|
||||||
<span className={s.uploadIcon}/>
|
<span className={s.uploadIcon}/>
|
||||||
<span>{t('datasetCreation.stepOne.uploader.button')}</span>
|
<span>
|
||||||
|
{t('datasetCreation.stepOne.uploader.button')}
|
||||||
<label className={s.browse} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
|
<label className={s.browse} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={s.tip}>{t('datasetCreation.stepOne.uploader.tip', { size: fileUploadConfig.file_size_limit })}</div>
|
<div className={s.tip}>{t('datasetCreation.stepOne.uploader.tip', { size: fileUploadConfig.file_size_limit })}</div>
|
||||||
{dragging && <div ref={dragRef} className={s.draggingCover}/>}
|
{dragging && <div ref={dragRef} className={s.draggingCover}/>}
|
||||||
|
@ -103,7 +103,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
|
<div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
|
||||||
<div className="flex flex-col w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
|
<div className="flex flex-col w-11 sm:w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
|
||||||
<StepsNavBar step={step} datasetId={datasetId} />
|
<StepsNavBar step={step} datasetId={datasetId} />
|
||||||
</div>
|
</div>
|
||||||
<div className="grow bg-white">
|
<div className="grow bg-white">
|
||||||
|
@ -15,9 +15,6 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dataSourceTypeList {
|
|
||||||
@apply flex items-center mb-8;
|
|
||||||
}
|
|
||||||
.dataSourceItem {
|
.dataSourceItem {
|
||||||
@apply box-border relative shrink-0 flex items-center mr-3 p-3 h-14 bg-white rounded-xl cursor-pointer;
|
@apply box-border relative shrink-0 flex items-center mr-3 p-3 h-14 bg-white rounded-xl cursor-pointer;
|
||||||
border: 0.5px solid #EAECF0;
|
border: 0.5px solid #EAECF0;
|
||||||
|
@ -106,7 +106,7 @@ const StepOne = ({
|
|||||||
<div className={s.form}>
|
<div className={s.form}>
|
||||||
{
|
{
|
||||||
shouldShowDataSourceTypeList && (
|
shouldShowDataSourceTypeList && (
|
||||||
<div className={s.dataSourceTypeList}>
|
<div className='flex items-center mb-8 flex-wrap gap-y-4'>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
s.dataSourceItem,
|
s.dataSourceItem,
|
||||||
|
@ -5,6 +5,7 @@ import cn from 'classnames'
|
|||||||
import EmbeddingProcess from '../embedding-process'
|
import EmbeddingProcess from '../embedding-process'
|
||||||
|
|
||||||
import s from './index.module.css'
|
import s from './index.module.css'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
import type { FullDocumentDetail, createDocumentResponse } from '@/models/datasets'
|
import type { FullDocumentDetail, createDocumentResponse } from '@/models/datasets'
|
||||||
|
|
||||||
type StepThreeProps = {
|
type StepThreeProps = {
|
||||||
@ -17,9 +18,12 @@ type StepThreeProps = {
|
|||||||
const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: StepThreeProps) => {
|
const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: StepThreeProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex w-full h-full'>
|
<div className='flex w-full h-full'>
|
||||||
<div className={'h-full w-full overflow-y-scroll px-16'}>
|
<div className={'h-full w-full overflow-y-scroll px-6 sm:px-16'}>
|
||||||
<div className='max-w-[636px]'>
|
<div className='max-w-[636px]'>
|
||||||
{!datasetId && (
|
{!datasetId && (
|
||||||
<>
|
<>
|
||||||
@ -46,13 +50,13 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: Step
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={cn(s.sideTip)}>
|
{!isMobile && <div className={cn(s.sideTip)}>
|
||||||
<div className={s.tipCard}>
|
<div className={s.tipCard}>
|
||||||
<span className={s.icon}/>
|
<span className={s.icon}/>
|
||||||
<div className={s.title}>{t('datasetCreation.stepThree.sideTipTitle')}</div>
|
<div className={s.title}>{t('datasetCreation.stepThree.sideTipTitle')}</div>
|
||||||
<div className={s.content}>{t('datasetCreation.stepThree.sideTipContent')}</div>
|
<div className={s.content}>{t('datasetCreation.stepThree.sideTipContent')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.pageHeader {
|
.pageHeader {
|
||||||
@apply px-16;
|
@apply px-16 flex justify-between items-center;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -251,7 +251,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ruleItem {
|
.ruleItem {
|
||||||
@apply flex items-center h-7;
|
@apply flex items-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.formFooter {
|
.formFooter {
|
||||||
@ -382,7 +382,7 @@
|
|||||||
|
|
||||||
.previewWrap {
|
.previewWrap {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 524px;
|
max-width: 524px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.previewHeader {
|
.previewHeader {
|
||||||
|
@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import { XMarkIcon } from '@heroicons/react/20/solid'
|
import { XMarkIcon } from '@heroicons/react/20/solid'
|
||||||
|
import { RocketLaunchIcon } from '@heroicons/react/24/outline'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { groupBy } from 'lodash-es'
|
import { groupBy } from 'lodash-es'
|
||||||
@ -20,6 +21,7 @@ import {
|
|||||||
} from '@/service/datasets'
|
} from '@/service/datasets'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
|
import FloatRightContainer from '@/app/components/base/float-right-container'
|
||||||
import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
|
import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
|
||||||
import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
|
import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
|
||||||
import { type RetrievalConfig } from '@/types/app'
|
import { type RetrievalConfig } from '@/types/app'
|
||||||
@ -37,6 +39,8 @@ import I18n from '@/context/i18n'
|
|||||||
import { IS_CE_EDITION } from '@/config'
|
import { IS_CE_EDITION } from '@/config'
|
||||||
import { RETRIEVE_METHOD } from '@/types/app'
|
import { RETRIEVE_METHOD } from '@/types/app'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
|
||||||
type ValueOf<T> = T[keyof T]
|
type ValueOf<T> = T[keyof T]
|
||||||
type StepTwoProps = {
|
type StepTwoProps = {
|
||||||
@ -84,6 +88,9 @@ const StepTwo = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const { locale } = useContext(I18n)
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const { dataset: currentDataset, mutateDatasetRes } = useDatasetDetailContext()
|
const { dataset: currentDataset, mutateDatasetRes } = useDatasetDetailContext()
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
const [scrolled, setScrolled] = useState(false)
|
const [scrolled, setScrolled] = useState(false)
|
||||||
@ -467,7 +474,7 @@ const StepTwo = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (segmentationType === SegmentType.AUTO) {
|
if (segmentationType === SegmentType.AUTO) {
|
||||||
setAutomaticFileIndexingEstimate(null)
|
setAutomaticFileIndexingEstimate(null)
|
||||||
setShowPreview()
|
!isMobile && setShowPreview()
|
||||||
fetchFileIndexingEstimate()
|
fetchFileIndexingEstimate()
|
||||||
setPreviewSwitched(false)
|
setPreviewSwitched(false)
|
||||||
}
|
}
|
||||||
@ -493,8 +500,23 @@ const StepTwo = ({
|
|||||||
return (
|
return (
|
||||||
<div className='flex w-full h-full'>
|
<div className='flex w-full h-full'>
|
||||||
<div ref={scrollRef} className='relative h-full w-full overflow-y-scroll'>
|
<div ref={scrollRef} className='relative h-full w-full overflow-y-scroll'>
|
||||||
<div className={cn(s.pageHeader, scrolled && s.fixed)}>{t('datasetCreation.steps.two')}</div>
|
<div className={cn(s.pageHeader, scrolled && s.fixed, isMobile && '!px-6')}>
|
||||||
<div className={cn(s.form)}>
|
<span>{t('datasetCreation.steps.two')}</span>
|
||||||
|
{isMobile && (
|
||||||
|
<Button
|
||||||
|
className='border-[0.5px] !h-8 hover:outline hover:outline-[0.5px] hover:outline-gray-300 text-gray-700 font-medium bg-white shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)]'
|
||||||
|
onClick={setShowPreview}
|
||||||
|
>
|
||||||
|
<Tooltip selector='data-preview-toggle'>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<RocketLaunchIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" />
|
||||||
|
<span className="text-[13px]">{t('datasetCreation.stepTwo.previewTitleButton')}</span>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={cn(s.form, isMobile && '!px-4')}>
|
||||||
<div className={s.label}>{t('datasetCreation.stepTwo.segmentation')}</div>
|
<div className={s.label}>{t('datasetCreation.stepTwo.segmentation')}</div>
|
||||||
<div className='max-w-[640px]'>
|
<div className='max-w-[640px]'>
|
||||||
<div
|
<div
|
||||||
@ -554,7 +576,7 @@ const StepTwo = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={s.formRow}>
|
<div className={s.formRow}>
|
||||||
<div className='w-full'>
|
<div className='w-full flex flex-col gap-1'>
|
||||||
<div className={s.label}>{t('datasetCreation.stepTwo.rules')}</div>
|
<div className={s.label}>{t('datasetCreation.stepTwo.rules')}</div>
|
||||||
{rules.map(rule => (
|
{rules.map(rule => (
|
||||||
<div key={rule.id} className={s.ruleItem}>
|
<div key={rule.id} className={s.ruleItem}>
|
||||||
@ -574,7 +596,7 @@ const StepTwo = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className={s.label}>{t('datasetCreation.stepTwo.indexMode')}</div>
|
<div className={s.label}>{t('datasetCreation.stepTwo.indexMode')}</div>
|
||||||
<div className='max-w-[640px]'>
|
<div className='max-w-[640px]'>
|
||||||
<div className='flex items-center gap-3'>
|
<div className='flex items-center gap-3 flex-wrap sm:flex-nowrap'>
|
||||||
{(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.QUALIFIED)) && (
|
{(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.QUALIFIED)) && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -797,9 +819,8 @@ const StepTwo = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(showPreview)
|
<FloatRightContainer isMobile={isMobile} isOpen={showPreview} onClose={hidePreview} footer={null}>
|
||||||
? (
|
{showPreview && <div ref={previewScrollRef} className={cn(s.previewWrap, 'relative h-full overflow-y-scroll border-l border-[#F2F4F7]')}>
|
||||||
<div ref={previewScrollRef} className={cn(s.previewWrap, 'relativeh-full overflow-y-scroll border-l border-[#F2F4F7]')}>
|
|
||||||
<div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`)}>
|
<div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`)}>
|
||||||
<div className='flex items-center justify-between px-8'>
|
<div className='flex items-center justify-between px-8'>
|
||||||
<div className='grow flex items-center'>
|
<div className='grow flex items-center'>
|
||||||
@ -845,9 +866,9 @@ const StepTwo = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>}
|
||||||
)
|
{!showPreview && (
|
||||||
: (<div className={cn(s.sideTip)}>
|
<div className={cn(s.sideTip)}>
|
||||||
<div className={s.tipCard}>
|
<div className={s.tipCard}>
|
||||||
<span className={s.icon} />
|
<span className={s.icon} />
|
||||||
<div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div>
|
<div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div>
|
||||||
@ -858,7 +879,9 @@ const StepTwo = ({
|
|||||||
<p>{t('datasetCreation.stepTwo.sideTipP4')}</p>
|
<p>{t('datasetCreation.stepTwo.sideTipP4')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>)}
|
</div>
|
||||||
|
)}
|
||||||
|
</FloatRightContainer>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,15 @@
|
|||||||
background-size: 16px;
|
background-size: 16px;
|
||||||
}
|
}
|
||||||
.stepList {
|
.stepList {
|
||||||
@apply p-4;
|
@apply p-4 relative;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stepItem {
|
.stepItem {
|
||||||
@apply relative flex justify-items-start pt-3 pr-0 pb-3;
|
@apply relative flex justify-items-start pt-3 pr-0 pb-3 box-content;
|
||||||
padding-left: 52px;
|
padding-left: 52px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stepItem.step1::before {
|
.stepItem.step1::before {
|
||||||
|
@ -3,46 +3,56 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
|
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
|
import { useCallback } from 'react'
|
||||||
import s from './index.module.css'
|
import s from './index.module.css'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
type IStepsNavBarProps = {
|
type IStepsNavBarProps = {
|
||||||
step: number,
|
step: number
|
||||||
datasetId?: string,
|
datasetId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STEP_T_MAP: Record<number, string> = {
|
||||||
|
1: 'datasetCreation.steps.one',
|
||||||
|
2: 'datasetCreation.steps.two',
|
||||||
|
3: 'datasetCreation.steps.three',
|
||||||
|
}
|
||||||
|
|
||||||
|
const STEP_LIST = [1, 2, 3]
|
||||||
|
|
||||||
const StepsNavBar = ({
|
const StepsNavBar = ({
|
||||||
step,
|
step,
|
||||||
datasetId,
|
datasetId,
|
||||||
}: IStepsNavBarProps) => {
|
}: IStepsNavBarProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const navBackHandle = () => {
|
|
||||||
if (!datasetId) {
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
|
const navBackHandle = useCallback(() => {
|
||||||
|
if (!datasetId)
|
||||||
router.replace('/datasets')
|
router.replace('/datasets')
|
||||||
} else {
|
else
|
||||||
router.replace(`/datasets/${datasetId}/documents`)
|
router.replace(`/datasets/${datasetId}/documents`)
|
||||||
}
|
}, [router, datasetId])
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full pt-4'>
|
<div className='w-full pt-4'>
|
||||||
<div className={s.stepsHeader}>
|
<div className={cn(s.stepsHeader, isMobile && '!px-0 justify-center')}>
|
||||||
<div onClick={navBackHandle} className={s.navBack} />
|
<div onClick={navBackHandle} className={cn(s.navBack, isMobile && '!mr-0')} />
|
||||||
{!datasetId ? t('datasetCreation.steps.header.creation') : t('datasetCreation.steps.header.update')}
|
{!isMobile && (!datasetId ? t('datasetCreation.steps.header.creation') : t('datasetCreation.steps.header.update'))}
|
||||||
</div>
|
</div>
|
||||||
<div className={cn(s.stepList)}>
|
<div className={cn(s.stepList, isMobile && '!p-0')}>
|
||||||
<div className={cn(s.stepItem, s.step1, step === 1 && s.active, step !== 1 && s.done)}>
|
{STEP_LIST.map(item => (
|
||||||
<div className={cn(s.stepNum)}>{step === 1 ? 1 : ''}</div>
|
<div
|
||||||
<div className={cn(s.stepName)}>{t('datasetCreation.steps.one')}</div>
|
key={item}
|
||||||
</div>
|
className={cn(s.stepItem, s[`step${item}`], step === item && s.active, step > item && s.done, isMobile && 'px-0')}
|
||||||
<div className={cn(s.stepItem, s.step2, step === 2 && s.active, step === 3 && s.done)}>
|
>
|
||||||
<div className={cn(s.stepNum)}>{step !== 3 ? 2 : ''}</div>
|
<div className={cn(s.stepNum)}>{item}</div>
|
||||||
<div className={cn(s.stepName)}>{t('datasetCreation.steps.two')}</div>
|
<div className={cn(s.stepName)}>{isMobile ? '' : t(STEP_T_MAP[item])}</div>
|
||||||
</div>
|
|
||||||
<div className={cn(s.stepItem, s.step3, step === 3 && s.active)}>
|
|
||||||
<div className={cn(s.stepNum)}>3</div>
|
|
||||||
<div className={cn(s.stepName)}>{t('datasetCreation.steps.three')}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -178,7 +178,7 @@ const SegmentDetailComponent: FC<ISegmentDetailProps> = ({
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className={cn(s.footer, s.numberInfo)}>
|
<div className={cn(s.footer, s.numberInfo)}>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center flex-wrap gap-y-2'>
|
||||||
<div className={cn(s.commonIcon, s.typeSquareIcon)} /><span className='mr-8'>{formatNumber(segInfo?.word_count as number)} {t('datasetDocuments.segment.characters')}</span>
|
<div className={cn(s.commonIcon, s.typeSquareIcon)} /><span className='mr-8'>{formatNumber(segInfo?.word_count as number)} {t('datasetDocuments.segment.characters')}</span>
|
||||||
<div className={cn(s.commonIcon, s.targetIcon)} /><span className='mr-8'>{formatNumber(segInfo?.hit_count as number)} {t('datasetDocuments.segment.hitCount')}</span>
|
<div className={cn(s.commonIcon, s.targetIcon)} /><span className='mr-8'>{formatNumber(segInfo?.hit_count as number)} {t('datasetDocuments.segment.hitCount')}</span>
|
||||||
<div className={cn(s.commonIcon, s.bezierCurveIcon)} /><span className={s.hashText}>{t('datasetDocuments.segment.vectorHash')}{segInfo?.index_node_hash}</span>
|
<div className={cn(s.commonIcon, s.bezierCurveIcon)} /><span className={s.hashText}>{t('datasetDocuments.segment.vectorHash')}{segInfo?.index_node_hash}</span>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
@apply text-gray-900 font-medium text-base flex-1;
|
@apply text-gray-900 font-medium text-base flex-1;
|
||||||
}
|
}
|
||||||
.docSearchWrapper {
|
.docSearchWrapper {
|
||||||
@apply sticky w-full h-10 -top-3 bg-white flex items-center mb-3 justify-between z-10;
|
@apply sticky w-full py-1 -top-3 bg-white flex items-center mb-3 justify-between z-10 flex-wrap gap-y-1;
|
||||||
}
|
}
|
||||||
.listContainer {
|
.listContainer {
|
||||||
height: calc(100% - 3.25rem);
|
height: calc(100% - 3.25rem);
|
||||||
@ -18,7 +18,7 @@
|
|||||||
@apply grid gap-4 grid-cols-3 min-w-[902px] last:mb-[30px];
|
@apply grid gap-4 grid-cols-3 min-w-[902px] last:mb-[30px];
|
||||||
}
|
}
|
||||||
.segWrapper {
|
.segWrapper {
|
||||||
@apply box-border h-[180px] min-w-[290px] bg-gray-50 px-4 pt-4 flex flex-col text-opacity-50 rounded-xl border border-transparent hover:border-gray-200 hover:shadow-lg hover:cursor-pointer hover:bg-white;
|
@apply box-border h-[180px] w-full xl:min-w-[290px] bg-gray-50 px-4 pt-4 flex flex-col text-opacity-50 rounded-xl border border-transparent hover:border-gray-200 hover:shadow-lg hover:cursor-pointer hover:bg-white;
|
||||||
}
|
}
|
||||||
.segTitleWrapper {
|
.segTitleWrapper {
|
||||||
@apply flex items-center justify-between;
|
@apply flex items-center justify-between;
|
||||||
@ -48,7 +48,7 @@
|
|||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
@apply flex items-center justify-between box-border border-t-gray-200 border-t-[0.5px] pt-3 mt-4;
|
@apply flex items-center justify-between box-border border-t-gray-200 border-t-[0.5px] pt-3 mt-4 flex-wrap gap-y-2;
|
||||||
}
|
}
|
||||||
.numberInfo {
|
.numberInfo {
|
||||||
@apply text-gray-500 text-xs font-medium;
|
@apply text-gray-500 text-xs font-medium;
|
||||||
|
@ -23,6 +23,8 @@ import { checkSegmentBatchImportProgress, fetchDocumentDetail, segmentBatchImpor
|
|||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import type { DocForm } from '@/models/datasets'
|
import type { DocForm } from '@/models/datasets'
|
||||||
import { useDatasetDetailContext } from '@/context/dataset-detail'
|
import { useDatasetDetailContext } from '@/context/dataset-detail'
|
||||||
|
import FloatRightContainer from '@/app/components/base/float-right-container'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
export const DocumentContext = createContext<{ datasetId?: string; documentId?: string; docForm: string }>({ docForm: '' })
|
export const DocumentContext = createContext<{ datasetId?: string; documentId?: string; docForm: string }>({ docForm: '' })
|
||||||
|
|
||||||
@ -50,10 +52,14 @@ type Props = {
|
|||||||
const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
|
const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
const { dataset } = useDatasetDetailContext()
|
const { dataset } = useDatasetDetailContext()
|
||||||
const embeddingAvailable = !!dataset?.embedding_available
|
const embeddingAvailable = !!dataset?.embedding_available
|
||||||
const [showMetadata, setShowMetadata] = useState(true)
|
const [showMetadata, setShowMetadata] = useState(!isMobile)
|
||||||
const [newSegmentModalVisible, setNewSegmentModalVisible] = useState(false)
|
const [newSegmentModalVisible, setNewSegmentModalVisible] = useState(false)
|
||||||
const [batchModalVisible, setBatchModalVisible] = useState(false)
|
const [batchModalVisible, setBatchModalVisible] = useState(false)
|
||||||
const [importStatus, setImportStatus] = useState<ProcessStatus | string>()
|
const [importStatus, setImportStatus] = useState<ProcessStatus | string>()
|
||||||
@ -124,12 +130,13 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
|
|||||||
return (
|
return (
|
||||||
<DocumentContext.Provider value={{ datasetId, documentId, docForm: documentDetail?.doc_form || '' }}>
|
<DocumentContext.Provider value={{ datasetId, documentId, docForm: documentDetail?.doc_form || '' }}>
|
||||||
<div className='flex flex-col h-full'>
|
<div className='flex flex-col h-full'>
|
||||||
<div className='flex h-16 border-b-gray-100 border-b items-center p-4'>
|
<div className='flex min-h-16 border-b-gray-100 border-b items-center p-4 justify-between flex-wrap gap-y-2'>
|
||||||
<div onClick={backToPrev} className={'rounded-full w-8 h-8 flex justify-center items-center border-gray-100 cursor-pointer border hover:border-gray-300 shadow-[0px_12px_16px_-4px_rgba(16,24,40,0.08),0px_4px_6px_-2px_rgba(16,24,40,0.03)]'}>
|
<div onClick={backToPrev} className={'shrink-0 rounded-full w-8 h-8 flex justify-center items-center border-gray-100 cursor-pointer border hover:border-gray-300 shadow-[0px_12px_16px_-4px_rgba(16,24,40,0.08),0px_4px_6px_-2px_rgba(16,24,40,0.03)]'}>
|
||||||
<ArrowLeftIcon className='text-primary-600 fill-current stroke-current h-4 w-4' />
|
<ArrowLeftIcon className='text-primary-600 fill-current stroke-current h-4 w-4' />
|
||||||
</div>
|
</div>
|
||||||
<Divider className='!h-4' type='vertical' />
|
<Divider className='!h-4' type='vertical' />
|
||||||
<DocumentTitle extension={documentDetail?.data_source_info?.upload_file?.extension} name={documentDetail?.name} />
|
<DocumentTitle extension={documentDetail?.data_source_info?.upload_file?.extension} name={documentDetail?.name} />
|
||||||
|
<div className='flex items-center flex-wrap gap-y-2'>
|
||||||
<StatusItem status={documentDetail?.display_status || 'available'} scene='detail' errorMessage={documentDetail?.error || ''} />
|
<StatusItem status={documentDetail?.display_status || 'available'} scene='detail' errorMessage={documentDetail?.error || ''} />
|
||||||
{embeddingAvailable && documentDetail && !documentDetail.archived && (
|
{embeddingAvailable && documentDetail && !documentDetail.archived && (
|
||||||
<SegmentAdd
|
<SegmentAdd
|
||||||
@ -158,10 +165,11 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
|
|||||||
onClick={() => setShowMetadata(!showMetadata)}
|
onClick={() => setShowMetadata(!showMetadata)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className='flex flex-row flex-1' style={{ height: 'calc(100% - 4rem)' }}>
|
<div className='flex flex-row flex-1' style={{ height: 'calc(100% - 4rem)' }}>
|
||||||
{isDetailLoading
|
{isDetailLoading
|
||||||
? <Loading type='app' />
|
? <Loading type='app' />
|
||||||
: <div className={`box-border h-full w-full overflow-y-scroll ${embedding ? 'py-12 px-16' : 'pb-[30px] pt-3 px-6'}`}>
|
: <div className={`h-full w-full flex flex-col ${embedding ? 'px-6 py-3 sm:py-12 sm:px-16' : 'pb-[30px] pt-3 px-6'}`}>
|
||||||
{embedding
|
{embedding
|
||||||
? <Embedding detail={documentDetail} detailUpdate={detailMutate} />
|
? <Embedding detail={documentDetail} detailUpdate={detailMutate} />
|
||||||
: <Completed
|
: <Completed
|
||||||
@ -174,11 +182,13 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{showMetadata && <Metadata
|
<FloatRightContainer showClose isOpen={showMetadata} onClose={() => setShowMetadata(false)} isMobile={isMobile} panelClassname='!justify-start' footer={null}>
|
||||||
|
<Metadata
|
||||||
docDetail={{ ...documentDetail, ...documentMetadata, doc_type: documentMetadata?.doc_type === 'others' ? '' : documentMetadata?.doc_type } as any}
|
docDetail={{ ...documentDetail, ...documentMetadata, doc_type: documentMetadata?.doc_type === 'others' ? '' : documentMetadata?.doc_type } as any}
|
||||||
loading={isMetadataLoading}
|
loading={isMetadataLoading}
|
||||||
onUpdate={metadataMutate}
|
onUpdate={metadataMutate}
|
||||||
/>}
|
/>
|
||||||
|
</FloatRightContainer>
|
||||||
</div>
|
</div>
|
||||||
<BatchModal
|
<BatchModal
|
||||||
isShow={batchModalVisible}
|
isShow={batchModalVisible}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.main {
|
.main {
|
||||||
@apply w-96 xl:w-[360px] flex-shrink-0 px-6 py-5 overflow-y-auto border-l-gray-100 border-l;
|
@apply w-full sm:w-96 xl:w-[360px] flex-shrink-0 p-0 sm:px-6 sm:py-5 overflow-y-auto border-none sm:border-l-gray-100 sm:border-l;
|
||||||
}
|
}
|
||||||
.operationWrapper {
|
.operationWrapper {
|
||||||
@apply flex flex-col items-center gap-4 mt-7 mb-8;
|
@apply flex flex-col items-center gap-4 mt-7 mb-8;
|
||||||
|
@ -198,7 +198,7 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
|||||||
<p className={s.desc}>{t('datasetDocuments.list.desc')}</p>
|
<p className={s.desc}>{t('datasetDocuments.list.desc')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col px-6 py-4 flex-1'>
|
<div className='flex flex-col px-6 py-4 flex-1'>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between flex-wrap gap-y-2 '>
|
||||||
<Input
|
<Input
|
||||||
showPrefix
|
showPrefix
|
||||||
wrapperClassName='!w-[200px]'
|
wrapperClassName='!w-[200px]'
|
||||||
@ -207,7 +207,7 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
|
|||||||
value={searchValue}
|
value={searchValue}
|
||||||
/>
|
/>
|
||||||
{embeddingAvailable && (
|
{embeddingAvailable && (
|
||||||
<Button type='primary' onClick={routeToDocCreate} className='!h-8 !text-[13px]'>
|
<Button type='primary' onClick={routeToDocCreate} className='!h-8 !text-[13px] !shrink-0'>
|
||||||
<PlusIcon className='h-4 w-4 mr-2 stroke-current' />
|
<PlusIcon className='h-4 w-4 mr-2 stroke-current' />
|
||||||
{isDataSourceNotion && t('datasetDocuments.list.addPages')}
|
{isDataSourceNotion && t('datasetDocuments.list.addPages')}
|
||||||
{!isDataSourceNotion && t('datasetDocuments.list.addFile')}
|
{!isDataSourceNotion && t('datasetDocuments.list.addFile')}
|
||||||
|
@ -316,8 +316,8 @@ const DocumentList: FC<IDocumentListProps> = ({ embeddingAvailable, documents =
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className='w-full h-full overflow-x-auto'>
|
||||||
<table className={`w-full border-collapse border-0 text-sm mt-3 ${s.documentTable}`}>
|
<table className={`min-w-[700px] max-w-full w-full border-collapse border-0 text-sm mt-3 ${s.documentTable}`}>
|
||||||
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-medium text-xs uppercase">
|
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-medium text-xs uppercase">
|
||||||
<tr>
|
<tr>
|
||||||
<td className='w-12'>#</td>
|
<td className='w-12'>#</td>
|
||||||
@ -380,7 +380,7 @@ const DocumentList: FC<IDocumentListProps> = ({ embeddingAvailable, documents =
|
|||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +65,8 @@ const HitDetail: FC<IHitDetailProps> = ({ segInfo, vectorInfo }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'flex flex-row'}>
|
<div className='flex flex-row overflow-x-auto'>
|
||||||
<div className="flex-1 bg-gray-25 p-6">
|
<div className="flex-1 bg-gray-25 p-6 min-w-[300px]">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<SegmentIndexTag
|
<SegmentIndexTag
|
||||||
positionId={segInfo?.position || ''}
|
positionId={segInfo?.position || ''}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { useMemo, useState } from 'react'
|
import React, { useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { omit } from 'lodash-es'
|
import { omit } from 'lodash-es'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import { useBoolean } from 'ahooks'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import SegmentCard from '../documents/detail/completed/SegmentCard'
|
import SegmentCard from '../documents/detail/completed/SegmentCard'
|
||||||
import docStyle from '../documents/detail/completed/style.module.css'
|
import docStyle from '../documents/detail/completed/style.module.css'
|
||||||
@ -17,9 +18,11 @@ import type { HitTestingResponse, HitTesting as HitTestingType } from '@/models/
|
|||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
import Pagination from '@/app/components/base/pagination'
|
import Pagination from '@/app/components/base/pagination'
|
||||||
|
import FloatRightContainer from '@/app/components/base/float-right-container'
|
||||||
import { fetchTestingRecords } from '@/service/datasets'
|
import { fetchTestingRecords } from '@/service/datasets'
|
||||||
import DatasetDetailContext from '@/context/dataset-detail'
|
import DatasetDetailContext from '@/context/dataset-detail'
|
||||||
import type { RetrievalConfig } from '@/types/app'
|
import type { RetrievalConfig } from '@/types/app'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
const limit = 10
|
const limit = 10
|
||||||
|
|
||||||
@ -39,6 +42,10 @@ const RecordsEmpty: FC = () => {
|
|||||||
|
|
||||||
const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const [hitResult, setHitResult] = useState<HitTestingResponse | undefined>() // 初始化记录为空数组
|
const [hitResult, setHitResult] = useState<HitTestingResponse | undefined>() // 初始化记录为空数组
|
||||||
const [submitLoading, setSubmitLoading] = useState(false)
|
const [submitLoading, setSubmitLoading] = useState(false)
|
||||||
const [currParagraph, setCurrParagraph] = useState<{ paraInfo?: HitTestingType; showModal: boolean }>({ showModal: false })
|
const [currParagraph, setCurrParagraph] = useState<{ paraInfo?: HitTestingType; showModal: boolean }>({ showModal: false })
|
||||||
@ -63,6 +70,11 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
|
|
||||||
const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict as RetrievalConfig)
|
const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict as RetrievalConfig)
|
||||||
const [isShowModifyRetrievalModal, setIsShowModifyRetrievalModal] = useState(false)
|
const [isShowModifyRetrievalModal, setIsShowModifyRetrievalModal] = useState(false)
|
||||||
|
const [isShowRightPanel, { setTrue: showRightPanel, setFalse: hideRightPanel, set: setShowRightPanel }] = useBoolean(!isMobile)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setShowRightPanel(!isMobile)
|
||||||
|
}, [isMobile, setShowRightPanel])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={s.container}>
|
<div className={s.container}>
|
||||||
@ -74,6 +86,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
<Textarea
|
<Textarea
|
||||||
datasetId={datasetId}
|
datasetId={datasetId}
|
||||||
setHitResult={setHitResult}
|
setHitResult={setHitResult}
|
||||||
|
onSubmit={showRightPanel}
|
||||||
onUpdateList={recordsMutate}
|
onUpdateList={recordsMutate}
|
||||||
loading={submitLoading}
|
loading={submitLoading}
|
||||||
setLoading={setSubmitLoading}
|
setLoading={setSubmitLoading}
|
||||||
@ -131,7 +144,8 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
<RecordsEmpty />
|
<RecordsEmpty />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={s.rightDiv}>
|
<FloatRightContainer panelClassname='!justify-start !overflow-y-auto' showClose isMobile={isMobile} isOpen={isShowRightPanel} onClose={hideRightPanel} footer={null}>
|
||||||
|
<div className={cn(s.rightDiv, 'p-0 sm:px-8 sm:pt-[42px] sm:pb-[26px]')}>
|
||||||
{submitLoading
|
{submitLoading
|
||||||
? <div className={s.cardWrapper}>
|
? <div className={s.cardWrapper}>
|
||||||
<SegmentCard
|
<SegmentCard
|
||||||
@ -176,8 +190,10 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
</FloatRightContainer>
|
||||||
<Modal
|
<Modal
|
||||||
className='!max-w-[960px] !p-0'
|
className='!max-w-[960px] !p-0'
|
||||||
|
wrapperClassName='!z-40'
|
||||||
closable
|
closable
|
||||||
onClose={() => setCurrParagraph({ showModal: false })}
|
onClose={() => setCurrParagraph({ showModal: false })}
|
||||||
isShow={currParagraph.showModal}
|
isShow={currParagraph.showModal}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.container {
|
.container {
|
||||||
@apply flex h-full w-full relative;
|
@apply flex h-full w-full relative overflow-y-auto;
|
||||||
}
|
}
|
||||||
.container > div {
|
.container > div {
|
||||||
@apply flex-1 h-full;
|
@apply flex-1 h-full;
|
||||||
@ -8,7 +8,7 @@
|
|||||||
@apply border-r border-gray-100 px-6 py-3 flex flex-col;
|
@apply border-r border-gray-100 px-6 py-3 flex flex-col;
|
||||||
}
|
}
|
||||||
.rightDiv {
|
.rightDiv {
|
||||||
@apply px-8 pt-[42px] pb-[26px] flex flex-col;
|
@apply flex flex-col;
|
||||||
}
|
}
|
||||||
.titleWrapper {
|
.titleWrapper {
|
||||||
@apply flex flex-col justify-center gap-1 mb-5;
|
@apply flex flex-col justify-center gap-1 mb-5;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import type { FC } from 'react'
|
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
@ -13,7 +12,7 @@ import { hitTesting } from '@/service/datasets'
|
|||||||
import { asyncRunSafe } from '@/utils'
|
import { asyncRunSafe } from '@/utils'
|
||||||
import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
|
import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
|
||||||
|
|
||||||
type Props = {
|
type TextAreaWithButtonIProps = {
|
||||||
datasetId: string
|
datasetId: string
|
||||||
onUpdateList: () => void
|
onUpdateList: () => void
|
||||||
setHitResult: (res: HitTestingResponse) => void
|
setHitResult: (res: HitTestingResponse) => void
|
||||||
@ -24,9 +23,10 @@ type Props = {
|
|||||||
onClickRetrievalMethod: () => void
|
onClickRetrievalMethod: () => void
|
||||||
retrievalConfig: RetrievalConfig
|
retrievalConfig: RetrievalConfig
|
||||||
isEconomy: boolean
|
isEconomy: boolean
|
||||||
|
onSubmit?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const TextAreaWithButton: FC<Props> = ({
|
const TextAreaWithButton = ({
|
||||||
datasetId,
|
datasetId,
|
||||||
onUpdateList,
|
onUpdateList,
|
||||||
setHitResult,
|
setHitResult,
|
||||||
@ -37,7 +37,8 @@ const TextAreaWithButton: FC<Props> = ({
|
|||||||
onClickRetrievalMethod,
|
onClickRetrievalMethod,
|
||||||
retrievalConfig,
|
retrievalConfig,
|
||||||
isEconomy,
|
isEconomy,
|
||||||
}) => {
|
onSubmit: _onSubmit,
|
||||||
|
}: TextAreaWithButtonIProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { indexingTechnique } = useContext(DatasetDetailContext)
|
const { indexingTechnique } = useContext(DatasetDetailContext)
|
||||||
|
|
||||||
@ -55,6 +56,7 @@ const TextAreaWithButton: FC<Props> = ({
|
|||||||
onUpdateList?.()
|
onUpdateList?.()
|
||||||
}
|
}
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
_onSubmit && _onSubmit()
|
||||||
}
|
}
|
||||||
|
|
||||||
const retrievalMethod = isEconomy ? RETRIEVE_METHOD.invertedIndex : retrievalConfig.search_method
|
const retrievalMethod = isEconomy ? RETRIEVE_METHOD.invertedIndex : retrievalConfig.search_method
|
||||||
|
@ -24,13 +24,13 @@ import { useModalContext } from '@/context/modal-context'
|
|||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
|
import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
|
||||||
const rowClass = `
|
const rowClass = `
|
||||||
flex justify-between py-4
|
flex justify-between py-4 flex-wrap gap-y-2
|
||||||
`
|
`
|
||||||
const labelClass = `
|
const labelClass = `
|
||||||
flex items-center w-[168px] h-9
|
flex items-center w-[168px] h-9
|
||||||
`
|
`
|
||||||
const inputClass = `
|
const inputClass = `
|
||||||
w-[480px] px-3 bg-gray-100 text-sm text-gray-800 rounded-lg outline-none appearance-none
|
w-full max-w-[480px] px-3 bg-gray-100 text-sm text-gray-800 rounded-lg outline-none appearance-none
|
||||||
`
|
`
|
||||||
const useInitialValue: <T>(depend: T, dispatch: Dispatch<T>) => void = (depend, dispatch) => {
|
const useInitialValue: <T>(depend: T, dispatch: Dispatch<T>) => void = (depend, dispatch) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -118,14 +118,14 @@ const Form = () => {
|
|||||||
useInitialValue<DataSet['indexing_technique'] | undefined>(currentDataset?.indexing_technique, setIndexMethod)
|
useInitialValue<DataSet['indexing_technique'] | undefined>(currentDataset?.indexing_technique, setIndexMethod)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-[800px] px-16 py-6'>
|
<div className='w-full sm:w-[800px] p-4 sm:px-16 sm:py-6'>
|
||||||
<div className={rowClass}>
|
<div className={rowClass}>
|
||||||
<div className={labelClass}>
|
<div className={labelClass}>
|
||||||
<div>{t('datasetSettings.form.name')}</div>
|
<div>{t('datasetSettings.form.name')}</div>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
disabled={!currentDataset?.embedding_available}
|
disabled={!currentDataset?.embedding_available}
|
||||||
className={cn(inputClass, !currentDataset?.embedding_available && 'opacity-60')}
|
className={cn(inputClass, !currentDataset?.embedding_available && 'opacity-60', 'h-9')}
|
||||||
value={name}
|
value={name}
|
||||||
onChange={e => setName(e.target.value)}
|
onChange={e => setName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@ -134,7 +134,7 @@ const Form = () => {
|
|||||||
<div className={labelClass}>
|
<div className={labelClass}>
|
||||||
<div>{t('datasetSettings.form.desc')}</div>
|
<div>{t('datasetSettings.form.desc')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className='w-full max-w-[480px]'>
|
||||||
<textarea
|
<textarea
|
||||||
disabled={!currentDataset?.embedding_available}
|
disabled={!currentDataset?.embedding_available}
|
||||||
className={cn(`${inputClass} block mb-2 h-[120px] py-2 resize-none`, !currentDataset?.embedding_available && 'opacity-60')}
|
className={cn(`${inputClass} block mb-2 h-[120px] py-2 resize-none`, !currentDataset?.embedding_available && 'opacity-60')}
|
||||||
@ -152,7 +152,7 @@ const Form = () => {
|
|||||||
<div className={labelClass}>
|
<div className={labelClass}>
|
||||||
<div>{t('datasetSettings.form.permissions')}</div>
|
<div>{t('datasetSettings.form.permissions')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-[480px]'>
|
<div className='w-full sm:w-[480px]'>
|
||||||
<PermissionsRadio
|
<PermissionsRadio
|
||||||
disable={!currentDataset?.embedding_available}
|
disable={!currentDataset?.embedding_available}
|
||||||
value={permission}
|
value={permission}
|
||||||
@ -167,7 +167,7 @@ const Form = () => {
|
|||||||
<div className={labelClass}>
|
<div className={labelClass}>
|
||||||
<div>{t('datasetSettings.form.indexMethod')}</div>
|
<div>{t('datasetSettings.form.indexMethod')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-[480px]'>
|
<div className='w-full sm:w-[480px]'>
|
||||||
<IndexMethodRadio
|
<IndexMethodRadio
|
||||||
disable={!currentDataset?.embedding_available}
|
disable={!currentDataset?.embedding_available}
|
||||||
value={indexMethod}
|
value={indexMethod}
|
||||||
|
@ -5,7 +5,7 @@ import s from './index.module.css'
|
|||||||
import type { DataSet } from '@/models/datasets'
|
import type { DataSet } from '@/models/datasets'
|
||||||
|
|
||||||
const itemClass = `
|
const itemClass = `
|
||||||
w-[234px] p-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
|
w-full sm:w-[234px] p-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
|
||||||
`
|
`
|
||||||
const radioClass = `
|
const radioClass = `
|
||||||
w-4 h-4 border-[2px] border-gray-200 rounded-full
|
w-4 h-4 border-[2px] border-gray-200 rounded-full
|
||||||
@ -40,7 +40,7 @@ const IndexMethodRadio = ({
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(s.wrapper, 'flex justify-between w-full')}>
|
<div className={classNames(s.wrapper, 'flex justify-between w-full flex-wrap gap-y-2')}>
|
||||||
{
|
{
|
||||||
options.map(option => (
|
options.map(option => (
|
||||||
<div
|
<div
|
||||||
|
@ -5,7 +5,7 @@ import s from './index.module.css'
|
|||||||
import type { DataSet } from '@/models/datasets'
|
import type { DataSet } from '@/models/datasets'
|
||||||
|
|
||||||
const itemClass = `
|
const itemClass = `
|
||||||
flex items-center w-[234px] h-12 px-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
|
flex items-center w-full sm:w-[234px] h-12 px-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
|
||||||
`
|
`
|
||||||
const radioClass = `
|
const radioClass = `
|
||||||
w-4 h-4 border-[2px] border-gray-200 rounded-full
|
w-4 h-4 border-[2px] border-gray-200 rounded-full
|
||||||
@ -36,7 +36,7 @@ const PermissionsRadio = ({
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(s.wrapper, 'flex justify-between w-full')}>
|
<div className={classNames(s.wrapper, 'flex justify-between w-full flex-wrap gap-y-2')}>
|
||||||
{
|
{
|
||||||
options.map(option => (
|
options.map(option => (
|
||||||
<div
|
<div
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
|
import s from './secret-key/style.module.css'
|
||||||
import Doc from '@/app/components/develop/doc'
|
import Doc from '@/app/components/develop/doc'
|
||||||
import InputCopy from '@/app/components/develop/secret-key/input-copy'
|
import InputCopy from '@/app/components/develop/secret-key/input-copy'
|
||||||
import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
|
import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
|
||||||
import { fetchAppDetail } from '@/service/apps'
|
import { fetchAppDetail } from '@/service/apps'
|
||||||
import s from './secret-key/style.module.css'
|
|
||||||
|
|
||||||
type IDevelopMainProps = {
|
type IDevelopMainProps = {
|
||||||
appId: string
|
appId: string
|
||||||
@ -21,10 +21,10 @@ const DevelopMain = ({ appId, dictionary }: IDevelopMainProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative flex flex-col h-full overflow-hidden'>
|
<div className='relative flex flex-col h-full overflow-hidden'>
|
||||||
<div className='flex items-center justify-between flex-shrink-0 px-6 border-b border-solid h-14 border-b-gray-100'>
|
<div className='flex items-center justify-between flex-shrink-0 px-6 border-b border-solid py-2 border-b-gray-100'>
|
||||||
<div className='text-lg font-medium text-gray-900'>{dictionary.app?.develop?.title}</div>
|
<div className='text-lg font-medium text-gray-900'>{dictionary.app?.develop?.title}</div>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center flex-wrap gap-y-1'>
|
||||||
<InputCopy className={`flex-shrink-0 mr-1 w-60 ${s.w320}`} value={appDetail?.api_base_url}>
|
<InputCopy className='flex-shrink-0 mr-1 w-52 sm:w-80' value={appDetail?.api_base_url}>
|
||||||
<div className={`ml-2 border border-gray-200 border-solid flex-shrink-0 px-2 py-0.5 rounded-[6px] text-gray-500 text-[0.625rem] ${s.customApi}`}>
|
<div className={`ml-2 border border-gray-200 border-solid flex-shrink-0 px-2 py-0.5 rounded-[6px] text-gray-500 text-[0.625rem] ${s.customApi}`}>
|
||||||
{t('appApi.apiServer')}
|
{t('appApi.apiServer')}
|
||||||
</div>
|
</div>
|
||||||
@ -37,7 +37,7 @@ const DevelopMain = ({ appId, dictionary }: IDevelopMainProps) => {
|
|||||||
<SecretKeyButton className='flex-shrink-0' appId={appId} />
|
<SecretKeyButton className='flex-shrink-0' appId={appId} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='px-10 py-4 overflow-auto grow'>
|
<div className='px-4 sm:px-10 py-4 overflow-auto grow'>
|
||||||
<Doc appDetail={appDetail} />
|
<Doc appDetail={appDetail} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,10 +16,6 @@
|
|||||||
width: 4rem;
|
width: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w320 {
|
|
||||||
width: 20rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.customApi {
|
.customApi {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ const InstalledApp: FC<IInstalledAppProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='h-full p-2'>
|
<div className='h-full py-2 pl-0 pr-2 sm:p-2'>
|
||||||
{installedApp?.app.mode === 'chat'
|
{installedApp?.app.mode === 'chat'
|
||||||
? (
|
? (
|
||||||
<ChatApp isInstalledApp installedAppInfo={installedApp} />
|
<ChatApp isInstalledApp installedAppInfo={installedApp} />
|
||||||
|
@ -9,6 +9,7 @@ import ItemOperation from '@/app/components/explore/item-operation'
|
|||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
|
|
||||||
export type IAppNavItemProps = {
|
export type IAppNavItemProps = {
|
||||||
|
isMobile: boolean
|
||||||
name: string
|
name: string
|
||||||
id: string
|
id: string
|
||||||
icon: string
|
icon: string
|
||||||
@ -21,6 +22,7 @@ export type IAppNavItemProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function AppNavItem({
|
export default function AppNavItem({
|
||||||
|
isMobile,
|
||||||
name,
|
name,
|
||||||
id,
|
id,
|
||||||
icon,
|
icon,
|
||||||
@ -42,17 +44,19 @@ export default function AppNavItem({
|
|||||||
className={cn(
|
className={cn(
|
||||||
s.item,
|
s.item,
|
||||||
isSelected ? s.active : 'hover:bg-gray-200',
|
isSelected ? s.active : 'hover:bg-gray-200',
|
||||||
'flex h-8 items-center justify-between px-2 rounded-lg text-sm font-normal ',
|
'flex h-8 items-center justify-between mobile:justify-center px-2 mobile:px-1 rounded-lg text-sm font-normal',
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push(url) // use Link causes popup item always trigger jump. Can not be solved by e.stopPropagation().
|
router.push(url) // use Link causes popup item always trigger jump. Can not be solved by e.stopPropagation().
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{isMobile && <AppIcon size='tiny' icon={icon} background={icon_background} />}
|
||||||
|
{!isMobile && (
|
||||||
|
<>
|
||||||
<div className='flex items-center space-x-2 w-0 grow'>
|
<div className='flex items-center space-x-2 w-0 grow'>
|
||||||
<AppIcon size='tiny' icon={icon} background={icon_background} />
|
<AppIcon size='tiny' icon={icon} background={icon_background} />
|
||||||
<div className='overflow-hidden text-ellipsis whitespace-nowrap'>{name}</div>
|
<div className='overflow-hidden text-ellipsis whitespace-nowrap'>{name}</div>
|
||||||
</div>
|
</div>
|
||||||
{
|
|
||||||
<div className='shrink-0 h-6' onClick={e => e.stopPropagation()}>
|
<div className='shrink-0 h-6' onClick={e => e.stopPropagation()}>
|
||||||
<ItemOperation
|
<ItemOperation
|
||||||
isPinned={isPinned}
|
isPinned={isPinned}
|
||||||
@ -62,7 +66,8 @@ export default function AppNavItem({
|
|||||||
onDelete={() => onDelete(id)}
|
onDelete={() => onDelete(id)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import Item from './app-nav-item'
|
|||||||
import { fetchInstalledAppList as doFetchInstalledAppList, uninstallApp, updatePinStatus } from '@/service/explore'
|
import { fetchInstalledAppList as doFetchInstalledAppList, uninstallApp, updatePinStatus } from '@/service/explore'
|
||||||
import ExploreContext from '@/context/explore-context'
|
import ExploreContext from '@/context/explore-context'
|
||||||
import Confirm from '@/app/components/base/confirm'
|
import Confirm from '@/app/components/base/confirm'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
const SelectedDiscoveryIcon = () => (
|
const SelectedDiscoveryIcon = () => (
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -36,9 +37,11 @@ const ChatIcon = () => (
|
|||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|
||||||
const SideBar: FC<{
|
export type IExploreSideBarProps = {
|
||||||
controlUpdateInstalledApps: number
|
controlUpdateInstalledApps: number
|
||||||
}> = ({
|
}
|
||||||
|
|
||||||
|
const SideBar: FC<IExploreSideBarProps> = ({
|
||||||
controlUpdateInstalledApps,
|
controlUpdateInstalledApps,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -48,6 +51,9 @@ const SideBar: FC<{
|
|||||||
const isChatSelected = lastSegment === 'chat'
|
const isChatSelected = lastSegment === 'chat'
|
||||||
const { installedApps, setInstalledApps } = useContext(ExploreContext)
|
const { installedApps, setInstalledApps } = useContext(ExploreContext)
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const fetchInstalledAppList = async () => {
|
const fetchInstalledAppList = async () => {
|
||||||
const { installed_apps }: any = await doFetchInstalledAppList()
|
const { installed_apps }: any = await doFetchInstalledAppList()
|
||||||
setInstalledApps(installed_apps)
|
setInstalledApps(installed_apps)
|
||||||
@ -84,28 +90,28 @@ const SideBar: FC<{
|
|||||||
}, [controlUpdateInstalledApps])
|
}, [controlUpdateInstalledApps])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-[216px] shrink-0 pt-6 px-4 border-gray-200 cursor-pointer'>
|
<div className='w-fit sm:w-[216px] shrink-0 pt-6 px-4 border-gray-200 cursor-pointer'>
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
href='/explore/apps'
|
href='/explore/apps'
|
||||||
className={cn(isDiscoverySelected ? 'text-primary-600 bg-white font-semibold' : 'text-gray-700 font-medium', 'flex items-center h-9 pl-3 space-x-2 rounded-lg')}
|
className={cn(isDiscoverySelected ? 'text-primary-600 bg-white font-semibold' : 'text-gray-700 font-medium', 'flex items-center mobile:justify-center mobile:w-fit h-9 px-3 mobile:px-2 gap-2 rounded-lg')}
|
||||||
style={isDiscoverySelected ? { boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)' } : {}}
|
style={isDiscoverySelected ? { boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)' } : {}}
|
||||||
>
|
>
|
||||||
{isDiscoverySelected ? <SelectedDiscoveryIcon /> : <DiscoveryIcon />}
|
{isDiscoverySelected ? <SelectedDiscoveryIcon /> : <DiscoveryIcon />}
|
||||||
<div className='text-sm'>{t('explore.sidebar.discovery')}</div>
|
{!isMobile && <div className='text-sm'>{t('explore.sidebar.discovery')}</div>}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href='/explore/chat'
|
href='/explore/chat'
|
||||||
className={cn(isChatSelected ? 'text-primary-600 bg-white font-semibold' : 'text-gray-700 font-medium', 'flex items-center h-9 pl-3 space-x-2 rounded-lg')}
|
className={cn(isChatSelected ? 'text-primary-600 bg-white font-semibold' : 'text-gray-700 font-medium', 'flex items-center mobile:justify-center mobile:w-fit h-9 px-3 mobile:px-2 gap-2 rounded-lg')}
|
||||||
style={isChatSelected ? { boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)' } : {}}
|
style={isChatSelected ? { boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)' } : {}}
|
||||||
>
|
>
|
||||||
{isChatSelected ? <SelectedChatIcon /> : <ChatIcon />}
|
{isChatSelected ? <SelectedChatIcon /> : <ChatIcon />}
|
||||||
<div className='text-sm'>{t('explore.sidebar.chat')}</div>
|
{!isMobile && <div className='text-sm'>{t('explore.sidebar.chat')}</div>}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{installedApps.length > 0 && (
|
{installedApps.length > 0 && (
|
||||||
<div className='mt-10'>
|
<div className='mt-10'>
|
||||||
<div className='pl-2 text-xs text-gray-500 font-medium uppercase'>{t('explore.sidebar.workspace')}</div>
|
<p className='pl-2 mobile:px-0 text-xs text-gray-500 break-all font-medium uppercase'>{t('explore.sidebar.workspace')}</p>
|
||||||
<div className='mt-3 space-y-1 overflow-y-auto overflow-x-hidden'
|
<div className='mt-3 space-y-1 overflow-y-auto overflow-x-hidden'
|
||||||
style={{
|
style={{
|
||||||
height: 'calc(100vh - 250px)',
|
height: 'calc(100vh - 250px)',
|
||||||
@ -115,6 +121,7 @@ const SideBar: FC<{
|
|||||||
return (
|
return (
|
||||||
<Item
|
<Item
|
||||||
key={id}
|
key={id}
|
||||||
|
isMobile={isMobile}
|
||||||
name={name}
|
name={name}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
icon_background={icon_background}
|
icon_background={icon_background}
|
||||||
|
@ -10,7 +10,6 @@ import produce from 'immer'
|
|||||||
import { useBoolean, useGetState } from 'ahooks'
|
import { useBoolean, useGetState } from 'ahooks'
|
||||||
import AppUnavailable from '../../base/app-unavailable'
|
import AppUnavailable from '../../base/app-unavailable'
|
||||||
import useConversation from './hooks/use-conversation'
|
import useConversation from './hooks/use-conversation'
|
||||||
import s from './style.module.css'
|
|
||||||
import Init from './init'
|
import Init from './init'
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import Sidebar from '@/app/components/share/chat/sidebar'
|
import Sidebar from '@/app/components/share/chat/sidebar'
|
||||||
@ -721,10 +720,10 @@ const Main: FC<IMainProps> = () => {
|
|||||||
return <Loading type='app' />
|
return <Loading type='app' />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-gray-100'>
|
<div className='bg-gray-100 h-full'>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex rounded-t-2xl bg-white overflow-hidden rounded-b-2xl',
|
'flex rounded-t-2xl bg-white overflow-hidden rounded-b-2xl h-full',
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)',
|
boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)',
|
||||||
@ -744,8 +743,7 @@ const Main: FC<IMainProps> = () => {
|
|||||||
)}
|
)}
|
||||||
{/* main */}
|
{/* main */}
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
s.installedApp,
|
'h-full flex-grow flex flex-col overflow-y-auto',
|
||||||
'flex-grow flex flex-col overflow-y-auto',
|
|
||||||
)
|
)
|
||||||
}>
|
}>
|
||||||
{(!isNewConversation || isResponsing || errorHappened) && (
|
{(!isNewConversation || isResponsing || errorHappened) && (
|
||||||
|
@ -8,7 +8,7 @@ import Config from '../config'
|
|||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
|
|
||||||
const Line = (
|
const Line = (
|
||||||
<svg width="720" height="1" viewBox="0 0 720 1" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="100%" height="1" viewBox="0 0 720 1" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<line y1="0.5" x2="720" y2="0.5" stroke="url(#paint0_linear_6845_53470)"/>
|
<line y1="0.5" x2="720" y2="0.5" stroke="url(#paint0_linear_6845_53470)"/>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="paint0_linear_6845_53470" x1="0" y1="1" x2="720" y2="1" gradientUnits="userSpaceOnUse">
|
<linearGradient id="paint0_linear_6845_53470" x1="0" y1="1" x2="720" y2="1" gradientUnits="userSpaceOnUse">
|
||||||
@ -26,16 +26,16 @@ const Init: FC<IConfigProps> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='h-full flex items-center'>
|
<div className='h-full flex items-center justify-center'>
|
||||||
<div>
|
<div>
|
||||||
<div className='w-[480px] mx-auto text-center'>
|
<div className='text-center'>
|
||||||
<div className={cn(s.textGradient, 'mb-2 leading-[32px] font-semibold text-[24px]')}>{t('explore.universalChat.welcome')}</div>
|
<div className={cn(s.textGradient, 'mb-2 leading-[32px] font-semibold text-[24px]')}>{t('explore.universalChat.welcome')}</div>
|
||||||
<div className='mb-2 font-normal text-sm text-gray-500'>{t('explore.universalChat.welcomeDescribe')}</div>
|
<div className='mb-2 font-normal text-sm text-gray-500'>{t('explore.universalChat.welcomeDescribe')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex mb-2 mx-auto h-8 items-center'>
|
<div className='flex mb-2 h-8 items-center'>
|
||||||
{Line}
|
{Line}
|
||||||
</div>
|
</div>
|
||||||
<Config className='w-[480px] mx-auto' {...configProps} />
|
<Config {...configProps} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
.installedApp {
|
|
||||||
height: calc(100vh - 74px);
|
|
||||||
}
|
|
@ -2,7 +2,6 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { usePathname } from 'next/navigation'
|
import { usePathname } from 'next/navigation'
|
||||||
import s from './index.module.css'
|
import s from './index.module.css'
|
||||||
import { useAppContext } from '@/context/app-context'
|
|
||||||
|
|
||||||
type HeaderWrapperProps = {
|
type HeaderWrapperProps = {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
@ -12,23 +11,17 @@ const HeaderWrapper = ({
|
|||||||
children,
|
children,
|
||||||
}: HeaderWrapperProps) => {
|
}: HeaderWrapperProps) => {
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const { langeniusVersionInfo } = useAppContext()
|
|
||||||
const isBordered = ['/apps', '/datasets'].includes(pathname)
|
const isBordered = ['/apps', '/datasets'].includes(pathname)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(
|
<div className={classNames(
|
||||||
'sticky top-0 left-0 right-0 z-20 flex bg-gray-100 grow-0 shrink-0 basis-auto h-14',
|
'sticky top-0 left-0 right-0 z-20 flex flex-col bg-gray-100 grow-0 shrink-0 basis-auto min-h-[56px]',
|
||||||
s.header,
|
s.header,
|
||||||
isBordered ? 'border-b border-gray-200' : '',
|
isBordered ? 'border-b border-gray-200' : '',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={classNames(
|
|
||||||
s[`header-${langeniusVersionInfo.current_env}`],
|
|
||||||
'flex flex-1 items-center justify-between px-4',
|
|
||||||
)}>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default HeaderWrapper
|
export default HeaderWrapper
|
||||||
|
@ -17,7 +17,11 @@ import { ArrowUpRight, ChevronDown } from '@/app/components/base/icons/src/vende
|
|||||||
import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general'
|
import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { useModalContext } from '@/context/modal-context'
|
import { useModalContext } from '@/context/modal-context'
|
||||||
|
|
||||||
export default function AppSelector() {
|
export type IAppSelecotr = {
|
||||||
|
isMobile: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AppSelector({ isMobile }: IAppSelecotr) {
|
||||||
const itemClassName = `
|
const itemClassName = `
|
||||||
flex items-center w-full h-9 px-3 text-gray-700 text-[14px]
|
flex items-center w-full h-9 px-3 text-gray-700 text-[14px]
|
||||||
rounded-lg font-normal hover:bg-gray-50 cursor-pointer
|
rounded-lg font-normal hover:bg-gray-50 cursor-pointer
|
||||||
@ -50,12 +54,15 @@ export default function AppSelector() {
|
|||||||
inline-flex items-center
|
inline-flex items-center
|
||||||
rounded-[20px] py-1 pr-2.5 pl-1 text-sm
|
rounded-[20px] py-1 pr-2.5 pl-1 text-sm
|
||||||
text-gray-700 hover:bg-gray-200
|
text-gray-700 hover:bg-gray-200
|
||||||
|
mobile:px-1
|
||||||
${open && 'bg-gray-200'}
|
${open && 'bg-gray-200'}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<Avatar name={userProfile.name} className='mr-2' size={32} />
|
<Avatar name={userProfile.name} className='sm:mr-2 mr-0' size={32} />
|
||||||
|
{!isMobile && <>
|
||||||
{userProfile.name}
|
{userProfile.name}
|
||||||
<ChevronDown className="w-3 h-3 ml-1 text-gray-700"/>
|
<ChevronDown className="w-3 h-3 ml-1 text-gray-700"/>
|
||||||
|
</>}
|
||||||
</Menu.Button>
|
</Menu.Button>
|
||||||
</div>
|
</div>
|
||||||
<Transition
|
<Transition
|
||||||
|
@ -70,7 +70,7 @@ const ApiBasedExtensionSelector: FC<ApiBasedExtensionSelectorProps> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent className='w-[576px] z-[11]'>
|
<PortalToFollowElemContent className='w-[calc(100%-32px)] max-w-[576px] z-[11]'>
|
||||||
<div className='w-full rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg z-10'>
|
<div className='w-full rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg z-10'>
|
||||||
<div className='p-1'>
|
<div className='p-1'>
|
||||||
<div className='flex items-center justify-between px-3 pt-2 pb-1'>
|
<div className='flex items-center justify-between px-3 pt-2 pb-1'>
|
||||||
|
@ -76,7 +76,7 @@ const DataSourceNotion = ({
|
|||||||
: (
|
: (
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
`flex items-center px-3 h-7 bg-white border-[0.5px] border-gray-200 text-xs font-medium text-primary-600 rounded-md
|
`flex items-center px-3 py-1 min-h-7 bg-white border-[0.5px] border-gray-200 text-xs font-medium text-primary-600 rounded-md
|
||||||
${isCurrentWorkspaceManager ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
|
${isCurrentWorkspaceManager ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
|
||||||
}
|
}
|
||||||
onClick={handleConnectNotion}
|
onClick={handleConnectNotion}
|
||||||
|
@ -23,6 +23,7 @@ import { User01 as User01Solid, Users01 as Users01Solid } from '@/app/components
|
|||||||
import { Globe01 } from '@/app/components/base/icons/src/vender/line/mapsAndTravel'
|
import { Globe01 } from '@/app/components/base/icons/src/vender/line/mapsAndTravel'
|
||||||
import { AtSign, XClose } from '@/app/components/base/icons/src/vender/line/general'
|
import { AtSign, XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes'
|
import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
const iconClassName = `
|
const iconClassName = `
|
||||||
w-4 h-4 ml-3 mr-2
|
w-4 h-4 ml-3 mr-2
|
||||||
@ -42,6 +43,10 @@ export default function AccountSetting({
|
|||||||
}: IAccountSettingProps) {
|
}: IAccountSettingProps) {
|
||||||
const [activeMenu, setActiveMenu] = useState(activeTab)
|
const [activeMenu, setActiveMenu] = useState(activeTab)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{
|
{
|
||||||
key: 'workspace-group',
|
key: 'workspace-group',
|
||||||
@ -130,9 +135,9 @@ export default function AccountSetting({
|
|||||||
wrapperClassName='!z-20 pt-[60px]'
|
wrapperClassName='!z-20 pt-[60px]'
|
||||||
>
|
>
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<div className='w-[200px] p-4 border border-gray-100'>
|
<div className='w-[44px] sm:w-[200px] px-[1px] py-4 sm:p-4 border border-gray-100 shrink-0 sm:shrink-1 flex flex-col items-center sm:items-start'>
|
||||||
<div className='mb-8 ml-2 text-base font-medium leading-6 text-gray-900'>{t('common.userProfile.settings')}</div>
|
<div className='mb-8 ml-0 sm:ml-2 text-sm sm:text-base font-medium leading-6 text-gray-900'>{t('common.userProfile.settings')}</div>
|
||||||
<div>
|
<div className='w-full'>
|
||||||
{
|
{
|
||||||
menuItems.map(menuItem => (
|
menuItems.map(menuItem => (
|
||||||
<div key={menuItem.key} className='mb-4'>
|
<div key={menuItem.key} className='mb-4'>
|
||||||
@ -150,7 +155,7 @@ export default function AccountSetting({
|
|||||||
onClick={() => setActiveMenu(item.key)}
|
onClick={() => setActiveMenu(item.key)}
|
||||||
>
|
>
|
||||||
{activeMenu === item.key ? item.activeIcon : item.icon}
|
{activeMenu === item.key ? item.activeIcon : item.icon}
|
||||||
<div className='truncate'>{item.name}</div>
|
{!isMobile && <div className='truncate'>{item.name}</div>}
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -167,7 +172,7 @@ export default function AccountSetting({
|
|||||||
<XClose className='w-4 h-4 text-gray-500' />
|
<XClose className='w-4 h-4 text-gray-500' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='px-8 pt-2'>
|
<div className='px-4 sm:px-8 pt-2'>
|
||||||
{activeMenu === 'account' && <AccountPage />}
|
{activeMenu === 'account' && <AccountPage />}
|
||||||
{activeMenu === 'members' && <MembersPage />}
|
{activeMenu === 'members' && <MembersPage />}
|
||||||
{activeMenu === 'integrations' && <IntegrationsPage />}
|
{activeMenu === 'integrations' && <IntegrationsPage />}
|
||||||
|
@ -51,13 +51,13 @@ const MembersPage = () => {
|
|||||||
{t('common.members.invite')}
|
{t('common.members.invite')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className='overflow-x-auto'>
|
||||||
<div className='flex items-center py-[7px] border-b border-gray-200'>
|
<div className='flex items-center py-[7px] border-b border-gray-200 min-w-[480px]'>
|
||||||
<div className='grow px-3 text-xs font-medium text-gray-500'>{t('common.members.name')}</div>
|
<div className='grow px-3 text-xs font-medium text-gray-500'>{t('common.members.name')}</div>
|
||||||
<div className='shrink-0 w-[104px] text-xs font-medium text-gray-500'>{t('common.members.lastActive')}</div>
|
<div className='shrink-0 w-[104px] text-xs font-medium text-gray-500'>{t('common.members.lastActive')}</div>
|
||||||
<div className='shrink-0 w-[96px] px-3 text-xs font-medium text-gray-500'>{t('common.members.role')}</div>
|
<div className='shrink-0 w-[96px] px-3 text-xs font-medium text-gray-500'>{t('common.members.role')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className='min-w-[480px]'>
|
||||||
{
|
{
|
||||||
accounts.map(account => (
|
accounts.map(account => (
|
||||||
<div key={account.id} className='flex border-b border-gray-100'>
|
<div key={account.id} className='flex border-b border-gray-100'>
|
||||||
|
@ -218,7 +218,7 @@ const ModelPage = () => {
|
|||||||
}
|
}
|
||||||
<SystemModel onUpdate={() => mutateProviders()} />
|
<SystemModel onUpdate={() => mutateProviders()} />
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-cols-2 gap-4 mb-6'>
|
<div className='grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6'>
|
||||||
{
|
{
|
||||||
MODEL_CARD_LIST.map((model, index) => (
|
MODEL_CARD_LIST.map((model, index) => (
|
||||||
<ModelCard
|
<ModelCard
|
||||||
|
@ -34,7 +34,7 @@ const ModelItem: FC<ModelItemProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mb-2 bg-gray-50 rounded-xl'>
|
<div className='mb-2 bg-gray-50 rounded-xl'>
|
||||||
<div className='flex justify-between items-center px-4 h-14'>
|
<div className='flex justify-between items-center p-4 min-h-[56px] flex-wrap gap-y-1'>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
{modelItem.titleIcon[locale]}
|
{modelItem.titleIcon[locale]}
|
||||||
{
|
{
|
||||||
|
@ -159,7 +159,7 @@ const Form: FC<FormProps> = ({
|
|||||||
options?.map(option => (
|
options?.map(option => (
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
flex items-center px-3 h-9 rounded-lg border border-gray-100 bg-gray-25 cursor-pointer
|
flex items-center px-3 py-2 rounded-lg border border-gray-100 bg-gray-25 cursor-pointer
|
||||||
${value?.[field.key] === option.key && 'bg-white border-[1.5px] border-primary-400 shadow-sm'}
|
${value?.[field.key] === option.key && 'bg-white border-[1.5px] border-primary-400 shadow-sm'}
|
||||||
`}
|
`}
|
||||||
onClick={() => handleFormChange(field.key, option.key)}
|
onClick={() => handleFormChange(field.key, option.key)}
|
||||||
|
@ -96,7 +96,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
<PortalToFollowElem open>
|
<PortalToFollowElem open>
|
||||||
<PortalToFollowElemContent className='w-full h-full z-[60]'>
|
<PortalToFollowElemContent className='w-full h-full z-[60]'>
|
||||||
<div className='fixed inset-0 flex items-center justify-center bg-black/[.25]'>
|
<div className='fixed inset-0 flex items-center justify-center bg-black/[.25]'>
|
||||||
<div className='w-[640px] max-h-[calc(100vh-120px)] bg-white shadow-xl rounded-2xl overflow-y-auto'>
|
<div className='mx-2 w-[640px] max-h-[calc(100vh-120px)] bg-white shadow-xl rounded-2xl overflow-y-auto'>
|
||||||
<div className='px-8 pt-8'>
|
<div className='px-8 pt-8'>
|
||||||
<div className='flex justify-between items-center mb-2'>
|
<div className='flex justify-between items-center mb-2'>
|
||||||
<div className='text-xl font-semibold text-gray-900'>{renderTitlePrefix()}</div>
|
<div className='text-xl font-semibold text-gray-900'>{renderTitlePrefix()}</div>
|
||||||
@ -113,7 +113,7 @@ const ModelModal: FC<ModelModalProps> = ({
|
|||||||
onClearedChange={setCleared}
|
onClearedChange={setCleared}
|
||||||
onValidating={handleValidating}
|
onValidating={handleValidating}
|
||||||
/>
|
/>
|
||||||
<div className='flex justify-between items-center py-6'>
|
<div className='flex justify-between items-center py-6 flex-wrap gap-y-2'>
|
||||||
<a
|
<a
|
||||||
href={modelModal?.link.href}
|
href={modelModal?.link.href}
|
||||||
target='_blank'
|
target='_blank'
|
||||||
|
@ -1,43 +1,90 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import { useSelectedLayoutSegment } from 'next/navigation'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { Bars3Icon } from '@heroicons/react/20/solid'
|
||||||
|
import { useBoolean } from 'ahooks'
|
||||||
import AccountDropdown from './account-dropdown'
|
import AccountDropdown from './account-dropdown'
|
||||||
import AppNav from './app-nav'
|
import AppNav from './app-nav'
|
||||||
import DatasetNav from './dataset-nav'
|
import DatasetNav from './dataset-nav'
|
||||||
import EnvNav from './env-nav'
|
import EnvNav from './env-nav'
|
||||||
import ExploreNav from './explore-nav'
|
import ExploreNav from './explore-nav'
|
||||||
import GithubStar from './github-star'
|
import GithubStar from './github-star'
|
||||||
|
import s from './index.module.css'
|
||||||
import { WorkspaceProvider } from '@/context/workspace-context'
|
import { WorkspaceProvider } from '@/context/workspace-context'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import LogoSite from '@/app/components/base/logo/logo-site'
|
import LogoSite from '@/app/components/base/logo/logo-site'
|
||||||
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
|
|
||||||
const navClassName = `
|
const navClassName = `
|
||||||
flex items-center relative mr-3 px-3 h-8 rounded-xl
|
flex items-center relative mr-3 px-3 h-9 rounded-xl
|
||||||
font-medium text-sm
|
font-medium text-sm
|
||||||
cursor-pointer
|
cursor-pointer
|
||||||
`
|
`
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const { isCurrentWorkspaceManager } = useAppContext()
|
const selectedSegment = useSelectedLayoutSegment()
|
||||||
|
const { isCurrentWorkspaceManager, langeniusVersionInfo } = useAppContext()
|
||||||
|
const media = useBreakpoints()
|
||||||
|
const isMobile = media === MediaType.mobile
|
||||||
|
const [isShowNavMenu, { toggle, setFalse: hideNavMenu }] = useBoolean(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
hideNavMenu()
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [selectedSegment])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<div className={classNames(
|
||||||
|
s[`header-${langeniusVersionInfo.current_env}`],
|
||||||
|
'flex flex-1 items-center justify-between px-4',
|
||||||
|
)}>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
|
{isMobile && <div
|
||||||
|
className='flex items-center justify-center h-8 w-8 cursor-pointer'
|
||||||
|
onClick={toggle}
|
||||||
|
>
|
||||||
|
<Bars3Icon className="h-4 w-4 text-gray-500" />
|
||||||
|
</div>}
|
||||||
|
{!isMobile && <>
|
||||||
|
<Link href="/apps" className='flex items-center mr-4'>
|
||||||
|
<LogoSite />
|
||||||
|
</Link>
|
||||||
|
<GithubStar />
|
||||||
|
</>}
|
||||||
|
</div>
|
||||||
|
{isMobile && (
|
||||||
|
<div className='flex'>
|
||||||
<Link href="/apps" className='flex items-center mr-4'>
|
<Link href="/apps" className='flex items-center mr-4'>
|
||||||
<LogoSite />
|
<LogoSite />
|
||||||
</Link>
|
</Link>
|
||||||
<GithubStar />
|
<GithubStar />
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{!isMobile && (
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
<ExploreNav className={navClassName} />
|
<ExploreNav className={navClassName} />
|
||||||
<AppNav />
|
<AppNav />
|
||||||
{isCurrentWorkspaceManager && <DatasetNav />}
|
{isCurrentWorkspaceManager && <DatasetNav />}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div className='flex items-center flex-shrink-0'>
|
<div className='flex items-center flex-shrink-0'>
|
||||||
<EnvNav />
|
<EnvNav />
|
||||||
<WorkspaceProvider>
|
<WorkspaceProvider>
|
||||||
<AccountDropdown />
|
<AccountDropdown isMobile={isMobile} />
|
||||||
</WorkspaceProvider>
|
</WorkspaceProvider>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{(isMobile && isShowNavMenu) && (
|
||||||
|
<div className='w-full flex flex-col p-2 gap-y-1'>
|
||||||
|
<ExploreNav className={navClassName} />
|
||||||
|
<AppNav />
|
||||||
|
{isCurrentWorkspaceManager && <DatasetNav />}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ const Nav = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`
|
<div className={`
|
||||||
flex items-center h-8 mr-3 px-0.5 rounded-xl text-sm shrink-0 font-medium
|
flex items-center h-8 mr-0 sm:mr-3 px-0.5 rounded-xl text-sm shrink-0 font-medium
|
||||||
${isActived && 'bg-white shadow-md font-semibold'}
|
${isActived && 'bg-white shadow-md font-semibold'}
|
||||||
${!curNav && !isActived && 'hover:bg-gray-200'}
|
${!curNav && !isActived && 'hover:bg-gray-200'}
|
||||||
`}>
|
`}>
|
||||||
|
@ -11,7 +11,6 @@ import { useBoolean, useGetState } from 'ahooks'
|
|||||||
import AppUnavailable from '../../base/app-unavailable'
|
import AppUnavailable from '../../base/app-unavailable'
|
||||||
import { checkOrSetAccessToken } from '../utils'
|
import { checkOrSetAccessToken } from '../utils'
|
||||||
import useConversation from './hooks/use-conversation'
|
import useConversation from './hooks/use-conversation'
|
||||||
import s from './style.module.css'
|
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import Sidebar from '@/app/components/share/chat/sidebar'
|
import Sidebar from '@/app/components/share/chat/sidebar'
|
||||||
import ConfigSence from '@/app/components/share/chat/config-scence'
|
import ConfigSence from '@/app/components/share/chat/config-scence'
|
||||||
@ -683,7 +682,7 @@ const Main: FC<IMainProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-gray-100'>
|
<div className='bg-gray-100 h-full'>
|
||||||
{!isInstalledApp && (
|
{!isInstalledApp && (
|
||||||
<Header
|
<Header
|
||||||
title={siteInfo.title}
|
title={siteInfo.title}
|
||||||
@ -720,7 +719,7 @@ const Main: FC<IMainProps> = ({
|
|||||||
)}
|
)}
|
||||||
{/* main */}
|
{/* main */}
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
isInstalledApp ? s.installedApp : 'h-[calc(100vh_-_3rem)] tablet:h-screen',
|
isInstalledApp ? 'h-full' : 'h-[calc(100vh_-_3rem)] tablet:h-screen',
|
||||||
'flex-grow flex flex-col overflow-y-auto',
|
'flex-grow flex flex-col overflow-y-auto',
|
||||||
)
|
)
|
||||||
}>
|
}>
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
.installedApp {
|
|
||||||
height: calc(100vh - 74px);
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ import { useBoolean, useGetState } from 'ahooks'
|
|||||||
import { checkOrSetAccessToken } from '../utils'
|
import { checkOrSetAccessToken } from '../utils'
|
||||||
import AppUnavailable from '../../base/app-unavailable'
|
import AppUnavailable from '../../base/app-unavailable'
|
||||||
import useConversation from './hooks/use-conversation'
|
import useConversation from './hooks/use-conversation'
|
||||||
import s from './style.module.css'
|
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import ConfigScene from '@/app/components/share/chatbot/config-scence'
|
import ConfigScene from '@/app/components/share/chatbot/config-scence'
|
||||||
import Header from '@/app/components/share/header'
|
import Header from '@/app/components/share/header'
|
||||||
@ -561,7 +560,7 @@ const Main: FC<IMainProps> = ({
|
|||||||
|
|
||||||
<div className={'flex bg-white overflow-hidden'}>
|
<div className={'flex bg-white overflow-hidden'}>
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
isInstalledApp ? s.installedApp : 'h-[calc(100vh_-_3rem)]',
|
isInstalledApp ? 'h-full' : 'h-[calc(100vh_-_3rem)]',
|
||||||
'flex-grow flex flex-col overflow-y-auto',
|
'flex-grow flex flex-col overflow-y-auto',
|
||||||
)
|
)
|
||||||
}>
|
}>
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
.installedApp {
|
|
||||||
height: calc(100vh - 74px);
|
|
||||||
}
|
|
@ -33,7 +33,7 @@ const Header: FC<IHeaderProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
shrink-0 flex items-center justify-between h-12 px-3 bg-gray-100
|
shrink-0 flex items-center justify-between h-14 px-4 bg-gray-100
|
||||||
bg-gradient-to-r from-blue-600 to-sky-500
|
bg-gradient-to-r from-blue-600 to-sky-500
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
@ -52,7 +52,7 @@ const Header: FC<IHeaderProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="shrink-0 flex items-center justify-between h-12 px-3 bg-gray-100">
|
<div className="shrink-0 flex items-center justify-between h-14 px-4 bg-gray-100">
|
||||||
<div
|
<div
|
||||||
className='flex items-center justify-center h-8 w-8 cursor-pointer'
|
className='flex items-center justify-center h-8 w-8 cursor-pointer'
|
||||||
onClick={() => onShowSideBar?.()}
|
onClick={() => onShowSideBar?.()}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.installedApp {
|
.installedApp {
|
||||||
height: calc(100vh - 74px);
|
height: 100%;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||||
}
|
}
|
||||||
|
@ -120,9 +120,9 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
|
|||||||
isCurrentWorkspaceManager,
|
isCurrentWorkspaceManager,
|
||||||
mutateCurrentWorkspace,
|
mutateCurrentWorkspace,
|
||||||
}}>
|
}}>
|
||||||
<div className='flex flex-col h-full'>
|
<div className='flex flex-col h-full overflow-y-auto'>
|
||||||
{globalThis.document?.body?.getAttribute('data-public-maintenance-notice') && <MaintenanceNotice />}
|
{globalThis.document?.body?.getAttribute('data-public-maintenance-notice') && <MaintenanceNotice />}
|
||||||
<div ref={pageContainerRef} className='grow relative flex flex-col overflow-auto bg-gray-100'>
|
<div ref={pageContainerRef} className='grow relative flex flex-col overflow-y-auto overflow-x-hidden bg-gray-100'>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,6 +21,7 @@ const translation = {
|
|||||||
operation: {
|
operation: {
|
||||||
applyConfig: 'Publish',
|
applyConfig: 'Publish',
|
||||||
resetConfig: 'Reset',
|
resetConfig: 'Reset',
|
||||||
|
debugConfig: 'Debug',
|
||||||
addFeature: 'Add Feature',
|
addFeature: 'Add Feature',
|
||||||
automatic: 'Automatic',
|
automatic: 'Automatic',
|
||||||
stopResponding: 'Stop responding',
|
stopResponding: 'Stop responding',
|
||||||
|
@ -21,6 +21,7 @@ const translation = {
|
|||||||
operation: {
|
operation: {
|
||||||
applyConfig: '发布',
|
applyConfig: '发布',
|
||||||
resetConfig: '重置',
|
resetConfig: '重置',
|
||||||
|
debugConfig: '调试',
|
||||||
addFeature: '添加功能',
|
addFeature: '添加功能',
|
||||||
automatic: '自动编排',
|
automatic: '自动编排',
|
||||||
stopResponding: '停止响应',
|
stopResponding: '停止响应',
|
||||||
|
@ -96,6 +96,7 @@ const translation = {
|
|||||||
sideTipP3: 'Cleaning removes unnecessary characters and formats, making datasets cleaner and easier to parse.',
|
sideTipP3: 'Cleaning removes unnecessary characters and formats, making datasets cleaner and easier to parse.',
|
||||||
sideTipP4: 'Proper segmentation and cleaning improve model performance, providing more accurate and valuable results.',
|
sideTipP4: 'Proper segmentation and cleaning improve model performance, providing more accurate and valuable results.',
|
||||||
previewTitle: 'Preview',
|
previewTitle: 'Preview',
|
||||||
|
previewTitleButton: 'Preview',
|
||||||
previewButton: 'Switching to Q&A format',
|
previewButton: 'Switching to Q&A format',
|
||||||
previewSwitchTipStart: 'The current segment preview is in text format, switching to a question-and-answer format preview will',
|
previewSwitchTipStart: 'The current segment preview is in text format, switching to a question-and-answer format preview will',
|
||||||
previewSwitchTipEnd: ' consume additional tokens',
|
previewSwitchTipEnd: ' consume additional tokens',
|
||||||
|
@ -96,6 +96,7 @@ const translation = {
|
|||||||
sideTipP3: '清洗则是对文本进行预处理,删除不必要的字符、符号或格式,使数据集更加干净、整洁,便于模型解析。',
|
sideTipP3: '清洗则是对文本进行预处理,删除不必要的字符、符号或格式,使数据集更加干净、整洁,便于模型解析。',
|
||||||
sideTipP4: '通过对数据集进行适当的分段和清洗,可以提高模型在实际应用中的表现,从而为用户提供更准确、更有价值的结果。',
|
sideTipP4: '通过对数据集进行适当的分段和清洗,可以提高模型在实际应用中的表现,从而为用户提供更准确、更有价值的结果。',
|
||||||
previewTitle: '分段预览',
|
previewTitle: '分段预览',
|
||||||
|
previewTitleButton: '预览',
|
||||||
previewButton: '切换至 Q&A 形式',
|
previewButton: '切换至 Q&A 形式',
|
||||||
previewSwitchTipStart: '当前分段预览是文本模式,切换到 Q&A 模式将会',
|
previewSwitchTipStart: '当前分段预览是文本模式,切换到 Q&A 模式将会',
|
||||||
previewSwitchTipEnd: '消耗额外的 token',
|
previewSwitchTipEnd: '消耗额外的 token',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user