Feat: new entry point for app creation (#10847)
|
@ -21,8 +21,6 @@ import Divider from '@/app/components/base/divider'
|
|||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||
import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
|
||||
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
|
||||
import EditAppModal from '@/app/components/explore/create-app-modal'
|
||||
import SwitchAppModal from '@/app/components/app/switch-app-modal'
|
||||
|
@ -32,6 +30,7 @@ import type { EnvironmentVariable } from '@/app/components/workflow/types'
|
|||
import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal'
|
||||
import { fetchWorkflowDraft } from '@/service/workflow'
|
||||
import { fetchInstalledAppList } from '@/service/explore'
|
||||
import { AppTypeIcon } from '@/app/components/app/type-selector'
|
||||
|
||||
export type AppCardProps = {
|
||||
app: App
|
||||
|
@ -277,7 +276,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
|||
e.preventDefault()
|
||||
getRedirection(isCurrentWorkspaceEditor, app, push)
|
||||
}}
|
||||
className='relative group col-span-1 bg-white border-2 border-solid border-transparent rounded-xl shadow-sm flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
|
||||
className='relative h-[160px] group col-span-1 bg-components-card-bg border-[1px] border-solid border-components-card-border rounded-xl shadow-sm inline-flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
|
||||
>
|
||||
<div className='flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
|
||||
<div className='relative shrink-0'>
|
||||
|
@ -288,30 +287,14 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
|||
background={app.icon_background}
|
||||
imageUrl={app.icon_url}
|
||||
/>
|
||||
<span className='absolute bottom-[-3px] right-[-3px] w-4 h-4 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm'>
|
||||
{app.mode === 'advanced-chat' && (
|
||||
<ChatBot className='w-3 h-3 text-[#1570EF]' />
|
||||
)}
|
||||
{app.mode === 'agent-chat' && (
|
||||
<CuteRobot className='w-3 h-3 text-indigo-600' />
|
||||
)}
|
||||
{app.mode === 'chat' && (
|
||||
<ChatBot className='w-3 h-3 text-[#1570EF]' />
|
||||
)}
|
||||
{app.mode === 'completion' && (
|
||||
<AiText className='w-3 h-3 text-[#0E9384]' />
|
||||
)}
|
||||
{app.mode === 'workflow' && (
|
||||
<Route className='w-3 h-3 text-[#f79009]' />
|
||||
)}
|
||||
</span>
|
||||
<AppTypeIcon type={app.mode} wrapperClassName='absolute -bottom-0.5 -right-0.5 w-4 h-4 shadow-sm' className='w-3 h-3' />
|
||||
</div>
|
||||
<div className='grow w-0 py-[1px]'>
|
||||
<div className='flex items-center text-sm leading-5 font-semibold text-gray-800'>
|
||||
<div className='flex items-center text-sm leading-5 font-semibold text-text-secondary'>
|
||||
<div className='truncate' title={app.name}>{app.name}</div>
|
||||
</div>
|
||||
<div className='flex items-center text-[10px] leading-[18px] text-gray-500 font-medium'>
|
||||
{app.mode === 'advanced-chat' && <div className='truncate'>{t('app.types.chatbot').toUpperCase()}</div>}
|
||||
<div className='flex items-center text-[10px] leading-[18px] text-text-tertiary font-medium'>
|
||||
{app.mode === 'advanced-chat' && <div className='truncate'>{t('app.types.advanced').toUpperCase()}</div>}
|
||||
{app.mode === 'chat' && <div className='truncate'>{t('app.types.chatbot').toUpperCase()}</div>}
|
||||
{app.mode === 'agent-chat' && <div className='truncate'>{t('app.types.agent').toUpperCase()}</div>}
|
||||
{app.mode === 'workflow' && <div className='truncate'>{t('app.types.workflow').toUpperCase()}</div>}
|
||||
|
@ -319,7 +302,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='title-wrapper h-[90px] px-[14px] text-xs leading-normal text-gray-500'>
|
||||
<div className='title-wrapper h-[90px] px-[14px] text-xs leading-normal text-text-tertiary'>
|
||||
<div
|
||||
className={cn(tags.length ? 'line-clamp-2' : 'line-clamp-4', 'group-hover:line-clamp-2')}
|
||||
title={app.description}
|
||||
|
@ -352,7 +335,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-gray-200' />
|
||||
<div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px]' />
|
||||
<div className='!hidden group-hover:!flex shrink-0'>
|
||||
<CustomPopover
|
||||
htmlContent={<Operations />}
|
||||
|
@ -362,7 +345,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
|
|||
<div
|
||||
className='flex items-center justify-center w-8 h-8 cursor-pointer rounded-md'
|
||||
>
|
||||
<RiMoreFill className='w-4 h-4 text-gray-700' />
|
||||
<RiMoreFill className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
}
|
||||
btnClassName={open =>
|
||||
|
|
|
@ -125,7 +125,7 @@ const Apps = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className='sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-10 flex-wrap gap-y-2'>
|
||||
<div className='sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-background-body z-10 flex-wrap gap-y-2'>
|
||||
<TabSliderNew
|
||||
value={activeTab}
|
||||
onChange={setActiveTab}
|
||||
|
@ -143,14 +143,20 @@ const Apps = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<nav className='grid content-start grid-cols-1 gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'>
|
||||
{isCurrentWorkspaceEditor
|
||||
&& <NewAppCard onSuccess={mutate} />}
|
||||
{data?.map(({ data: apps }) => apps.map(app => (
|
||||
<AppCard key={app.id} app={app} onRefresh={mutate} />
|
||||
)))}
|
||||
<CheckModal />
|
||||
</nav>
|
||||
{(data && data[0].total > 0)
|
||||
? <div className='grid content-start grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6 gap-4 px-12 pt-2 grow relative'>
|
||||
{isCurrentWorkspaceEditor
|
||||
&& <NewAppCard onSuccess={mutate} />}
|
||||
{data.map(({ data: apps }) => apps.map(app => (
|
||||
<AppCard key={app.id} app={app} onRefresh={mutate} />
|
||||
)))}
|
||||
</div>
|
||||
: <div className='grid content-start grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6 gap-4 px-12 pt-2 grow relative overflow-hidden'>
|
||||
{isCurrentWorkspaceEditor
|
||||
&& <NewAppCard className='z-10' onSuccess={mutate} />}
|
||||
<NoAppsFound />
|
||||
</div>}
|
||||
<CheckModal />
|
||||
<div ref={anchorRef} className='h-0'> </div>
|
||||
{showTagManagementModal && (
|
||||
<TagManagementModal type='app' show={showTagManagementModal} />
|
||||
|
@ -160,3 +166,21 @@ const Apps = () => {
|
|||
}
|
||||
|
||||
export default Apps
|
||||
|
||||
function NoAppsFound() {
|
||||
const { t } = useTranslation()
|
||||
function renderDefaultCard() {
|
||||
const defaultCards = Array.from({ length: 36 }, (_, index) => (
|
||||
<div key={index} className='h-[160px] inline-flex rounded-xl bg-background-default-lighter'></div>
|
||||
))
|
||||
return defaultCards
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{renderDefaultCard()}
|
||||
<div className='absolute top-0 left-0 right-0 bottom-0 flex items-center justify-center bg-gradient-to-t from-background-body to-transparent'>
|
||||
<span className='system-md-medium text-text-tertiary'>{t('app.newApp.noAppsFound')}</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,13 +11,15 @@ import CreateAppModal from '@/app/components/app/create-app-modal'
|
|||
import CreateFromDSLModal, { CreateFromDSLModalTab } from '@/app/components/app/create-from-dsl-modal'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { FileArrow01, FilePlus01, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
export type CreateAppCardProps = {
|
||||
className?: string
|
||||
onSuccess?: () => void
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuccess }, ref) => {
|
||||
const CreateAppCard = forwardRef<HTMLDivElement, CreateAppCardProps>(({ className, onSuccess }, ref) => {
|
||||
const { t } = useTranslation()
|
||||
const { onPlanInfoChanged } = useProviderContext()
|
||||
const searchParams = useSearchParams()
|
||||
|
@ -36,26 +38,26 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
|
|||
}, [dslUrl])
|
||||
|
||||
return (
|
||||
<a
|
||||
<div
|
||||
ref={ref}
|
||||
className='relative col-span-1 flex flex-col justify-between min-h-[160px] bg-gray-200 rounded-xl border-[0.5px] border-black/5'
|
||||
className={cn('relative col-span-1 inline-flex flex-col justify-between h-[160px] bg-components-card-bg rounded-xl border-[0.5px] border-components-card-border', className)}
|
||||
>
|
||||
<div className='grow p-2 rounded-t-xl'>
|
||||
<div className='px-6 pt-2 pb-1 text-xs font-medium leading-[18px] text-gray-500'>{t('app.createApp')}</div>
|
||||
<div className='flex items-center mb-1 px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:text-primary-600 hover:bg-white' onClick={() => setShowNewAppModal(true)}>
|
||||
<div className='px-6 pt-2 pb-1 text-xs font-medium leading-[18px] text-text-tertiary'>{t('app.createApp')}</div>
|
||||
<div className='flex items-center mb-1 px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover' onClick={() => setShowNewAppModal(true)}>
|
||||
<FilePlus01 className='shrink-0 mr-2 w-4 h-4' />
|
||||
{t('app.newApp.startFromBlank')}
|
||||
</div>
|
||||
<div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:text-primary-600 hover:bg-white' onClick={() => setShowNewAppTemplateDialog(true)}>
|
||||
<div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover' onClick={() => setShowNewAppTemplateDialog(true)}>
|
||||
<FilePlus02 className='shrink-0 mr-2 w-4 h-4' />
|
||||
{t('app.newApp.startFromTemplate')}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className='p-2 border-t-[0.5px] border-black/5 rounded-b-xl'
|
||||
className='p-2 border-t-[0.5px] border-components-card-border rounded-b-xl'
|
||||
onClick={() => setShowCreateFromDSLModal(true)}
|
||||
>
|
||||
<div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:text-primary-600 hover:bg-white'>
|
||||
<div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover'>
|
||||
<FileArrow01 className='shrink-0 mr-2 w-4 h-4' />
|
||||
{t('app.importDSL')}
|
||||
</div>
|
||||
|
@ -68,6 +70,10 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
|
|||
if (onSuccess)
|
||||
onSuccess()
|
||||
}}
|
||||
onCreateFromTemplate={() => {
|
||||
setShowNewAppTemplateDialog(true)
|
||||
setShowNewAppModal(false)
|
||||
}}
|
||||
/>
|
||||
<CreateAppTemplateDialog
|
||||
show={showNewAppTemplateDialog}
|
||||
|
@ -77,6 +83,10 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
|
|||
if (onSuccess)
|
||||
onSuccess()
|
||||
}}
|
||||
onCreateFromBlank={() => {
|
||||
setShowNewAppModal(true)
|
||||
setShowNewAppTemplateDialog(false)
|
||||
}}
|
||||
/>
|
||||
<CreateFromDSLModal
|
||||
show={showCreateFromDSLModal}
|
||||
|
@ -94,7 +104,7 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
|
|||
onSuccess()
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
'use client'
|
||||
import { useContextSelector } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiDiscordFill, RiGithubFill } from '@remixicon/react'
|
||||
import Link from 'next/link'
|
||||
import style from '../list.module.css'
|
||||
import Apps from './Apps'
|
||||
import classNames from '@/utils/classnames'
|
||||
import AppContext from '@/context/app-context'
|
||||
import { LicenseStatus } from '@/types/feature'
|
||||
|
||||
|
@ -12,14 +13,18 @@ const AppList = () => {
|
|||
const systemFeatures = useContextSelector(AppContext, v => v.systemFeatures)
|
||||
|
||||
return (
|
||||
<div className='relative flex flex-col overflow-y-auto bg-gray-100 shrink-0 h-0 grow'>
|
||||
<div className='relative flex flex-col overflow-y-auto bg-background-body shrink-0 h-0 grow'>
|
||||
<Apps />
|
||||
{systemFeatures.license.status === LicenseStatus.NONE && <footer className='px-12 py-6 grow-0 shrink-0'>
|
||||
<h3 className='text-xl font-semibold leading-tight text-gradient'>{t('app.join')}</h3>
|
||||
<p className='mt-1 text-sm font-normal leading-tight text-gray-700'>{t('app.communityIntro')}</p>
|
||||
<p className='mt-1 system-sm-regular text-text-tertiary'>{t('app.communityIntro')}</p>
|
||||
<div className='flex items-center gap-2 mt-3'>
|
||||
<a className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://github.com/langgenius/dify'><span className={classNames(style.socialMediaIcon, style.githubIcon)} /></a>
|
||||
<a className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://discord.gg/FngNHpbcY7'><span className={classNames(style.socialMediaIcon, style.discordIcon)} /></a>
|
||||
<Link className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://github.com/langgenius/dify'>
|
||||
<RiGithubFill className='w-5 h-5 text-text-tertiary' />
|
||||
</Link>
|
||||
<Link className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://discord.gg/FngNHpbcY7'>
|
||||
<RiDiscordFill className='w-5 h-5 text-text-tertiary' />
|
||||
</Link>
|
||||
</div>
|
||||
</footer>}
|
||||
</div >
|
||||
|
|
|
@ -201,14 +201,6 @@
|
|||
@apply block w-6 h-6 bg-center bg-contain;
|
||||
}
|
||||
|
||||
.githubIcon {
|
||||
background-image: url("./apps/assets/github.svg");
|
||||
}
|
||||
|
||||
.discordIcon {
|
||||
background-image: url("./apps/assets/discord.svg");
|
||||
}
|
||||
|
||||
/* #region new app dialog */
|
||||
.newItemCaption {
|
||||
@apply inline-flex items-center mb-2 text-sm font-medium;
|
||||
|
|
|
@ -237,7 +237,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
|||
{appDetail.mode === 'advanced-chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.newApp.advanced').toUpperCase()}</div>
|
||||
<div title={t('app.types.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.advanced').toUpperCase()}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'agent-chat' && (
|
||||
|
@ -246,13 +246,13 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
|||
{appDetail.mode === 'chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.newApp.basic').toUpperCase())}</div>
|
||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'completion' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.newApp.basic').toUpperCase())}</div>
|
||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'workflow' && (
|
||||
|
@ -299,7 +299,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
|||
{appDetail.mode === 'advanced-chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.newApp.advanced').toUpperCase()}</div>
|
||||
<div title={t('app.types.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.advanced').toUpperCase()}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'agent-chat' && (
|
||||
|
@ -308,13 +308,13 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
|||
{appDetail.mode === 'chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.newApp.basic').toUpperCase())}</div>
|
||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'completion' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
|
||||
<div title={t('app.newApp.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.newApp.basic').toUpperCase())}</div>
|
||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'workflow' && (
|
||||
|
@ -398,7 +398,7 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
|||
)} />
|
||||
<div className='px-4 pb-2'>
|
||||
<div className='flex items-center gap-1 text-gray-700 text-md leading-6 font-semibold'>
|
||||
{showSwitchTip === 'chat' ? t('app.newApp.advanced') : t('app.types.workflow')}
|
||||
{showSwitchTip === 'chat' ? t('app.types.advanced') : t('app.types.workflow')}
|
||||
<span className='px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span>
|
||||
</div>
|
||||
<div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.advancedFor').toLocaleUpperCase()}</div>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
'use client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { PlusIcon } from '@heroicons/react/20/solid'
|
||||
import { AppTypeIcon, AppTypeLabel } from '../../type-selector'
|
||||
import Button from '@/app/components/base/button'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { App } from '@/models/explore'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
|
||||
export type AppCardProps = {
|
||||
app: App
|
||||
canCreate: boolean
|
||||
onCreate: () => void
|
||||
}
|
||||
|
||||
const AppCard = ({
|
||||
app,
|
||||
onCreate,
|
||||
}: AppCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { app: appBasicInfo } = app
|
||||
return (
|
||||
<div className={cn('p-4 h-[132px] relative overflow-hidden flex flex-col group bg-components-panel-on-panel-item-bg border-[0.5px] border-components-panel-border rounded-xl shadow-xs hover:shadow-lg cursor-pointer')}>
|
||||
<div className='flex items-center gap-3 pb-2 grow-0 shrink-0'>
|
||||
<div className='relative shrink-0'>
|
||||
<AppIcon
|
||||
size='large'
|
||||
iconType={app.app.icon_type}
|
||||
icon={app.app.icon}
|
||||
background={app.app.icon_background}
|
||||
imageUrl={app.app.icon_url}
|
||||
/>
|
||||
<AppTypeIcon wrapperClassName='absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-[4px] border border-divider-regular outline outline-components-panel-on-panel-item-bg'
|
||||
className='w-3 h-3' type={appBasicInfo.mode} />
|
||||
</div>
|
||||
<div className='grow flex flex-col gap-1'>
|
||||
<div className='line-clamp-1'>
|
||||
<span className='system-md-semibold text-text-secondary' title={appBasicInfo.name}>{appBasicInfo.name}</span>
|
||||
</div>
|
||||
<AppTypeLabel className='system-2xs-medium-uppercase text-text-tertiary' type={app.app.mode} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-1 system-xs-regular text-text-tertiary">
|
||||
<div className='line-clamp-3'>
|
||||
{app.description}
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn('hidden absolute bottom-0 left-0 right-0 p-4 pt-8 group-hover:flex bg-gradient-to-t from-[60.27%] from-components-panel-gradient-2 to-transparent')}>
|
||||
<div className={cn('flex items-center w-full space-x-2 h-8')}>
|
||||
<Button variant='primary' className='grow' onClick={() => onCreate()}>
|
||||
<PlusIcon className='w-4 h-4 mr-1' />
|
||||
<span className='text-xs'>{t('app.newApp.useTemplate')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppCard
|
|
@ -0,0 +1,247 @@
|
|||
'use client'
|
||||
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import useSWR from 'swr'
|
||||
import { useDebounceFn } from 'ahooks'
|
||||
import { RiRobot2Line } from '@remixicon/react'
|
||||
import AppCard from '../app-card'
|
||||
import Sidebar, { AppCategories, AppCategoryLabel } from './sidebar'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import cn from '@/utils/classnames'
|
||||
import ExploreContext from '@/context/explore-context'
|
||||
import type { App } from '@/models/explore'
|
||||
import { fetchAppDetail, fetchAppList } from '@/service/explore'
|
||||
import { importDSL } from '@/service/apps'
|
||||
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
|
||||
import CreateAppModal from '@/app/components/explore/create-app-modal'
|
||||
import AppTypeSelector from '@/app/components/app/type-selector'
|
||||
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import Input from '@/app/components/base/input'
|
||||
import type { AppMode } from '@/types/app'
|
||||
import { DSLImportMode } from '@/models/app'
|
||||
|
||||
type AppsProps = {
|
||||
onSuccess?: () => void
|
||||
onCreateFromBlank?: () => void
|
||||
}
|
||||
|
||||
// export enum PageType {
|
||||
// EXPLORE = 'explore',
|
||||
// CREATE = 'create',
|
||||
// }
|
||||
|
||||
const Apps = ({
|
||||
onSuccess,
|
||||
onCreateFromBlank,
|
||||
}: AppsProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { isCurrentWorkspaceEditor } = useAppContext()
|
||||
const { push } = useRouter()
|
||||
const { hasEditPermission } = useContext(ExploreContext)
|
||||
const allCategoriesEn = AppCategories.RECOMMENDED
|
||||
|
||||
const [keywords, setKeywords] = useState('')
|
||||
const [searchKeywords, setSearchKeywords] = useState('')
|
||||
|
||||
const { run: handleSearch } = useDebounceFn(() => {
|
||||
setSearchKeywords(keywords)
|
||||
}, { wait: 500 })
|
||||
|
||||
const handleKeywordsChange = (value: string) => {
|
||||
setKeywords(value)
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
const [currentType, setCurrentType] = useState<AppMode[]>([])
|
||||
const [currCategory, setCurrCategory] = useTabSearchParams({
|
||||
defaultTab: allCategoriesEn,
|
||||
disableSearchParams: true,
|
||||
})
|
||||
|
||||
const {
|
||||
data: { categories, allList },
|
||||
} = useSWR(
|
||||
['/explore/apps'],
|
||||
() =>
|
||||
fetchAppList().then(({ categories, recommended_apps }) => ({
|
||||
categories,
|
||||
allList: recommended_apps.sort((a, b) => a.position - b.position),
|
||||
})),
|
||||
{
|
||||
fallbackData: {
|
||||
categories: [],
|
||||
allList: [],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
const filteredList = useMemo(() => {
|
||||
const filteredByCategory = allList.filter((item) => {
|
||||
if (currCategory === allCategoriesEn)
|
||||
return true
|
||||
return item.category === currCategory
|
||||
})
|
||||
if (currentType.length === 0)
|
||||
return filteredByCategory
|
||||
return filteredByCategory.filter((item) => {
|
||||
if (currentType.includes('chat') && item.app.mode === 'chat')
|
||||
return true
|
||||
if (currentType.includes('advanced-chat') && item.app.mode === 'advanced-chat')
|
||||
return true
|
||||
if (currentType.includes('agent-chat') && item.app.mode === 'agent-chat')
|
||||
return true
|
||||
if (currentType.includes('completion') && item.app.mode === 'completion')
|
||||
return true
|
||||
if (currentType.includes('workflow') && item.app.mode === 'workflow')
|
||||
return true
|
||||
return false
|
||||
})
|
||||
}, [currentType, currCategory, allCategoriesEn, allList])
|
||||
|
||||
const searchFilteredList = useMemo(() => {
|
||||
if (!searchKeywords || !filteredList || filteredList.length === 0)
|
||||
return filteredList
|
||||
|
||||
const lowerCaseSearchKeywords = searchKeywords.toLowerCase()
|
||||
|
||||
return filteredList.filter(item =>
|
||||
item.app && item.app.name && item.app.name.toLowerCase().includes(lowerCaseSearchKeywords),
|
||||
)
|
||||
}, [searchKeywords, filteredList])
|
||||
|
||||
const [currApp, setCurrApp] = React.useState<App | null>(null)
|
||||
const [isShowCreateModal, setIsShowCreateModal] = React.useState(false)
|
||||
const onCreate: CreateAppModalProps['onConfirm'] = async ({
|
||||
name,
|
||||
icon_type,
|
||||
icon,
|
||||
icon_background,
|
||||
description,
|
||||
}) => {
|
||||
const { export_data } = await fetchAppDetail(
|
||||
currApp?.app.id as string,
|
||||
)
|
||||
try {
|
||||
const app = await importDSL({
|
||||
mode: DSLImportMode.YAML_CONTENT,
|
||||
yaml_content: export_data,
|
||||
name,
|
||||
icon_type,
|
||||
icon,
|
||||
icon_background,
|
||||
description,
|
||||
})
|
||||
setIsShowCreateModal(false)
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: t('app.newApp.appCreated'),
|
||||
})
|
||||
if (onSuccess)
|
||||
onSuccess()
|
||||
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
||||
getRedirection(isCurrentWorkspaceEditor, app, push)
|
||||
}
|
||||
catch (e) {
|
||||
Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||
}
|
||||
}
|
||||
|
||||
if (!categories || categories.length === 0) {
|
||||
return (
|
||||
<div className="flex h-full items-center">
|
||||
<Loading type="area" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='h-full flex flex-col'>
|
||||
<div className='flex justify-between items-center py-3 border-b border-divider-burn'>
|
||||
<div className='min-w-[180px] pl-5'>
|
||||
<span className='title-xl-semi-bold text-text-primary'>{t('app.newApp.startFromTemplate')}</span>
|
||||
</div>
|
||||
<div className='flex-1 max-w-[548px] p-1.5 flex items-center rounded-xl shadow-md bg-components-panel-bg-blur border border-components-panel-border'>
|
||||
<AppTypeSelector value={currentType} onChange={setCurrentType} />
|
||||
<div className='h-[14px]'>
|
||||
<Divider type='vertical' />
|
||||
</div>
|
||||
<Input
|
||||
showClearIcon
|
||||
wrapperClassName='w-full flex-1'
|
||||
className='bg-transparent hover:bg-transparent focus:bg-transparent hover:border-transparent focus:border-transparent focus:shadow-none'
|
||||
placeholder={t('app.newAppFromTemplate.searchAllTemplate') as string}
|
||||
value={keywords}
|
||||
onChange={e => handleKeywordsChange(e.target.value)}
|
||||
onClear={() => handleKeywordsChange('')}
|
||||
/>
|
||||
</div>
|
||||
<div className='w-[180px] h-8'></div>
|
||||
</div>
|
||||
<div className='relative flex flex-1 overflow-y-auto'>
|
||||
{!searchKeywords && <div className='w-[200px] h-full p-4'>
|
||||
<Sidebar current={currCategory as AppCategories} onClick={(category) => { setCurrCategory(category) }} onCreateFromBlank={onCreateFromBlank} />
|
||||
</div>}
|
||||
<div className='flex-1 h-full overflow-auto shrink-0 grow p-6 pt-2 border-l border-divider-burn'>
|
||||
{searchFilteredList && searchFilteredList.length > 0 && <>
|
||||
<div className='pt-4 pb-1'>
|
||||
{searchKeywords
|
||||
? <p className='title-md-semi-bold text-text-tertiary'>{searchFilteredList.length > 1 ? t('app.newApp.foundResults', { count: searchFilteredList.length }) : t('app.newApp.foundResult', { count: searchFilteredList.length })}</p>
|
||||
: <AppCategoryLabel category={currCategory as AppCategories} className='title-md-semi-bold text-text-primary' />}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'grid content-start shrink-0 gap-3 grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6',
|
||||
)}>
|
||||
{searchFilteredList.map(app => (
|
||||
<AppCard
|
||||
key={app.app_id}
|
||||
app={app}
|
||||
canCreate={hasEditPermission}
|
||||
onCreate={() => {
|
||||
setCurrApp(app)
|
||||
setIsShowCreateModal(true)
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>}
|
||||
{(!searchFilteredList || searchFilteredList.length === 0) && <NoTemplateFound />}
|
||||
</div>
|
||||
</div>
|
||||
{isShowCreateModal && (
|
||||
<CreateAppModal
|
||||
appIconType={currApp?.app.icon_type || 'emoji'}
|
||||
appIcon={currApp?.app.icon || ''}
|
||||
appIconBackground={currApp?.app.icon_background || ''}
|
||||
appIconUrl={currApp?.app.icon_url}
|
||||
appName={currApp?.app.name || ''}
|
||||
appDescription={currApp?.app.description || ''}
|
||||
show={isShowCreateModal}
|
||||
onConfirm={onCreate}
|
||||
onHide={() => setIsShowCreateModal(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Apps)
|
||||
|
||||
function NoTemplateFound() {
|
||||
const { t } = useTranslation()
|
||||
return <div className='p-4 rounded-lg w-full bg-workflow-process-bg'>
|
||||
<div className='w-8 h-8 rounded-lg inline-flex items-center justify-center mb-2 shadow-lg bg-components-card-bg'>
|
||||
<RiRobot2Line className='w-5 h-5 text-text-tertiary' />
|
||||
</div>
|
||||
<p className='title-md-semi-bold text-text-primary'>{t('app.newApp.noTemplateFound')}</p>
|
||||
<p className='system-sm-regular text-text-tertiary'>{t('app.newApp.noTemplateFoundTip')}</p>
|
||||
</div>
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
'use client'
|
||||
import { RiAppsFill, RiChatSmileAiFill, RiExchange2Fill, RiPassPendingFill, RiQuillPenAiFill, RiSpeakAiFill, RiStickyNoteAddLine, RiTerminalBoxFill, RiThumbUpFill } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import classNames from '@/utils/classnames'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
|
||||
export enum AppCategories {
|
||||
RECOMMENDED = 'Recommended',
|
||||
ASSISTANT = 'Assistant',
|
||||
AGENT = 'Agent',
|
||||
HR = 'HR',
|
||||
PROGRAMMING = 'Programming',
|
||||
WORKFLOW = 'Workflow',
|
||||
WRITING = 'Writing',
|
||||
}
|
||||
|
||||
type SidebarProps = {
|
||||
current: AppCategories
|
||||
onClick?: (category: AppCategories) => void
|
||||
onCreateFromBlank?: () => void
|
||||
}
|
||||
|
||||
export default function Sidebar({ current, onClick, onCreateFromBlank }: SidebarProps) {
|
||||
const { t } = useTranslation()
|
||||
return <div className="w-full h-full flex flex-col">
|
||||
<ul>
|
||||
<CategoryItem category={AppCategories.RECOMMENDED} active={current === AppCategories.RECOMMENDED} onClick={onClick} />
|
||||
</ul>
|
||||
<div className='px-3 pt-2 pb-1 system-xs-medium-uppercase text-text-tertiary'>{t('app.newAppFromTemplate.byCategories')}</div>
|
||||
<ul className='flex-grow flex flex-col gap-0.5'>
|
||||
<CategoryItem category={AppCategories.ASSISTANT} active={current === AppCategories.ASSISTANT} onClick={onClick} />
|
||||
<CategoryItem category={AppCategories.AGENT} active={current === AppCategories.AGENT} onClick={onClick} />
|
||||
<CategoryItem category={AppCategories.HR} active={current === AppCategories.HR} onClick={onClick} />
|
||||
<CategoryItem category={AppCategories.PROGRAMMING} active={current === AppCategories.PROGRAMMING} onClick={onClick} />
|
||||
<CategoryItem category={AppCategories.WORKFLOW} active={current === AppCategories.WORKFLOW} onClick={onClick} />
|
||||
<CategoryItem category={AppCategories.WRITING} active={current === AppCategories.WRITING} onClick={onClick} />
|
||||
</ul>
|
||||
<Divider bgStyle='gradient' />
|
||||
<div className='px-3 py-1 flex items-center gap-1 text-text-tertiary cursor-pointer' onClick={onCreateFromBlank}>
|
||||
<RiStickyNoteAddLine className='w-3.5 h-3.5' />
|
||||
<span className='system-xs-regular'>{t('app.newApp.startFromBlank')}</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
type CategoryItemProps = {
|
||||
active: boolean
|
||||
category: AppCategories
|
||||
onClick?: (category: AppCategories) => void
|
||||
}
|
||||
function CategoryItem({ category, active, onClick }: CategoryItemProps) {
|
||||
return <li
|
||||
className={classNames('p-1 pl-3 rounded-lg flex items-center gap-2 group cursor-pointer hover:bg-state-base-hover [&.active]:bg-state-base-active', active && 'active')}
|
||||
onClick={() => { onClick?.(category) }}>
|
||||
<div className='w-5 h-5 inline-flex items-center justify-center rounded-md border border-divider-regular bg-components-icon-bg-midnight-solid group-[.active]:bg-components-icon-bg-blue-solid'>
|
||||
<AppCategoryIcon category={category} />
|
||||
</div>
|
||||
<AppCategoryLabel category={category}
|
||||
className={classNames('system-sm-medium text-components-menu-item-text group-[.active]:text-components-menu-item-text-active group-hover:text-components-menu-item-text-hover', active && 'system-sm-semibold')} />
|
||||
</li >
|
||||
}
|
||||
|
||||
type AppCategoryLabelProps = {
|
||||
category: AppCategories
|
||||
className?: string
|
||||
}
|
||||
export function AppCategoryLabel({ category, className }: AppCategoryLabelProps) {
|
||||
const { t } = useTranslation()
|
||||
return <span className={className}>{t(`app.newAppFromTemplate.sidebar.${category}`)}</span>
|
||||
}
|
||||
|
||||
type AppCategoryIconProps = {
|
||||
category: AppCategories
|
||||
}
|
||||
function AppCategoryIcon({ category }: AppCategoryIconProps) {
|
||||
if (category === AppCategories.AGENT)
|
||||
return <RiSpeakAiFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
|
||||
if (category === AppCategories.ASSISTANT)
|
||||
return <RiChatSmileAiFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
|
||||
if (category === AppCategories.HR)
|
||||
return <RiPassPendingFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
|
||||
if (category === AppCategories.PROGRAMMING)
|
||||
return <RiTerminalBoxFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
|
||||
if (category === AppCategories.RECOMMENDED)
|
||||
return <RiThumbUpFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
|
||||
if (category === AppCategories.WRITING)
|
||||
return <RiQuillPenAiFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
|
||||
if (category === AppCategories.WORKFLOW)
|
||||
return <RiExchange2Fill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
|
||||
return <RiAppsFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
|
||||
}
|
|
@ -1,36 +1,26 @@
|
|||
'use client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import NewAppDialog from './newAppDialog'
|
||||
import AppList, { PageType } from '@/app/components/explore/app-list'
|
||||
import AppList from './app-list'
|
||||
import FullScreenModal from '@/app/components/base/fullscreen-modal'
|
||||
|
||||
type CreateAppDialogProps = {
|
||||
show: boolean
|
||||
onSuccess: () => void
|
||||
onClose: () => void
|
||||
onCreateFromBlank?: () => void
|
||||
}
|
||||
|
||||
const CreateAppTemplateDialog = ({ show, onSuccess, onClose }: CreateAppDialogProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const CreateAppTemplateDialog = ({ show, onSuccess, onClose, onCreateFromBlank }: CreateAppDialogProps) => {
|
||||
return (
|
||||
<NewAppDialog
|
||||
className='flex'
|
||||
show={show}
|
||||
onClose={() => {}}
|
||||
<FullScreenModal
|
||||
open={show}
|
||||
closable
|
||||
onClose={onClose}
|
||||
>
|
||||
{/* template list */}
|
||||
<div className='grow flex flex-col h-full bg-gray-100'>
|
||||
<div className='shrink-0 pl-8 pr-6 pt-6 pb-3 bg-gray-100 rounded-se-xl text-xl leading-[30px] font-semibold text-gray-900 z-10'>{t('app.newApp.startFromTemplate')}</div>
|
||||
<AppList onSuccess={() => {
|
||||
onSuccess()
|
||||
onClose()
|
||||
}} pageType={PageType.CREATE} />
|
||||
</div>
|
||||
<div className='absolute right-6 top-6 p-2 cursor-pointer z-20' onClick={onClose}>
|
||||
<RiCloseLine className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</NewAppDialog>
|
||||
<AppList onCreateFromBlank={onCreateFromBlank} onSuccess={() => {
|
||||
onSuccess()
|
||||
onClose()
|
||||
}} />
|
||||
</FullScreenModal>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Before Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 6.0 KiB |
|
@ -1,16 +0,0 @@
|
|||
<svg width="125" height="74" viewBox="0 0 125 74" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Grid BG">
|
||||
<mask id="mask0_3169_30051" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="125" height="74">
|
||||
<rect id="Mask" width="125" height="74" fill="url(#paint0_linear_3169_30051)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_3169_30051)">
|
||||
<path id="Grid" d="M7 -3H-1V5M7 -3V5M7 -3H15M7 5H-1M7 5H15M7 5V13M-1 5V13M15 -3V5M15 -3H23M15 5H23M15 5V13M23 -3V5M23 -3H31M23 5H31M23 5V13M31 -3V5M31 -3H39M31 5H39M31 5V13M39 -3V5M39 -3H47M39 5H47M39 5V13M47 -3V5M47 -3H55M47 5H55M47 5V13M55 -3V5M55 -3H63M55 5H63M55 5V13M63 -3V5M63 -3H71M63 5H71M63 5V13M71 -3V5M71 -3H79M71 5H79M71 5V13M79 -3V5M79 -3H87M79 5H87M79 5V13M87 -3V5M87 -3H95M87 5H95M87 5V13M95 -3V5M95 -3H103M95 5H103M95 5V13M103 -3V5M103 -3H111M103 5H111M103 5V13M111 -3V5M111 -3H119M111 5H119M111 5V13M119 -3V5M119 -3H127V5M119 5H127M119 5V13M127 5V13M7 13H-1M7 13H15M7 13V21M-1 13V21M15 13H23M15 13V21M23 13H31M23 13V21M31 13H39M31 13V21M39 13H47M39 13V21M47 13H55M47 13V21M55 13H63M55 13V21M63 13H71M63 13V21M71 13H79M71 13V21M79 13H87M79 13V21M87 13H95M87 13V21M95 13H103M95 13V21M103 13H111M103 13V21M111 13H119M111 13V21M119 13H127M119 13V21M127 13V21M7 21H-1M7 21H15M7 21V29M-1 21V29M15 21H23M15 21V29M23 21H31M23 21V29M31 21H39M31 21V29M39 21H47M39 21V29M47 21H55M47 21V29M55 21H63M55 21V29M63 21H71M63 21V29M71 21H79M71 21V29M79 21H87M79 21V29M87 21H95M87 21V29M95 21H103M95 21V29M103 21H111M103 21V29M111 21H119M111 21V29M119 21H127M119 21V29M127 21V29M7 29H-1M7 29H15M7 29V37M-1 29V37M15 29H23M15 29V37M23 29H31M23 29V37M31 29H39M31 29V37M39 29H47M39 29V37M47 29H55M47 29V37M55 29H63M55 29V37M63 29H71M63 29V37M71 29H79M71 29V37M79 29H87M79 29V37M87 29H95M87 29V37M95 29H103M95 29V37M103 29H111M103 29V37M111 29H119M111 29V37M119 29H127M119 29V37M127 29V37M7 37H-1M7 37H15M7 37V45M-1 37V45M15 37H23M15 37V45M23 37H31M23 37V45M31 37H39M31 37V45M39 37H47M39 37V45M47 37H55M47 37V45M55 37H63M55 37V45M63 37H71M63 37V45M71 37H79M71 37V45M79 37H87M79 37V45M87 37H95M87 37V45M95 37H103M95 37V45M103 37H111M103 37V45M111 37H119M111 37V45M119 37H127M119 37V45M127 37V45M7 45H-1M7 45H15M7 45V53M-1 45V53M15 45H23M15 45V53M23 45H31M23 45V53M31 45H39M31 45V53M39 45H47M39 45V53M47 45H55M47 45V53M55 45H63M55 45V53M63 45H71M63 45V53M71 45H79M71 45V53M79 45H87M79 45V53M87 45H95M87 45V53M95 45H103M95 45V53M103 45H111M103 45V53M111 45H119M111 45V53M119 45H127M119 45V53M127 45V53M7 53H-1M7 53H15M7 53V61M-1 53V61M15 53H23M15 53V61M23 53H31M23 53V61M31 53H39M31 53V61M39 53H47M39 53V61M47 53H55M47 53V61M55 53H63M55 53V61M63 53H71M63 53V61M71 53H79M71 53V61M79 53H87M79 53V61M87 53H95M87 53V61M95 53H103M95 53V61M103 53H111M103 53V61M111 53H119M111 53V61M119 53H127M119 53V61M127 53V61M7 61H-1M7 61H15M7 61V69M-1 61V69M15 61H23M15 61V69M23 61H31M23 61V69M31 61H39M31 61V69M39 61H47M39 61V69M47 61H55M47 61V69M55 61H63M55 61V69M63 61H71M63 61V69M71 61H79M71 61V69M79 61H87M79 61V69M87 61H95M87 61V69M95 61H103M95 61V69M103 61H111M103 61V69M111 61H119M111 61V69M119 61H127M119 61V69M127 61V69M7 69H-1M7 69H15M7 69V77M-1 69V77H7M15 69H23M15 69V77M23 69H31M23 69V77M31 69H39M31 69V77M39 69H47M39 69V77M47 69H55M47 69V77M55 69H63M55 69V77M63 69H71M63 69V77M71 69H79M71 69V77M79 69H87M79 69V77M87 69H95M87 69V77M95 69H103M95 69V77M103 69H111M103 69V77M111 69H119M111 69V77M119 69H127M119 69V77M127 69V77H119M7 77H15M15 77H23M23 77H31M31 77H39M39 77H47M47 77H55M55 77H63M63 77H71M71 77H79M79 77H87M87 77H95M95 77H103M103 77H111M111 77H119" stroke="#1570EF" stroke-width="0.5"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_3169_30051" x1="62.5" y1="0" x2="62.5" y2="74" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D9D9D9" stop-opacity="0.08"/>
|
||||
<stop offset="1" stop-color="#737373" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 6.0 KiB |
|
@ -1,48 +1,46 @@
|
|||
'use client'
|
||||
import type { MouseEventHandler } from 'react'
|
||||
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiCloseLine,
|
||||
RiQuestionLine,
|
||||
} from '@remixicon/react'
|
||||
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useContext, useContextSelector } from 'use-context-selector'
|
||||
import { RiArrowRightLine, RiCommandLine, RiCornerDownLeftLine, RiExchange2Fill } from '@remixicon/react'
|
||||
import Link from 'next/link'
|
||||
import { useDebounceFn, useKeyPress } from 'ahooks'
|
||||
import Image from 'next/image'
|
||||
import AppIconPicker from '../../base/app-icon-picker'
|
||||
import type { AppIconSelection } from '../../base/app-icon-picker'
|
||||
import s from './style.module.css'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import cn from '@/utils/classnames'
|
||||
import AppsContext, { useAppContext } from '@/context/app-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import type { AppMode } from '@/types/app'
|
||||
import { createApp } from '@/service/apps'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Textarea from '@/app/components/base/textarea'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
|
||||
import { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||
import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { BubbleTextMod, ChatBot, ListSparkle, Logic } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import FullScreenModal from '@/app/components/base/fullscreen-modal'
|
||||
|
||||
type CreateAppDialogProps = {
|
||||
show: boolean
|
||||
type CreateAppProps = {
|
||||
onSuccess: () => void
|
||||
onClose: () => void
|
||||
onCreateFromTemplate?: () => void
|
||||
}
|
||||
|
||||
const CreateAppModal = ({ show, onSuccess, onClose }: CreateAppDialogProps) => {
|
||||
function CreateApp({ onClose, onSuccess, onCreateFromTemplate }: CreateAppProps) {
|
||||
const { t } = useTranslation()
|
||||
const { push } = useRouter()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const mutateApps = useContextSelector(AppsContext, state => state.mutateApps)
|
||||
|
||||
const [appMode, setAppMode] = useState<AppMode>('chat')
|
||||
const [showChatBotType, setShowChatBotType] = useState<boolean>(true)
|
||||
const [appIcon, setAppIcon] = useState<AppIconSelection>({ type: 'emoji', icon: '🤖', background: '#FFEAD5' })
|
||||
const [showAppIconPicker, setShowAppIconPicker] = useState(false)
|
||||
const [name, setName] = useState('')
|
||||
|
@ -53,7 +51,8 @@ const CreateAppModal = ({ show, onSuccess, onClose }: CreateAppDialogProps) => {
|
|||
const { isCurrentWorkspaceEditor } = useAppContext()
|
||||
|
||||
const isCreatingRef = useRef(false)
|
||||
const onCreate: MouseEventHandler = useCallback(async () => {
|
||||
|
||||
const onCreate = useCallback(async () => {
|
||||
if (!appMode) {
|
||||
notify({ type: 'error', message: t('app.newApp.appTypeRequired') })
|
||||
return
|
||||
|
@ -87,237 +86,281 @@ const CreateAppModal = ({ show, onSuccess, onClose }: CreateAppDialogProps) => {
|
|||
isCreatingRef.current = false
|
||||
}, [name, notify, t, appMode, appIcon, description, onSuccess, onClose, mutateApps, push, isCurrentWorkspaceEditor])
|
||||
|
||||
return (
|
||||
<Modal
|
||||
overflowVisible
|
||||
className='!p-0 !max-w-[720px] !w-[720px] rounded-xl'
|
||||
isShow={show}
|
||||
onClose={() => { }}
|
||||
>
|
||||
{/* Heading */}
|
||||
<div className='shrink-0 flex flex-col h-full bg-white rounded-t-xl'>
|
||||
<div className='shrink-0 pl-8 pr-6 pt-6 pb-3 bg-white text-xl rounded-t-xl leading-[30px] font-semibold text-gray-900 z-10'>{t('app.newApp.startFromBlank')}</div>
|
||||
</div>
|
||||
{/* app type */}
|
||||
<div className='py-2 px-8'>
|
||||
<div className='py-2 text-sm leading-[20px] font-medium text-gray-900'>{t('app.newApp.captionAppType')}</div>
|
||||
<div className='flex'>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='max-w-[280px] leading-[18px] text-xs text-gray-700'>{t('app.newApp.chatbotDescription')}</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'relative grow box-border w-[158px] mr-2 px-0.5 pt-3 pb-2 flex flex-col items-center justify-center gap-1 rounded-lg border border-gray-100 bg-white text-gray-700 cursor-pointer shadow-xs hover:border-gray-300',
|
||||
showChatBotType && 'border-[1.5px] border-primary-400 hover:border-[1.5px] hover:border-primary-400',
|
||||
s['grid-bg-chat'],
|
||||
)}
|
||||
onClick={() => {
|
||||
setAppMode('chat')
|
||||
setShowChatBotType(true)
|
||||
}}
|
||||
>
|
||||
<ChatBot className='w-6 h-6 text-[#1570EF]' />
|
||||
<div className='h-5 text-[13px] font-medium leading-[18px]'>{t('app.types.chatbot')}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='flex flex-col max-w-[320px] leading-[18px] text-xs'>
|
||||
<div className='text-gray-700'>{t('app.newApp.completionDescription')}</div>
|
||||
const { run: handleCreateApp } = useDebounceFn(onCreate, { wait: 300 })
|
||||
useKeyPress(['meta.enter', 'ctrl.enter'], () => {
|
||||
if (isAppsFull)
|
||||
return
|
||||
handleCreateApp()
|
||||
})
|
||||
return <>
|
||||
<div className='flex justify-center h-full overflow-y-auto overflow-x-hidden'>
|
||||
<div className='flex-1 shrink-0 flex justify-end'>
|
||||
<div className='px-10'>
|
||||
<div className='w-full h-6 2xl:h-[139px]' />
|
||||
<div className='pt-1 pb-6'>
|
||||
<span className='title-2xl-semi-bold text-text-primary'>{t('app.newApp.startFromBlank')}</span>
|
||||
</div>
|
||||
<div className='leading-6 mb-2'>
|
||||
<span className='system-sm-semibold text-text-secondary'>{t('app.newApp.chooseAppType')}</span>
|
||||
</div>
|
||||
<div className='flex flex-col w-[660px] gap-4'>
|
||||
<div>
|
||||
<div className='mb-2'>
|
||||
<span className='system-2xs-medium-uppercase text-text-tertiary'>{t('app.newApp.forBeginners')}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'relative grow box-border w-[158px] mr-2 px-0.5 pt-3 pb-2 flex flex-col items-center justify-center gap-1 rounded-lg border border-gray-100 text-gray-700 cursor-pointer bg-white shadow-xs hover:border-gray-300',
|
||||
s['grid-bg-completion'],
|
||||
appMode === 'completion' && 'border-[1.5px] border-primary-400 hover:border-[1.5px] hover:border-primary-400',
|
||||
)}
|
||||
onClick={() => {
|
||||
setAppMode('completion')
|
||||
setShowChatBotType(false)
|
||||
}}
|
||||
>
|
||||
<AiText className='w-6 h-6 text-[#0E9384]' />
|
||||
<div className='h-5 text-[13px] font-medium leading-[18px]'>{t('app.newApp.completeApp')}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='max-w-[280px] leading-[18px] text-xs text-gray-700'>{t('app.newApp.agentDescription')}</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'relative grow box-border w-[158px] mr-2 px-0.5 pt-3 pb-2 flex flex-col items-center justify-center gap-1 rounded-lg border border-gray-100 text-gray-700 cursor-pointer bg-white shadow-xs hover:border-gray-300',
|
||||
s['grid-bg-agent-chat'],
|
||||
appMode === 'agent-chat' && 'border-[1.5px] border-primary-400 hover:border-[1.5px] hover:border-primary-400',
|
||||
)}
|
||||
onClick={() => {
|
||||
setAppMode('agent-chat')
|
||||
setShowChatBotType(false)
|
||||
}}
|
||||
>
|
||||
<CuteRobot className='w-6 h-6 text-indigo-600' />
|
||||
<div className='h-5 text-[13px] font-medium leading-[18px]'>{t('app.types.agent')}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='flex flex-col max-w-[320px] leading-[18px] text-xs'>
|
||||
<div className='text-gray-700'>{t('app.newApp.workflowDescription')}</div>
|
||||
<div className='flex flex-row gap-2'>
|
||||
<AppTypeCard
|
||||
active={appMode === 'chat'}
|
||||
title={t('app.types.chatbot')}
|
||||
description={t('app.newApp.chatbotShortDescription')}
|
||||
icon={<div className='w-6 h-6 bg-components-icon-bg-blue-solid rounded-md flex items-center justify-center'>
|
||||
<ChatBot className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
|
||||
</div>}
|
||||
onClick={() => {
|
||||
setAppMode('chat')
|
||||
}} />
|
||||
<AppTypeCard
|
||||
active={appMode === 'agent-chat'}
|
||||
title={t('app.types.agent')}
|
||||
description={t('app.newApp.agentShortDescription')}
|
||||
icon={<div className='w-6 h-6 bg-components-icon-bg-violet-solid rounded-md flex items-center justify-center'>
|
||||
<Logic className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
|
||||
</div>}
|
||||
onClick={() => {
|
||||
setAppMode('agent-chat')
|
||||
}} />
|
||||
<AppTypeCard
|
||||
active={appMode === 'completion'}
|
||||
title={t('app.newApp.completeApp')}
|
||||
description={t('app.newApp.completionShortDescription')}
|
||||
icon={<div className='w-6 h-6 bg-components-icon-bg-teal-solid rounded-md flex items-center justify-center'>
|
||||
<ListSparkle className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
|
||||
</div>}
|
||||
onClick={() => {
|
||||
setAppMode('completion')
|
||||
}} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'relative grow box-border w-[158px] px-0.5 pt-3 pb-2 flex flex-col items-center justify-center gap-1 rounded-lg border border-gray-100 text-gray-700 cursor-pointer bg-white shadow-xs hover:border-gray-300',
|
||||
s['grid-bg-workflow'],
|
||||
appMode === 'workflow' && 'border-[1.5px] border-primary-400 hover:border-[1.5px] hover:border-primary-400',
|
||||
)}
|
||||
onClick={() => {
|
||||
setAppMode('workflow')
|
||||
setShowChatBotType(false)
|
||||
}}
|
||||
>
|
||||
<Route className='w-6 h-6 text-[#f79009]' />
|
||||
<div className='h-5 text-[13px] font-medium leading-[18px]'>{t('app.types.workflow')}</div>
|
||||
<span className='absolute top-[-3px] right-[-3px] px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
{showChatBotType && (
|
||||
<div className='py-2 px-8'>
|
||||
<div className='py-2 text-sm leading-[20px] font-medium text-gray-900'>{t('app.newApp.chatbotType')}</div>
|
||||
<div className='flex gap-2'>
|
||||
<div
|
||||
className={cn(
|
||||
'relative grow flex-[50%] pl-4 py-[10px] pr-[10px] rounded-lg border border-gray-100 bg-gray-25 text-gray-700 cursor-pointer hover:bg-white hover:shadow-xs hover:border-gray-300',
|
||||
appMode === 'chat' && 'bg-white shadow-xs border-[1.5px] border-primary-400 hover:border-[1.5px] hover:border-primary-400',
|
||||
)}
|
||||
onClick={() => {
|
||||
setAppMode('chat')
|
||||
}}
|
||||
>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='h-5 text-sm font-medium leading-5'>{t('app.newApp.basic')}</div>
|
||||
<div className='group'>
|
||||
<RiQuestionLine className='w-[14px] h-[14px] text-gray-400 hover:text-gray-500' />
|
||||
<div
|
||||
className={cn(
|
||||
'hidden z-20 absolute left-[327px] top-[-158px] w-[376px] rounded-xl bg-white border-[0.5px] border-[rgba(0,0,0,0.05)] shadow-lg group-hover:block',
|
||||
)}
|
||||
>
|
||||
<div className={cn('w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl', s.basicPic)} />
|
||||
<div className='px-4 pb-2'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='text-gray-700 text-md leading-6 font-semibold'>{t('app.newApp.basic')}</div>
|
||||
<div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.basicFor')}</div>
|
||||
</div>
|
||||
<div className='mt-1 text-gray-500 text-sm leading-5'>{t('app.newApp.basicDescription')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className='mb-2'>
|
||||
<span className='system-2xs-medium-uppercase text-text-tertiary'>{t('app.newApp.forAdvanced')}</span>
|
||||
</div>
|
||||
<div className='flex flex-row gap-2'>
|
||||
<AppTypeCard
|
||||
beta
|
||||
active={appMode === 'advanced-chat'}
|
||||
title={t('app.types.advanced')}
|
||||
description={t('app.newApp.advancedShortDescription')}
|
||||
icon={<div className='w-6 h-6 bg-components-icon-bg-blue-light-solid rounded-md flex items-center justify-center'>
|
||||
<BubbleTextMod className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
|
||||
</div>}
|
||||
onClick={() => {
|
||||
setAppMode('advanced-chat')
|
||||
}} />
|
||||
<AppTypeCard
|
||||
beta
|
||||
active={appMode === 'workflow'}
|
||||
title={t('app.types.workflow')}
|
||||
description={t('app.newApp.workflowShortDescription')}
|
||||
icon={<div className='w-6 h-6 bg-components-icon-bg-indigo-solid rounded-md flex items-center justify-center'>
|
||||
<RiExchange2Fill className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
|
||||
</div>}
|
||||
onClick={() => {
|
||||
setAppMode('workflow')
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<div className='flex space-x-3 items-center'>
|
||||
<div className='flex-1'>
|
||||
<div className='h-6 flex items-center mb-1'>
|
||||
<label className='system-sm-semibold text-text-secondary'>{t('app.newApp.captionName')}</label>
|
||||
</div>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
placeholder={t('app.newApp.appNamePlaceholder') || ''}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-[2px] text-gray-500 text-xs leading-[18px]'>{t('app.newApp.basicTip')}</div>
|
||||
<AppIcon
|
||||
iconType={appIcon.type}
|
||||
icon={appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId}
|
||||
background={appIcon.type === 'emoji' ? appIcon.background : undefined}
|
||||
imageUrl={appIcon.type === 'image' ? appIcon.url : undefined}
|
||||
size='xxl' className='cursor-pointer rounded-2xl'
|
||||
onClick={() => { setShowAppIconPicker(true) }}
|
||||
/>
|
||||
{showAppIconPicker && <AppIconPicker
|
||||
onSelect={(payload) => {
|
||||
setAppIcon(payload)
|
||||
setShowAppIconPicker(false)
|
||||
}}
|
||||
onClose={() => {
|
||||
setShowAppIconPicker(false)
|
||||
}}
|
||||
/>}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'relative grow flex-[50%] pl-3 py-2 pr-2 rounded-lg border border-gray-100 bg-gray-25 text-gray-700 cursor-pointer hover:bg-white hover:shadow-xs hover:border-gray-300',
|
||||
appMode === 'advanced-chat' && 'bg-white shadow-xs border-[1.5px] border-primary-400 hover:border-[1.5px] hover:border-primary-400',
|
||||
)}
|
||||
onClick={() => {
|
||||
setAppMode('advanced-chat')
|
||||
}}
|
||||
>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex items-center'>
|
||||
<div className='mr-1 h-5 text-sm font-medium leading-5'>{t('app.newApp.advanced')}</div>
|
||||
<span className='px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span>
|
||||
</div>
|
||||
<div className='group'>
|
||||
<RiQuestionLine className='w-[14px] h-[14px] text-gray-400 hover:text-gray-500' />
|
||||
<div
|
||||
className={cn(
|
||||
'hidden z-20 absolute right-[26px] top-[-158px] w-[376px] rounded-xl bg-white border-[0.5px] border-[rgba(0,0,0,0.05)] shadow-lg group-hover:block',
|
||||
)}
|
||||
>
|
||||
<div className={cn('w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl', s.advancedPic)} />
|
||||
<div className='px-4 pb-2'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex items-center'>
|
||||
<div className='mr-1 text-gray-700 text-md leading-6 font-semibold'>{t('app.newApp.advanced')}</div>
|
||||
<span className='px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span>
|
||||
</div>
|
||||
<div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.advancedFor').toLocaleUpperCase()}</div>
|
||||
</div>
|
||||
<div className='mt-1 text-gray-500 text-sm leading-5'>{t('app.newApp.advancedDescription')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className='h-6 flex items-center mb-1'>
|
||||
<label className='system-sm-semibold text-text-secondary'>{t('app.newApp.captionDescription')}</label>
|
||||
<span className='system-xs-regular text-text-tertiary ml-1'>({t('app.newApp.optional')})</span>
|
||||
</div>
|
||||
<div className='mt-[2px] text-gray-500 text-xs leading-[18px]'>{t('app.newApp.advancedFor')}</div>
|
||||
<Textarea
|
||||
className='resize-none'
|
||||
placeholder={t('app.newApp.appDescriptionPlaceholder') || ''}
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='pt-5 pb-10 flex justify-between items-center'>
|
||||
<div className='flex gap-1 items-center system-xs-regular text-text-tertiary cursor-pointer' onClick={onCreateFromTemplate}>
|
||||
<span>{t('app.newApp.noIdeaTip')}</span>
|
||||
<div className='p-[1px]'>
|
||||
<RiArrowRightLine className='w-3.5 h-3.5' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<Button onClick={onClose}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button disabled={isAppsFull || !name} className='gap-1' variant="primary" onClick={handleCreateApp}>
|
||||
<span>{t('app.newApp.Create')}</span>
|
||||
<div className='flex gap-0.5'>
|
||||
<RiCommandLine size={14} className='p-0.5 system-kbd bg-components-kbd-bg-white rounded-sm' />
|
||||
<RiCornerDownLeftLine size={14} className='p-0.5 system-kbd bg-components-kbd-bg-white rounded-sm' />
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* icon & name */}
|
||||
<div className='pt-2 px-8'>
|
||||
<div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>{t('app.newApp.captionName')}</div>
|
||||
<div className='flex items-center justify-between space-x-2'>
|
||||
<AppIcon
|
||||
iconType={appIcon.type}
|
||||
icon={appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId}
|
||||
background={appIcon.type === 'emoji' ? appIcon.background : undefined}
|
||||
imageUrl={appIcon.type === 'image' ? appIcon.url : undefined}
|
||||
size='large' className='cursor-pointer'
|
||||
onClick={() => { setShowAppIconPicker(true) }}
|
||||
/>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
placeholder={t('app.newApp.appNamePlaceholder') || ''}
|
||||
className='grow h-10'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex-1 shrink h-full flex justify-start relative overflow-hidden'>
|
||||
<div className='h-6 2xl:h-[139px] absolute left-0 top-0 right-0 border-b border-b-divider-subtle'></div>
|
||||
<div className='max-w-[760px] border-x border-x-divider-subtle'>
|
||||
<div className='h-6 2xl:h-[139px]' />
|
||||
<AppPreview mode={appMode} />
|
||||
<div className='absolute left-0 right-0 border-b border-b-divider-subtle'></div>
|
||||
<div className='w-[664px] h-[448px] flex items-center justify-center' style={{ background: 'repeating-linear-gradient(135deg, transparent, transparent 2px, rgba(16,24,40,0.04) 4px,transparent 3px, transparent 6px)' }}>
|
||||
<AppScreenShot show={appMode === 'chat'} mode='chat' />
|
||||
<AppScreenShot show={appMode === 'advanced-chat'} mode='advanced-chat' />
|
||||
<AppScreenShot show={appMode === 'agent-chat'} mode='agent-chat' />
|
||||
<AppScreenShot show={appMode === 'completion'} mode='completion' />
|
||||
<AppScreenShot show={appMode === 'workflow'} mode='workflow' />
|
||||
</div>
|
||||
<div className='absolute left-0 right-0 border-b border-b-divider-subtle'></div>
|
||||
</div>
|
||||
{showAppIconPicker && <AppIconPicker
|
||||
onSelect={(payload) => {
|
||||
setAppIcon(payload)
|
||||
setShowAppIconPicker(false)
|
||||
}}
|
||||
onClose={() => {
|
||||
setShowAppIconPicker(false)
|
||||
}}
|
||||
/>}
|
||||
</div>
|
||||
{/* description */}
|
||||
<div className='pt-2 px-8'>
|
||||
<div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>{t('app.newApp.captionDescription')}</div>
|
||||
<Textarea
|
||||
className='resize-none'
|
||||
placeholder={t('app.newApp.appDescriptionPlaceholder') || ''}
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{isAppsFull && (
|
||||
</div>
|
||||
{
|
||||
isAppsFull && (
|
||||
<div className='px-8 py-2'>
|
||||
<AppsFull loc='app-create' />
|
||||
</div>
|
||||
)}
|
||||
<div className='px-8 py-6 flex justify-end'>
|
||||
<Button className='mr-2' onClick={onClose}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button disabled={isAppsFull || !name} variant="primary" onClick={onCreate}>{t('app.newApp.Create')}</Button>
|
||||
</div>
|
||||
<div className='absolute right-6 top-6 p-2 cursor-pointer z-20' onClick={onClose}>
|
||||
<RiCloseLine className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
</>
|
||||
}
|
||||
type CreateAppDialogProps = CreateAppProps & {
|
||||
show: boolean
|
||||
}
|
||||
const CreateAppModal = ({ show, onClose, onSuccess, onCreateFromTemplate }: CreateAppDialogProps) => {
|
||||
return (
|
||||
<FullScreenModal
|
||||
overflowVisible
|
||||
closable
|
||||
open={show}
|
||||
onClose={onClose}
|
||||
>
|
||||
<CreateApp onClose={onClose} onSuccess={onSuccess} onCreateFromTemplate={onCreateFromTemplate} />
|
||||
</FullScreenModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateAppModal
|
||||
|
||||
type AppTypeCardProps = {
|
||||
icon: JSX.Element
|
||||
beta?: boolean
|
||||
title: string
|
||||
description: string
|
||||
active: boolean
|
||||
onClick: () => void
|
||||
}
|
||||
function AppTypeCard({ icon, title, beta = false, description, active, onClick }: AppTypeCardProps) {
|
||||
const { t } = useTranslation()
|
||||
return <div
|
||||
className={
|
||||
cn(`w-[191px] h-[84px] p-3 border-[0.5px] relative box-content
|
||||
rounded-xl border-components-option-card-option-border
|
||||
bg-components-panel-on-panel-item-bg shadow-xs cursor-pointer hover:shadow-md`, active
|
||||
? 'outline outline-[1.5px] outline-components-option-card-option-selected-border shadow-md'
|
||||
: '')
|
||||
}
|
||||
onClick={onClick}
|
||||
>
|
||||
{beta && <div className='px-[5px] py-[3px]
|
||||
rounded-[5px] min-w-[18px] absolute top-3 right-3
|
||||
border border-divider-deep system-2xs-medium-uppercase text-text-tertiary'>{t('common.menus.status')}</div>}
|
||||
{icon}
|
||||
<div className='system-sm-semibold text-text-secondary mt-2 mb-0.5'>{title}</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>{description}</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
function AppPreview({ mode }: { mode: AppMode }) {
|
||||
const { t } = useTranslation()
|
||||
const modeToPreviewInfoMap = {
|
||||
'chat': {
|
||||
title: t('app.types.chatbot'),
|
||||
description: t('app.newApp.chatbotUserDescription'),
|
||||
link: 'https://docs.dify.ai/guides/application-orchestrate/conversation-application?fallback=true',
|
||||
},
|
||||
'advanced-chat': {
|
||||
title: t('app.types.advanced'),
|
||||
description: t('app.newApp.advancedUserDescription'),
|
||||
link: 'https://docs.dify.ai/guides/workflow',
|
||||
},
|
||||
'agent-chat': {
|
||||
title: t('app.types.agent'),
|
||||
description: t('app.newApp.agentUserDescription'),
|
||||
link: 'https://docs.dify.ai/guides/application-orchestrate/agent',
|
||||
},
|
||||
'completion': {
|
||||
title: t('app.newApp.completeApp'),
|
||||
description: t('app.newApp.completionUserDescription'),
|
||||
link: null,
|
||||
},
|
||||
'workflow': {
|
||||
title: t('app.types.workflow'),
|
||||
description: t('app.newApp.workflowUserDescription'),
|
||||
link: 'https://docs.dify.ai/guides/workflow',
|
||||
},
|
||||
}
|
||||
const previewInfo = modeToPreviewInfoMap[mode]
|
||||
return <div className='px-8 py-4'>
|
||||
<h4 className='system-sm-semibold-uppercase text-text-secondary'>{previewInfo.title}</h4>
|
||||
<div className='mt-1 system-xs-regular text-text-tertiary max-w-96 min-h-8'>
|
||||
<span>{previewInfo.description}</span>
|
||||
{previewInfo.link && <Link target='_blank' href={previewInfo.link} className='text-text-accent ml-1'>{t('app.newApp.learnMore')}</Link>}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
function AppScreenShot({ mode, show }: { mode: AppMode; show: boolean }) {
|
||||
const theme = useContextSelector(AppsContext, state => state.theme)
|
||||
const modeToImageMap = {
|
||||
'chat': 'Chatbot',
|
||||
'advanced-chat': 'Chatflow',
|
||||
'agent-chat': 'Agent',
|
||||
'completion': 'TextGenerator',
|
||||
'workflow': 'Workflow',
|
||||
}
|
||||
return <picture>
|
||||
<source media="(resolution: 1x)" srcSet={`/screenshots/${theme}/${modeToImageMap[mode]}.png`} />
|
||||
<source media="(resolution: 2x)" srcSet={`/screenshots/${theme}/${modeToImageMap[mode]}@2x.png`} />
|
||||
<source media="(resolution: 3x)" srcSet={`/screenshots/${theme}/${modeToImageMap[mode]}@3x.png`} />
|
||||
<Image className={show ? '' : 'hidden'}
|
||||
src={`/screenshots/${theme}/${modeToImageMap[mode]}.png`}
|
||||
alt='App Screen Shot'
|
||||
width={664} height={448} />
|
||||
</picture>
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
.grid-bg-chat {
|
||||
background-image: url('./grid-bg-chat.svg');
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.grid-bg-completion {
|
||||
background-image: url('./grid-bg-completion.svg');
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.grid-bg-agent-chat {
|
||||
background-image: url('./grid-bg-agent-chat.svg');
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.grid-bg-workflow {
|
||||
background-image: url('./grid-bg-workflow.svg');
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.basicPic {
|
||||
background-image: url('./basic.png')
|
||||
}
|
||||
|
||||
.advancedPic {
|
||||
background-image: url('./advanced.png')
|
||||
}
|
|
@ -1,23 +1,23 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import React, { useState } from 'react'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import { RiArrowDownSLine, RiCloseCircleFill, RiExchange2Fill, RiFilter3Line } from '@remixicon/react'
|
||||
import Checkbox from '../../base/checkbox'
|
||||
import cn from '@/utils/classnames'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { Check, DotsGrid } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||
import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
|
||||
import { BubbleTextMod, ChatBot, ListSparkle, Logic } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||
import { type AppMode } from '@/types/app'
|
||||
export type AppSelectorProps = {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
value: Array<AppMode>
|
||||
onChange: (value: AppSelectorProps['value']) => void
|
||||
}
|
||||
|
||||
const allTypes: AppMode[] = ['chat', 'agent-chat', 'completion', 'advanced-chat', 'workflow']
|
||||
|
||||
const AppTypeSelector = ({ value, onChange }: AppSelectorProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
|
@ -33,96 +33,136 @@ const AppTypeSelector = ({ value, onChange }: AppSelectorProps) => {
|
|||
className='block'
|
||||
>
|
||||
<div className={cn(
|
||||
'flex items-center gap-1 h-8 text-gray-700 text-[13px] leading-[18px] cursor-pointer px-2 rounded-lg bg-white shadow-xs hover:bg-gray-200',
|
||||
open && !value && '!bg-gray-200 hover:!bg-gray-200',
|
||||
!!value && '!bg-white hover:!bg-white',
|
||||
'flex items-center justify-between rounded-md cursor-pointer px-2 space-x-1 hover:bg-state-base-hover',
|
||||
)}>
|
||||
{!value && (
|
||||
<>
|
||||
<div className='w-4 h-4 p-[1px]'>
|
||||
<DotsGrid className='w-3.5 h-3.5' />
|
||||
</div>
|
||||
<div className=''>{t('app.typeSelector.all')}</div>
|
||||
<div className='w-4 h-4 p-[1px]'>
|
||||
<RiArrowDownSLine className='w-3.5 h-3.5' />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{value === 'chatbot' && (
|
||||
<>
|
||||
<div className='w-4 h-4 p-[1px]'>
|
||||
<ChatBot className='w-3.5 h-3.5 text-[#1570EF]' />
|
||||
</div>
|
||||
<div className=''>{t('app.typeSelector.chatbot')}</div>
|
||||
<div className='w-4 h-4 p-[1px]' onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onChange('')
|
||||
}}>
|
||||
<XCircle className='w-3.5 h-3.5 text-gray-400 cursor-pointer hover:text-gray-600' />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{value === 'agent' && (
|
||||
<>
|
||||
<div className='w-4 h-4 p-[1px]'>
|
||||
<CuteRobot className='w-3.5 h-3.5 text-indigo-600' />
|
||||
</div>
|
||||
<div className=''>{t('app.typeSelector.agent')}</div>
|
||||
<div className='w-4 h-4 p-[1px]' onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onChange('')
|
||||
}}>
|
||||
<XCircle className='w-3.5 h-3.5 text-gray-400 cursor-pointer hover:text-gray-600' />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{value === 'workflow' && (
|
||||
<>
|
||||
<div className='w-4 h-4 p-[1px]'>
|
||||
<Route className='w-3.5 h-3.5 text-[#F79009]' />
|
||||
</div>
|
||||
<div className=''>{t('app.typeSelector.workflow')}</div>
|
||||
<div className='w-4 h-4 p-[1px]' onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onChange('')
|
||||
}}>
|
||||
<XCircle className='w-3.5 h-3.5 text-gray-400 cursor-pointer hover:text-gray-600' />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<AppTypeSelectTrigger values={value} />
|
||||
{value && value.length > 0 && <div className='w-4 h-4' onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onChange([])
|
||||
}}>
|
||||
<RiCloseCircleFill className='w-3.5 h-3.5 text-text-quaternary hover:text-text-tertiary cursor-pointer' />
|
||||
</div>}
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[1002]'>
|
||||
<div className='relative p-1 w-[180px] bg-white rounded-lg shadow-xl'>
|
||||
<div className='flex items-center pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-gray-50' onClick={() => {
|
||||
onChange('chatbot')
|
||||
setOpen(false)
|
||||
}}>
|
||||
<ChatBot className='mr-2 w-4 h-4 text-[#1570EF]' />
|
||||
<div className='grow text-gray-700 text-[13px] font-medium leading-[18px]'>{t('app.typeSelector.chatbot')}</div>
|
||||
{value === 'chatbot' && <Check className='w-4 h-4 text-primary-600' />}
|
||||
</div>
|
||||
<div className='flex items-center pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-gray-50' onClick={() => {
|
||||
onChange('agent')
|
||||
setOpen(false)
|
||||
}}>
|
||||
<CuteRobot className='mr-2 w-4 h-4 text-indigo-600' />
|
||||
<div className='grow text-gray-700 text-[13px] font-medium leading-[18px]'>{t('app.typeSelector.agent')}</div>
|
||||
{value === 'agent' && <Check className='w-4 h-4 text-primary-600' />}
|
||||
</div>
|
||||
<div className='flex items-center pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-gray-50' onClick={() => {
|
||||
onChange('workflow')
|
||||
setOpen(false)
|
||||
}}>
|
||||
<Route className='mr-2 w-4 h-4 text-[#F79009]' />
|
||||
<div className='grow text-gray-700 text-[13px] font-medium leading-[18px]'>{t('app.typeSelector.workflow')}</div>
|
||||
{value === 'workflow' && <Check className='w-4 h-4 text-primary-600' />}
|
||||
</div>
|
||||
</div>
|
||||
<ul className='relative p-1 w-[240px] bg-components-panel-bg-blur backdrop-blur-[5px] rounded-xl shadow-lg border border-components-panel-border'>
|
||||
{allTypes.map(mode => (
|
||||
<AppTypeSelectorItem key={mode} type={mode}
|
||||
checked={Boolean(value.length > 0 && value?.indexOf(mode) !== -1)}
|
||||
onClick={() => {
|
||||
if (value?.indexOf(mode) !== -1)
|
||||
onChange(value?.filter(v => v !== mode) ?? [])
|
||||
else
|
||||
onChange([...(value || []), mode])
|
||||
}} />
|
||||
))}
|
||||
</ul>
|
||||
</PortalToFollowElemContent>
|
||||
</div>
|
||||
</PortalToFollowElem>
|
||||
</div >
|
||||
</PortalToFollowElem >
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(AppTypeSelector)
|
||||
export default AppTypeSelector
|
||||
|
||||
function AppTypeSelectTrigger({ values }: { values: AppSelectorProps['value'] }) {
|
||||
const { t } = useTranslation()
|
||||
if (!values || values.length === 0) {
|
||||
return <div className={cn(
|
||||
'flex items-center justify-between gap-1 h-8',
|
||||
)}>
|
||||
<RiFilter3Line className='w-4 h-4 text-text-tertiary' />
|
||||
<div className='grow min-w-[65px] text-center system-sm-medium text-text-tertiary'>{t('app.typeSelector.all')}</div>
|
||||
<RiArrowDownSLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
}
|
||||
if (values.length === 1) {
|
||||
return <div className={cn(
|
||||
'flex items-center justify-between gap-1 h-8 flex-nowrap',
|
||||
)}>
|
||||
<AppTypeIcon type={values[0]} />
|
||||
<div className='flex flex-1 items-center text-center line-clamp-1'>
|
||||
<AppTypeLabel type={values[0]} className="system-sm-medium text-components-menu-item-text" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
return <div className={cn(
|
||||
'flex items-center justify-between h-8 -space-x-2 relative',
|
||||
)}>
|
||||
{values.map((mode, index) => (<AppTypeIcon key={mode} type={mode} wrapperClassName='border border-components-panel-on-panel-item-bg' style={{ zIndex: 5 - index }} />))}
|
||||
</div>
|
||||
}
|
||||
|
||||
type AppTypeSelectorItemProps = {
|
||||
checked: boolean
|
||||
type: AppMode
|
||||
onClick: () => void
|
||||
}
|
||||
function AppTypeSelectorItem({ checked, type, onClick }: AppTypeSelectorItemProps) {
|
||||
return <li className='flex items-center space-x-2 pl-2 py-1 pr-1 rounded-lg cursor-pointer hover:bg-state-base-hover' onClick={onClick}>
|
||||
<Checkbox checked={checked} />
|
||||
<AppTypeIcon type={type} />
|
||||
<div className='grow p-1 pl-0'>
|
||||
<AppTypeLabel type={type} className="system-sm-medium text-components-menu-item-text" />
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
|
||||
type AppTypeIconProps = {
|
||||
type: AppMode
|
||||
style?: React.CSSProperties
|
||||
className?: string
|
||||
wrapperClassName?: string
|
||||
}
|
||||
|
||||
export function AppTypeIcon({ type, className, wrapperClassName, style }: AppTypeIconProps) {
|
||||
const wrapperClassNames = cn('w-5 h-5 inline-flex items-center justify-center rounded-md border border-divider-regular', wrapperClassName)
|
||||
const iconClassNames = cn('w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100', className)
|
||||
if (type === 'chat') {
|
||||
return <div style={style} className={cn(wrapperClassNames, 'bg-components-icon-bg-blue-solid')}>
|
||||
<ChatBot className={iconClassNames} />
|
||||
</div>
|
||||
}
|
||||
if (type === 'agent-chat') {
|
||||
return <div style={style} className={cn(wrapperClassNames, 'bg-components-icon-bg-violet-solid')}>
|
||||
<Logic className={iconClassNames} />
|
||||
</div>
|
||||
}
|
||||
if (type === 'advanced-chat') {
|
||||
return <div style={style} className={cn(wrapperClassNames, 'bg-components-icon-bg-blue-light-solid')}>
|
||||
<BubbleTextMod className={iconClassNames} />
|
||||
</div>
|
||||
}
|
||||
if (type === 'workflow') {
|
||||
return <div style={style} className={cn(wrapperClassNames, 'bg-components-icon-bg-indigo-solid')}>
|
||||
<RiExchange2Fill className={iconClassNames} />
|
||||
</div>
|
||||
}
|
||||
if (type === 'completion') {
|
||||
return <div style={style} className={cn(wrapperClassNames, 'bg-components-icon-bg-teal-solid')}>
|
||||
<ListSparkle className={iconClassNames} />
|
||||
</div>
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
type AppTypeLabelProps = {
|
||||
type: AppMode
|
||||
className?: string
|
||||
}
|
||||
export function AppTypeLabel({ type, className }: AppTypeLabelProps) {
|
||||
const { t } = useTranslation()
|
||||
let label = ''
|
||||
if (type === 'chat')
|
||||
label = t('app.typeSelector.chatbot')
|
||||
if (type === 'agent-chat')
|
||||
label = t('app.typeSelector.agent')
|
||||
if (type === 'completion')
|
||||
label = t('app.typeSelector.completion')
|
||||
if (type === 'advanced-chat')
|
||||
label = t('app.typeSelector.advanced')
|
||||
if (type === 'workflow')
|
||||
label = t('app.typeSelector.workflow')
|
||||
|
||||
return <span className={className}>{label}</span>
|
||||
}
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
import type { FC } from 'react'
|
||||
import { init } from 'emoji-mart'
|
||||
import data from '@emoji-mart/data'
|
||||
import style from './style.module.css'
|
||||
import classNames from '@/utils/classnames'
|
||||
import Image from 'next/image'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import type { AppIconType } from '@/types/app'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
init({ data })
|
||||
|
||||
export type AppIconProps = {
|
||||
size?: 'xs' | 'tiny' | 'small' | 'medium' | 'large'
|
||||
size?: 'xs' | 'tiny' | 'small' | 'medium' | 'large' | 'xl' | 'xxl'
|
||||
rounded?: boolean
|
||||
iconType?: AppIconType | null
|
||||
icon?: string
|
||||
|
@ -20,7 +21,28 @@ export type AppIconProps = {
|
|||
innerIcon?: React.ReactNode
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const appIconVariants = cva(
|
||||
'flex items-center justify-center relative text-lg rounded-lg grow-0 shrink-0 overflow-hidden leading-none',
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
xs: 'w-4 h-4 text-xs',
|
||||
tiny: 'w-6 h-6 text-base',
|
||||
small: 'w-8 h-8 text-xl',
|
||||
medium: 'w-9 h-9 text-[22px]',
|
||||
large: 'w-10 h-10 text-[24px]',
|
||||
xl: 'w-12 h-12 text-[28px]',
|
||||
xxl: 'w-14 h-14 text-[32px]',
|
||||
},
|
||||
rounded: {
|
||||
true: 'rounded-full',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'medium',
|
||||
rounded: false,
|
||||
},
|
||||
})
|
||||
const AppIcon: FC<AppIconProps> = ({
|
||||
size = 'medium',
|
||||
rounded = false,
|
||||
|
@ -32,23 +54,15 @@ const AppIcon: FC<AppIconProps> = ({
|
|||
innerIcon,
|
||||
onClick,
|
||||
}) => {
|
||||
const wrapperClassName = classNames(
|
||||
style.appIcon,
|
||||
size !== 'medium' && style[size],
|
||||
rounded && style.rounded,
|
||||
className ?? '',
|
||||
'overflow-hidden',
|
||||
)
|
||||
|
||||
const isValidImageIcon = iconType === 'image' && imageUrl
|
||||
|
||||
return <span
|
||||
className={wrapperClassName}
|
||||
className={classNames(appIconVariants({ size, rounded }), className)}
|
||||
style={{ background: isValidImageIcon ? undefined : (background || '#FFEAD5') }}
|
||||
onClick={onClick}
|
||||
>
|
||||
{isValidImageIcon
|
||||
? <img src={imageUrl} className="w-full h-full" alt="app icon" />
|
||||
? <Image src={imageUrl} className="w-full h-full" alt="app icon" />
|
||||
: (innerIcon || ((icon && icon !== '') ? <em-emoji id={icon} /> : <em-emoji id='🤖' />))
|
||||
}
|
||||
</span>
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
.appIcon {
|
||||
@apply flex items-center justify-center relative w-9 h-9 text-lg rounded-lg grow-0 shrink-0;
|
||||
}
|
||||
|
||||
.appIcon.large {
|
||||
@apply w-10 h-10;
|
||||
}
|
||||
|
||||
.appIcon.small {
|
||||
@apply w-8 h-8;
|
||||
}
|
||||
|
||||
.appIcon.tiny {
|
||||
@apply w-6 h-6 text-base;
|
||||
}
|
||||
|
||||
.appIcon.xs {
|
||||
@apply w-3 h-3 text-base;
|
||||
}
|
||||
|
||||
.appIcon.rounded {
|
||||
@apply rounded-full;
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Fragment } from 'react'
|
||||
import { RiCloseLargeLine } from '@remixicon/react'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
type IModal = {
|
||||
className?: string
|
||||
wrapperClassName?: string
|
||||
open: boolean
|
||||
onClose?: () => void
|
||||
title?: React.ReactNode
|
||||
description?: React.ReactNode
|
||||
children?: React.ReactNode
|
||||
closable?: boolean
|
||||
overflowVisible?: boolean
|
||||
}
|
||||
|
||||
export default function FullScreenModal({
|
||||
className,
|
||||
wrapperClassName,
|
||||
open,
|
||||
onClose = () => { },
|
||||
children,
|
||||
closable = false,
|
||||
overflowVisible = false,
|
||||
}: IModal) {
|
||||
return (
|
||||
<Transition show={open} as={Fragment}>
|
||||
<Dialog as="div" className={classNames('modal-dialog', wrapperClassName)} onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-background-overlay-backdrop backdrop-blur-[6px]" />
|
||||
</Transition.Child>
|
||||
|
||||
<div
|
||||
className="fixed inset-0 h-screen w-screen p-4"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<div className="w-full h-full bg-background-default-subtle rounded-2xl border border-effects-highlight relative">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className={classNames(
|
||||
'h-full',
|
||||
overflowVisible ? 'overflow-visible' : 'overflow-hidden',
|
||||
className,
|
||||
)}>
|
||||
{closable
|
||||
&& <div
|
||||
className='absolute z-50 top-3 right-3 w-9 h-9 flex items-center justify-center rounded-[10px]
|
||||
bg-components-button-tertiary-bg hover:bg-components-button-tertiary-bg-hover cursor-pointer'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onClose()
|
||||
}}>
|
||||
<RiCloseLargeLine className='w-3.5 h-3.5 text-components-button-tertiary-text' />
|
||||
</div>}
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 9C2 5.68629 4.68629 3 8 3H16C19.3137 3 22 5.68629 22 9V15C22 18.3137 19.3137 21 16 21H3C2.44772 21 2 20.5523 2 20V9ZM9 9C8.44772 9 8 9.44772 8 10C8 10.5523 8.44772 11 9 11H15C15.5523 11 16 10.5523 16 10C16 9.44772 15.5523 9 15 9H9ZM9 13C8.44772 13 8 13.4477 8 14C8 14.5523 8.44772 15 9 15H12C12.5523 15 13 14.5523 13 14C13 13.4477 12.5523 13 12 13H9Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 522 B |
|
@ -0,0 +1,6 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 5C3.44772 5 3 5.44772 3 6C3 6.55228 3.44772 7 4 7H20C20.5523 7 21 6.55228 21 6C21 5.44772 20.5523 5 20 5H4Z" fill="black"/>
|
||||
<path d="M17.9191 9.60608C17.7616 9.2384 17.4 9 17 9C16.6 9 16.2384 9.2384 16.0809 9.60608L14.7384 12.7384L11.6061 14.0809C11.2384 14.2384 11 14.6 11 15C11 15.4 11.2384 15.7616 11.6061 15.9191L14.7384 17.2616L16.0809 20.3939C16.2384 20.7616 16.6 21 17 21C17.4 21 17.7616 20.7616 17.9191 20.3939L19.2616 17.2616L22.3939 15.9191C22.7616 15.7616 23 15.4 23 15C23 14.6 22.7616 14.2384 22.3939 14.0809L19.2616 12.7384L17.9191 9.60608Z" fill="black"/>
|
||||
<path d="M4 11C3.44772 11 3 11.4477 3 12C3 12.5523 3.44772 13 4 13H9C9.55228 13 10 12.5523 10 12C10 11.4477 9.55228 11 9 11H4Z" fill="black"/>
|
||||
<path d="M4 17C3.44772 17 3 17.4477 3 18C3 18.5523 3.44772 19 4 19H7C7.55228 19 8 18.5523 8 18C8 17.4477 7.55228 17 7 17H4Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 968 B |
|
@ -0,0 +1,8 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="logic">
|
||||
<g id="Vector">
|
||||
<path d="M12.9089 11.9999C13.913 11.9999 14.727 11.186 14.727 10.1819C14.727 9.17775 13.913 8.36376 12.9089 8.36376C11.9048 8.36376 11.0908 9.17775 11.0908 10.1819C11.0908 11.186 11.9048 11.9999 12.9089 11.9999Z" fill="black"/>
|
||||
<path d="M12.2871 1.11229C9.95219 1.3228 7.78275 2.40696 6.21264 4.14796C4.64254 5.88897 3.78749 8.15849 3.81849 10.5027V10.8763L2.09676 14.3207C2.04261 14.4277 2.01016 14.5444 2.00129 14.6639C1.99241 14.7835 2.00729 14.9037 2.04506 15.0175C2.08283 15.1313 2.14275 15.2366 2.22136 15.3271C2.29997 15.4177 2.39573 15.4918 2.50311 15.5452L3.81849 16.1979V18.3632C3.81849 19.0865 4.10581 19.7802 4.61725 20.2916C5.12869 20.803 5.82234 21.0904 6.54562 21.0904H9.27276V22.9084H19.2722V16.6606C20.5995 15.3604 21.496 13.6844 21.8409 11.8588C22.1858 10.0331 21.9625 8.14562 21.2012 6.45084C20.4398 4.75606 19.1769 3.33556 17.583 2.38094C15.989 1.42633 14.1406 0.983541 12.2871 1.11229ZM17.4542 11.0909H16.416C16.3316 11.4163 16.2016 11.7282 16.0297 12.0172L16.7651 12.7526C16.8519 12.8365 16.9212 12.9368 16.9688 13.0477C17.0165 13.1586 17.0415 13.2779 17.0426 13.3986C17.0436 13.5193 17.0206 13.639 16.9749 13.7507C16.9292 13.8624 16.8617 13.9639 16.7764 14.0493C16.691 14.1346 16.5895 14.2021 16.4778 14.2478C16.3661 14.2935 16.2464 14.3165 16.1257 14.3155C16.005 14.3144 15.8857 14.2893 15.7748 14.2417C15.6639 14.1941 15.5636 14.1248 15.4797 14.038L14.7443 13.3026C14.4553 13.4745 14.1434 13.6045 13.818 13.6889V14.727C13.818 14.9681 13.7222 15.1994 13.5517 15.3698C13.3812 15.5403 13.15 15.6361 12.9089 15.6361C12.6678 15.6361 12.4366 15.5403 12.2661 15.3698C12.0957 15.1994 11.9999 14.9681 11.9999 14.727V13.6889C11.6744 13.6045 11.3625 13.4745 11.0736 13.3026L10.3382 14.038C10.1667 14.2036 9.93708 14.2952 9.69873 14.2931C9.46038 14.2911 9.23239 14.1955 9.06384 14.0269C8.8953 13.8584 8.79969 13.6304 8.79762 13.392C8.79555 13.1537 8.88718 12.924 9.05277 12.7526L9.78818 12.0172C9.61629 11.7282 9.48622 11.4163 9.40184 11.0909H8.36371C8.12262 11.0909 7.8914 10.9951 7.72092 10.8246C7.55044 10.6541 7.45467 10.4229 7.45467 10.1818C7.45467 9.94073 7.55044 9.70951 7.72092 9.53903C7.8914 9.36855 8.12262 9.27278 8.36371 9.27278H9.40184C9.48622 8.94731 9.61629 8.63544 9.78818 8.34647L9.05277 7.61105C8.88718 7.4396 8.79555 7.20997 8.79762 6.97163C8.79969 6.73328 8.8953 6.50528 9.06384 6.33673C9.23239 6.16819 9.46038 6.07259 9.69873 6.07052C9.93708 6.06844 10.1667 6.16007 10.3382 6.32566L11.0736 7.06108C11.3625 6.88918 11.6744 6.75911 11.9999 6.67473V5.63661C11.9999 5.39551 12.0957 5.16429 12.2661 4.99381C12.4366 4.82334 12.6678 4.72756 12.9089 4.72756C13.15 4.72756 13.3812 4.82334 13.5517 4.99381C13.7222 5.16429 13.818 5.39551 13.818 5.63661V6.67473C14.1434 6.75911 14.4553 6.88918 14.7443 7.06108L15.4797 6.32566C15.5636 6.23884 15.6639 6.16958 15.7748 6.12194C15.8857 6.0743 16.005 6.04922 16.1257 6.04817C16.2464 6.04713 16.3661 6.07013 16.4778 6.11583C16.5895 6.16154 16.691 6.22904 16.7764 6.31439C16.8617 6.39975 16.9292 6.50124 16.9749 6.61296C17.0206 6.72468 17.0436 6.84438 17.0426 6.96508C17.0415 7.08578 17.0165 7.20507 16.9688 7.31598C16.9212 7.42688 16.8519 7.52719 16.7651 7.61105L16.0297 8.34647C16.2016 8.63544 16.3316 8.94731 16.416 9.27278H17.4542C17.6952 9.27278 17.9265 9.36855 18.0969 9.53903C18.2674 9.70951 18.3632 9.94073 18.3632 10.1818C18.3632 10.4229 18.2674 10.6541 18.0969 10.8246C17.9265 10.9951 17.6952 11.0909 17.4542 11.0909Z" fill="black"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"viewBox": "0 0 16 16",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "lock"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Vector",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M8 1.75C6.27411 1.75 4.875 3.14911 4.875 4.875V6.125C3.83947 6.125 3 6.96444 3 8V12.375C3 13.4106 3.83947 14.25 4.875 14.25H11.125C12.1606 14.25 13 13.4106 13 12.375V8C13 6.96444 12.1606 6.125 11.125 6.125V4.875C11.125 3.14911 9.72587 1.75 8 1.75ZM9.875 6.125V4.875C9.875 3.83947 9.03556 3 8 3C6.96444 3 6.125 3.83947 6.125 4.875V6.125H9.875ZM8 8.625C8.34519 8.625 8.625 8.90481 8.625 9.25V11.125C8.625 11.4702 8.34519 11.75 8 11.75C7.65481 11.75 7.375 11.4702 7.375 11.125V9.25C7.375 8.90481 7.65481 8.625 8 8.625Z",
|
||||
"fill": "#155AEF"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Lock"
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Lock.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'Lock'
|
||||
|
||||
export default Icon
|
|
@ -3,6 +3,7 @@ export { default as DiagonalDividingLine } from './DiagonalDividingLine'
|
|||
export { default as Dify } from './Dify'
|
||||
export { default as Github } from './Github'
|
||||
export { default as Line3 } from './Line3'
|
||||
export { default as Lock } from './Lock'
|
||||
export { default as MessageChatSquare } from './MessageChatSquare'
|
||||
export { default as MultiPathRetrieval } from './MultiPathRetrieval'
|
||||
export { default as NTo1Retrieval } from './NTo1Retrieval'
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M2 9C2 5.68629 4.68629 3 8 3H16C19.3137 3 22 5.68629 22 9V15C22 18.3137 19.3137 21 16 21H3C2.44772 21 2 20.5523 2 20V9ZM9 9C8.44772 9 8 9.44772 8 10C8 10.5523 8.44772 11 9 11H15C15.5523 11 16 10.5523 16 10C16 9.44772 15.5523 9 15 9H9ZM9 13C8.44772 13 8 13.4477 8 14C8 14.5523 8.44772 15 9 15H12C12.5523 15 13 14.5523 13 14C13 13.4477 12.5523 13 12 13H9Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "BubbleTextMod"
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './BubbleTextMod.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'BubbleTextMod'
|
||||
|
||||
export default Icon
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M4 5C3.44772 5 3 5.44772 3 6C3 6.55228 3.44772 7 4 7H20C20.5523 7 21 6.55228 21 6C21 5.44772 20.5523 5 20 5H4Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M17.9191 9.60608C17.7616 9.2384 17.4 9 17 9C16.6 9 16.2384 9.2384 16.0809 9.60608L14.7384 12.7384L11.6061 14.0809C11.2384 14.2384 11 14.6 11 15C11 15.4 11.2384 15.7616 11.6061 15.9191L14.7384 17.2616L16.0809 20.3939C16.2384 20.7616 16.6 21 17 21C17.4 21 17.7616 20.7616 17.9191 20.3939L19.2616 17.2616L22.3939 15.9191C22.7616 15.7616 23 15.4 23 15C23 14.6 22.7616 14.2384 22.3939 14.0809L19.2616 12.7384L17.9191 9.60608Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M4 11C3.44772 11 3 11.4477 3 12C3 12.5523 3.44772 13 4 13H9C9.55228 13 10 12.5523 10 12C10 11.4477 9.55228 11 9 11H4Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M4 17C3.44772 17 3 17.4477 3 18C3 18.5523 3.44772 19 4 19H7C7.55228 19 8 18.5523 8 18C8 17.4477 7.55228 17 7 17H4Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "ListSparkle"
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './ListSparkle.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'ListSparkle'
|
||||
|
||||
export default Icon
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "logic"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Vector"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M12.9089 11.9999C13.913 11.9999 14.727 11.186 14.727 10.1819C14.727 9.17775 13.913 8.36376 12.9089 8.36376C11.9048 8.36376 11.0908 9.17775 11.0908 10.1819C11.0908 11.186 11.9048 11.9999 12.9089 11.9999Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M12.2871 1.11229C9.95219 1.3228 7.78275 2.40696 6.21264 4.14796C4.64254 5.88897 3.78749 8.15849 3.81849 10.5027V10.8763L2.09676 14.3207C2.04261 14.4277 2.01016 14.5444 2.00129 14.6639C1.99241 14.7835 2.00729 14.9037 2.04506 15.0175C2.08283 15.1313 2.14275 15.2366 2.22136 15.3271C2.29997 15.4177 2.39573 15.4918 2.50311 15.5452L3.81849 16.1979V18.3632C3.81849 19.0865 4.10581 19.7802 4.61725 20.2916C5.12869 20.803 5.82234 21.0904 6.54562 21.0904H9.27276V22.9084H19.2722V16.6606C20.5995 15.3604 21.496 13.6844 21.8409 11.8588C22.1858 10.0331 21.9625 8.14562 21.2012 6.45084C20.4398 4.75606 19.1769 3.33556 17.583 2.38094C15.989 1.42633 14.1406 0.983541 12.2871 1.11229ZM17.4542 11.0909H16.416C16.3316 11.4163 16.2016 11.7282 16.0297 12.0172L16.7651 12.7526C16.8519 12.8365 16.9212 12.9368 16.9688 13.0477C17.0165 13.1586 17.0415 13.2779 17.0426 13.3986C17.0436 13.5193 17.0206 13.639 16.9749 13.7507C16.9292 13.8624 16.8617 13.9639 16.7764 14.0493C16.691 14.1346 16.5895 14.2021 16.4778 14.2478C16.3661 14.2935 16.2464 14.3165 16.1257 14.3155C16.005 14.3144 15.8857 14.2893 15.7748 14.2417C15.6639 14.1941 15.5636 14.1248 15.4797 14.038L14.7443 13.3026C14.4553 13.4745 14.1434 13.6045 13.818 13.6889V14.727C13.818 14.9681 13.7222 15.1994 13.5517 15.3698C13.3812 15.5403 13.15 15.6361 12.9089 15.6361C12.6678 15.6361 12.4366 15.5403 12.2661 15.3698C12.0957 15.1994 11.9999 14.9681 11.9999 14.727V13.6889C11.6744 13.6045 11.3625 13.4745 11.0736 13.3026L10.3382 14.038C10.1667 14.2036 9.93708 14.2952 9.69873 14.2931C9.46038 14.2911 9.23239 14.1955 9.06384 14.0269C8.8953 13.8584 8.79969 13.6304 8.79762 13.392C8.79555 13.1537 8.88718 12.924 9.05277 12.7526L9.78818 12.0172C9.61629 11.7282 9.48622 11.4163 9.40184 11.0909H8.36371C8.12262 11.0909 7.8914 10.9951 7.72092 10.8246C7.55044 10.6541 7.45467 10.4229 7.45467 10.1818C7.45467 9.94073 7.55044 9.70951 7.72092 9.53903C7.8914 9.36855 8.12262 9.27278 8.36371 9.27278H9.40184C9.48622 8.94731 9.61629 8.63544 9.78818 8.34647L9.05277 7.61105C8.88718 7.4396 8.79555 7.20997 8.79762 6.97163C8.79969 6.73328 8.8953 6.50528 9.06384 6.33673C9.23239 6.16819 9.46038 6.07259 9.69873 6.07052C9.93708 6.06844 10.1667 6.16007 10.3382 6.32566L11.0736 7.06108C11.3625 6.88918 11.6744 6.75911 11.9999 6.67473V5.63661C11.9999 5.39551 12.0957 5.16429 12.2661 4.99381C12.4366 4.82334 12.6678 4.72756 12.9089 4.72756C13.15 4.72756 13.3812 4.82334 13.5517 4.99381C13.7222 5.16429 13.818 5.39551 13.818 5.63661V6.67473C14.1434 6.75911 14.4553 6.88918 14.7443 7.06108L15.4797 6.32566C15.5636 6.23884 15.6639 6.16958 15.7748 6.12194C15.8857 6.0743 16.005 6.04922 16.1257 6.04817C16.2464 6.04713 16.3661 6.07013 16.4778 6.11583C16.5895 6.16154 16.691 6.22904 16.7764 6.31439C16.8617 6.39975 16.9292 6.50124 16.9749 6.61296C17.0206 6.72468 17.0436 6.84438 17.0426 6.96508C17.0415 7.08578 17.0165 7.20507 16.9688 7.31598C16.9212 7.42688 16.8519 7.52719 16.7651 7.61105L16.0297 8.34647C16.2016 8.63544 16.3316 8.94731 16.416 9.27278H17.4542C17.6952 9.27278 17.9265 9.36855 18.0969 9.53903C18.2674 9.70951 18.3632 9.94073 18.3632 10.1818C18.3632 10.4229 18.2674 10.6541 18.0969 10.8246C17.9265 10.9951 17.6952 11.0909 17.4542 11.0909Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Logic"
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Logic.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'Logic'
|
||||
|
||||
export default Icon
|
|
@ -1,7 +1,10 @@
|
|||
export { default as AiText } from './AiText'
|
||||
export { default as BubbleTextMod } from './BubbleTextMod'
|
||||
export { default as ChatBot } from './ChatBot'
|
||||
export { default as CuteRobot } from './CuteRobot'
|
||||
export { default as EditList } from './EditList'
|
||||
export { default as ListSparkle } from './ListSparkle'
|
||||
export { default as Logic } from './Logic'
|
||||
export { default as MessageDotsCircle } from './MessageDotsCircle'
|
||||
export { default as MessageFast } from './MessageFast'
|
||||
export { default as MessageHeartCircle } from './MessageHeartCircle'
|
||||
|
|
|
@ -25,8 +25,8 @@ const TabSliderNew: FC<TabSliderProps> = ({
|
|||
key={option.value}
|
||||
onClick={() => onChange(option.value)}
|
||||
className={cn(
|
||||
'mr-1 px-3 py-[7px] h-[32px] flex items-center rounded-lg border-[0.5px] border-transparent text-gray-700 text-[13px] font-medium leading-[18px] cursor-pointer hover:bg-gray-200',
|
||||
value === option.value && 'bg-white border-gray-200 shadow-xs text-primary-600 hover:bg-white',
|
||||
'mr-1 px-3 py-[7px] h-[32px] flex items-center rounded-lg border-[0.5px] border-transparent text-text-tertiary text-[13px] font-medium leading-[18px] cursor-pointer hover:bg-components-main-nav-nav-button-bg-active',
|
||||
value === option.value && 'bg-components-main-nav-nav-button-bg-active border-components-main-nav-nav-button-border shadow-xs text-components-main-nav-nav-button-text-active',
|
||||
)}
|
||||
>
|
||||
{option.icon}
|
||||
|
|
|
@ -78,24 +78,23 @@ const TagFilter: FC<TagFilterProps> = ({
|
|||
className='block'
|
||||
>
|
||||
<div className={cn(
|
||||
'flex items-center gap-1 px-2 h-8 rounded-lg border-[0.5px] border-transparent bg-gray-200 cursor-pointer hover:bg-gray-300',
|
||||
open && !value.length && '!bg-gray-300 hover:bg-gray-300',
|
||||
!open && !!value.length && '!bg-white/80 shadow-xs !border-black/5 hover:!bg-gray-200',
|
||||
open && !!value.length && '!bg-gray-200 !border-black/5 shadow-xs hover:!bg-gray-200',
|
||||
'flex items-center gap-1 px-2 h-8 rounded-lg border-[0.5px] border-transparent bg-components-input-bg-normal cursor-pointer',
|
||||
!open && !!value.length && 'shadow-xs',
|
||||
open && !!value.length && 'shadow-xs',
|
||||
)}>
|
||||
<div className='p-[1px]'>
|
||||
<Tag01 className='h-3.5 w-3.5 text-gray-700' />
|
||||
<Tag01 className='h-3.5 w-3.5 text-text-tertiary' />
|
||||
</div>
|
||||
<div className='text-[13px] leading-[18px] text-gray-700'>
|
||||
<div className='text-[13px] leading-[18px] text-text-secondary'>
|
||||
{!value.length && t('common.tag.placeholder')}
|
||||
{!!value.length && currentTag?.name}
|
||||
</div>
|
||||
{value.length > 1 && (
|
||||
<div className='text-xs font-medium leading-[18px] text-gray-500'>{`+${value.length - 1}`}</div>
|
||||
<div className='text-xs font-medium leading-[18px] text-text-tertiary'>{`+${value.length - 1}`}</div>
|
||||
)}
|
||||
{!value.length && (
|
||||
<div className='p-[1px]'>
|
||||
<RiArrowDownSLine className='h-3.5 w-3.5 text-gray-700' />
|
||||
<RiArrowDownSLine className='h-3.5 w-3.5 text-text-tertiary' />
|
||||
</div>
|
||||
)}
|
||||
{!!value.length && (
|
||||
|
@ -103,14 +102,14 @@ const TagFilter: FC<TagFilterProps> = ({
|
|||
e.stopPropagation()
|
||||
onChange([])
|
||||
}}>
|
||||
<XCircle className='h-3.5 w-3.5 text-gray-400 group-hover/clear:text-gray-600' />
|
||||
<XCircle className='h-3.5 w-3.5 text-text-tertiary group-hover/clear:text-text-secondary' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[1002]'>
|
||||
<div className='relative w-[240px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
|
||||
<div className='p-2 border-b-[0.5px] border-black/5'>
|
||||
<div className='relative w-[240px] bg-components-panel-bg-blur backdrop-blur-[5px] rounded-lg border-[0.5px] border-components-panel-border shadow-lg'>
|
||||
<div className='p-2'>
|
||||
<Input
|
||||
showLeftIcon
|
||||
showClearIcon
|
||||
|
@ -123,17 +122,17 @@ const TagFilter: FC<TagFilterProps> = ({
|
|||
{filteredTagList.map(tag => (
|
||||
<div
|
||||
key={tag.id}
|
||||
className='flex items-center gap-2 pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-gray-100'
|
||||
className='flex items-center gap-2 pl-3 py-[6px] pr-2 rounded-lg cursor-pointer hover:bg-state-base-hover'
|
||||
onClick={() => selectTag(tag)}
|
||||
>
|
||||
<div title={tag.name} className='grow text-sm text-gray-700 leading-5 truncate'>{tag.name}</div>
|
||||
{value.includes(tag.id) && <Check className='shrink-0 w-4 h-4 text-primary-600' />}
|
||||
<div title={tag.name} className='grow text-sm text-text-tertiary leading-5 truncate'>{tag.name}</div>
|
||||
{value.includes(tag.id) && <Check className='shrink-0 w-4 h-4 text-text-secondary' />}
|
||||
</div>
|
||||
))}
|
||||
{!filteredTagList.length && (
|
||||
<div className='p-3 flex flex-col items-center gap-1'>
|
||||
<Tag03 className='h-6 w-6 text-gray-300' />
|
||||
<div className='text-gray-500 text-xs leading-[14px]'>{t('common.tag.noTag')}</div>
|
||||
<Tag03 className='h-6 w-6 text-text-tertiary' />
|
||||
<div className='text-text-tertiary text-xs leading-[14px]'>{t('common.tag.noTag')}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@ const HeaderWrapper = ({
|
|||
<div className={classNames(
|
||||
'sticky top-0 left-0 right-0 z-30 flex flex-col grow-0 shrink-0 basis-auto min-h-[56px]',
|
||||
s.header,
|
||||
isBordered ? 'border-b border-gray-200' : '',
|
||||
isBordered ? 'border-b border-divider-regular' : '',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -6,6 +6,8 @@ const translation = {
|
|||
agent: 'Agent',
|
||||
workflow: 'Workflow',
|
||||
completion: 'Completion',
|
||||
advanced: 'Chatflow',
|
||||
basic: 'Basic',
|
||||
},
|
||||
duplicate: 'Duplicate',
|
||||
mermaid: {
|
||||
|
@ -31,26 +33,33 @@ const translation = {
|
|||
'Discuss with team members, contributors and developers on different channels.',
|
||||
roadmap: 'See our roadmap',
|
||||
newApp: {
|
||||
learnMore: 'Learn more',
|
||||
startFromBlank: 'Create from Blank',
|
||||
startFromTemplate: 'Create from Template',
|
||||
captionAppType: 'What type of app do you want to create?',
|
||||
chatbotDescription: 'Build a chat-based application. This app uses a question-and-answer format, allowing for multiple rounds of continuous conversation.',
|
||||
completionDescription: 'Build an application that generates high-quality text based on prompts, such as generating articles, summaries, translations, and more.',
|
||||
completionWarning: 'This type of app will no longer be supported.',
|
||||
agentDescription: 'Build an intelligent Agent which can autonomously choose tools to complete the tasks',
|
||||
workflowDescription: 'Build an application that generates high-quality text based on workflow orchestrates with a high degree of customization. It is suitable for experienced users.',
|
||||
foundResult: '{{count}} Result',
|
||||
foundResults: '{{count}} Results',
|
||||
noAppsFound: 'No apps found',
|
||||
noTemplateFound: 'No templates found',
|
||||
noTemplateFoundTip: 'Try searching using different keywords.',
|
||||
chatbotShortDescription: 'LLM-based chatbot with simple setup',
|
||||
chatbotUserDescription: 'Quickly build an LLM-based chatbot with simple configuration. You can switch to Chatflow later.',
|
||||
completionShortDescription: 'AI assistant for text generation tasks',
|
||||
completionUserDescription: 'Quickly build an AI assistant for text generation tasks with simple configuration.',
|
||||
agentShortDescription: 'Intelligent agent with reasoning and autonomous tool use',
|
||||
agentUserDescription: 'An intelligent agent capable of iterative reasoning and autonomous tool use to achieve task goals.',
|
||||
workflowShortDescription: 'Orchestration for single-turn automation tasks',
|
||||
workflowUserDescription: 'Workflow orchestration for single-round tasks like automation and batch processing.',
|
||||
workflowWarning: 'Currently in beta',
|
||||
chatbotType: 'Chatbot orchestrate method',
|
||||
basic: 'Basic',
|
||||
basicTip: 'For beginners, can switch to Chatflow later',
|
||||
basicFor: 'FOR BEGINNERS',
|
||||
basicDescription: 'Basic Orchestrate allows for the orchestration of a Chatbot app using simple settings, without the ability to modify built-in prompts. It is suitable for beginners.',
|
||||
advanced: 'Chatflow',
|
||||
advancedFor: 'For advanced users',
|
||||
advancedDescription: 'Workflow Orchestrate orchestrates Chatbots in the form of workflows, offering a high degree of customization, including the ability to edit built-in prompts. It is suitable for experienced users.',
|
||||
captionName: 'App icon & name',
|
||||
advancedShortDescription: 'Workflow for complex multi-turn dialogues with memory',
|
||||
advancedUserDescription: 'Workflow orchestration for multi-round complex dialogue tasks with memory capabilities.',
|
||||
chooseAppType: 'Choose App Type',
|
||||
forBeginners: 'FOR BEGINNERS',
|
||||
forAdvanced: 'FOR ADVANCED USERS',
|
||||
noIdeaTip: 'No ideas? Check out our templates',
|
||||
captionName: 'App Name & Icon',
|
||||
appNamePlaceholder: 'Give your app a name',
|
||||
captionDescription: 'Description',
|
||||
optional: 'Optional',
|
||||
appDescriptionPlaceholder: 'Enter the description of the app',
|
||||
useTemplate: 'Use this template',
|
||||
previewDemo: 'Preview demo',
|
||||
|
@ -79,6 +88,19 @@ const translation = {
|
|||
appCreateDSLErrorPart4: 'System-supported DSL version: ',
|
||||
appCreateFailed: 'Failed to create app',
|
||||
},
|
||||
newAppFromTemplate: {
|
||||
byCategories: 'BY CATEGORIES',
|
||||
searchAllTemplate: 'Search all templates...',
|
||||
sidebar: {
|
||||
Recommended: 'Recommended',
|
||||
Agent: 'Agent',
|
||||
Assistant: 'Assistant',
|
||||
HR: 'HR',
|
||||
Workflow: 'Workflow',
|
||||
Writing: 'Writing',
|
||||
Programming: 'Programming',
|
||||
},
|
||||
},
|
||||
editApp: 'Edit Info',
|
||||
editAppTitle: 'Edit App Info',
|
||||
editDone: 'App info updated',
|
||||
|
@ -108,6 +130,7 @@ const translation = {
|
|||
agent: 'Agent',
|
||||
workflow: 'Workflow',
|
||||
completion: 'Completion',
|
||||
advanced: 'Chatflow',
|
||||
},
|
||||
tracing: {
|
||||
title: 'Tracing app performance',
|
||||
|
|
|
@ -6,6 +6,8 @@ const translation = {
|
|||
agent: 'Agent',
|
||||
workflow: '工作流',
|
||||
completion: '文本生成',
|
||||
advanced: 'Chatflow',
|
||||
basic: '基础编排',
|
||||
},
|
||||
duplicate: '复制',
|
||||
mermaid: {
|
||||
|
@ -30,25 +32,34 @@ const translation = {
|
|||
communityIntro: '与团队成员、贡献者和开发者在不同频道中交流',
|
||||
roadmap: '产品路线图',
|
||||
newApp: {
|
||||
learnMore: '了解更多',
|
||||
startFromBlank: '创建空白应用',
|
||||
startFromTemplate: '从应用模版创建',
|
||||
captionAppType: '想要哪种应用类型?',
|
||||
chatbotDescription: '使用大型语言模型构建基于聊天的助手',
|
||||
completionDescription: '构建一个根据提示生成高质量文本的应用程序,例如生成文章、摘要、翻译等。',
|
||||
foundResult: '{{count}} 个结果',
|
||||
foundResults: '{{count}} 个结果',
|
||||
noAppsFound: '未找到应用',
|
||||
noTemplateFound: '未找到模板',
|
||||
noTemplateFoundTip: '请尝试使用不同的关键字进行搜索。',
|
||||
chatbotShortDescription: '简单配置即可构建基于 LLM 的对话机器人',
|
||||
chatbotUserDescription: '通过简单的配置快速搭建一个基于 LLM 的对话机器人。支持切换为 Chatflow 编排。',
|
||||
completionShortDescription: '用于文本生成任务的 AI 助手',
|
||||
completionUserDescription: '通过简单的配置快速搭建一个面向文本生成类任务的 AI 助手。',
|
||||
completionWarning: '该类型不久后将不再支持创建',
|
||||
agentDescription: '构建一个智能Agent,可以自主选择工具来完成任务',
|
||||
workflowDescription: '以工作流的形式编排生成型应用,提供更多的自定义能力。 它适合有经验的用户。',
|
||||
agentShortDescription: '具备推理与自主工具调用的智能助手',
|
||||
agentUserDescription: '能够迭代式的规划推理、自主工具调用,直至完成任务目标的智能助手。',
|
||||
workflowShortDescription: '面向单轮自动化任务的编排工作流',
|
||||
workflowUserDescription: '基于工作流编排,适用于自动化、批处理等单轮生成类任务的场景。',
|
||||
workflowWarning: '正在进行 Beta 测试',
|
||||
chatbotType: '聊天助手编排方法',
|
||||
basic: '基础编排',
|
||||
basicTip: '新手适用,可以切换成工作流编排',
|
||||
basicFor: '新手适用',
|
||||
basicDescription: '基本编排允许使用简单的设置编排聊天机器人应用程序,而无需修改内置提示。 它适合初学者。',
|
||||
advanced: '工作流编排',
|
||||
advancedFor: '进阶用户适用',
|
||||
advancedDescription: '工作流编排以工作流的形式编排聊天机器人,提供高度的自定义,包括编辑内置提示的能力。 它适合有经验的用户。',
|
||||
captionName: '图标 & 名称',
|
||||
advancedShortDescription: '支持记忆的复杂多轮对话工作流',
|
||||
advancedUserDescription: '基于工作流编排,适用于定义等复杂流程的多轮对话场景,具有记忆功能。',
|
||||
chooseAppType: '选择应用类型',
|
||||
forBeginners: '新手适用',
|
||||
forAdvanced: '进阶用户适用',
|
||||
noIdeaTip: '没有想法?试试我们的模板',
|
||||
captionName: '应用名称 & 图标',
|
||||
appNamePlaceholder: '给你的应用起个名字',
|
||||
optional: '可选',
|
||||
captionDescription: '描述',
|
||||
appDescriptionPlaceholder: '输入应用的描述',
|
||||
useTemplate: '使用该模板',
|
||||
|
@ -78,6 +89,19 @@ const translation = {
|
|||
appCreateFailed: '应用创建失败',
|
||||
Confirm: '确认',
|
||||
},
|
||||
newAppFromTemplate: {
|
||||
byCategories: '分类',
|
||||
searchAllTemplate: '搜索所有模版...',
|
||||
sidebar: {
|
||||
Recommended: '推荐',
|
||||
Agent: 'Agent',
|
||||
Assistant: '助手',
|
||||
HR: '人力资源',
|
||||
Workflow: '工作流',
|
||||
Writing: '写作',
|
||||
Programming: '编程',
|
||||
},
|
||||
},
|
||||
editApp: '编辑信息',
|
||||
editAppTitle: '编辑应用信息',
|
||||
editDone: '应用信息已更新',
|
||||
|
@ -106,6 +130,7 @@ const translation = {
|
|||
agent: 'Agent',
|
||||
workflow: '工作流',
|
||||
completion: '文本生成',
|
||||
advanced: 'Chatflow',
|
||||
},
|
||||
tracing: {
|
||||
title: '追踪应用性能',
|
||||
|
|
|
@ -39,7 +39,7 @@ const translation = {
|
|||
advanced: '工作流編排',
|
||||
advancedFor: '進階使用者適用',
|
||||
advancedDescription: '工作流編排以工作流的形式編排聊天機器人,提供高度的自定義,包括編輯內建提示的能力。 它適合有經驗的使用者。',
|
||||
captionName: '圖示 & 名稱',
|
||||
captionName: '應用名稱 & 圖示',
|
||||
appNamePlaceholder: '給你的應用起個名字',
|
||||
captionDescription: '描述',
|
||||
appDescriptionPlaceholder: '輸入應用的描述',
|
||||
|
|
|
@ -11,7 +11,7 @@ export type AppBasicInfo = {
|
|||
use_icon_as_answer_icon: boolean
|
||||
}
|
||||
|
||||
export type AppCategory = 'Writing' | 'Translate' | 'HR' | 'Programming' | 'Assistant'
|
||||
export type AppCategory = 'Writing' | 'Translate' | 'HR' | 'Programming' | 'Assistant' | 'Agent' | 'Recommended' | 'Workflow'
|
||||
|
||||
export type App = {
|
||||
app: AppBasicInfo
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
"@mdx-js/react": "^2.3.0",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@next/mdx": "^14.0.4",
|
||||
"@remixicon/react": "^4.2.0",
|
||||
"@remixicon/react": "^4.5.0",
|
||||
"@sentry/react": "^7.54.0",
|
||||
"@sentry/utils": "^7.54.0",
|
||||
"@svgdotjs/svg.js": "^3.2.4",
|
||||
|
|
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 144 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 180 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 144 KiB |
|
@ -65,12 +65,13 @@ module.exports = {
|
|||
...tailwindThemeVarDefine,
|
||||
},
|
||||
screens: {
|
||||
mobile: '100px',
|
||||
'mobile': '100px',
|
||||
// => @media (min-width: 100px) { ... }
|
||||
tablet: '640px', // 391
|
||||
'tablet': '640px', // 391
|
||||
// => @media (min-width: 600px) { ... }
|
||||
pc: '769px',
|
||||
'pc': '769px',
|
||||
// => @media (min-width: 769px) { ... }
|
||||
'2k': '2560px',
|
||||
},
|
||||
boxShadow: {
|
||||
'xs': '0px 1px 2px 0px rgba(16, 24, 40, 0.05)',
|
||||
|
|
|
@ -2242,10 +2242,10 @@
|
|||
classcat "^5.0.3"
|
||||
zustand "^4.4.1"
|
||||
|
||||
"@remixicon/react@^4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npmjs.org/@remixicon/react/-/react-4.2.0.tgz"
|
||||
integrity sha512-eGhKpZ88OU0qkcY9pJu6khBmItDV82nU130E6C68yc+FbljueHlUYy/4CrJsmf860RIDMay2Rpzl27OSJ81miw==
|
||||
"@remixicon/react@^4.5.0":
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@remixicon/react/-/react-4.5.0.tgz#5600d122ee4995bff2c4442cb056eeb4f11ecb5a"
|
||||
integrity sha512-Xr20SxMpRNlgXZnoF5BCMyZuQEhXY3yJCyms8kxB/vJCCiV1nWdiO48XqRG5LBd1192iSHC4m658AIWi6rmBFg==
|
||||
|
||||
"@rgrove/parse-xml@^4.1.0":
|
||||
version "4.1.0"
|
||||
|
|