Chore: update app detail panel (#13337)

This commit is contained in:
Yi Xiao 2025-02-07 18:56:43 +08:00 committed by GitHub
parent ca19bd31d4
commit ae6f67420c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 368 additions and 370 deletions

View File

@ -161,9 +161,9 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
} }
return ( return (
<div className={cn(s.app, 'flex', 'overflow-hidden')}> <div className={cn(s.app, 'flex relative', 'overflow-hidden')}>
{appDetail && ( {appDetail && (
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background} desc={appDetail.mode} navigation={navigation} /> <AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background as string} desc={appDetail.mode} navigation={navigation} />
)} )}
<div className="bg-components-panel-bg grow overflow-hidden"> <div className="bg-components-panel-bg grow overflow-hidden">
{children} {children}

View File

@ -24,9 +24,11 @@ import AppContext from '@/context/app-context'
export type ICardViewProps = { export type ICardViewProps = {
appId: string appId: string
isInPanel?: boolean
className?: string
} }
const CardView: FC<ICardViewProps> = ({ appId }) => { const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { notify } = useContext(ToastContext) const { notify } = useContext(ToastContext)
const appDetail = useAppStore(state => state.appDetail) const appDetail = useAppStore(state => state.appDetail)
@ -120,10 +122,11 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
return <Loading /> return <Loading />
return ( return (
<div className="grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6"> <div className={className || 'grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'}>
<AppCard <AppCard
appInfo={appDetail} appInfo={appDetail}
cardType="webapp" cardType="webapp"
isInPanel={isInPanel}
onChangeStatus={onChangeSiteStatus} onChangeStatus={onChangeSiteStatus}
onGenerateCode={onGenerateCode} onGenerateCode={onGenerateCode}
onSaveSiteConfig={onSaveSiteConfig} onSaveSiteConfig={onSaveSiteConfig}
@ -131,6 +134,7 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
<AppCard <AppCard
cardType="api" cardType="api"
appInfo={appDetail} appInfo={appDetail}
isInPanel={isInPanel}
onChangeStatus={onChangeApiStatus} onChangeStatus={onChangeApiStatus}
/> />
</div> </div>

View File

@ -31,8 +31,6 @@ const ApiServer: FC<ApiServerProps> = ({
</div> </div>
<SecretKeyButton <SecretKeyButton
className='flex-shrink-0 !h-8 bg-white' className='flex-shrink-0 !h-8 bg-white'
textCls='!text-gray-700 font-medium'
iconCls='stroke-[1.2px]'
/> />
</div> </div>
) )

View File

@ -1,18 +1,18 @@
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { useContext, useContextSelector } from 'use-context-selector' import { useContext, useContextSelector } from 'use-context-selector'
import { RiArrowDownSLine } from '@remixicon/react'
import React, { useCallback, useState } from 'react' import React, { useCallback, useState } from 'react'
import {
RiDeleteBinLine,
RiEditLine,
RiEqualizer2Line,
RiFileCopy2Line,
RiFileDownloadLine,
RiFileUploadLine,
} from '@remixicon/react'
import AppIcon from '../base/app-icon' import AppIcon from '../base/app-icon'
import SwitchAppModal from '../app/switch-app-modal' import SwitchAppModal from '../app/switch-app-modal'
import s from './style.module.css'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import Divider from '@/app/components/base/divider'
import Confirm from '@/app/components/base/confirm' import Confirm from '@/app/components/base/confirm'
import { useStore as useAppStore } from '@/app/components/app/store' import { useStore as useAppStore } from '@/app/components/app/store'
import { ToastContext } from '@/app/components/base/toast' import { ToastContext } from '@/app/components/base/toast'
@ -22,8 +22,6 @@ import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/ap
import DuplicateAppModal from '@/app/components/app/duplicate-modal' import DuplicateAppModal from '@/app/components/app/duplicate-modal'
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal' import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
import CreateAppModal from '@/app/components/explore/create-app-modal' import CreateAppModal from '@/app/components/explore/create-app-modal'
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 type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { getRedirection } from '@/utils/app-redirection' import { getRedirection } from '@/utils/app-redirection'
@ -31,6 +29,9 @@ import UpdateDSLModal from '@/app/components/workflow/update-dsl-modal'
import type { EnvironmentVariable } from '@/app/components/workflow/types' import type { EnvironmentVariable } from '@/app/components/workflow/types'
import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal' import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal'
import { fetchWorkflowDraft } from '@/service/workflow' import { fetchWorkflowDraft } from '@/service/workflow'
import ContentDialog from '@/app/components/base/content-dialog'
import Button from '@/app/components/base/button'
import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView'
export type IAppInfoProps = { export type IAppInfoProps = {
expand: boolean expand: boolean
@ -47,7 +48,6 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
const [showEditModal, setShowEditModal] = useState(false) const [showEditModal, setShowEditModal] = useState(false)
const [showDuplicateModal, setShowDuplicateModal] = useState(false) const [showDuplicateModal, setShowDuplicateModal] = useState(false)
const [showConfirmDelete, setShowConfirmDelete] = useState(false) const [showConfirmDelete, setShowConfirmDelete] = useState(false)
const [showSwitchTip, setShowSwitchTip] = useState<string>('')
const [showSwitchModal, setShowSwitchModal] = useState<boolean>(false) const [showSwitchModal, setShowSwitchModal] = useState<boolean>(false)
const [showImportDSLModal, setShowImportDSLModal] = useState<boolean>(false) const [showImportDSLModal, setShowImportDSLModal] = useState<boolean>(false)
const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([]) const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
@ -183,291 +183,199 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
return null return null
return ( return (
<PortalToFollowElem <div>
open={open} <button
onOpenChange={setOpen} onClick={() => {
placement='bottom-start' if (isCurrentWorkspaceEditor)
offset={4} setOpen(v => !v)
> }}
<div className='relative'> className='block w-full'
<PortalToFollowElemTrigger >
onClick={() => { <div className={cn('flex rounded-lg', expand ? 'p-2 pb-2.5 flex-col gap-2' : 'p-1 gap-1 justify-center items-start', open && 'bg-state-base-hover', isCurrentWorkspaceEditor && 'hover:bg-state-base-hover cursor-pointer')}>
if (isCurrentWorkspaceEditor) <div className={`flex items-center self-stretch ${expand ? 'justify-between' : 'flex-col gap-1'}`}>
setOpen(v => !v) <AppIcon
}} size={expand ? 'large' : 'small'}
className='block' iconType={appDetail.icon_type}
> icon={appDetail.icon}
<div className={cn('flex p-1 rounded-lg', open && 'bg-gray-100', isCurrentWorkspaceEditor && 'hover:bg-gray-100 cursor-pointer')}> background={appDetail.icon_background}
<div className='relative shrink-0 mr-2'> imageUrl={appDetail.icon_url}
<AppIcon />
size={expand ? 'large' : 'small'} <div className='flex p-0.5 justify-center items-center rounded-md'>
iconType={appDetail.icon_type} <div className='flex w-5 h-5 justify-center items-center'>
icon={appDetail.icon} <RiEqualizer2Line className='w-4 h-4 text-text-tertiary' />
background={appDetail.icon_background}
imageUrl={appDetail.icon_url}
/>
<span className={cn(
'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',
!expand && '!w-3.5 !h-3.5 !bottom-[-2px] !right-[-2px]',
)}>
{appDetail.mode === 'advanced-chat' && (
<ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} />
)}
{appDetail.mode === 'agent-chat' && (
<CuteRobot className={cn('w-3 h-3 text-indigo-600', !expand && '!w-2.5 !h-2.5')} />
)}
{appDetail.mode === 'chat' && (
<ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} />
)}
{appDetail.mode === 'completion' && (
<AiText className={cn('w-3 h-3 text-[#0E9384]', !expand && '!w-2.5 !h-2.5')} />
)}
{appDetail.mode === 'workflow' && (
<Route className={cn('w-3 h-3 text-[#f79009]', !expand && '!w-2.5 !h-2.5')} />
)}
</span>
</div>
{expand && (
<div className="grow w-0">
<div className='flex justify-between items-center text-sm leading-5 font-medium text-text-secondary'>
<div className='truncate' title={appDetail.name}>{appDetail.name}</div>
{isCurrentWorkspaceEditor && <RiArrowDownSLine className='shrink-0 ml-[2px] w-3 h-3 text-gray-500' />}
</div>
<div className='flex items-center text-[10px] leading-[18px] font-medium text-gray-500 gap-1'>
{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.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' && (
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
)}
{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.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.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' && (
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
)}
</div>
</div> </div>
)} </div>
</div> </div>
</PortalToFollowElemTrigger> {
<PortalToFollowElemContent className='z-[1002]'> expand && (
<div className='relative w-[320px] bg-white rounded-2xl shadow-xl'> <div className='flex flex-col items-start gap-1'>
{/* header */} <div className='flex w-full'>
<div className={cn('flex pl-4 pt-3 pr-3', !appDetail.description && 'pb-2')}> <div className='text-text-secondary system-md-semibold truncate'>{appDetail.name}</div>
<div className='relative shrink-0 mr-2'>
<AppIcon
size="large"
iconType={appDetail.icon_type}
icon={appDetail.icon}
background={appDetail.icon_background}
imageUrl={appDetail.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'>
{appDetail.mode === 'advanced-chat' && (
<ChatBot className='w-3 h-3 text-[#1570EF]' />
)}
{appDetail.mode === 'agent-chat' && (
<CuteRobot className='w-3 h-3 text-indigo-600' />
)}
{appDetail.mode === 'chat' && (
<ChatBot className='w-3 h-3 text-[#1570EF]' />
)}
{appDetail.mode === 'completion' && (
<AiText className='w-3 h-3 text-[#0E9384]' />
)}
{appDetail.mode === 'workflow' && (
<Route className='w-3 h-3 text-[#f79009]' />
)}
</span>
</div>
<div className='grow w-0'>
<div title={appDetail.name} className='flex justify-between items-center text-sm leading-5 font-medium text-gray-900 truncate'>{appDetail.name}</div>
<div className='flex items-center text-[10px] leading-[18px] font-medium text-gray-500 gap-1'>
{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.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' && (
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
)}
{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.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.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' && (
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
)}
</div> </div>
<div className='text-text-tertiary system-2xs-medium-uppercase'>{appDetail.mode === 'advanced-chat' ? t('app.types.chatbot') : appDetail.mode === 'agent-chat' ? t('app.types.agent') : appDetail.mode === 'chat' ? t('app.types.chatbot') : appDetail.mode === 'completion' ? t('app.types.completion') : t('app.types.workflow')}</div>
</div> </div>
)
}
</div>
</button>
<ContentDialog
show={open}
onClose={() => setOpen(false)}
className='!p-0 flex flex-col absolute left-2 top-2 bottom-2 w-[420px] rounded-2xl'
>
<div className='flex p-4 flex-col justify-center items-start gap-3 self-stretch shrink-0'>
<div className='flex items-center gap-3 self-stretch'>
<AppIcon
size="large"
iconType={appDetail.icon_type}
icon={appDetail.icon}
background={appDetail.icon_background}
imageUrl={appDetail.icon_url}
/>
<div className='flex flex-col justify-center items-start grow w-full'>
<div className='text-text-secondary system-md-semibold truncate w-full'>{appDetail.name}</div>
<div className='text-text-tertiary system-2xs-medium-uppercase'>{appDetail.mode === 'advanced-chat' ? t('app.types.chatbot') : appDetail.mode === 'agent-chat' ? t('app.types.agent') : appDetail.mode === 'chat' ? t('app.types.chatbot') : appDetail.mode === 'completion' ? t('app.types.completion') : t('app.types.workflow')}</div>
</div> </div>
{/* description */} </div>
{appDetail.description && ( {/* description */}
<div className='px-4 py-2 text-gray-500 text-xs leading-[18px]'>{appDetail.description}</div> {appDetail.description && (
)} <div className='text-text-tertiary system-xs-regular'>{appDetail.description}</div>
{/* operations */} )}
<Divider className="!my-1" /> {/* operations */}
<div className="w-full py-1"> <div className='flex items-center gap-1 self-stretch'>
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => { <Button
size={'small'}
variant={'secondary'}
className='gap-[1px]'
onClick={() => {
setOpen(false) setOpen(false)
setShowEditModal(true) setShowEditModal(true)
}}> }}
<span className='text-gray-700 text-sm leading-5'>{t('app.editApp')}</span> >
</div> <RiEditLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => { <span className='text-components-button-secondary-text system-xs-medium'>{t('app.editApp')}</span>
</Button>
<Button
size={'small'}
variant={'secondary'}
className='gap-[1px]'
onClick={() => {
setOpen(false) setOpen(false)
setShowDuplicateModal(true) setShowDuplicateModal(true)
}}> }}
<span className='text-gray-700 text-sm leading-5'>{t('app.duplicate')}</span>
</div>
{(appDetail.mode === 'completion' || appDetail.mode === 'chat') && (
<>
<Divider className="!my-1" />
<div
className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer'
onMouseEnter={() => setShowSwitchTip(appDetail.mode)}
onMouseLeave={() => setShowSwitchTip('')}
onClick={() => {
setOpen(false)
setShowSwitchModal(true)
}}
>
<span className='text-gray-700 text-sm leading-5'>{t('app.switch')}</span>
</div>
</>
)}
<Divider className="!my-1" />
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={exportCheck}>
<span className='text-gray-700 text-sm leading-5'>{t('app.export')}</span>
</div>
{
(appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (
<div
className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer'
onClick={() => {
setOpen(false)
setShowImportDSLModal(true)
}}>
<span className='text-gray-700 text-sm leading-5'>{t('workflow.common.importDSL')}</span>
</div>
)
}
<Divider className="!my-1" />
<div className='group h-9 py-2 px-3 mx-1 flex items-center hover:bg-red-50 rounded-lg cursor-pointer' onClick={() => {
setOpen(false)
setShowConfirmDelete(true)
}}>
<span className='text-gray-700 text-sm leading-5 group-hover:text-red-500'>
{t('common.operation.delete')}
</span>
</div>
</div>
{/* switch tip */}
<div
className={cn(
'hidden absolute left-[324px] top-0 w-[376px] rounded-xl bg-white border-[0.5px] border-[rgba(0,0,0,0.05)] shadow-lg',
showSwitchTip && '!block',
)}
> >
<div className={cn( <RiFileCopy2Line className='w-3.5 h-3.5 text-components-button-secondary-text' />
'w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl', <span className='text-components-button-secondary-text system-xs-medium'>{t('app.duplicate')}</span>
showSwitchTip === 'chat' && s.expertPic, </Button>
showSwitchTip === 'completion' && s.completionPic, <Button
)} /> size={'small'}
<div className='px-4 pb-2'> variant={'secondary'}
<div className='flex items-center gap-1 text-gray-700 text-md leading-6 font-semibold'> className='gap-[1px]'
{showSwitchTip === 'chat' ? t('app.types.advanced') : t('app.types.workflow')} onClick={exportCheck}
<span className='px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span> >
</div> <RiFileDownloadLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
<div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.advancedFor').toLocaleUpperCase()}</div> <span className='text-components-button-secondary-text system-xs-medium'>{t('app.export')}</span>
<div className='mt-1 text-gray-500 text-sm leading-5'>{t('app.newApp.advancedDescription')}</div> </Button>
</div> {
</div> (appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (
<Button
size={'small'}
variant={'secondary'}
className='gap-[1px]'
onClick={() => {
setOpen(false)
setShowImportDSLModal(true)
}}
>
<RiFileUploadLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
<span className='text-components-button-secondary-text system-xs-medium'>{t('workflow.common.importDSL')}</span>
</Button>
)
}
</div> </div>
</PortalToFollowElemContent> </div>
{showSwitchModal && ( <div className='flex flex-1'>
<SwitchAppModal <CardView
inAppDetail appId={appDetail.id}
show={showSwitchModal} isInPanel={true}
appDetail={appDetail} className='flex flex-col px-2 py-1 gap-2 grow overflow-auto'
onClose={() => setShowSwitchModal(false)}
onSuccess={() => setShowSwitchModal(false)}
/> />
)} </div>
{showEditModal && ( <div className='flex p-2 flex-col justify-center items-start gap-3 self-stretch border-t-[0.5px] border-divider-subtle shrink-0 min-h-fit'>
<CreateAppModal <Button
isEditModal size={'medium'}
appName={appDetail.name} variant={'ghost'}
appIconType={appDetail.icon_type} className='gap-0.5'
appIcon={appDetail.icon} onClick={() => {
appIconBackground={appDetail.icon_background} setOpen(false)
appIconUrl={appDetail.icon_url} setShowConfirmDelete(true)
appDescription={appDetail.description} }}
appMode={appDetail.mode} >
appUseIconAsAnswerIcon={appDetail.use_icon_as_answer_icon} <RiDeleteBinLine className='w-4 h-4 text-text-tertiary' />
show={showEditModal} <span className='text-text-tertiary system-sm-medium'>{t('common.operation.deleteApp')}</span>
onConfirm={onEdit} </Button>
onHide={() => setShowEditModal(false)} </div>
/> </ContentDialog>
)} {showSwitchModal && (
{showDuplicateModal && ( <SwitchAppModal
<DuplicateAppModal inAppDetail
appName={appDetail.name} show={showSwitchModal}
icon_type={appDetail.icon_type} appDetail={appDetail}
icon={appDetail.icon} onClose={() => setShowSwitchModal(false)}
icon_background={appDetail.icon_background} onSuccess={() => setShowSwitchModal(false)}
icon_url={appDetail.icon_url} />
show={showDuplicateModal} )}
onConfirm={onCopy} {showEditModal && (
onHide={() => setShowDuplicateModal(false)} <CreateAppModal
/> isEditModal
)} appName={appDetail.name}
{showConfirmDelete && ( appIconType={appDetail.icon_type}
<Confirm appIcon={appDetail.icon}
title={t('app.deleteAppConfirmTitle')} appIconBackground={appDetail.icon_background}
content={t('app.deleteAppConfirmContent')} appIconUrl={appDetail.icon_url}
isShow={showConfirmDelete} appDescription={appDetail.description}
onConfirm={onConfirmDelete} appMode={appDetail.mode}
onCancel={() => setShowConfirmDelete(false)} appUseIconAsAnswerIcon={appDetail.use_icon_as_answer_icon}
/> show={showEditModal}
)} onConfirm={onEdit}
{showImportDSLModal && ( onHide={() => setShowEditModal(false)}
<UpdateDSLModal />
onCancel={() => setShowImportDSLModal(false)} )}
onBackup={exportCheck} {showDuplicateModal && (
/> <DuplicateAppModal
)} appName={appDetail.name}
{secretEnvList.length > 0 && ( icon_type={appDetail.icon_type}
<DSLExportConfirmModal icon={appDetail.icon}
envList={secretEnvList} icon_background={appDetail.icon_background}
onConfirm={onExport} icon_url={appDetail.icon_url}
onClose={() => setSecretEnvList([])} show={showDuplicateModal}
/> onConfirm={onCopy}
)} onHide={() => setShowDuplicateModal(false)}
</div> />
</PortalToFollowElem> )}
{showConfirmDelete && (
<Confirm
title={t('app.deleteAppConfirmTitle')}
content={t('app.deleteAppConfirmContent')}
isShow={showConfirmDelete}
onConfirm={onConfirmDelete}
onCancel={() => setShowConfirmDelete(false)}
/>
)}
{showImportDSLModal && (
<UpdateDSLModal
onCancel={() => setShowImportDSLModal(false)}
onBackup={exportCheck}
/>
)}
{secretEnvList.length > 0 && (
<DSLExportConfirmModal
envList={secretEnvList}
onConfirm={onExport}
onClose={() => setSecretEnvList([])}
/>
)}
</div>
) )
} }

