Forráskód Böngészése

Chore: update app detail panel (#13337)

Yi Xiao 2 hónapja
szülő
commit
ae6f67420c

+ 2 - 2
web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx

@@ -161,9 +161,9 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
   }
 
   return (
-    <div className={cn(s.app, 'flex', 'overflow-hidden')}>
+    <div className={cn(s.app, 'flex relative', 'overflow-hidden')}>
       {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">
         {children}

+ 6 - 2
web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView.tsx

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

+ 0 - 2
web/app/(commonLayout)/datasets/ApiServer.tsx

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

+ 192 - 284
web/app/components/app-sidebar/app-info.tsx

@@ -1,18 +1,18 @@
 import { useTranslation } from 'react-i18next'
 import { useRouter } from 'next/navigation'
 import { useContext, useContextSelector } from 'use-context-selector'
-import { RiArrowDownSLine } from '@remixicon/react'
 import React, { useCallback, useState } from 'react'
+import {
+  RiDeleteBinLine,
+  RiEditLine,
+  RiEqualizer2Line,
+  RiFileCopy2Line,
+  RiFileDownloadLine,
+  RiFileUploadLine,
+} from '@remixicon/react'
 import AppIcon from '../base/app-icon'
 import SwitchAppModal from '../app/switch-app-modal'
-import s from './style.module.css'
 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 { useStore as useAppStore } from '@/app/components/app/store'
 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 type { DuplicateAppModalProps } from '@/app/components/app/duplicate-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 { NEED_REFRESH_APP_LIST_KEY } from '@/config'
 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 DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal'
 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 = {
   expand: boolean
@@ -47,7 +48,6 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
   const [showEditModal, setShowEditModal] = useState(false)
   const [showDuplicateModal, setShowDuplicateModal] = useState(false)
   const [showConfirmDelete, setShowConfirmDelete] = useState(false)
-  const [showSwitchTip, setShowSwitchTip] = useState<string>('')
   const [showSwitchModal, setShowSwitchModal] = useState<boolean>(false)
   const [showImportDSLModal, setShowImportDSLModal] = useState<boolean>(false)
   const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
@@ -183,291 +183,199 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
     return null
 
   return (
-    <PortalToFollowElem
-      open={open}
-      onOpenChange={setOpen}
-      placement='bottom-start'
-      offset={4}
-    >
-      <div className='relative'>
-        <PortalToFollowElemTrigger
-          onClick={() => {
-            if (isCurrentWorkspaceEditor)
-              setOpen(v => !v)
-          }}
-          className='block'
-        >
-          <div className={cn('flex p-1 rounded-lg', open && 'bg-gray-100', isCurrentWorkspaceEditor && 'hover:bg-gray-100 cursor-pointer')}>
-            <div className='relative shrink-0 mr-2'>
-              <AppIcon
-                size={expand ? 'large' : 'small'}
-                iconType={appDetail.icon_type}
-                icon={appDetail.icon}
-                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>
+      <button
+        onClick={() => {
+          if (isCurrentWorkspaceEditor)
+            setOpen(v => !v)
+        }}
+        className='block w-full'
+      >
+        <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')}>
+          <div className={`flex items-center self-stretch ${expand ? 'justify-between' : 'flex-col gap-1'}`}>
+            <AppIcon
+              size={expand ? 'large' : 'small'}
+              iconType={appDetail.icon_type}
+              icon={appDetail.icon}
+              background={appDetail.icon_background}
+              imageUrl={appDetail.icon_url}
+            />
+            <div className='flex p-0.5 justify-center items-center rounded-md'>
+              <div className='flex w-5 h-5 justify-center items-center'>
+                <RiEqualizer2Line className='w-4 h-4 text-text-tertiary' />
               </div>
-            )}
+            </div>
           </div>
-        </PortalToFollowElemTrigger>
-        <PortalToFollowElemContent className='z-[1002]'>
-          <div className='relative w-[320px] bg-white rounded-2xl shadow-xl'>
-            {/* header */}
-            <div className={cn('flex pl-4 pt-3 pr-3', !appDetail.description && 'pb-2')}>
-              <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>
-                  )}
+          {
+            expand && (
+              <div className='flex flex-col items-start gap-1'>
+                <div className='flex w-full'>
+                  <div className='text-text-secondary system-md-semibold truncate'>{appDetail.name}</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>
+      </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>
-            {/* description */}
-            {appDetail.description && (
-              <div className='px-4 py-2 text-gray-500 text-xs leading-[18px]'>{appDetail.description}</div>
-            )}
-            {/* operations */}
-            <Divider className="!my-1" />
-            <div className="w-full py-1">
-              <div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
+          </div>
+          {/* description */}
+          {appDetail.description && (
+            <div className='text-text-tertiary system-xs-regular'>{appDetail.description}</div>
+          )}
+          {/* operations */}
+          <div className='flex items-center gap-1 self-stretch'>
+            <Button
+              size={'small'}
+              variant={'secondary'}
+              className='gap-[1px]'
+              onClick={() => {
                 setOpen(false)
                 setShowEditModal(true)
-              }}>
-                <span className='text-gray-700 text-sm leading-5'>{t('app.editApp')}</span>
-              </div>
-              <div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
+              }}
+            >
+              <RiEditLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
+              <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)
                 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(
-                'w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl',
-                showSwitchTip === 'chat' && s.expertPic,
-                showSwitchTip === 'completion' && s.completionPic,
-              )} />
-              <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.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>
-                <div className='mt-1 text-gray-500 text-sm leading-5'>{t('app.newApp.advancedDescription')}</div>
-              </div>
-            </div>
+              <RiFileCopy2Line className='w-3.5 h-3.5 text-components-button-secondary-text' />
+              <span className='text-components-button-secondary-text system-xs-medium'>{t('app.duplicate')}</span>
+            </Button>
+            <Button
+              size={'small'}
+              variant={'secondary'}
+              className='gap-[1px]'
+              onClick={exportCheck}
+            >
+              <RiFileDownloadLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
+              <span className='text-components-button-secondary-text system-xs-medium'>{t('app.export')}</span>
+            </Button>
+            {
+              (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>
-        </PortalToFollowElemContent>
-        {showSwitchModal && (
-          <SwitchAppModal
-            inAppDetail
-            show={showSwitchModal}
-            appDetail={appDetail}
-            onClose={() => setShowSwitchModal(false)}
-            onSuccess={() => setShowSwitchModal(false)}
-          />
-        )}
-        {showEditModal && (
-          <CreateAppModal
-            isEditModal
-            appName={appDetail.name}
-            appIconType={appDetail.icon_type}
-            appIcon={appDetail.icon}
-            appIconBackground={appDetail.icon_background}
-            appIconUrl={appDetail.icon_url}
-            appDescription={appDetail.description}
-            appMode={appDetail.mode}
-            appUseIconAsAnswerIcon={appDetail.use_icon_as_answer_icon}
-            show={showEditModal}
-            onConfirm={onEdit}
-            onHide={() => setShowEditModal(false)}
-          />
-        )}
-        {showDuplicateModal && (
-          <DuplicateAppModal
-            appName={appDetail.name}
-            icon_type={appDetail.icon_type}
-            icon={appDetail.icon}
-            icon_background={appDetail.icon_background}
-            icon_url={appDetail.icon_url}
-            show={showDuplicateModal}
-            onConfirm={onCopy}
-            onHide={() => setShowDuplicateModal(false)}
-          />
-        )}
-        {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>
+        <div className='flex flex-1'>
+          <CardView
+            appId={appDetail.id}
+            isInPanel={true}
+            className='flex flex-col px-2 py-1 gap-2 grow overflow-auto'
           />
-        )}
-      </div>
-    </PortalToFollowElem>
+        </div>
+        <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'>
+          <Button
+            size={'medium'}
+            variant={'ghost'}
+            className='gap-0.5'
+            onClick={() => {
+              setOpen(false)
+              setShowConfirmDelete(true)
+            }}
+          >
+            <RiDeleteBinLine className='w-4 h-4 text-text-tertiary' />
+            <span className='text-text-tertiary system-sm-medium'>{t('common.operation.deleteApp')}</span>
+          </Button>
+        </div>
+      </ContentDialog>
+      {showSwitchModal && (
+        <SwitchAppModal
+          inAppDetail
+          show={showSwitchModal}
+          appDetail={appDetail}
+          onClose={() => setShowSwitchModal(false)}
+          onSuccess={() => setShowSwitchModal(false)}
+        />
+      )}
+      {showEditModal && (
+        <CreateAppModal
+          isEditModal
+          appName={appDetail.name}
+          appIconType={appDetail.icon_type}
+          appIcon={appDetail.icon}
+          appIconBackground={appDetail.icon_background}
+          appIconUrl={appDetail.icon_url}
+          appDescription={appDetail.description}
+          appMode={appDetail.mode}
+          appUseIconAsAnswerIcon={appDetail.use_icon_as_answer_icon}
+          show={showEditModal}
+          onConfirm={onEdit}
+          onHide={() => setShowEditModal(false)}
+        />
+      )}
+      {showDuplicateModal && (
+        <DuplicateAppModal
+          appName={appDetail.name}
+          icon_type={appDetail.icon_type}
+          icon={appDetail.icon}
+          icon_background={appDetail.icon_background}
+          icon_url={appDetail.icon_url}
+          show={showDuplicateModal}
+          onConfirm={onCopy}
+          onHide={() => setShowDuplicateModal(false)}
+        />
+      )}
+      {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>
   )
 }
 