View File

@ -58,7 +58,7 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<div className="flex items-start p-1"> <div className="flex items-center grow">
{icon && icon_background && iconType === 'app' && ( {icon && icon_background && iconType === 'app' && (
<div className='flex-shrink-0 mr-3'> <div className='flex-shrink-0 mr-3'>
<AppIcon icon={icon} background={icon_background} /> <AppIcon icon={icon} background={icon_background} />
@ -71,8 +71,10 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
} }
{mode === 'expand' && <div className="group"> {mode === 'expand' && <div className="group">
<div className={`flex flex-row items-center text-sm font-semibold text-gray-700 group-hover:text-gray-900 break-all ${textStyle?.main ?? ''}`}> <div className={`flex flex-row items-center system-md-semibold text-text-secondary group-hover:text-text-primary ${textStyle?.main ?? ''}`}>
{name} <div className="max-w-[180px] truncate">
{name}
</div>
{hoverTip {hoverTip
&& <Tooltip && <Tooltip
popupContent={ popupContent={
@ -86,7 +88,6 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
/> />
} }
</div> </div>
<div className={`text-xs font-normal text-gray-500 group-hover:text-gray-700 break-all ${textStyle?.extra ?? ''}`}>{type}</div>
<div className='text-text-tertiary system-2xs-medium-uppercase'>{isExternal ? t('dataset.externalTag') : ''}</div> <div className='text-text-tertiary system-2xs-medium-uppercase'>{isExternal ? t('dataset.externalTag') : ''}</div>
</div>} </div>}
</div> </div>

View File

@ -57,7 +57,7 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
<div <div
className={` className={`
shrink-0 shrink-0
${expand ? 'p-3' : 'p-2'} ${expand ? 'p-2' : 'p-1'}
`} `}
> >
{iconType === 'app' && ( {iconType === 'app' && (

View File

@ -1,14 +1,14 @@
'use client' 'use client'
import type { HTMLProps } from 'react'
import React, { useMemo, useState } from 'react' import React, { useMemo, useState } from 'react'
import {
Cog8ToothIcon,
DocumentTextIcon,
PaintBrushIcon,
RocketLaunchIcon,
} from '@heroicons/react/24/outline'
import { usePathname, useRouter } from 'next/navigation' import { usePathname, useRouter } from 'next/navigation'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import {
RiBookOpenLine,
RiEqualizer2Line,
RiExternalLinkLine,
RiPaintBrushLine,
RiWindowLine,
} from '@remixicon/react'
import SettingsModal from './settings' import SettingsModal from './settings'
import EmbeddedModal from './embedded' import EmbeddedModal from './embedded'
import CustomizeModal from './customize' import CustomizeModal from './customize'
@ -18,7 +18,6 @@ import Tooltip from '@/app/components/base/tooltip'
import AppBasic from '@/app/components/app-sidebar/basic' import AppBasic from '@/app/components/app-sidebar/basic'
import { asyncRunSafe, randomString } from '@/utils' import { asyncRunSafe, randomString } from '@/utils'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import Tag from '@/app/components/base/tag'
import Switch from '@/app/components/base/switch' import Switch from '@/app/components/base/switch'
import Divider from '@/app/components/base/divider' import Divider from '@/app/components/base/divider'
import CopyFeedback from '@/app/components/base/copy-feedback' import CopyFeedback from '@/app/components/base/copy-feedback'
@ -28,10 +27,12 @@ import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-butt
import type { AppDetailResponse } from '@/models/app' import type { AppDetailResponse } from '@/models/app'
import { useAppContext } from '@/context/app-context' import { useAppContext } from '@/context/app-context'
import type { AppSSO } from '@/types/app' import type { AppSSO } from '@/types/app'
import Indicator from '@/app/components/header/indicator'
export type IAppCardProps = { export type IAppCardProps = {
className?: string className?: string
appInfo: AppDetailResponse & Partial<AppSSO> appInfo: AppDetailResponse & Partial<AppSSO>
isInPanel?: boolean
cardType?: 'api' | 'webapp' cardType?: 'api' | 'webapp'
customBgColor?: string customBgColor?: string
onChangeStatus: (val: boolean) => Promise<void> onChangeStatus: (val: boolean) => Promise<void>
@ -39,12 +40,9 @@ export type IAppCardProps = {
onGenerateCode?: () => Promise<void> onGenerateCode?: () => Promise<void>
} }
const EmbedIcon = ({ className = '' }: HTMLProps<HTMLDivElement>) => {
return <div className={`${style.codeBrowserIcon} ${className}`}></div>
}
function AppCard({ function AppCard({
appInfo, appInfo,
isInPanel,
cardType = 'webapp', cardType = 'webapp',
customBgColor, customBgColor,
onChangeStatus, onChangeStatus,
@ -66,17 +64,18 @@ function AppCard({
const OPERATIONS_MAP = useMemo(() => { const OPERATIONS_MAP = useMemo(() => {
const operationsMap = { const operationsMap = {
webapp: [ webapp: [
{ opName: t('appOverview.overview.appInfo.preview'), opIcon: RocketLaunchIcon }, { opName: t('appOverview.overview.appInfo.launch'), opIcon: RiExternalLinkLine },
{ opName: t('appOverview.overview.appInfo.customize.entry'), opIcon: PaintBrushIcon },
] as { opName: string; opIcon: any }[], ] as { opName: string; opIcon: any }[],
api: [{ opName: t('appOverview.overview.apiInfo.doc'), opIcon: DocumentTextIcon }], api: [{ opName: t('appOverview.overview.apiInfo.doc'), opIcon: RiBookOpenLine }],
app: [], app: [],
} }
if (appInfo.mode !== 'completion' && appInfo.mode !== 'workflow') if (appInfo.mode !== 'completion' && appInfo.mode !== 'workflow')
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.embedded.entry'), opIcon: EmbedIcon }) operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.embedded.entry'), opIcon: RiWindowLine })
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.customize.entry'), opIcon: RiPaintBrushLine })
if (isCurrentWorkspaceEditor) if (isCurrentWorkspaceEditor)
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.settings.entry'), opIcon: Cog8ToothIcon }) operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.settings.entry'), opIcon: RiEqualizer2Line })
return operationsMap return operationsMap
}, [isCurrentWorkspaceEditor, appInfo, t]) }, [isCurrentWorkspaceEditor, appInfo, t])
@ -92,13 +91,9 @@ function AppCard({
const appUrl = `${app_base_url}/${appMode}/${access_token}` const appUrl = `${app_base_url}/${appMode}/${access_token}`
const apiUrl = appInfo?.api_base_url const apiUrl = appInfo?.api_base_url
let bgColor = 'bg-primary-50 bg-opacity-40'
if (cardType === 'api')
bgColor = 'bg-purple-50'
const genClickFuncByName = (opName: string) => { const genClickFuncByName = (opName: string) => {
switch (opName) { switch (opName) {
case t('appOverview.overview.appInfo.preview'): case t('appOverview.overview.appInfo.launch'):
return () => { return () => {
window.open(appUrl, '_blank') window.open(appUrl, '_blank')
} }
@ -135,49 +130,50 @@ function AppCard({
return ( return (
<div <div
className={ className={
`shadow-xs border-[0.5px] rounded-lg border-gray-200 ${className ?? ''}`} `${isInPanel ? 'border-l-[0.5px] border-t' : 'shadow-xs border-[0.5px]'} rounded-xl border-effects-highlight w-full max-w-full ${className ?? ''}`}
> >
<div className={`px-6 py-5 ${customBgColor ?? bgColor} rounded-lg`}> <div className={`${customBgColor ?? 'bg-background-default'} rounded-xl`}>
<div className="mb-2.5 flex flex-row items-start justify-between"> <div className='flex flex-col p-3 justify-center items-start gap-3 self-stretch border-b-[0.5px] border-divider-subtle w-full'>
<AppBasic <div className='flex items-center gap-3 self-stretch w-full'>
iconType={cardType} <AppBasic
icon={appInfo.icon} iconType={cardType}
icon_background={appInfo.icon_background} icon={appInfo.icon}
name={basicName} icon_background={appInfo.icon_background}
type={ name={basicName}
isApp type={
? t('appOverview.overview.appInfo.explanation') isApp
: t('appOverview.overview.apiInfo.explanation') ? t('appOverview.overview.appInfo.explanation')
} : t('appOverview.overview.apiInfo.explanation')
/> }
<div className="flex flex-row items-center h-9"> />
<Tag className="mr-2" color={runningStatus ? 'green' : 'yellow'}> <div className='flex items-center gap-1'>
{runningStatus <Indicator color={runningStatus ? 'green' : 'yellow'} />
? t('appOverview.overview.status.running') <div className={`${runningStatus ? 'text-text-success' : 'text-text-warning'} system-xs-semibold-uppercase`}>
: t('appOverview.overview.status.disable')} {runningStatus
</Tag> ? t('appOverview.overview.status.running')
: t('appOverview.overview.status.disable')}
</div>
</div>
<Switch defaultValue={runningStatus} onChange={onChangeStatus} disabled={toggleDisabled} /> <Switch defaultValue={runningStatus} onChange={onChangeStatus} disabled={toggleDisabled} />
</div> </div>
</div> <div className='flex flex-col justify-center items-start self-stretch'>
<div className="flex flex-col justify-center py-2"> <div className="pb-1 system-xs-medium text-text-tertiary">
<div className="py-1">
<div className="pb-1 text-xs text-gray-500">
{isApp {isApp
? t('appOverview.overview.appInfo.accessibleAddress') ? t('appOverview.overview.appInfo.accessibleAddress')
: t('appOverview.overview.apiInfo.accessibleAddress')} : t('appOverview.overview.apiInfo.accessibleAddress')}
</div> </div>
<div className="w-full h-9 pl-2 pr-0.5 py-0.5 bg-black bg-opacity-2 rounded-lg border border-black border-opacity-5 justify-start items-center inline-flex"> <div className="w-full h-9 pl-2 p-1 bg-components-input-bg-normal rounded-lg items-center inline-flex gap-0.5">
<div className="h-4 px-2 justify-start items-start gap-2 flex flex-1 min-w-0"> <div className="h-4 px-1 justify-start items-start gap-2 flex flex-1 min-w-0">
<div className="text-gray-700 text-xs font-medium text-ellipsis overflow-hidden whitespace-nowrap"> <div className="text-text-secondary text-xs font-medium text-ellipsis overflow-hidden whitespace-nowrap">
{isApp ? appUrl : apiUrl} {isApp ? appUrl : apiUrl}
</div> </div>
</div> </div>
<Divider type="vertical" className="!h-3.5 shrink-0 !mx-0.5" />
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} selectorId={randomString(8)} className={'hover:bg-gray-200'} />}
<CopyFeedback <CopyFeedback
content={isApp ? appUrl : apiUrl} content={isApp ? appUrl : apiUrl}
className={'hover:bg-gray-200'} className={'!size-6'}
/> />
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} className='z-50 !size-6 hover:bg-state-base-hover rounded-md' selectorId={randomString(8)} />}
{isApp && <Divider type="vertical" className="!h-3.5 shrink-0 !mx-0.5" />}
{/* button copy link/ button regenerate */} {/* button copy link/ button regenerate */}
{showConfirmDelete && ( {showConfirmDelete && (
<Confirm <Confirm
@ -197,7 +193,7 @@ function AppCard({
popupContent={t('appOverview.overview.appInfo.regenerate') || ''} popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
> >
<div <div
className="w-8 h-8 ml-0.5 cursor-pointer hover:bg-gray-200 rounded-lg" className="w-6 h-6 cursor-pointer hover:bg-state-base-hover rounded-md"
onClick={() => setShowConfirmDelete(true)} onClick={() => setShowConfirmDelete(true)}
> >
<div <div
@ -210,8 +206,8 @@ function AppCard({
</div> </div>
</div> </div>
</div> </div>
<div className={'pt-2 flex flex-row items-center flex-wrap gap-y-2'}> <div className={'flex p-3 items-center gap-1 self-stretch'}>
{!isApp && <SecretKeyButton className='flex-shrink-0 !h-8 bg-white mr-2' textCls='!text-gray-700 font-medium' iconCls='stroke-[1.2px]' appId={appInfo.id} />} {!isApp && <SecretKeyButton appId={appInfo.id} />}
{OPERATIONS_MAP[cardType].map((op) => { {OPERATIONS_MAP[cardType].map((op) => {
const disabled const disabled
= op.opName === t('appOverview.overview.appInfo.settings.entry') = op.opName === t('appOverview.overview.appInfo.settings.entry')
@ -219,7 +215,9 @@ function AppCard({
: !runningStatus : !runningStatus
return ( return (
<Button <Button
className="mr-2" className="mr-1 min-w-[88px]"
size="small"
variant={'ghost'}
key={op.opName} key={op.opName}
onClick={genClickFuncByName(op.opName)} onClick={genClickFuncByName(op.opName)}
disabled={disabled} disabled={disabled}
@ -230,9 +228,9 @@ function AppCard({
} }
popupClassName={disabled ? 'mt-[-8px]' : '!hidden'} popupClassName={disabled ? 'mt-[-8px]' : '!hidden'}
> >
<div className="flex flex-row items-center"> <div className="flex items-center justify-center gap-[1px]">
<op.opIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" /> <op.opIcon className="h-3.5 w-3.5" />
<span className="text-[13px]">{op.opName}</span> <div className={`${runningStatus ? 'text-text-tertiary' : 'text-components-button-ghost-text-disabled'} system-xs-medium px-[3px]`}>{op.opName}</div>
</div> </div>
</Tooltip> </Tooltip>
</Button> </Button>

View File

@ -0,0 +1,59 @@
import { Fragment, type ReactNode } from 'react'
import { Transition } from '@headlessui/react'
import classNames from '@/utils/classnames'
type ContentDialogProps = {
className?: string
show: boolean
onClose?: () => void
children: ReactNode
}
const ContentDialog = ({
className,
show,
onClose,
children,
}: ContentDialogProps) => {
return (
<Transition
show={show}
as="div"
className="absolute left-0 top-0 w-full h-full z-20 p-2 box-border"
>
<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="absolute left-0 inset-0 w-full bg-app-detail-overlay-bg"
onClick={onClose}
/>
</Transition.Child>
<Transition.Child
as={Fragment}
enter="transform transition ease-out duration-300"
enterFrom="-translate-x-full"
enterTo="translate-x-0"
leave="transform transition ease-in duration-200"
leaveFrom="translate-x-0"
leaveTo="-translate-x-full"
>
<div className={classNames(
'absolute left-0 w-full bg-app-detail-bg border-r border-divider-burn',
className,
)}>
{children}
</div>
</Transition.Child>
</Transition>
)
}
export default ContentDialog

View File

@ -35,7 +35,7 @@ const CopyFeedback = ({ content, className }: Props) => {
} }
> >
<div <div
className={`w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg ${ className={`w-8 h-8 cursor-pointer hover:bg-state-base-hover rounded-md ${
className ?? '' className ?? ''
}`} }`}
> >

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import QRCode from 'qrcode.react' import { QRCodeSVG } from 'qrcode.react'
import QrcodeStyle from './style.module.css' import QrcodeStyle from './style.module.css'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
@ -54,20 +54,20 @@ const ShareQRCode = ({ content, selectorId, className }: Props) => {
popupContent={t(`${prefixEmbedded}`) || ''} popupContent={t(`${prefixEmbedded}`) || ''}
> >
<div <div
className={`w-8 h-8 cursor-pointer rounded-lg ${className ?? ''}`} className={`w-8 h-8 cursor-pointer rounded-lg relative ${className ?? ''}`}
onClick={toggleQRCode} onClick={toggleQRCode}
> >
<div className={`w-full h-full ${QrcodeStyle.QrcodeIcon} ${isShow ? QrcodeStyle.show : ''}`} /> <div className={`w-full h-full ${QrcodeStyle.QrcodeIcon} ${isShow ? QrcodeStyle.show : ''}`} />
{isShow && ( {isShow && (
<div <div
ref={qrCodeRef} ref={qrCodeRef}
className={QrcodeStyle.qrcodeform} className={`${QrcodeStyle.qrcodeform} !absolute right-0 top-0`}
onClick={handlePanelClick} onClick={handlePanelClick}
> >
<QRCode size={160} value={content} className={QrcodeStyle.qrcodeimage}/> <QRCodeSVG size={160} value={content} className={QrcodeStyle.qrcodeimage}/>
<div className={QrcodeStyle.text}> <div className={QrcodeStyle.text}>
<div className={`text-gray-500 ${QrcodeStyle.scan}`}>{t('appOverview.overview.appInfo.qrcode.scan')}</div> <div className={`text-text-tertiary ${QrcodeStyle.scan}`}>{t('appOverview.overview.appInfo.qrcode.scan')}</div>
<div className={`text-gray-500 ${QrcodeStyle.scan}`}>·</div> <div className={`text-text-tertiary ${QrcodeStyle.scan}`}>·</div>
<div className={QrcodeStyle.download} onClick={downloadQR}>{t('appOverview.overview.appInfo.qrcode.download')}</div> <div className={QrcodeStyle.download} onClick={downloadQR}>{t('appOverview.overview.appInfo.qrcode.download')}</div>
</div> </div>
</div> </div>

View File

@ -1,29 +1,31 @@
'use client' 'use client'
import { useState } from 'react' import { useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { RiKey2Line } from '@remixicon/react'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import SecretKeyModal from '@/app/components/develop/secret-key/secret-key-modal' import SecretKeyModal from '@/app/components/develop/secret-key/secret-key-modal'
// import { KeyIcon } from '@heroicons/react/20/solid'
type ISecretKeyButtonProps = { type ISecretKeyButtonProps = {
className?: string className?: string
appId?: string appId?: string
iconCls?: string
textCls?: string textCls?: string
} }
const SecretKeyButton = ({ className, appId, iconCls, textCls }: ISecretKeyButtonProps) => { const SecretKeyButton = ({ className, appId, textCls }: ISecretKeyButtonProps) => {
const [isVisible, setVisible] = useState(false) const [isVisible, setVisible] = useState(false)
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<> <>
<Button className={`px-3 ${className}`} onClick={() => setVisible(true)}> <Button
<div className={'flex items-center justify-center w-4 h-4 mr-2'}> className={`px-3 ${className}`}
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" className={iconCls}> onClick={() => setVisible(true)}
<path d="M9 3.66672C9.35362 3.66672 9.69276 3.80719 9.94281 4.05724C10.1929 4.30729 10.3333 4.64643 10.3333 5.00005M13 5.00005C13.0002 5.62483 12.854 6.24097 12.5732 6.79908C12.2924 7.3572 11.8847 7.84177 11.3829 8.21397C10.8811 8.58617 10.2991 8.83564 9.68347 8.94239C9.06788 9.04915 8.43584 9.01022 7.838 8.82872L6.33333 10.3334H5V11.6667H3.66667V13.0001H1.66667C1.48986 13.0001 1.32029 12.9298 1.19526 12.8048C1.07024 12.6798 1 12.5102 1 12.3334V10.6094C1.00004 10.4326 1.0703 10.263 1.19533 10.1381L5.17133 6.16205C5.00497 5.61206 4.95904 5.03268 5.0367 4.46335C5.11435 3.89402 5.31375 3.3481 5.62133 2.86275C5.92891 2.3774 6.33744 1.96401 6.81913 1.65073C7.30082 1.33745 7.84434 1.13162 8.41272 1.04725C8.9811 0.96289 9.56098 1.00197 10.1129 1.16184C10.6648 1.32171 11.1758 1.59861 11.6111 1.97369C12.0464 2.34878 12.3958 2.81324 12.6354 3.33548C12.8751 3.85771 12.9994 4.42545 13 5.00005Z" stroke="#1F2A37" strokeLinecap="round" strokeLinejoin="round" /> size='small'
</svg> variant='ghost'
>
<div className={'flex items-center justify-center w-3.5 h-3.5'}>
<RiKey2Line className='w-3.5 h-3.5 text-text-tertiary' />
</div> </div>
<div className={`text-[13px] text-gray-800 ${textCls}`}>{t('appApi.apiKey')}</div> <div className={`text-text-tertiary system-xs-medium px-[3px] ${textCls}`}>{t('appApi.apiKey')}</div>
</Button> </Button>
<SecretKeyModal isShow={isVisible} onClose={() => setVisible(false)} appId={appId} /> <SecretKeyModal isShow={isVisible} onClose={() => setVisible(false)} appId={appId} />
</> </>

View File

@ -33,6 +33,7 @@ const translation = {
explanation: 'Ready-to-use AI WebApp', explanation: 'Ready-to-use AI WebApp',
accessibleAddress: 'Public URL', accessibleAddress: 'Public URL',
preview: 'Preview', preview: 'Preview',
launch: 'Launch',
regenerate: 'Regenerate', regenerate: 'Regenerate',
regenerateNotice: 'Do you want to regenerate the public URL?', regenerateNotice: 'Do you want to regenerate the public URL?',
preUseReminder: 'Please enable WebApp before continuing.', preUseReminder: 'Please enable WebApp before continuing.',

View File

@ -27,6 +27,7 @@ const translation = {
sure: 'I\'m sure', sure: 'I\'m sure',
download: 'Download', download: 'Download',
delete: 'Delete', delete: 'Delete',
deleteApp: 'Delete App',
settings: 'Settings', settings: 'Settings',
setup: 'Setup', setup: 'Setup',
getForFree: 'Get for free', getForFree: 'Get for free',

View File

@ -33,6 +33,7 @@ const translation = {
explanation: '开箱即用的 AI WebApp', explanation: '开箱即用的 AI WebApp',
accessibleAddress: '公开访问 URL', accessibleAddress: '公开访问 URL',
preview: '预览', preview: '预览',
launch: '启动',
regenerate: '重新生成', regenerate: '重新生成',
regenerateNotice: '您是否要重新生成公开访问 URL', regenerateNotice: '您是否要重新生成公开访问 URL',
preUseReminder: '使用前请先打开开关', preUseReminder: '使用前请先打开开关',

View File

@ -27,6 +27,7 @@ const translation = {
sure: '我确定', sure: '我确定',
download: '下载', download: '下载',
delete: '删除', delete: '删除',
deleteApp: '删除应用',
settings: '设置', settings: '设置',
setup: '设置', setup: '设置',
getForFree: '免费获取', getForFree: '免费获取',

View File

@ -99,6 +99,8 @@ const config = {
'chatbot-bg': 'var(--color-chatbot-bg)', 'chatbot-bg': 'var(--color-chatbot-bg)',
'chat-bubble-bg': 'var(--color-chat-bubble-bg)', 'chat-bubble-bg': 'var(--color-chat-bubble-bg)',
'workflow-process-bg': 'var(--color-workflow-process-bg)', 'workflow-process-bg': 'var(--color-workflow-process-bg)',
'app-detail-bg': 'var(--color-app-detail-bg)',
'app-detail-overlay-bg': 'var(--color-app-detail-overlay-bg)',
'dataset-chunk-process-success-bg': 'var(--color-dataset-chunk-process-success-bg)', 'dataset-chunk-process-success-bg': 'var(--color-dataset-chunk-process-success-bg)',
'dataset-chunk-process-error-bg': 'var(--color-dataset-chunk-process-error-bg)', 'dataset-chunk-process-error-bg': 'var(--color-dataset-chunk-process-error-bg)',
'dataset-chunk-detail-card-hover-bg': 'var(--color-dataset-chunk-detail-card-hover-bg)', 'dataset-chunk-detail-card-hover-bg': 'var(--color-dataset-chunk-detail-card-hover-bg)',

View File

@ -19,6 +19,17 @@ html[data-theme="dark"] {
rgba(34, 34, 37, 0.9) -0.1%, rgba(34, 34, 37, 0.9) -0.1%,
rgba(29, 29, 32, 0.9) 98.26% rgba(29, 29, 32, 0.9) 98.26%
); );
--color-app-detail-bg: linear-gradient(
169deg,
#1D1D20 1.18%,
#222225 99.52%
);
--color-app-detail-overlay-bg: linear-gradient(
270deg,
rgba(0, 0, 0, 0.00) 0%,
rgba(24, 24, 27, 0.02) 8%,
rgba(24, 24, 27, 0.54) 100%
);
--color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%); --color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
--color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%); --color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
--color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #1D1D20 0%, #222225 100%); --color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #1D1D20 0%, #222225 100%);

View File

@ -19,6 +19,17 @@ html[data-theme="light"] {
rgba(249, 250, 251, 0.9) -0.1%, rgba(249, 250, 251, 0.9) -0.1%,
rgba(242, 244, 247, 0.9) 98.26% rgba(242, 244, 247, 0.9) 98.26%
); );
--color-app-detail-bg: linear-gradient(
169deg,
#F2F4F7 1.18%,
#F9FAFB 99.52%
);
--color-app-detail-overlay-bg: linear-gradient(
270deg,
rgba(0, 0, 0, 0.00) 0%,
rgba(16, 24, 40, 0.01) 8%,
rgba(16, 24, 40, 0.18) 100%
);
--color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%); --color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
--color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%); --color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
--color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #F2F4F7 0%, #F9FAFB 100%); --color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #F2F4F7 0%, #F9FAFB 100%);