+ 5 - 4
web/app/components/app-sidebar/basic.tsx

@@ -58,7 +58,7 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
   const { t } = useTranslation()
 
   return (
-    <div className="flex items-start p-1">
+    <div className="flex items-center grow">
       {icon && icon_background && iconType === 'app' && (
         <div className='flex-shrink-0 mr-3'>
           <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">
-        <div className={`flex flex-row items-center text-sm font-semibold text-gray-700 group-hover:text-gray-900 break-all ${textStyle?.main ?? ''}`}>
-          {name}
+        <div className={`flex flex-row items-center system-md-semibold text-text-secondary group-hover:text-text-primary ${textStyle?.main ?? ''}`}>
+          <div className="max-w-[180px] truncate">
+            {name}
+          </div>
           {hoverTip
             && <Tooltip
               popupContent={
@@ -86,7 +88,6 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
             />
           }
         </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>}
     </div>

+ 1 - 1
web/app/components/app-sidebar/index.tsx

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

+ 57 - 59
web/app/components/app/overview/appCard.tsx

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

+ 59 - 0
web/app/components/base/content-dialog/index.tsx

@@ -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

+ 1 - 1
web/app/components/base/copy-feedback/index.tsx

@@ -35,7 +35,7 @@ const CopyFeedback = ({ content, className }: Props) => {
       }
     >
       <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 ?? ''
         }`}
       >

+ 6 - 6
web/app/components/base/qrcode/index.tsx

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

+ 11 - 9
web/app/components/develop/secret-key/secret-key-button.tsx

@@ -1,29 +1,31 @@
 'use client'
 import { useState } from 'react'
 import { useTranslation } from 'react-i18next'
+import { RiKey2Line } from '@remixicon/react'
 import Button from '@/app/components/base/button'
 import SecretKeyModal from '@/app/components/develop/secret-key/secret-key-modal'
-// import { KeyIcon } from '@heroicons/react/20/solid'
 
 type ISecretKeyButtonProps = {
   className?: string
   appId?: string
-  iconCls?: string
   textCls?: string
 }
 
-const SecretKeyButton = ({ className, appId, iconCls, textCls }: ISecretKeyButtonProps) => {
+const SecretKeyButton = ({ className, appId, textCls }: ISecretKeyButtonProps) => {
   const [isVisible, setVisible] = useState(false)
   const { t } = useTranslation()
   return (
     <>
-      <Button className={`px-3 ${className}`} onClick={() => setVisible(true)}>
-        <div className={'flex items-center justify-center w-4 h-4 mr-2'}>
-          <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" className={iconCls}>
-            <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" />
-          </svg>
+      <Button
+        className={`px-3 ${className}`}
+        onClick={() => setVisible(true)}
+        size='small'
+        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 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>
       <SecretKeyModal isShow={isVisible} onClose={() => setVisible(false)} appId={appId} />
     </>

+ 1 - 0
web/i18n/en-US/app-overview.ts

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

+ 1 - 0
web/i18n/en-US/common.ts

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

+ 1 - 0
web/i18n/zh-Hans/app-overview.ts

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

+ 1 - 0
web/i18n/zh-Hans/common.ts

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

+ 2 - 0
web/tailwind.config.js

@@ -99,6 +99,8 @@ const config = {
         'chatbot-bg': 'var(--color-chatbot-bg)',
         'chat-bubble-bg': 'var(--color-chat-bubble-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-error-bg': 'var(--color-dataset-chunk-process-error-bg)',
         'dataset-chunk-detail-card-hover-bg': 'var(--color-dataset-chunk-detail-card-hover-bg)',

+ 11 - 0
web/themes/manual-dark.css

@@ -19,6 +19,17 @@ html[data-theme="dark"] {
     rgba(34, 34, 37, 0.9) -0.1%,
     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-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%);

+ 11 - 0
web/themes/manual-light.css

@@ -19,6 +19,17 @@ html[data-theme="light"] {
     rgba(249, 250, 251, 0.9) -0.1%,
     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-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%);