Quellcode durchsuchen

feat: fe mobile responsive next (#1609)

Yuhao vor 1 Jahr
Ursprung
Commit
a9c1c7d239
89 geänderte Dateien mit 767 neuen und 484 gelöschten Zeilen
  1. 3 3
      web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx
  2. 1 1
      web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView.tsx
  3. 1 1
      web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx
  4. 3 3
      web/app/(commonLayout)/apps/NewAppDialog.tsx
  5. 89 45
      web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx
  6. 2 4
      web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx
  7. 2 2
      web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/style.module.css
  8. 4 4
      web/app/(commonLayout)/datasets/ApiServer.tsx
  9. 9 11
      web/app/(commonLayout)/datasets/Container.tsx
  10. 1 1
      web/app/(commonLayout)/datasets/Doc.tsx
  11. 9 8
      web/app/components/app-sidebar/basic.tsx
  12. 9 5
      web/app/components/app-sidebar/index.tsx
  13. 3 1
      web/app/components/app-sidebar/navLink.tsx
  14. 10 5
      web/app/components/app/configuration/config-model/index.tsx
  15. 1 1
      web/app/components/app/configuration/config-model/model-mode-type-label.tsx
  16. 1 1
      web/app/components/app/configuration/config-model/model-name.tsx
  17. 1 1
      web/app/components/app/configuration/config-model/param-item.tsx
  18. 2 2
      web/app/components/app/configuration/config-var/index.tsx
  19. 1 1
      web/app/components/app/configuration/config-vision/param-config.tsx
  20. 30 5
      web/app/components/app/configuration/config/automatic/get-automatic-res.tsx
  21. 13 9
      web/app/components/app/configuration/dataset-config/card-item/item.tsx
  22. 1 1
      web/app/components/app/configuration/dataset-config/params-config/index.tsx
  23. 6 13
      web/app/components/app/configuration/dataset-config/settings-modal/index.tsx
  24. 28 6
      web/app/components/app/configuration/index.tsx
  25. 2 2
      web/app/components/app/log/filter.tsx
  26. 19 14
      web/app/components/app/log/list.tsx
  27. 4 4
      web/app/components/app/overview/appCard.tsx
  28. 2 2
      web/app/components/app/overview/customize/index.tsx
  29. 2 2
      web/app/components/app/overview/embedded/index.tsx
  30. 8 2
      web/app/components/base/drawer/index.tsx
  31. 37 0
      web/app/components/base/float-popover-container/index.tsx
  32. 23 0
      web/app/components/base/float-right-container/index.tsx
  33. 1 1
      web/app/components/base/popover/style.module.css
  34. 1 1
      web/app/components/base/portal-to-follow-elem/index.tsx
  35. 1 1
      web/app/components/base/select/index.tsx
  36. 2 2
      web/app/components/datasets/create/file-uploader/index.module.css
  37. 5 3
      web/app/components/datasets/create/file-uploader/index.tsx
  38. 1 1
      web/app/components/datasets/create/index.tsx
  39. 0 3
      web/app/components/datasets/create/step-one/index.module.css
  40. 1 1
      web/app/components/datasets/create/step-one/index.tsx
  41. 7 3
      web/app/components/datasets/create/step-three/index.tsx
  42. 3 3
      web/app/components/datasets/create/step-two/index.module.css
  43. 85 62
      web/app/components/datasets/create/step-two/index.tsx
  44. 3 2
      web/app/components/datasets/create/steps-nav-bar/index.module.css
  45. 33 23
      web/app/components/datasets/create/steps-nav-bar/index.tsx
  46. 1 1
      web/app/components/datasets/documents/detail/completed/index.tsx
  47. 3 3
      web/app/components/datasets/documents/detail/completed/style.module.css
  48. 45 35
      web/app/components/datasets/documents/detail/index.tsx
  49. 1 1
      web/app/components/datasets/documents/detail/metadata/style.module.css
  50. 2 2
      web/app/components/datasets/documents/index.tsx
  51. 3 3
      web/app/components/datasets/documents/list.tsx
  52. 2 2
      web/app/components/datasets/hit-testing/hit-detail.tsx
  53. 60 44
      web/app/components/datasets/hit-testing/index.tsx
  54. 2 2
      web/app/components/datasets/hit-testing/style.module.css
  55. 6 4
      web/app/components/datasets/hit-testing/textarea.tsx
  56. 7 7
      web/app/components/datasets/settings/form/index.tsx
  57. 2 2
      web/app/components/datasets/settings/index-method-radio/index.tsx
  58. 2 2
      web/app/components/datasets/settings/permissions-radio/index.tsx
  59. 5 5
      web/app/components/develop/index.tsx
  60. 0 4
      web/app/components/develop/secret-key/style.module.css
  61. 1 1
      web/app/components/explore/installed-app/index.tsx
  62. 21 16
      web/app/components/explore/sidebar/app-nav-item/index.tsx
  63. 15 8
      web/app/components/explore/sidebar/index.tsx
  64. 3 5
      web/app/components/explore/universal-chat/index.tsx
  65. 5 5
      web/app/components/explore/universal-chat/init/index.tsx
  66. 0 3
      web/app/components/explore/universal-chat/style.module.css
  67. 2 9
      web/app/components/header/HeaderWrapper.tsx
  68. 11 4
      web/app/components/header/account-dropdown/index.tsx
  69. 1 1
      web/app/components/header/account-setting/api-based-extension-page/selector.tsx
  70. 1 1
      web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx
  71. 10 5
      web/app/components/header/account-setting/index.tsx
  72. 3 3
      web/app/components/header/account-setting/members-page/index.tsx
  73. 1 1
      web/app/components/header/account-setting/model-page/index.tsx
  74. 1 1
      web/app/components/header/account-setting/model-page/model-item/index.tsx
  75. 1 1
      web/app/components/header/account-setting/model-page/model-modal/Form.tsx
  76. 2 2
      web/app/components/header/account-setting/model-page/model-modal/index.tsx
  77. 65 18
      web/app/components/header/index.tsx
  78. 1 1
      web/app/components/header/nav/index.tsx
  79. 2 3
      web/app/components/share/chat/index.tsx
  80. 0 3
      web/app/components/share/chat/style.module.css
  81. 1 2
      web/app/components/share/chatbot/index.tsx
  82. 0 3
      web/app/components/share/chatbot/style.module.css
  83. 2 2
      web/app/components/share/header.tsx
  84. 1 1
      web/app/components/share/text-generation/style.module.css
  85. 2 2
      web/context/app-context.tsx
  86. 1 0
      web/i18n/lang/app-debug.en.ts
  87. 1 0
      web/i18n/lang/app-debug.zh.ts
  88. 1 0
      web/i18n/lang/dataset-creation.en.ts
  89. 1 0
      web/i18n/lang/dataset-creation.zh.ts

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

@@ -39,10 +39,10 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
   const navigation = useMemo(() => {
     const navs = [
       { name: t('common.appMenus.overview'), href: `/app/${appId}/overview`, icon: ChartBarSquareIcon, selectedIcon: ChartBarSquareSolidIcon },
-      isCurrentWorkspaceManager ? { name: t('common.appMenus.promptEng'), href: `/app/${appId}/configuration`, icon: Cog8ToothIcon, selectedIcon: Cog8ToothSolidIcon } : false,
+      ...(isCurrentWorkspaceManager ? [{ name: t('common.appMenus.promptEng'), href: `/app/${appId}/configuration`, icon: Cog8ToothIcon, selectedIcon: Cog8ToothSolidIcon }] : []),
       { name: t('common.appMenus.apiAccess'), href: `/app/${appId}/develop`, icon: CommandLineIcon, selectedIcon: CommandLineSolidIcon },
       { name: t('common.appMenus.logAndAnn'), href: `/app/${appId}/logs`, icon: DocumentTextIcon, selectedIcon: DocumentTextSolidIcon },
-    ].filter(nav => !!nav)
+    ]
     return navs
   }, [appId, isCurrentWorkspaceManager, t])
 
@@ -56,7 +56,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
   return (
     <div className={cn(s.app, 'flex', 'overflow-hidden')}>
       <AppSideBar title={response.name} icon={response.icon} icon_background={response.icon_background} desc={appModeName} navigation={navigation} />
-      <div className="bg-white grow">{children}</div>
+      <div className="bg-white grow overflow-hidden">{children}</div>
     </div>
   )
 }

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

@@ -93,7 +93,7 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
   }
 
   return (
-    <div className="min-w-max grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6">
+    <div className="grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6">
       <AppCard
         appInfo={response}
         cardType="webapp"

+ 1 - 1
web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx

@@ -19,7 +19,7 @@ const Overview = async ({
   */
   const { t } = await translate(locale, 'app-overview')
   return (
-    <div className="h-full px-16 py-6 overflow-scroll">
+    <div className="h-full px-4 sm:px-16 py-6 overflow-scroll">
       <ApikeyInfoPanel />
       <div className='flex flex-row items-center justify-between mb-4 text-xl text-gray-900'>
         {t('overview.title')}

+ 3 - 3
web/app/(commonLayout)/apps/NewAppDialog.tsx

@@ -122,7 +122,7 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
         <input ref={nameInputRef} className='h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' placeholder={t('app.appNamePlaceholder') || ''}/>
       </div>
 
-      <div className='h-[247px] overflow-y-auto'>
+      <div className='overflow-y-auto'>
         <div className={style.newItemCaption}>
           <h3 className='inline'>{t('app.newApp.captionAppType')}</h3>
           {isWithTemplate && (
@@ -139,7 +139,7 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
         </div>
         {isWithTemplate
           ? (
-            <ul className='grid grid-cols-2 gap-4'>
+            <ul className='grid grid-cols-1 md:grid-cols-2 gap-4'>
               {templates?.data?.map((template, index) => (
                 <li
                   key={index}
@@ -161,7 +161,7 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
           )
           : (
             <>
-              <ul className='grid grid-cols-2 gap-4'>
+              <ul className='grid grid-cols-1 md:grid-cols-2 gap-4'>
                 <li
                   className={classNames(style.listItem, style.selectable, newAppMode === 'chat' && style.selected)}
                   onClick={() => setNewAppMode('chat')}

+ 89 - 45
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx

@@ -4,6 +4,8 @@ import React, { useEffect } from 'react'
 import { usePathname } from 'next/navigation'
 import useSWR from 'swr'
 import { useTranslation } from 'react-i18next'
+import classNames from 'classnames'
+import { useBoolean } from 'ahooks'
 import {
   Cog8ToothIcon,
   // CommandLineIcon,
@@ -11,6 +13,8 @@ import {
   // eslint-disable-next-line sort-imports
   PuzzlePieceIcon,
   DocumentTextIcon,
+  PaperClipIcon,
+  QuestionMarkCircleIcon,
 } from '@heroicons/react/24/outline'
 import {
   Cog8ToothIcon as Cog8ToothSolidIcon,
@@ -20,29 +24,39 @@ import {
 import Link from 'next/link'
 import s from './style.module.css'
 import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
-import type { RelatedApp } from '@/models/datasets'
+import type { RelatedApp, RelatedAppResponse } from '@/models/datasets'
 import { getLocaleOnClient } from '@/i18n/client'
 import AppSideBar from '@/app/components/app-sidebar'
 import Divider from '@/app/components/base/divider'
 import Indicator from '@/app/components/header/indicator'
 import AppIcon from '@/app/components/base/app-icon'
 import Loading from '@/app/components/base/loading'
+import FloatPopoverContainer from '@/app/components/base/float-popover-container'
 import DatasetDetailContext from '@/context/dataset-detail'
 import { DataSourceType } from '@/models/datasets'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 export type IAppDetailLayoutProps = {
   children: React.ReactNode
   params: { datasetId: string }
 }
 
-const LikedItem: FC<{ type?: 'plugin' | 'app'; appStatus?: boolean; detail: RelatedApp }> = ({
+type ILikedItemProps = {
+  type?: 'plugin' | 'app'
+  appStatus?: boolean
+  detail: RelatedApp
+  isMobile: boolean
+}
+
+const LikedItem = ({
   type = 'app',
   appStatus = true,
   detail,
-}) => {
+  isMobile,
+}: ILikedItemProps) => {
   return (
-    <Link className={s.itemWrapper} href={`/app/${detail?.id}/overview`}>
-      <div className={s.iconWrapper}>
+    <Link className={classNames(s.itemWrapper, 'px-0 sm:px-3 justify-center sm:justify-start')} href={`/app/${detail?.id}/overview`}>
+      <div className={classNames(s.iconWrapper, 'mr-0 sm:mr-2')}>
         <AppIcon size='tiny' icon={detail?.icon} background={detail?.icon_background}/>
         {type === 'app' && (
           <div className={s.statusPoint}>
@@ -50,7 +64,7 @@ const LikedItem: FC<{ type?: 'plugin' | 'app'; appStatus?: boolean; detail: Rela
           </div>
         )}
       </div>
-      <div className={s.appInfo}>{detail?.name || '--'}</div>
+      {!isMobile && <div className={s.appInfo}>{detail?.name || '--'}</div>}
     </Link>
   )
 }
@@ -83,6 +97,68 @@ const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
   </svg>
 }
 
+type IExtraInfoProps = {
+  isMobile: boolean
+  relatedApps?: RelatedAppResponse
+}
+
+const ExtraInfo = ({ isMobile, relatedApps }: IExtraInfoProps) => {
+  const locale = getLocaleOnClient()
+  const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
+  const { t } = useTranslation()
+
+  useEffect(() => {
+    setShowTips(!isMobile)
+  }, [isMobile, setShowTips])
+
+  return <div className='w-full flex flex-col items-center'>
+    <Divider className='mt-5' />
+    {(relatedApps?.data && relatedApps?.data?.length > 0) && (
+      <>
+        {!isMobile && <div className={s.subTitle}>{relatedApps?.total || '--'} {t('common.datasetMenus.relatedApp')}</div>}
+        {isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
+          {relatedApps?.total || '--'}
+          <PaperClipIcon className='h-4 w-4 text-gray-700' />
+        </div>}
+        {relatedApps?.data?.map((item, index) => (<LikedItem key={index} isMobile={isMobile} detail={item} />))}
+      </>
+    )}
+    {!relatedApps?.data?.length && (
+      <FloatPopoverContainer
+        placement='bottom-start'
+        open={isShowTips}
+        toggle={toggleTips}
+        isMobile={isMobile}
+        triggerElement={
+          <div className={classNames('h-7 w-7 inline-flex justify-center items-center rounded-lg bg-transparent', isShowTips && '!bg-gray-50')}>
+            <QuestionMarkCircleIcon className='h-4 w-4 flex-shrink-0 text-gray-500' />
+          </div>
+        }
+      >
+        <div className={classNames('mt-5 p-3', isMobile && 'border-[0.5px] border-gray-200 shadow-lg rounded-lg bg-white w-[150px]')}>
+          <div className='flex items-center justify-start gap-2'>
+            <div className={s.emptyIconDiv}>
+              <Squares2X2Icon className='w-3 h-3 text-gray-500' />
+            </div>
+            <div className={s.emptyIconDiv}>
+              <PuzzlePieceIcon className='w-3 h-3 text-gray-500' />
+            </div>
+          </div>
+          <div className='text-xs text-gray-500 mt-2'>{t('common.datasetMenus.emptyTip')}</div>
+          <a
+            className='inline-flex items-center text-xs text-primary-600 mt-2 cursor-pointer'
+            href={`https://docs.dify.ai/${locale === 'zh-Hans' ? 'v/zh-hans' : ''}/application/prompt-engineering`}
+            target='_blank'
+          >
+            <BookOpenIcon className='mr-1' />
+            {t('common.datasetMenus.viewDoc')}
+          </a>
+        </div>
+      </FloatPopoverContainer>
+    )}
+  </div>
+}
+
 const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
   const {
     children,
@@ -91,6 +167,10 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
   const pathname = usePathname()
   const hideSideBar = /documents\/create$/.test(pathname)
   const { t } = useTranslation()
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   const { data: datasetRes, error, mutate: mutateDatasetRes } = useSWR({
     url: 'fetchDatasetDetail',
     datasetId,
@@ -113,54 +193,18 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
       document.title = `${datasetRes.name || 'Dataset'} - Dify`
   }, [datasetRes])
 
-  const ExtraInfo: FC = () => {
-    const locale = getLocaleOnClient()
-
-    return <div className='w-full'>
-      <Divider className='mt-5' />
-      {relatedApps?.data?.length
-        ? (
-          <>
-            <div className={s.subTitle}>{relatedApps?.total || '--'} {t('common.datasetMenus.relatedApp')}</div>
-            {relatedApps?.data?.map((item, index) => (<LikedItem key={index} detail={item} />))}
-          </>
-        )
-        : (
-          <div className='mt-5 p-3'>
-            <div className='flex items-center justify-start gap-2'>
-              <div className={s.emptyIconDiv}>
-                <Squares2X2Icon className='w-3 h-3 text-gray-500' />
-              </div>
-              <div className={s.emptyIconDiv}>
-                <PuzzlePieceIcon className='w-3 h-3 text-gray-500' />
-              </div>
-            </div>
-            <div className='text-xs text-gray-500 mt-2'>{t('common.datasetMenus.emptyTip')}</div>
-            <a
-              className='inline-flex items-center text-xs text-primary-600 mt-2 cursor-pointer'
-              href={`https://docs.dify.ai/${locale === 'zh-Hans' ? 'v/zh-hans' : ''}/application/prompt-engineering`}
-              target='_blank'
-            >
-              <BookOpenIcon className='mr-1' />
-              {t('common.datasetMenus.viewDoc')}
-            </a>
-          </div>
-        )}
-    </div>
-  }
-
   if (!datasetRes && !error)
     return <Loading />
 
   return (
-    <div className='flex'>
+    <div className='flex overflow-hidden'>
       {!hideSideBar && <AppSideBar
         title={datasetRes?.name || '--'}
         icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
         icon_background={datasetRes?.icon_background || '#F5F5F5'}
         desc={datasetRes?.description || '--'}
         navigation={navigation}
-        extraInfo={<ExtraInfo />}
+        extraInfo={<ExtraInfo isMobile={isMobile} relatedApps={relatedApps} />}
         iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
       />}
       <DatasetDetailContext.Provider value={{
@@ -168,7 +212,7 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
         dataset: datasetRes,
         mutateDatasetRes: () => mutateDatasetRes(),
       }}>
-        <div className="bg-white grow" style={{ minHeight: 'calc(100vh - 56px)' }}>{children}</div>
+        <div className="bg-white grow overflow-hidden">{children}</div>
       </DatasetDetailContext.Provider>
     </div>
   )

+ 2 - 4
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx

@@ -14,14 +14,12 @@ const Settings = async ({
   const { t } = await useTranslation(locale, 'dataset-settings')
 
   return (
-    <div className='bg-white h-full'>
+    <div className='bg-white h-full overflow-y-auto'>
       <div className='px-6 py-3'>
         <div className='mb-1 text-lg font-semibold text-gray-900'>{t('title')}</div>
         <div className='text-sm text-gray-500'>{t('desc')}</div>
       </div>
-      <div>
-        <Form datasetId={datasetId} />
-      </div>
+      <Form datasetId={datasetId} />
     </div>
   )
 }

+ 2 - 2
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/style.module.css

@@ -1,11 +1,11 @@
 .itemWrapper {
-  @apply flex items-center w-full h-10 px-3 rounded-lg hover:bg-gray-50 cursor-pointer;
+  @apply flex items-center w-full h-10 rounded-lg hover:bg-gray-50 cursor-pointer;
 }
 .appInfo {
   @apply truncate text-gray-700 text-sm font-normal;
 }
 .iconWrapper {
-  @apply relative w-6 h-6 mr-2 bg-[#D5F5F6] rounded-md;
+  @apply relative w-6 h-6 bg-[#D5F5F6] rounded-md;
 }
 .statusPoint {
   @apply flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded;

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

@@ -15,10 +15,10 @@ const ApiServer: FC<ApiServerProps> = ({
   const { t } = useTranslation()
 
   return (
-    <div className='flex items-center'>
-      <div className='flex items-center mr-2 pl-1.5 pr-1 h-8 bg-white/80 border-[0.5px] border-white rounded-lg'>
-        <div className='mr-0.5 px-1.5 h-5 border border-gray-200 text-[11px] text-gray-500 rounded-md'>{t('appApi.apiServer')}</div>
-        <div className='px-1 w-[248px] text-[13px] font-medium text-gray-800'>{apiBaseUrl}</div>
+    <div className='flex items-center flex-wrap gap-y-2'>
+      <div className='flex items-center mr-2 pl-1.5 pr-1 h-8 bg-white/80 border-[0.5px] border-white rounded-lg leading-5'>
+        <div className='mr-0.5 px-1.5 h-5 border border-gray-200 text-[11px] text-gray-500 rounded-md shrink-0'>{t('appApi.apiServer')}</div>
+        <div className='px-1 truncate w-fit sm:w-[248px] text-[13px] font-medium text-gray-800'>{apiBaseUrl}</div>
         <div className='mx-1 w-[1px] h-[14px] bg-gray-200'></div>
         <CopyFeedback
           content={apiBaseUrl}

+ 9 - 11
web/app/(commonLayout)/datasets/Container.tsx

@@ -29,7 +29,7 @@ const Container = () => {
 
   return (
     <div ref={containerRef} className='grow relative flex flex-col bg-gray-100 overflow-y-auto'>
-      <div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 h-14 bg-gray-100 z-10'>
+      <div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-10 flex-wrap gap-y-2'>
         <TabSlider
           value={activeTab}
           onChange={newActiveTab => setActiveTab(newActiveTab)}
@@ -38,16 +38,14 @@ const Container = () => {
         {activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />}
       </div>
 
-      {activeTab === 'dataset'
-        ? (
-          <>
-            <Datasets containerRef={containerRef} />
-            <DatasetFooter />
-          </>
-        )
-        : (
-          activeTab === 'api' && data && <Doc apiBaseUrl={data.api_base_url || ''} />
-        )}
+      {activeTab === 'dataset' && (
+        <>
+          <Datasets containerRef={containerRef} />
+          <DatasetFooter />
+        </>
+      )}
+
+      {activeTab === 'api' && data && <Doc apiBaseUrl={data.api_base_url || ''} />}
     </div>
 
   )

+ 1 - 1
web/app/(commonLayout)/datasets/Doc.tsx

@@ -15,7 +15,7 @@ const Doc: FC<DocProps> = ({
   const { locale } = useContext(I18n)
 
   return (
-    <article className='mx-12 pt-16 bg-white rounded-t-xl prose prose-xl'>
+    <article className='mx-1 px-4 sm:mx-12 pt-16 bg-white rounded-t-xl prose prose-xl'>
       {
         locale === 'en'
           ? <TemplateEn apiBaseUrl={apiBaseUrl} />

+ 9 - 8
web/app/components/app-sidebar/basic.tsx

@@ -15,6 +15,7 @@ export type IAppBasicProps = {
   hoverTip?: string
   textStyle?: { main?: string; extra?: string }
   isExtraInLine?: boolean
+  mode?: 'expand' | 'collapse'
 }
 
 const ApiSvg = <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -31,18 +32,18 @@ const DatasetSvg = <svg width="20" height="20" viewBox="0 0 20 20" fill="none" x
 </svg>
 
 const WebappSvg = <svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
-  <path d="M14.375 5.45825L7.99998 8.99992M7.99998 8.99992L1.62498 5.45825M7.99998 8.99992L8 16.1249M14.75 12.0439V5.95603C14.75 5.69904 14.75 5.57055 14.7121 5.45595C14.6786 5.35457 14.6239 5.26151 14.5515 5.18299C14.4697 5.09424 14.3574 5.03184 14.1328 4.90704L8.58277 1.8237C8.37007 1.70553 8.26372 1.64645 8.15109 1.62329C8.05141 1.60278 7.9486 1.60278 7.84891 1.62329C7.73628 1.64645 7.62993 1.70553 7.41723 1.8237L1.86723 4.90704C1.64259 5.03184 1.53026 5.09424 1.44847 5.18299C1.37612 5.26151 1.32136 5.35457 1.28786 5.45595C1.25 5.57055 1.25 5.69904 1.25 5.95603V12.0439C1.25 12.3008 1.25 12.4293 1.28786 12.5439C1.32136 12.6453 1.37612 12.7384 1.44847 12.8169C1.53026 12.9056 1.64259 12.968 1.86723 13.0928L7.41723 16.1762C7.62993 16.2943 7.73628 16.3534 7.84891 16.3766C7.9486 16.3971 8.05141 16.3971 8.15109 16.3766C8.26372 16.3534 8.37007 16.2943 8.58277 16.1762L14.1328 13.0928C14.3574 12.968 14.4697 12.9056 14.5515 12.8169C14.6239 12.7384 14.6786 12.6453 14.7121 12.5439C14.75 12.4293 14.75 12.3008 14.75 12.0439Z" stroke="#155EEF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
+  <path d="M14.375 5.45825L7.99998 8.99992M7.99998 8.99992L1.62498 5.45825M7.99998 8.99992L8 16.1249M14.75 12.0439V5.95603C14.75 5.69904 14.75 5.57055 14.7121 5.45595C14.6786 5.35457 14.6239 5.26151 14.5515 5.18299C14.4697 5.09424 14.3574 5.03184 14.1328 4.90704L8.58277 1.8237C8.37007 1.70553 8.26372 1.64645 8.15109 1.62329C8.05141 1.60278 7.9486 1.60278 7.84891 1.62329C7.73628 1.64645 7.62993 1.70553 7.41723 1.8237L1.86723 4.90704C1.64259 5.03184 1.53026 5.09424 1.44847 5.18299C1.37612 5.26151 1.32136 5.35457 1.28786 5.45595C1.25 5.57055 1.25 5.69904 1.25 5.95603V12.0439C1.25 12.3008 1.25 12.4293 1.28786 12.5439C1.32136 12.6453 1.37612 12.7384 1.44847 12.8169C1.53026 12.9056 1.64259 12.968 1.86723 13.0928L7.41723 16.1762C7.62993 16.2943 7.73628 16.3534 7.84891 16.3766C7.9486 16.3971 8.05141 16.3971 8.15109 16.3766C8.26372 16.3534 8.37007 16.2943 8.58277 16.1762L14.1328 13.0928C14.3574 12.968 14.4697 12.9056 14.5515 12.8169C14.6239 12.7384 14.6786 12.6453 14.7121 12.5439C14.75 12.4293 14.75 12.3008 14.75 12.0439Z" stroke="#155EEF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
 </svg>
 
 const NotionSvg = <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
   <g clip-path="url(#clip0_6294_13848)">
-    <path fill-rule="evenodd" clip-rule="evenodd" d="M4.287 21.9133L1.70748 18.6999C1.08685 17.9267 0.75 16.976 0.75 15.9974V4.36124C0.75 2.89548 1.92269 1.67923 3.43553 1.57594L15.3991 0.759137C16.2682 0.699797 17.1321 0.930818 17.8461 1.41353L22.0494 4.25543C22.8018 4.76414 23.25 5.59574 23.25 6.48319V19.7124C23.25 21.1468 22.0969 22.3345 20.6157 22.4256L7.3375 23.243C6.1555 23.3158 5.01299 22.8178 4.287 21.9133Z" fill="white"/>
-    <path d="M8.43607 10.1842V10.0318C8.43607 9.64564 8.74535 9.32537 9.14397 9.29876L12.0475 9.10491L16.0628 15.0178V9.82823L15.0293 9.69046V9.6181C15.0293 9.22739 15.3456 8.90501 15.7493 8.88433L18.3912 8.74899V9.12918C18.3912 9.30765 18.2585 9.46031 18.0766 9.49108L17.4408 9.59861V18.0029L16.6429 18.2773C15.9764 18.5065 15.2343 18.2611 14.8527 17.6853L10.9545 11.803V17.4173L12.1544 17.647L12.1377 17.7583C12.0853 18.1069 11.7843 18.3705 11.4202 18.3867L8.43607 18.5195C8.39662 18.1447 8.67758 17.8093 9.06518 17.7686L9.45771 17.7273V10.2416L8.43607 10.1842Z" fill="black"/>
-    <path fill-rule="evenodd" clip-rule="evenodd" d="M15.5062 2.22521L3.5426 3.04201C2.82599 3.09094 2.27051 3.66706 2.27051 4.36136V15.9975C2.27051 16.6499 2.49507 17.2837 2.90883 17.7992L5.48835 21.0126C5.90541 21.5322 6.56174 21.8183 7.24076 21.7765L20.519 20.9591C21.1995 20.9172 21.7293 20.3716 21.7293 19.7125V6.48332C21.7293 6.07557 21.5234 5.69348 21.1777 5.45975L16.9743 2.61784C16.546 2.32822 16.0277 2.1896 15.5062 2.22521ZM4.13585 4.54287C3.96946 4.41968 4.04865 4.16303 4.25768 4.14804L15.5866 3.33545C15.9476 3.30956 16.3063 3.40896 16.5982 3.61578L18.8713 5.22622C18.9576 5.28736 18.9171 5.41935 18.8102 5.42516L6.8129 6.07764C6.44983 6.09739 6.09144 5.99073 5.80276 5.77699L4.13585 4.54287ZM6.25018 8.12315C6.25018 7.7334 6.56506 7.41145 6.9677 7.38952L19.6523 6.69871C20.0447 6.67734 20.375 6.97912 20.375 7.35898V18.8141C20.375 19.2031 20.0613 19.5247 19.6594 19.5476L7.05516 20.2648C6.61845 20.2896 6.25018 19.954 6.25018 19.5312V8.12315Z" fill="black"/>
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M4.287 21.9133L1.70748 18.6999C1.08685 17.9267 0.75 16.976 0.75 15.9974V4.36124C0.75 2.89548 1.92269 1.67923 3.43553 1.57594L15.3991 0.759137C16.2682 0.699797 17.1321 0.930818 17.8461 1.41353L22.0494 4.25543C22.8018 4.76414 23.25 5.59574 23.25 6.48319V19.7124C23.25 21.1468 22.0969 22.3345 20.6157 22.4256L7.3375 23.243C6.1555 23.3158 5.01299 22.8178 4.287 21.9133Z" fill="white" />
+    <path d="M8.43607 10.1842V10.0318C8.43607 9.64564 8.74535 9.32537 9.14397 9.29876L12.0475 9.10491L16.0628 15.0178V9.82823L15.0293 9.69046V9.6181C15.0293 9.22739 15.3456 8.90501 15.7493 8.88433L18.3912 8.74899V9.12918C18.3912 9.30765 18.2585 9.46031 18.0766 9.49108L17.4408 9.59861V18.0029L16.6429 18.2773C15.9764 18.5065 15.2343 18.2611 14.8527 17.6853L10.9545 11.803V17.4173L12.1544 17.647L12.1377 17.7583C12.0853 18.1069 11.7843 18.3705 11.4202 18.3867L8.43607 18.5195C8.39662 18.1447 8.67758 17.8093 9.06518 17.7686L9.45771 17.7273V10.2416L8.43607 10.1842Z" fill="black" />
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M15.5062 2.22521L3.5426 3.04201C2.82599 3.09094 2.27051 3.66706 2.27051 4.36136V15.9975C2.27051 16.6499 2.49507 17.2837 2.90883 17.7992L5.48835 21.0126C5.90541 21.5322 6.56174 21.8183 7.24076 21.7765L20.519 20.9591C21.1995 20.9172 21.7293 20.3716 21.7293 19.7125V6.48332C21.7293 6.07557 21.5234 5.69348 21.1777 5.45975L16.9743 2.61784C16.546 2.32822 16.0277 2.1896 15.5062 2.22521ZM4.13585 4.54287C3.96946 4.41968 4.04865 4.16303 4.25768 4.14804L15.5866 3.33545C15.9476 3.30956 16.3063 3.40896 16.5982 3.61578L18.8713 5.22622C18.9576 5.28736 18.9171 5.41935 18.8102 5.42516L6.8129 6.07764C6.44983 6.09739 6.09144 5.99073 5.80276 5.77699L4.13585 4.54287ZM6.25018 8.12315C6.25018 7.7334 6.56506 7.41145 6.9677 7.38952L19.6523 6.69871C20.0447 6.67734 20.375 6.97912 20.375 7.35898V18.8141C20.375 19.2031 20.0613 19.5247 19.6594 19.5476L7.05516 20.2648C6.61845 20.2896 6.25018 19.954 6.25018 19.5312V8.12315Z" fill="black" />
   </g>
   <defs>
     <clipPath id="clip0_6294_13848">
-      <rect width="24" height="24" fill="white"/>
+      <rect width="24" height="24" fill="white" />
     </clipPath>
   </defs>
 </svg>
@@ -55,7 +56,7 @@ const ICON_MAP = {
   notion: <AppIcon innerIcon={NotionSvg} className='!border-[0.5px] !border-indigo-100 !bg-white' />,
 }
 
-export default function AppBasic({ icon, icon_background, name, type, hoverTip, textStyle, iconType = 'app', isExtraInLine }: IAppBasicProps) {
+export default function AppBasic({ icon, icon_background, name, type, hoverTip, textStyle, mode = 'expand', iconType = 'app', isExtraInLine }: IAppBasicProps) {
   return (
     <div className="flex items-start">
       {icon && icon_background && iconType === 'app' && (
@@ -69,7 +70,7 @@ export default function AppBasic({ icon, icon_background, name, type, hoverTip,
         </div>
 
       }
-      <div className="group">
+      {mode === 'expand' && <div className="group">
         <div className={`flex flex-row items-center text-sm font-semibold text-gray-700 group-hover:text-gray-900 break-all ${textStyle?.main ?? ''}`}>
           {name}
           {hoverTip
@@ -78,7 +79,7 @@ export default function AppBasic({ icon, icon_background, name, type, hoverTip,
             </Tooltip>}
         </div>
         <div className={`text-xs font-normal text-gray-500 group-hover:text-gray-700 break-all ${textStyle?.extra ?? ''}`}>{type}</div>
-      </div>
+      </div>}
     </div>
   )
 }

+ 9 - 5
web/app/components/app-sidebar/index.tsx

@@ -1,8 +1,8 @@
 import React from 'react'
 import NavLink from './navLink'
-import AppBasic from './basic'
-
 import type { NavIcon } from './navLink'
+import AppBasic from './basic'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 export type IAppDetailNavProps = {
   iconType?: 'app' | 'dataset' | 'notion'
@@ -20,15 +20,19 @@ export type IAppDetailNavProps = {
 }
 
 const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInfo, iconType = 'app' }: IAppDetailNavProps) => {
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+  const mode = isMobile ? 'collapse' : 'expand'
+
   return (
-    <div className="flex flex-col w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
+    <div className="flex flex-col sm:w-56 w-16 overflow-y-auto bg-white border-r border-gray-200 shrink-0 mobile:h-screen">
       <div className="flex flex-shrink-0 p-4">
-        <AppBasic iconType={iconType} icon={icon} icon_background={icon_background} name={title} type={desc} />
+        <AppBasic mode={mode} iconType={iconType} icon={icon} icon_background={icon_background} name={title} type={desc} />
       </div>
       <nav className="flex-1 p-4 space-y-1 bg-white">
         {navigation.map((item, index) => {
           return (
-            <NavLink key={index} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
+            <NavLink key={index} mode={mode} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
           )
         })}
         {extraInfo ?? null}

+ 3 - 1
web/app/components/app-sidebar/navLink.tsx

@@ -18,12 +18,14 @@ export type NavLinkProps = {
     selected: NavIcon
     normal: NavIcon
   }
+  mode?: 'expand' | 'collapse'
 }
 
 export default function NavLink({
   name,
   href,
   iconMap,
+  mode = 'expand',
 }: NavLinkProps) {
   const segment = useSelectedLayoutSegment()
   const isActive = href.toLowerCase().split('/')?.pop() === segment?.toLowerCase()
@@ -45,7 +47,7 @@ export default function NavLink({
         )}
         aria-hidden="true"
       />
-      {name}
+      {mode === 'expand' && name}
     </Link>
   )
 }

+ 10 - 5
web/app/components/app/configuration/config-model/index.tsx

@@ -27,6 +27,7 @@ import Loading from '@/app/components/base/loading'
 import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
 import { ModelType, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
 import { useProviderContext } from '@/context/provider-context'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 import type { ModelModeType } from '@/types/app'
 export type IConfigModelProps = {
   isAdvancedMode: boolean
@@ -54,6 +55,10 @@ const ConfigModel: FC<IConfigModelProps> = ({
   const [maxTokenSettingTipVisible, setMaxTokenSettingTipVisible] = useState(false)
   const configContentRef = React.useRef(null)
   const currModel = textGenerationModelList.find(item => item.model_name === modelId)
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   // Cache loaded model param
   const [allParams, setAllParams, getAllParams] = useGetState<Record<string, Record<string, any>>>({})
   const currParams = allParams[provider]?.[modelId]
@@ -288,7 +293,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
       </div>
       {isShowConfig && (
         <Panel
-          className='absolute z-20 top-8 right-0 !w-[496px] bg-white !overflow-visible shadow-md'
+          className='absolute z-20 top-8 left-0 sm:left-[unset] sm:right-0 !w-fit sm:!w-[496px] bg-white !overflow-visible shadow-md'
           keepUnFold
           headerIcon={
             <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -340,7 +345,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
                       <div className='grow flex items-center' key={tone.id}>
                         <Radio
                           value={tone.id}
-                          className={cn(tone.id === toneId && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-2 !justify-center text-[13px] font-medium')}
+                          className={cn(tone.id === toneId && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-1 sm:!px-2 !justify-center text-[13px] font-medium')}
                           labelClassName={cn(tone.id === toneId
                             ? ({
                               1: 'text-[#6938EF]',
@@ -351,7 +356,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
                         >
                           <>
                             {getToneIcon(tone.id)}
-                            <div>{t(`common.model.tone.${tone.name}`) as string}</div>
+                            {!isMobile && <div>{t(`common.model.tone.${tone.name}`) as string}</div>}
                             <div className=""></div>
                           </>
                         </Radio>
@@ -361,12 +366,12 @@ const ConfigModel: FC<IConfigModelProps> = ({
                   </>
                   <Radio
                     value={TONE_LIST[3].id}
-                    className={cn(toneId === 4 && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-2 !justify-center text-[13px] font-medium')}
+                    className={cn(toneId === 4 && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-1 sm:!px-2 !justify-center text-[13px] font-medium')}
                     labelClassName={cn('flex items-center space-x-2 ', toneId === 4 ? 'text-[#155EEF]' : 'text-[#667085]')}
                   >
                     <>
                       {getToneIcon(TONE_LIST[3].id)}
-                      <div>{t(`common.model.tone.${TONE_LIST[3].name}`) as string}</div>
+                      {!isMobile && <div>{t(`common.model.tone.${TONE_LIST[3].name}`) as string}</div>}
                     </>
                   </Radio>
                 </Radio.Group>

+ 1 - 1
web/app/components/app/configuration/config-model/model-mode-type-label.tsx

@@ -20,7 +20,7 @@ const ModelModeTypeLabel: FC<Props> = ({
 
   return (
     <div
-      className={cn(className, isHighlight ? 'border-indigo-300 text-indigo-600' : 'border-gray-300 text-gray-500', 'flex items-center h-4 px-1 border  rounded text-xs font-semibold uppercase')}
+      className={cn(className, isHighlight ? 'border-indigo-300 text-indigo-600' : 'border-gray-300 text-gray-500', 'flex items-center h-4 px-1 border rounded text-xs font-semibold uppercase text-ellipsis overflow-hidden whitespace-nowrap')}
     >
       {t(`appDebug.modelConfig.modeType.${type}`)}
     </div>

+ 1 - 1
web/app/components/app/configuration/config-model/model-name.tsx

@@ -18,7 +18,7 @@ const ModelName: FC<IModelNameProps> = ({
   modelDisplayName,
 }) => {
   return (
-    <span title={modelDisplayName}>
+    <span className='text-ellipsis overflow-hidden whitespace-nowrap' title={modelDisplayName}>
       {modelDisplayName}
     </span>
   )

+ 1 - 1
web/app/components/app/configuration/config-model/param-item.tsx

@@ -49,7 +49,7 @@ const ParamItem: FC<IParamIteProps> = ({ id, name, tip, step = 0.1, min = 0, max
       onChange(id, getFitPrecisionValue(value, precision))
   }, [value, precision])
   return (
-    <div className="flex items-center justify-between">
+    <div className="flex items-center justify-between flex-wrap gap-y-2">
       <div className="flex flex-col flex-shrink-0">
         <div className="flex items-center">
           <span className="mr-[6px] text-gray-500 text-[13px] font-medium">{name}</span>

+ 2 - 2
web/app/components/app/configuration/config-var/index.tsx

@@ -185,8 +185,8 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
         <div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.notSetVar')}</div>
       )}
       {hasVar && (
-        <div className='rounded-lg border border-gray-200 bg-white'>
-          <table className={`${s.table} w-full border-collapse border-0 rounded-lg text-sm`}>
+        <div className='rounded-lg border border-gray-200 bg-white overflow-x-auto'>
+          <table className={`${s.table} min-w-[440px] max-w-full border-collapse border-0 rounded-lg text-sm`}>
             <thead className="border-b  border-gray-200 text-gray-500 text-xs font-medium">
               <tr className='uppercase'>
                 <td>{t('appDebug.variableTable.key')}</td>

+ 1 - 1
web/app/components/app/configuration/config-vision/param-config.tsx

@@ -31,7 +31,7 @@ const ParamsConfig: FC = () => {
         </div>
       </PortalToFollowElemTrigger>
       <PortalToFollowElemContent style={{ zIndex: 50 }}>
-        <div className='w-[412px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
+        <div className='w-80 sm:w-[412px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
           <ParamConfigContent />
         </div>
       </PortalToFollowElemContent>

+ 30 - 5
web/app/components/app/configuration/config/automatic/get-automatic-res.tsx

@@ -16,6 +16,7 @@ import Loading from '@/app/components/base/loading'
 import Confirm from '@/app/components/base/confirm'
 // type
 import type { AutomaticRes } from '@/service/debug'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 const noDataIcon = (
   <svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -47,6 +48,9 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
 }) => {
   const { t } = useTranslation()
 
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   const [audiences, setAudiences] = React.useState<string>('')
   const [hopingToSolve, setHopingToSolve] = React.useState<string>('')
   const isValid = () => {
@@ -103,15 +107,36 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
 
   const [showConfirmOverwrite, setShowConfirmOverwrite] = React.useState(false)
 
+  const isShowAutoPromptInput = () => {
+    if (isMobile) {
+      // hide prompt panel on mobile if it is loading or has had result
+      if (isLoading || res)
+        return false
+      return true
+    }
+
+    // alway display prompt panel on desktop mode
+    return true
+  }
+
+  const isShowAutoPromptResPlaceholder = () => {
+    if (isMobile) {
+      // hide placeholder panel on mobile
+      return false
+    }
+
+    return !isLoading && !res
+  }
+
   return (
     <Modal
       isShow={isShow}
       onClose={onClose}
-      className='min-w-[1120px] !p-0'
+      className='!p-0 sm:min-w-[768px] xl:min-w-[1120px]'
       closable
     >
-      <div className='flex h-[680px]'>
-        <div className='w-[480px] shrink-0 px-8 py-6 h-full overflow-y-auto border-r border-gray-100'>
+      <div className='flex h-[680px] flex-wrap gap-y-4 overflow-y-auto'>
+        {isShowAutoPromptInput() && <div className='w-full sm:w-[360px] xl:w-[480px] shrink-0 px-8 py-6 h-full overflow-y-auto border-r border-gray-100'>
           <div>
             <div className='mb-1 text-xl font-semibold text-primary-600'>{t('appDebug.automatic.title')}</div>
             <div className='text-[13px] font-normal text-gray-500'>{t('appDebug.automatic.description')}</div>
@@ -139,7 +164,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
               </Button>
             </div>
           </div>
-        </div>
+        </div>}
 
         {(!isLoading && res) && (
           <div className='grow px-8 pt-6 h-full overflow-y-auto'>
@@ -180,7 +205,7 @@ const GetAutomaticRes: FC<IGetAutomaticResProps> = ({
           </div>
         )}
         {isLoading && renderLoading}
-        {(!isLoading && !res) && renderNoData}
+        {isShowAutoPromptResPlaceholder() && renderNoData}
         {showConfirmOverwrite && (
           <Confirm
             title={t('appDebug.automatic.overwriteTitle')}

+ 13 - 9
web/app/components/app/configuration/dataset-config/card-item/item.tsx

@@ -9,6 +9,8 @@ import { formatNumber } from '@/utils/format'
 import FileIcon from '@/app/components/base/file-icon'
 import { Settings01, Trash03 } from '@/app/components/base/icons/src/vender/line/general'
 import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
+import Drawer from '@/app/components/base/drawer'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 type ItemProps = {
   className?: string
@@ -24,6 +26,10 @@ const Item: FC<ItemProps> = ({
   onRemove,
 }) => {
   const { t } = useTranslation()
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   const [showSettingsModal, setShowSettingsModal] = useState(false)
 
   const handleSave = (newDataset: DataSet) => {
@@ -74,15 +80,13 @@ const Item: FC<ItemProps> = ({
           <Trash03 className='w-4 h-4 text-gray-500 group-hover/action:text-[#D92D20]' />
         </div>
       </div>
-      {
-        showSettingsModal && (
-          <SettingsModal
-            currentDataset={config}
-            onCancel={() => setShowSettingsModal(false)}
-            onSave={handleSave}
-          />
-        )
-      }
+      <Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
+        <SettingsModal
+          currentDataset={config}
+          onCancel={() => setShowSettingsModal(false)}
+          onSave={handleSave}
+        />
+      </Drawer>
     </div>
   )
 }

+ 1 - 1
web/app/components/app/configuration/dataset-config/params-config/index.tsx

@@ -137,7 +137,7 @@ const ParamsConfig: FC = () => {
             onClose={() => {
               setOpen(false)
             }}
-            className='min-w-[528px]'
+            className='sm:min-w-[528px]'
             wrapperClassName='z-50'
             title={t('appDebug.datasetConfig.settingTitle')}
           >

+ 6 - 13
web/app/components/app/configuration/dataset-config/settings-modal/index.tsx

@@ -1,6 +1,5 @@
 import type { FC } from 'react'
 import { useRef, useState } from 'react'
-import { useClickAway } from 'ahooks'
 import { useTranslation } from 'react-i18next'
 import { isEqual } from 'lodash-es'
 import cn from 'classnames'
@@ -30,7 +29,7 @@ type SettingsModalProps = {
 }
 
 const rowClass = `
-  flex justify-between py-4
+  flex justify-between py-4 flex-wrap gap-y-2
 `
 
 const labelClass = `
@@ -45,10 +44,6 @@ const SettingsModal: FC<SettingsModalProps> = ({
   const { t } = useTranslation()
   const { notify } = useToastContext()
   const ref = useRef(null)
-  useClickAway(() => {
-    if (ref)
-      onCancel()
-  }, ref)
 
   const { setShowAccountSettingModal } = useModalContext()
   const [loading, setLoading] = useState(false)
@@ -122,10 +117,8 @@ const SettingsModal: FC<SettingsModalProps> = ({
 
   return (
     <div
-      className='fixed top-16 right-2 flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl z-10'
+      className='overflow-hidden w-full flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl'
       style={{
-        zIndex: 11,
-        width: 700,
         height: 'calc(100vh - 72px)',
       }}
       ref={ref}
@@ -179,12 +172,12 @@ const SettingsModal: FC<SettingsModalProps> = ({
           <div className={labelClass}>
             <div>{t('datasetSettings.form.permissions')}</div>
           </div>
-          <div className='w-[480px]'>
+          <div className='w-full sm:w-[480px]'>
             <PermissionsRadio
               disable={!localeCurrentDataset?.embedding_available}
               value={localeCurrentDataset.permission}
               onChange={v => handleValueChange('permission', v!)}
-              itemClassName='!w-[227px]'
+              itemClassName='sm:!w-[227px]'
             />
           </div>
         </div>
@@ -198,7 +191,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
               disable={!localeCurrentDataset?.embedding_available}
               value={indexMethod}
               onChange={v => setIndexMethod(v!)}
-              itemClassName='!w-[227px]'
+              itemClassName='sm:!w-[227px]'
             />
           </div>
         </div>
@@ -272,7 +265,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
       )}
 
       <div
-        className='absolute z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t bg-white '
+        className='sticky z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t bg-white '
         style={{
           borderColor: 'rgba(0, 0, 0, 0.05)',
         }}

+ 28 - 6
web/app/components/app/configuration/index.tsx

@@ -8,6 +8,7 @@ import produce from 'immer'
 import { useBoolean, useGetState } from 'ahooks'
 import cn from 'classnames'
 import { clone, isEqual } from 'lodash-es'
+import { CodeBracketIcon } from '@heroicons/react/20/solid'
 import Button from '../../base/button'
 import Loading from '../../base/loading'
 import s from './style.module.css'
@@ -44,6 +45,8 @@ import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/
 import SelectDataSet from '@/app/components/app/configuration/dataset-config/select-dataset'
 import I18n from '@/context/i18n'
 import { useModalContext } from '@/context/modal-context'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
+import Drawer from '@/app/components/base/drawer'
 
 type PublichConfig = {
   modelConfig: ModelConfig
@@ -64,6 +67,10 @@ const Configuration: FC = () => {
 
   const [conversationId, setConversationId] = useState<string | null>('')
 
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+  const [isShowDebugPanel, { setTrue: showDebugPanel, setFalse: hideDebugPanel }] = useBoolean(false)
+
   const [introduction, setIntroduction] = useState<string>('')
   const [controlClearChatMessage, setControlClearChatMessage] = useState(0)
   const [prevPromptConfig, setPrevPromptConfig] = useState<PromptConfig>({
@@ -600,7 +607,7 @@ const Configuration: FC = () => {
     >
       <>
         <div className="flex flex-col h-full">
-          <div className='flex items-center justify-between px-6 shrink-0 h-14'>
+          <div className='flex items-center justify-between px-6 shrink-0 py-3 flex-wrap gap-y-2'>
             <div className='flex items-end'>
               <div className={s.promptTitle}></div>
               <div className='flex items-center h-[14px] space-x-1 text-xs'>
@@ -630,7 +637,7 @@ const Configuration: FC = () => {
               </div>
             </div>
 
-            <div className='flex items-center'>
+            <div className='flex items-center flex-wrap gap-y-2 gap-x-2'>
               {/* Model and Parameters */}
               <ConfigModel
                 isAdvancedMode={isAdvancedMode}
@@ -644,22 +651,28 @@ const Configuration: FC = () => {
                 }}
                 disabled={!hasSetAPIKEY}
               />
-              <div className='mx-3 w-[1px] h-[14px] bg-gray-200'></div>
+              <div className='w-[1px] h-[14px] bg-gray-200'></div>
               <Button onClick={() => setShowConfirm(true)} className='shrink-0 mr-2 w-[70px] !h-8 !text-[13px] font-medium'>{t('appDebug.operation.resetConfig')}</Button>
+              {isMobile && (
+                <Button className='!h-8 !text-[13px] font-medium' onClick={showDebugPanel}>
+                  <span className='mr-1'>{t('appDebug.operation.debugConfig')}</span>
+                  <CodeBracketIcon className="h-4 w-4 text-gray-500" />
+                </Button>
+              )}
               <Button type='primary' onClick={() => handlePublish(false)} className={cn(cannotPublish && '!bg-primary-200 !cursor-not-allowed', 'shrink-0 w-[70px] !h-8 !text-[13px] font-medium')}>{t('appDebug.operation.applyConfig')}</Button>
             </div>
           </div>
           <div className='flex grow h-[200px]'>
-            <div className="w-1/2 min-w-[560px] shrink-0">
+            <div className="w-full sm:w-1/2 shrink-0">
               <Config />
             </div>
-            <div className="relative w-1/2  grow h-full overflow-y-auto  py-4 px-6 bg-gray-50 flex flex-col rounded-tl-2xl border-t border-l" style={{ borderColor: 'rgba(0, 0, 0, 0.02)' }}>
+            {!isMobile && <div className="relative w-1/2 grow h-full overflow-y-auto py-4 px-6 bg-gray-50 flex flex-col rounded-tl-2xl border-t border-l" style={{ borderColor: 'rgba(0, 0, 0, 0.02)' }}>
               <Debug
                 hasSetAPIKEY={hasSetAPIKEY}
                 onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
                 inputs={inputs}
               />
-            </div>
+            </div>}
           </div>
         </div>
         {showConfirm && (
@@ -707,6 +720,15 @@ const Configuration: FC = () => {
             }}
           />
         )}
+        {isMobile && (
+          <Drawer showClose isOpen={isShowDebugPanel} onClose={hideDebugPanel} mask footer={null} panelClassname='!bg-gray-50'>
+            <Debug
+              hasSetAPIKEY={hasSetAPIKEY}
+              onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
+              inputs={inputs}
+            />
+          </Drawer>
+        )}
       </>
     </ConfigContext.Provider>
   )

+ 2 - 2
web/app/components/app/log/filter.tsx

@@ -39,7 +39,7 @@ const Filter: FC<IFilterProps> = ({ appId, queryParams, setQueryParams }: IFilte
   if (!data)
     return null
   return (
-    <div className='flex flex-row items-center mb-4 text-gray-900 text-base'>
+    <div className='flex flex-row flex-wrap gap-y-2 gap-x-4 items-center mb-4 text-gray-900 text-base'>
       <SimpleSelect
         items={TIME_PERIOD_LIST.map(item => ({ value: item.value, name: t(`appLog.filter.period.${item.name}`) }))}
         className='mt-0 !w-40'
@@ -47,7 +47,7 @@ const Filter: FC<IFilterProps> = ({ appId, queryParams, setQueryParams }: IFilte
           setQueryParams({ ...queryParams, period: item.value })
         }}
         defaultValue={queryParams.period} />
-      <div className="relative ml-4 rounded-md mr-4">
+      <div className="relative rounded-md">
         <SimpleSelect
           defaultValue={'all'}
           className='!w-[300px]'

+ 19 - 14
web/app/components/app/log/list.tsx

@@ -33,6 +33,7 @@ import { TONE_LIST } from '@/config'
 import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
 import ModelName from '@/app/components/app/configuration/config-model/model-name'
 import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 type IConversationList = {
   logs?: ChatConversationsResponse | CompletionConversationsResponse
@@ -200,7 +201,7 @@ function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionCo
         <div className='text-gray-500 text-[10px] leading-[14px]'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div>
         <div className='text-gray-700 text-[13px] leading-[18px]'>{isChatMode ? detail.id?.split('-').slice(-1)[0] : dayjs.unix(detail.created_at).format(t('appLog.dateTimeFormat') as string)}</div>
       </div>
-      <div className='flex items-center'>
+      <div className='flex items-center flex-wrap gap-y-1 justify-end'>
         <div
           className={cn('mr-2 flex items-center border h-8 px-2 space-x-2 rounded-lg bg-indigo-25 border-[#2A87F5]')}
         >
@@ -412,6 +413,10 @@ const ChatConversationDetailComp: FC<{ appId?: string; conversationId?: string }
  */
 const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh }) => {
   const { t } = useTranslation()
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   const [showDrawer, setShowDrawer] = useState<boolean>(false) // Whether to display the chat details drawer
   const [currentConversation, setCurrentConversation] = useState<ChatConversationGeneralDetail | CompletionConversationGeneralDetail | undefined>() // Currently selected conversation
   const isChatMode = appDetail?.mode === 'chat' // Whether the app is a chat app
@@ -445,17 +450,17 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
     return <Loading />
 
   return (
-    <>
-      <table className={`w-full border-collapse border-0 text-sm mt-3 ${s.logTable}`}>
-        <thead className="h-8 leading-8 border-b  border-gray-200 text-gray-500 font-bold">
+    <div className='overflow-x-auto'>
+      <table className={`w-full min-w-[440px] border-collapse border-0 text-sm mt-3 ${s.logTable}`}>
+        <thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-bold">
           <tr>
-            <td className='w-[1.375rem]'></td>
-            <td>{t('appLog.table.header.time')}</td>
-            <td>{t('appLog.table.header.endUser')}</td>
-            <td>{isChatMode ? t('appLog.table.header.summary') : t('appLog.table.header.input')}</td>
-            <td>{isChatMode ? t('appLog.table.header.messageCount') : t('appLog.table.header.output')}</td>
-            <td>{t('appLog.table.header.userRate')}</td>
-            <td>{t('appLog.table.header.adminRate')}</td>
+            <td className='w-[1.375rem] whitespace-nowrap'></td>
+            <td className='whitespace-nowrap'>{t('appLog.table.header.time')}</td>
+            <td className='whitespace-nowrap'>{t('appLog.table.header.endUser')}</td>
+            <td className='whitespace-nowrap'>{isChatMode ? t('appLog.table.header.summary') : t('appLog.table.header.input')}</td>
+            <td className='whitespace-nowrap'>{isChatMode ? t('appLog.table.header.messageCount') : t('appLog.table.header.output')}</td>
+            <td className='whitespace-nowrap'>{t('appLog.table.header.userRate')}</td>
+            <td className='whitespace-nowrap'>{t('appLog.table.header.adminRate')}</td>
           </tr>
         </thead>
         <tbody className="text-gray-500">
@@ -504,9 +509,9 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
       <Drawer
         isOpen={showDrawer}
         onClose={onCloseDrawer}
-        mask={false}
+        mask={isMobile}
         footer={null}
-        panelClassname='mt-16 mr-2 mb-3 !p-0 !max-w-[640px] rounded-b-xl'
+        panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'
       >
         <DrawerContext.Provider value={{
           onClose: onCloseDrawer,
@@ -518,7 +523,7 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
           }
         </DrawerContext.Provider>
       </Drawer>
-    </>
+    </div>
   )
 }
 

+ 4 - 4
web/app/components/app/overview/appCard.tsx

@@ -129,7 +129,7 @@ function AppCard({
 
   return (
     <div
-      className={`min-w-max shadow-xs border-[0.5px] rounded-lg border-gray-200 ${
+      className={`shadow-xs border-[0.5px] rounded-lg border-gray-200 ${
         className ?? ''
       }`}
     >
@@ -163,8 +163,8 @@ function AppCard({
                 : t('appOverview.overview.apiInfo.accessibleAddress')}
             </div>
             <div className="w-full h-9 pl-2 pr-0.5 py-0.5 bg-black bg-opacity-[0.02] rounded-lg border border-black border-opacity-5 justify-start items-center inline-flex">
-              <div className="h-4 px-2 justify-start items-start gap-2 flex flex-1">
-                <div className="text-gray-700 text-xs font-medium">
+              <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">
                   {isApp ? appUrl : apiUrl}
                 </div>
               </div>
@@ -196,7 +196,7 @@ function AppCard({
             </div>
           </div>
         </div>
-        <div className={'pt-2 flex flex-row items-center'}>
+        <div className={'pt-2 flex flex-row items-center flex-wrap gap-y-2'}>
           {!isApp && <SecretKeyButton className='flex-shrink-0 !h-8 bg-white mr-2' textCls='!text-gray-700 font-medium' iconCls='stroke-[1.2px]' appId={appInfo.id} />}
           {OPERATIONS_MAP[cardType].map((op) => {
             const disabled

+ 2 - 2
web/app/components/app/overview/customize/index.tsx

@@ -81,10 +81,10 @@ const CustomizeModal: FC<IShareLinkProps> = ({
       </div>
       <div className='flex py-4'>
         <StepNum>3</StepNum>
-        <div className='flex flex-col w-full'>
+        <div className='flex flex-col w-full overflow-hidden'>
           <div className='text-gray-900'>{t(`${prefixCustomize}.way1.step3`)}</div>
           <div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step3Tip`)}</div>
-          <pre className='box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg select-text'>
+          <pre className='overflow-x-scroll box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg select-text'>
             NEXT_PUBLIC_APP_ID={`'${appId}'`} <br />
             NEXT_PUBLIC_APP_KEY={'\'<Web API Key From Dify>\''} <br />
             NEXT_PUBLIC_API_URL={`'${api_base_url}'`}

+ 2 - 2
web/app/components/app/overview/embedded/index.tsx

@@ -106,7 +106,7 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
       <div className="mb-4 mt-8 text-gray-900 text-[14px] font-medium leading-tight">
         {t(`${prefixEmbedded}.explanation`)}
       </div>
-      <div className="flex items-center justify-between">
+      <div className="flex items-center justify-between flex-wrap gap-y-2">
         {Object.keys(OPTION_MAP).map((v, index) => {
           return (
             <div
@@ -150,7 +150,7 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
             </Tooltip>
           </div>
         </div>
-        <div className="self-stretch p-3 justify-start items-start gap-2 inline-flex">
+        <div className="p-3 justify-start items-start gap-2 flex overflow-x-auto w-full">
           <div className="grow shrink basis-0 text-slate-700 text-[13px] leading-tight font-mono">
             <pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, isTestEnv)}</pre>
           </div>

+ 8 - 2
web/app/components/base/drawer/index.tsx

@@ -1,9 +1,10 @@
 'use client'
 import { Dialog } from '@headlessui/react'
 import { useTranslation } from 'react-i18next'
+import { XMarkIcon } from '@heroicons/react/24/outline'
 import Button from '../button'
 
-type DrawerProps = {
+export type IDrawerProps = {
   title?: string
   description?: string
   panelClassname?: string
@@ -12,6 +13,7 @@ type DrawerProps = {
   mask?: boolean
   isOpen: boolean
   // closable: boolean
+  showClose?: boolean
   onClose: () => void
   onCancel?: () => void
   onOk?: () => void
@@ -24,11 +26,12 @@ export default function Drawer({
   children,
   footer,
   mask = true,
+  showClose = false,
   isOpen,
   onClose,
   onCancel,
   onOk,
-}: DrawerProps) {
+}: IDrawerProps) {
   const { t } = useTranslation()
   return (
     <Dialog
@@ -52,6 +55,9 @@ export default function Drawer({
             >
               {title}
             </Dialog.Title>}
+            {showClose && <Dialog.Title className="flex items-center mb-4" as="div">
+              <XMarkIcon className='w-4 h-4 text-gray-500' onClick={onClose} />
+            </Dialog.Title>}
             {description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'>{description}</Dialog.Description>}
             {children}
           </>

+ 37 - 0
web/app/components/base/float-popover-container/index.tsx

@@ -0,0 +1,37 @@
+'use client'
+import {
+  PortalToFollowElem,
+  PortalToFollowElemContent,
+  PortalToFollowElemTrigger,
+} from '@/app/components/base/portal-to-follow-elem'
+import type { PortalToFollowElemOptions } from '@/app/components/base/portal-to-follow-elem'
+
+type IFloatRightContainerProps = {
+  isMobile: boolean
+  open: boolean
+  toggle: () => void
+  triggerElement?: React.ReactNode
+  children?: React.ReactNode
+} & PortalToFollowElemOptions
+
+const FloatRightContainer = ({ open, toggle, triggerElement, isMobile, children, ...portalProps }: IFloatRightContainerProps) => {
+  return (
+    <>
+      {isMobile && (
+        <PortalToFollowElem open={open} {...portalProps}>
+          <PortalToFollowElemTrigger onClick={toggle}>
+            {triggerElement}
+          </PortalToFollowElemTrigger>
+          <PortalToFollowElemContent>
+            {children}
+          </PortalToFollowElemContent>
+        </PortalToFollowElem>
+      )}
+      {!isMobile && open && (
+        <>{children}</>
+      )}
+    </>
+  )
+}
+
+export default FloatRightContainer

+ 23 - 0
web/app/components/base/float-right-container/index.tsx

@@ -0,0 +1,23 @@
+'use client'
+import Drawer from '@/app/components/base/drawer'
+import type { IDrawerProps } from '@/app/components/base/drawer'
+
+type IFloatRightContainerProps = {
+  isMobile: boolean
+  children?: React.ReactNode
+} & IDrawerProps
+
+const FloatRightContainer = ({ isMobile, children, isOpen, ...drawerProps }: IFloatRightContainerProps) => {
+  return (
+    <>
+      {isMobile && (
+        <Drawer isOpen={isOpen} {...drawerProps}>{children}</Drawer>
+      )}
+      {(!isMobile && isOpen) && (
+        <>{children}</>
+      )}
+    </>
+  )
+}
+
+export default FloatRightContainer

+ 1 - 1
web/app/components/base/popover/style.module.css

@@ -5,5 +5,5 @@
     @apply absolute z-10 w-full max-w-sm px-4 mt-1 sm:px-0 lg:max-w-3xl
 }
 .panelContainer {
-    @apply overflow-hidden bg-white w-full rounded-lg shadow-lg ring-1 ring-black ring-opacity-5
+    @apply overflow-hidden bg-white w-fit min-w-[130px] rounded-lg shadow-lg ring-1 ring-black ring-opacity-5
 }

+ 1 - 1
web/app/components/base/portal-to-follow-elem/index.tsx

@@ -17,7 +17,7 @@ import {
 
 import type { OffsetOptions, Placement } from '@floating-ui/react'
 
-type PortalToFollowElemOptions = {
+export type PortalToFollowElemOptions = {
   /*
   * top, bottom, left, right
   * start, end. Default is middle

+ 1 - 1
web/app/components/base/select/index.tsx

@@ -148,7 +148,7 @@ const Select: FC<ISelectProps> = ({
 
 const SimpleSelect: FC<ISelectProps> = ({
   className,
-  wrapperClassName,
+  wrapperClassName = '',
   items = defaultItems,
   defaultValue = 1,
   disabled = false,

+ 2 - 2
web/app/components/datasets/create/file-uploader/index.module.css

@@ -15,10 +15,10 @@
   color: #667085;
 }
 .uploader {
-  @apply relative box-border flex justify-center items-center mb-2;
+  @apply relative box-border flex justify-center items-center mb-2 p-3;
   flex-direction: column;
   max-width: 640px;
-  height: 80px;
+  min-height: 80px;
   background: #F9FAFB;
   border: 1px dashed #EAECF0;
   border-radius: 12px;

+ 5 - 3
web/app/components/datasets/create/file-uploader/index.tsx

@@ -234,10 +234,12 @@ const FileUploader = ({
       />
       <div className={cn(s.title, titleClassName)}>{t('datasetCreation.stepOne.uploader.title')}</div>
       <div ref={dropRef} className={cn(s.uploader, dragging && s.dragging)}>
-        <div className='flex justify-center items-center h-6 mb-2'>
+        <div className='flex justify-center items-center min-h-6 mb-2'>
           <span className={s.uploadIcon}/>
-          <span>{t('datasetCreation.stepOne.uploader.button')}</span>
-          <label className={s.browse} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
+          <span>
+            {t('datasetCreation.stepOne.uploader.button')}
+            <label className={s.browse} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
+          </span>
         </div>
         <div className={s.tip}>{t('datasetCreation.stepOne.uploader.tip', { size: fileUploadConfig.file_size_limit })}</div>
         {dragging && <div ref={dragRef} className={s.draggingCover}/>}

+ 1 - 1
web/app/components/datasets/create/index.tsx

@@ -103,7 +103,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
 
   return (
     <div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
-      <div className="flex flex-col w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
+      <div className="flex flex-col w-11 sm:w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
         <StepsNavBar step={step} datasetId={datasetId} />
       </div>
       <div className="grow bg-white">

+ 0 - 3
web/app/components/datasets/create/step-one/index.module.css

@@ -15,9 +15,6 @@
   background-color: #fff;
 }
 
-.dataSourceTypeList {
-  @apply flex items-center mb-8;
-}
 .dataSourceItem {
   @apply box-border relative shrink-0 flex items-center mr-3 p-3 h-14 bg-white rounded-xl cursor-pointer;
   border: 0.5px solid #EAECF0;

+ 1 - 1
web/app/components/datasets/create/step-one/index.tsx

@@ -106,7 +106,7 @@ const StepOne = ({
         <div className={s.form}>
           {
             shouldShowDataSourceTypeList && (
-              <div className={s.dataSourceTypeList}>
+              <div className='flex items-center mb-8 flex-wrap gap-y-4'>
                 <div
                   className={cn(
                     s.dataSourceItem,

+ 7 - 3
web/app/components/datasets/create/step-three/index.tsx

@@ -5,6 +5,7 @@ import cn from 'classnames'
 import EmbeddingProcess from '../embedding-process'
 
 import s from './index.module.css'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 import type { FullDocumentDetail, createDocumentResponse } from '@/models/datasets'
 
 type StepThreeProps = {
@@ -17,9 +18,12 @@ type StepThreeProps = {
 const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: StepThreeProps) => {
   const { t } = useTranslation()
 
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   return (
     <div className='flex w-full h-full'>
-      <div className={'h-full w-full overflow-y-scroll px-16'}>
+      <div className={'h-full w-full overflow-y-scroll px-6 sm:px-16'}>
         <div className='max-w-[636px]'>
           {!datasetId && (
             <>
@@ -46,13 +50,13 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: Step
           />
         </div>
       </div>
-      <div className={cn(s.sideTip)}>
+      {!isMobile && <div className={cn(s.sideTip)}>
         <div className={s.tipCard}>
           <span className={s.icon}/>
           <div className={s.title}>{t('datasetCreation.stepThree.sideTipTitle')}</div>
           <div className={s.content}>{t('datasetCreation.stepThree.sideTipContent')}</div>
         </div>
-      </div>
+      </div>}
     </div>
   )
 }

+ 3 - 3
web/app/components/datasets/create/step-two/index.module.css

@@ -1,5 +1,5 @@
 .pageHeader {
-  @apply px-16;
+  @apply px-16 flex justify-between items-center;
   position: sticky;
   top: 0;
   left: 0;
@@ -251,7 +251,7 @@
 }
 
 .ruleItem {
-  @apply flex items-center h-7;
+  @apply flex items-center;
 }
 
 .formFooter {
@@ -382,7 +382,7 @@
 
 .previewWrap {
   flex-shrink: 0;
-  width: 524px;
+  max-width: 524px;
 }
 
 .previewHeader {

+ 85 - 62
web/app/components/datasets/create/step-two/index.tsx

@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
 import { useContext } from 'use-context-selector'
 import { useBoolean } from 'ahooks'
 import { XMarkIcon } from '@heroicons/react/20/solid'
+import { RocketLaunchIcon } from '@heroicons/react/24/outline'
 import cn from 'classnames'
 import Link from 'next/link'
 import { groupBy } from 'lodash-es'
@@ -20,6 +21,7 @@ import {
 } from '@/service/datasets'
 import Button from '@/app/components/base/button'
 import Loading from '@/app/components/base/loading'
+import FloatRightContainer from '@/app/components/base/float-right-container'
 import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
 import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
 import { type RetrievalConfig } from '@/types/app'
@@ -37,6 +39,8 @@ import I18n from '@/context/i18n'
 import { IS_CE_EDITION } from '@/config'
 import { RETRIEVE_METHOD } from '@/types/app'
 import { useProviderContext } from '@/context/provider-context'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
+import Tooltip from '@/app/components/base/tooltip'
 
 type ValueOf<T> = T[keyof T]
 type StepTwoProps = {
@@ -84,6 +88,9 @@ const StepTwo = ({
   const { t } = useTranslation()
   const { locale } = useContext(I18n)
 
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   const { dataset: currentDataset, mutateDatasetRes } = useDatasetDetailContext()
   const scrollRef = useRef<HTMLDivElement>(null)
   const [scrolled, setScrolled] = useState(false)
@@ -467,7 +474,7 @@ const StepTwo = ({
   useEffect(() => {
     if (segmentationType === SegmentType.AUTO) {
       setAutomaticFileIndexingEstimate(null)
-      setShowPreview()
+      !isMobile && setShowPreview()
       fetchFileIndexingEstimate()
       setPreviewSwitched(false)
     }
@@ -493,8 +500,23 @@ const StepTwo = ({
   return (
     <div className='flex w-full h-full'>
       <div ref={scrollRef} className='relative h-full w-full overflow-y-scroll'>
-        <div className={cn(s.pageHeader, scrolled && s.fixed)}>{t('datasetCreation.steps.two')}</div>
-        <div className={cn(s.form)}>
+        <div className={cn(s.pageHeader, scrolled && s.fixed, isMobile && '!px-6')}>
+          <span>{t('datasetCreation.steps.two')}</span>
+          {isMobile && (
+            <Button
+              className='border-[0.5px] !h-8 hover:outline hover:outline-[0.5px] hover:outline-gray-300 text-gray-700 font-medium bg-white shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)]'
+              onClick={setShowPreview}
+            >
+              <Tooltip selector='data-preview-toggle'>
+                <div className="flex flex-row items-center">
+                  <RocketLaunchIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" />
+                  <span className="text-[13px]">{t('datasetCreation.stepTwo.previewTitleButton')}</span>
+                </div>
+              </Tooltip>
+            </Button>
+          )}
+        </div>
+        <div className={cn(s.form, isMobile && '!px-4')}>
           <div className={s.label}>{t('datasetCreation.stepTwo.segmentation')}</div>
           <div className='max-w-[640px]'>
             <div
@@ -554,7 +576,7 @@ const StepTwo = ({
                     </div>
                   </div>
                   <div className={s.formRow}>
-                    <div className='w-full'>
+                    <div className='w-full flex flex-col gap-1'>
                       <div className={s.label}>{t('datasetCreation.stepTwo.rules')}</div>
                       {rules.map(rule => (
                         <div key={rule.id} className={s.ruleItem}>
@@ -574,7 +596,7 @@ const StepTwo = ({
           </div>
           <div className={s.label}>{t('datasetCreation.stepTwo.indexMode')}</div>
           <div className='max-w-[640px]'>
-            <div className='flex items-center gap-3'>
+            <div className='flex items-center gap-3 flex-wrap sm:flex-nowrap'>
               {(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.QUALIFIED)) && (
                 <div
                   className={cn(
@@ -797,68 +819,69 @@ const StepTwo = ({
           </div>
         </div>
       </div>
-      {(showPreview)
-        ? (
-          <div ref={previewScrollRef} className={cn(s.previewWrap, 'relativeh-full overflow-y-scroll border-l border-[#F2F4F7]')}>
-            <div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`)}>
-              <div className='flex items-center justify-between px-8'>
-                <div className='grow flex items-center'>
-                  <div>{t('datasetCreation.stepTwo.previewTitle')}</div>
-                  {docForm === DocForm.QA && !previewSwitched && (
-                    <Button className='ml-2 !h-[26px] !py-[3px] !px-2 !text-xs !font-medium !text-primary-600' onClick={previewSwitch}>{t('datasetCreation.stepTwo.previewButton')}</Button>
-                  )}
-                </div>
-                <div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
-                  <XMarkIcon className='h-4 w-4'></XMarkIcon>
-                </div>
+      <FloatRightContainer isMobile={isMobile} isOpen={showPreview} onClose={hidePreview} footer={null}>
+        {showPreview && <div ref={previewScrollRef} className={cn(s.previewWrap, 'relative h-full overflow-y-scroll border-l border-[#F2F4F7]')}>
+          <div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`)}>
+            <div className='flex items-center justify-between px-8'>
+              <div className='grow flex items-center'>
+                <div>{t('datasetCreation.stepTwo.previewTitle')}</div>
+                {docForm === DocForm.QA && !previewSwitched && (
+                  <Button className='ml-2 !h-[26px] !py-[3px] !px-2 !text-xs !font-medium !text-primary-600' onClick={previewSwitch}>{t('datasetCreation.stepTwo.previewButton')}</Button>
+                )}
+              </div>
+              <div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
+                <XMarkIcon className='h-4 w-4'></XMarkIcon>
               </div>
-              {docForm === DocForm.QA && !previewSwitched && (
-                <div className='px-8 pr-12 text-xs text-gray-500'>
-                  <span>{t('datasetCreation.stepTwo.previewSwitchTipStart')}</span>
-                  <span className='text-amber-600'>{t('datasetCreation.stepTwo.previewSwitchTipEnd')}</span>
-                </div>
-              )}
-            </div>
-            <div className='my-4 px-8 space-y-4'>
-              {previewSwitched && docForm === DocForm.QA && fileIndexingEstimate?.qa_preview && (
-                <>
-                  {fileIndexingEstimate?.qa_preview.map((item, index) => (
-                    <PreviewItem type={PreviewType.QA} key={item.question} qa={item} index={index + 1} />
-                  ))}
-                </>
-              )}
-              {(docForm === DocForm.TEXT || !previewSwitched) && fileIndexingEstimate?.preview && (
-                <>
-                  {fileIndexingEstimate?.preview.map((item, index) => (
-                    <PreviewItem type={PreviewType.TEXT} key={item} content={item} index={index + 1} />
-                  ))}
-                </>
-              )}
-              {previewSwitched && docForm === DocForm.QA && !fileIndexingEstimate?.qa_preview && (
-                <div className='flex items-center justify-center h-[200px]'>
-                  <Loading type='area' />
-                </div>
-              )}
-              {!previewSwitched && !fileIndexingEstimate?.preview && (
-                <div className='flex items-center justify-center h-[200px]'>
-                  <Loading type='area' />
-                </div>
-              )}
             </div>
+            {docForm === DocForm.QA && !previewSwitched && (
+              <div className='px-8 pr-12 text-xs text-gray-500'>
+                <span>{t('datasetCreation.stepTwo.previewSwitchTipStart')}</span>
+                <span className='text-amber-600'>{t('datasetCreation.stepTwo.previewSwitchTipEnd')}</span>
+              </div>
+            )}
+          </div>
+          <div className='my-4 px-8 space-y-4'>
+            {previewSwitched && docForm === DocForm.QA && fileIndexingEstimate?.qa_preview && (
+              <>
+                {fileIndexingEstimate?.qa_preview.map((item, index) => (
+                  <PreviewItem type={PreviewType.QA} key={item.question} qa={item} index={index + 1} />
+                ))}
+              </>
+            )}
+            {(docForm === DocForm.TEXT || !previewSwitched) && fileIndexingEstimate?.preview && (
+              <>
+                {fileIndexingEstimate?.preview.map((item, index) => (
+                  <PreviewItem type={PreviewType.TEXT} key={item} content={item} index={index + 1} />
+                ))}
+              </>
+            )}
+            {previewSwitched && docForm === DocForm.QA && !fileIndexingEstimate?.qa_preview && (
+              <div className='flex items-center justify-center h-[200px]'>
+                <Loading type='area' />
+              </div>
+            )}
+            {!previewSwitched && !fileIndexingEstimate?.preview && (
+              <div className='flex items-center justify-center h-[200px]'>
+                <Loading type='area' />
+              </div>
+            )}
           </div>
-        )
-        : (<div className={cn(s.sideTip)}>
-          <div className={s.tipCard}>
-            <span className={s.icon} />
-            <div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div>
-            <div className={s.content}>
-              <p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP1')}</p>
-              <p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP2')}</p>
-              <p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP3')}</p>
-              <p>{t('datasetCreation.stepTwo.sideTipP4')}</p>
+        </div>}
+        {!showPreview && (
+          <div className={cn(s.sideTip)}>
+            <div className={s.tipCard}>
+              <span className={s.icon} />
+              <div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div>
+              <div className={s.content}>
+                <p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP1')}</p>
+                <p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP2')}</p>
+                <p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP3')}</p>
+                <p>{t('datasetCreation.stepTwo.sideTipP4')}</p>
+              </div>
             </div>
           </div>
-        </div>)}
+        )}
+      </FloatRightContainer>
     </div>
   )
 }

+ 3 - 2
web/app/components/datasets/create/steps-nav-bar/index.module.css

@@ -14,14 +14,15 @@
   background-size: 16px;
 }
 .stepList {
-  @apply p-4;
+  @apply p-4 relative;
   line-height: 18px;
 }
 
 .stepItem {
-  @apply relative flex justify-items-start pt-3 pr-0 pb-3;
+  @apply relative flex justify-items-start pt-3 pr-0 pb-3 box-content;
   padding-left: 52px;
   font-size: 13px;
+  height: 18px;
 }
 
 .stepItem.step1::before {

+ 33 - 23
web/app/components/datasets/create/steps-nav-bar/index.tsx

@@ -3,46 +3,56 @@ import { useTranslation } from 'react-i18next'
 import { useRouter } from 'next/navigation'
 
 import cn from 'classnames'
+import { useCallback } from 'react'
 import s from './index.module.css'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 type IStepsNavBarProps = {
-  step: number,
-  datasetId?: string,
+  step: number
+  datasetId?: string
 }
 
+const STEP_T_MAP: Record<number, string> = {
+  1: 'datasetCreation.steps.one',
+  2: 'datasetCreation.steps.two',
+  3: 'datasetCreation.steps.three',
+}
+
+const STEP_LIST = [1, 2, 3]
+
 const StepsNavBar = ({
   step,
   datasetId,
 }: IStepsNavBarProps) => {
   const { t } = useTranslation()
   const router = useRouter()
-  const navBackHandle = () => {
-    if (!datasetId) {
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
+  const navBackHandle = useCallback(() => {
+    if (!datasetId)
       router.replace('/datasets')
-    } else {
+    else
       router.replace(`/datasets/${datasetId}/documents`)
-    }
-  }
+  }, [router, datasetId])
 
   return (
     <div className='w-full pt-4'>
-      <div className={s.stepsHeader}>
-        <div onClick={navBackHandle} className={s.navBack} />
-        {!datasetId ? t('datasetCreation.steps.header.creation') : t('datasetCreation.steps.header.update')}
+      <div className={cn(s.stepsHeader, isMobile && '!px-0 justify-center')}>
+        <div onClick={navBackHandle} className={cn(s.navBack, isMobile && '!mr-0')} />
+        {!isMobile && (!datasetId ? t('datasetCreation.steps.header.creation') : t('datasetCreation.steps.header.update'))}
       </div>
-      <div className={cn(s.stepList)}>
-        <div className={cn(s.stepItem, s.step1, step === 1 && s.active, step !== 1 && s.done)}>
-          <div className={cn(s.stepNum)}>{step === 1 ? 1 : ''}</div>
-          <div className={cn(s.stepName)}>{t('datasetCreation.steps.one')}</div>
-        </div>
-        <div className={cn(s.stepItem, s.step2, step === 2 && s.active, step === 3 && s.done)}>
-          <div className={cn(s.stepNum)}>{step !== 3 ? 2 : ''}</div>
-          <div className={cn(s.stepName)}>{t('datasetCreation.steps.two')}</div>
-        </div>
-        <div className={cn(s.stepItem, s.step3, step === 3 && s.active)}>
-          <div className={cn(s.stepNum)}>3</div>
-          <div className={cn(s.stepName)}>{t('datasetCreation.steps.three')}</div>
-        </div>
+      <div className={cn(s.stepList, isMobile && '!p-0')}>
+        {STEP_LIST.map(item => (
+          <div
+            key={item}
+            className={cn(s.stepItem, s[`step${item}`], step === item && s.active, step > item && s.done, isMobile && 'px-0')}
+          >
+            <div className={cn(s.stepNum)}>{item}</div>
+            <div className={cn(s.stepName)}>{isMobile ? '' : t(STEP_T_MAP[item])}</div>
+          </div>
+        ))}
       </div>
     </div>
   )

+ 1 - 1
web/app/components/datasets/documents/detail/completed/index.tsx

@@ -178,7 +178,7 @@ const SegmentDetailComponent: FC<ISegmentDetailProps> = ({
         }
       </div>
       <div className={cn(s.footer, s.numberInfo)}>
-        <div className='flex items-center'>
+        <div className='flex items-center flex-wrap gap-y-2'>
           <div className={cn(s.commonIcon, s.typeSquareIcon)} /><span className='mr-8'>{formatNumber(segInfo?.word_count as number)} {t('datasetDocuments.segment.characters')}</span>
           <div className={cn(s.commonIcon, s.targetIcon)} /><span className='mr-8'>{formatNumber(segInfo?.hit_count as number)} {t('datasetDocuments.segment.hitCount')}</span>
           <div className={cn(s.commonIcon, s.bezierCurveIcon)} /><span className={s.hashText}>{t('datasetDocuments.segment.vectorHash')}{segInfo?.index_node_hash}</span>

+ 3 - 3
web/app/components/datasets/documents/detail/completed/style.module.css

@@ -8,7 +8,7 @@
   @apply text-gray-900 font-medium text-base flex-1;
 }
 .docSearchWrapper {
-  @apply sticky w-full h-10 -top-3 bg-white flex items-center mb-3 justify-between z-10;
+  @apply sticky w-full py-1 -top-3 bg-white flex items-center mb-3 justify-between z-10 flex-wrap gap-y-1;
 }
 .listContainer {
   height: calc(100% - 3.25rem);
@@ -18,7 +18,7 @@
   @apply grid gap-4 grid-cols-3 min-w-[902px] last:mb-[30px];
 }
 .segWrapper {
-  @apply box-border h-[180px] min-w-[290px] bg-gray-50 px-4 pt-4 flex flex-col text-opacity-50 rounded-xl border border-transparent hover:border-gray-200 hover:shadow-lg hover:cursor-pointer hover:bg-white;
+  @apply box-border h-[180px] w-full xl:min-w-[290px] bg-gray-50 px-4 pt-4 flex flex-col text-opacity-50 rounded-xl border border-transparent hover:border-gray-200 hover:shadow-lg hover:cursor-pointer hover:bg-white;
 }
 .segTitleWrapper {
   @apply flex items-center justify-between;
@@ -48,7 +48,7 @@
   white-space: pre-line;
 }
 .footer {
-  @apply flex items-center justify-between box-border border-t-gray-200 border-t-[0.5px] pt-3 mt-4;
+  @apply flex items-center justify-between box-border border-t-gray-200 border-t-[0.5px] pt-3 mt-4 flex-wrap gap-y-2;
 }
 .numberInfo {
   @apply text-gray-500 text-xs font-medium;

+ 45 - 35
web/app/components/datasets/documents/detail/index.tsx

@@ -23,6 +23,8 @@ import { checkSegmentBatchImportProgress, fetchDocumentDetail, segmentBatchImpor
 import { ToastContext } from '@/app/components/base/toast'
 import type { DocForm } from '@/models/datasets'
 import { useDatasetDetailContext } from '@/context/dataset-detail'
+import FloatRightContainer from '@/app/components/base/float-right-container'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 export const DocumentContext = createContext<{ datasetId?: string; documentId?: string; docForm: string }>({ docForm: '' })
 
@@ -50,10 +52,14 @@ type Props = {
 const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
   const router = useRouter()
   const { t } = useTranslation()
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   const { notify } = useContext(ToastContext)
   const { dataset } = useDatasetDetailContext()
   const embeddingAvailable = !!dataset?.embedding_available
-  const [showMetadata, setShowMetadata] = useState(true)
+  const [showMetadata, setShowMetadata] = useState(!isMobile)
   const [newSegmentModalVisible, setNewSegmentModalVisible] = useState(false)
   const [batchModalVisible, setBatchModalVisible] = useState(false)
   const [importStatus, setImportStatus] = useState<ProcessStatus | string>()
@@ -124,44 +130,46 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
   return (
     <DocumentContext.Provider value={{ datasetId, documentId, docForm: documentDetail?.doc_form || '' }}>
       <div className='flex flex-col h-full'>
-        <div className='flex h-16 border-b-gray-100 border-b items-center p-4'>
-          <div onClick={backToPrev} className={'rounded-full w-8 h-8 flex justify-center items-center border-gray-100 cursor-pointer border hover:border-gray-300 shadow-[0px_12px_16px_-4px_rgba(16,24,40,0.08),0px_4px_6px_-2px_rgba(16,24,40,0.03)]'}>
+        <div className='flex min-h-16 border-b-gray-100 border-b items-center p-4 justify-between flex-wrap gap-y-2'>
+          <div onClick={backToPrev} className={'shrink-0 rounded-full w-8 h-8 flex justify-center items-center border-gray-100 cursor-pointer border hover:border-gray-300 shadow-[0px_12px_16px_-4px_rgba(16,24,40,0.08),0px_4px_6px_-2px_rgba(16,24,40,0.03)]'}>
             <ArrowLeftIcon className='text-primary-600 fill-current stroke-current h-4 w-4' />
           </div>
           <Divider className='!h-4' type='vertical' />
           <DocumentTitle extension={documentDetail?.data_source_info?.upload_file?.extension} name={documentDetail?.name} />
-          <StatusItem status={documentDetail?.display_status || 'available'} scene='detail' errorMessage={documentDetail?.error || ''} />
-          {embeddingAvailable && documentDetail && !documentDetail.archived && (
-            <SegmentAdd
-              importStatus={importStatus}
-              clearProcessStatus={resetProcessStatus}
-              showNewSegmentModal={showNewSegmentModal}
-              showBatchModal={showBatchModal}
+          <div className='flex items-center flex-wrap gap-y-2'>
+            <StatusItem status={documentDetail?.display_status || 'available'} scene='detail' errorMessage={documentDetail?.error || ''} />
+            {embeddingAvailable && documentDetail && !documentDetail.archived && (
+              <SegmentAdd
+                importStatus={importStatus}
+                clearProcessStatus={resetProcessStatus}
+                showNewSegmentModal={showNewSegmentModal}
+                showBatchModal={showBatchModal}
+              />
+            )}
+            <OperationAction
+              scene='detail'
+              embeddingAvailable={embeddingAvailable}
+              detail={{
+                enabled: documentDetail?.enabled || false,
+                archived: documentDetail?.archived || false,
+                id: documentId,
+                data_source_type: documentDetail?.data_source_type || '',
+                doc_form: documentDetail?.doc_form || '',
+              }}
+              datasetId={datasetId}
+              onUpdate={handleOperate}
+              className='!w-[216px]'
+            />
+            <button
+              className={cn(style.layoutRightIcon, showMetadata ? style.iconShow : style.iconClose)}
+              onClick={() => setShowMetadata(!showMetadata)}
             />
-          )}
-          <OperationAction
-            scene='detail'
-            embeddingAvailable={embeddingAvailable}
-            detail={{
-              enabled: documentDetail?.enabled || false,
-              archived: documentDetail?.archived || false,
-              id: documentId,
-              data_source_type: documentDetail?.data_source_type || '',
-              doc_form: documentDetail?.doc_form || '',
-            }}
-            datasetId={datasetId}
-            onUpdate={handleOperate}
-            className='!w-[216px]'
-          />
-          <button
-            className={cn(style.layoutRightIcon, showMetadata ? style.iconShow : style.iconClose)}
-            onClick={() => setShowMetadata(!showMetadata)}
-          />
+          </div>
         </div>
         <div className='flex flex-row flex-1' style={{ height: 'calc(100% - 4rem)' }}>
           {isDetailLoading
             ? <Loading type='app' />
-            : <div className={`box-border h-full w-full overflow-y-scroll ${embedding ? 'py-12 px-16' : 'pb-[30px] pt-3 px-6'}`}>
+            : <div className={`h-full w-full flex flex-col ${embedding ? 'px-6 py-3 sm:py-12 sm:px-16' : 'pb-[30px] pt-3 px-6'}`}>
               {embedding
                 ? <Embedding detail={documentDetail} detailUpdate={detailMutate} />
                 : <Completed
@@ -174,11 +182,13 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
               }
             </div>
           }
-          {showMetadata && <Metadata
-            docDetail={{ ...documentDetail, ...documentMetadata, doc_type: documentMetadata?.doc_type === 'others' ? '' : documentMetadata?.doc_type } as any}
-            loading={isMetadataLoading}
-            onUpdate={metadataMutate}
-          />}
+          <FloatRightContainer showClose isOpen={showMetadata} onClose={() => setShowMetadata(false)} isMobile={isMobile} panelClassname='!justify-start' footer={null}>
+            <Metadata
+              docDetail={{ ...documentDetail, ...documentMetadata, doc_type: documentMetadata?.doc_type === 'others' ? '' : documentMetadata?.doc_type } as any}
+              loading={isMetadataLoading}
+              onUpdate={metadataMutate}
+            />
+          </FloatRightContainer>
         </div>
         <BatchModal
           isShow={batchModalVisible}

+ 1 - 1
web/app/components/datasets/documents/detail/metadata/style.module.css

@@ -1,5 +1,5 @@
 .main {
-  @apply w-96 xl:w-[360px] flex-shrink-0 px-6 py-5 overflow-y-auto border-l-gray-100 border-l;
+  @apply w-full sm:w-96 xl:w-[360px] flex-shrink-0 p-0 sm:px-6 sm:py-5 overflow-y-auto border-none sm:border-l-gray-100 sm:border-l;
 }
 .operationWrapper {
   @apply flex flex-col items-center gap-4 mt-7 mb-8;

+ 2 - 2
web/app/components/datasets/documents/index.tsx

@@ -198,7 +198,7 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
         <p className={s.desc}>{t('datasetDocuments.list.desc')}</p>
       </div>
       <div className='flex flex-col px-6 py-4 flex-1'>
-        <div className='flex items-center justify-between'>
+        <div className='flex items-center justify-between flex-wrap gap-y-2 '>
           <Input
             showPrefix
             wrapperClassName='!w-[200px]'
@@ -207,7 +207,7 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
             value={searchValue}
           />
           {embeddingAvailable && (
-            <Button type='primary' onClick={routeToDocCreate} className='!h-8 !text-[13px]'>
+            <Button type='primary' onClick={routeToDocCreate} className='!h-8 !text-[13px] !shrink-0'>
               <PlusIcon className='h-4 w-4 mr-2 stroke-current' />
               {isDataSourceNotion && t('datasetDocuments.list.addPages')}
               {!isDataSourceNotion && t('datasetDocuments.list.addFile')}

+ 3 - 3
web/app/components/datasets/documents/list.tsx

@@ -316,8 +316,8 @@ const DocumentList: FC<IDocumentListProps> = ({ embeddingAvailable, documents =
   }
 
   return (
-    <>
-      <table className={`w-full border-collapse border-0 text-sm mt-3 ${s.documentTable}`}>
+    <div className='w-full h-full overflow-x-auto'>
+      <table className={`min-w-[700px] max-w-full w-full border-collapse border-0 text-sm mt-3 ${s.documentTable}`}>
         <thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-medium text-xs uppercase">
           <tr>
             <td className='w-12'>#</td>
@@ -380,7 +380,7 @@ const DocumentList: FC<IDocumentListProps> = ({ embeddingAvailable, documents =
           })}
         </tbody>
       </table>
-    </>
+    </div>
   )
 }
 

+ 2 - 2
web/app/components/datasets/hit-testing/hit-detail.tsx

@@ -65,8 +65,8 @@ const HitDetail: FC<IHitDetailProps> = ({ segInfo, vectorInfo }) => {
   }
 
   return (
-    <div className={'flex flex-row'}>
-      <div className="flex-1 bg-gray-25 p-6">
+    <div className='flex flex-row overflow-x-auto'>
+      <div className="flex-1 bg-gray-25 p-6 min-w-[300px]">
         <div className="flex items-center">
           <SegmentIndexTag
             positionId={segInfo?.position || ''}

+ 60 - 44
web/app/components/datasets/hit-testing/index.tsx

@@ -1,11 +1,12 @@
 'use client'
 import type { FC } from 'react'
-import React, { useMemo, useState } from 'react'
+import React, { useEffect, useMemo, useState } from 'react'
 import { useTranslation } from 'react-i18next'
 import useSWR from 'swr'
 import { omit } from 'lodash-es'
 import cn from 'classnames'
 import dayjs from 'dayjs'
+import { useBoolean } from 'ahooks'
 import { useContext } from 'use-context-selector'
 import SegmentCard from '../documents/detail/completed/SegmentCard'
 import docStyle from '../documents/detail/completed/style.module.css'
@@ -17,9 +18,11 @@ import type { HitTestingResponse, HitTesting as HitTestingType } from '@/models/
 import Loading from '@/app/components/base/loading'
 import Modal from '@/app/components/base/modal'
 import Pagination from '@/app/components/base/pagination'
+import FloatRightContainer from '@/app/components/base/float-right-container'
 import { fetchTestingRecords } from '@/service/datasets'
 import DatasetDetailContext from '@/context/dataset-detail'
 import type { RetrievalConfig } from '@/types/app'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 const limit = 10
 
@@ -39,6 +42,10 @@ const RecordsEmpty: FC = () => {
 
 const HitTesting: FC<Props> = ({ datasetId }: Props) => {
   const { t } = useTranslation()
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   const [hitResult, setHitResult] = useState<HitTestingResponse | undefined>() // 初始化记录为空数组
   const [submitLoading, setSubmitLoading] = useState(false)
   const [currParagraph, setCurrParagraph] = useState<{ paraInfo?: HitTestingType; showModal: boolean }>({ showModal: false })
@@ -63,6 +70,11 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
 
   const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict as RetrievalConfig)
   const [isShowModifyRetrievalModal, setIsShowModifyRetrievalModal] = useState(false)
+  const [isShowRightPanel, { setTrue: showRightPanel, setFalse: hideRightPanel, set: setShowRightPanel }] = useBoolean(!isMobile)
+
+  useEffect(() => {
+    setShowRightPanel(!isMobile)
+  }, [isMobile, setShowRightPanel])
 
   return (
     <div className={s.container}>
@@ -74,6 +86,7 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
         <Textarea
           datasetId={datasetId}
           setHitResult={setHitResult}
+          onSubmit={showRightPanel}
           onUpdateList={recordsMutate}
           loading={submitLoading}
           setLoading={setSubmitLoading}
@@ -131,53 +144,56 @@ const HitTesting: FC<Props> = ({ datasetId }: Props) => {
               <RecordsEmpty />
             )}
       </div>
-      <div className={s.rightDiv}>
-        {submitLoading
-          ? <div className={s.cardWrapper}>
-            <SegmentCard
-              loading={true}
-              scene='hitTesting'
-              className='h-[216px]'
-            />
-            <SegmentCard
-              loading={true}
-              scene='hitTesting'
-              className='h-[216px]'
-            />
-          </div>
-          : !hitResult?.records.length
-            ? (
-              <div className='h-full flex flex-col justify-center items-center'>
-                <div className={cn(docStyle.commonIcon, docStyle.targetIcon, '!bg-gray-200 !h-14 !w-14')} />
-                <div className='text-gray-300 text-[13px] mt-3'>
-                  {t('datasetHitTesting.hit.emptyTip')}
-                </div>
-              </div>
-            )
-            : (
-              <>
-                <div className='text-gray-600 font-semibold mb-4'>{t('datasetHitTesting.hit.title')}</div>
-                <div className='overflow-auto flex-1'>
-                  <div className={s.cardWrapper}>
-                    {hitResult?.records.map((record, idx) => {
-                      return <SegmentCard
-                        key={idx}
-                        loading={false}
-                        detail={record.segment as any}
-                        score={record.score}
-                        scene='hitTesting'
-                        className='h-[216px] mb-4'
-                        onClick={() => onClickCard(record as any)}
-                      />
-                    })}
+      <FloatRightContainer panelClassname='!justify-start !overflow-y-auto' showClose isMobile={isMobile} isOpen={isShowRightPanel} onClose={hideRightPanel} footer={null}>
+        <div className={cn(s.rightDiv, 'p-0 sm:px-8 sm:pt-[42px] sm:pb-[26px]')}>
+          {submitLoading
+            ? <div className={s.cardWrapper}>
+              <SegmentCard
+                loading={true}
+                scene='hitTesting'
+                className='h-[216px]'
+              />
+              <SegmentCard
+                loading={true}
+                scene='hitTesting'
+                className='h-[216px]'
+              />
+            </div>
+            : !hitResult?.records.length
+              ? (
+                <div className='h-full flex flex-col justify-center items-center'>
+                  <div className={cn(docStyle.commonIcon, docStyle.targetIcon, '!bg-gray-200 !h-14 !w-14')} />
+                  <div className='text-gray-300 text-[13px] mt-3'>
+                    {t('datasetHitTesting.hit.emptyTip')}
                   </div>
                 </div>
-              </>
-            )
-        }
-      </div>
+              )
+              : (
+                <>
+                  <div className='text-gray-600 font-semibold mb-4'>{t('datasetHitTesting.hit.title')}</div>
+                  <div className='overflow-auto flex-1'>
+                    <div className={s.cardWrapper}>
+                      {hitResult?.records.map((record, idx) => {
+                        return <SegmentCard
+                          key={idx}
+                          loading={false}
+                          detail={record.segment as any}
+                          score={record.score}
+                          scene='hitTesting'
+                          className='h-[216px] mb-4'
+                          onClick={() => onClickCard(record as any)}
+                        />
+                      })}
+                    </div>
+                  </div>
+                </>
+              )
+          }
+        </div>
+      </FloatRightContainer>
       <Modal
         className='!max-w-[960px] !p-0'
+        wrapperClassName='!z-40'
         closable
         onClose={() => setCurrParagraph({ showModal: false })}
         isShow={currParagraph.showModal}

+ 2 - 2
web/app/components/datasets/hit-testing/style.module.css

@@ -1,5 +1,5 @@
 .container {
-  @apply flex h-full w-full relative;
+  @apply flex h-full w-full relative overflow-y-auto;
 }
 .container > div {
   @apply flex-1 h-full;
@@ -8,7 +8,7 @@
   @apply border-r border-gray-100 px-6 py-3 flex flex-col;
 }
 .rightDiv {
-  @apply px-8 pt-[42px] pb-[26px] flex flex-col;
+  @apply flex flex-col;
 }
 .titleWrapper {
   @apply flex flex-col justify-center gap-1 mb-5;

+ 6 - 4
web/app/components/datasets/hit-testing/textarea.tsx

@@ -1,4 +1,3 @@
-import type { FC } from 'react'
 import { useContext } from 'use-context-selector'
 import { useTranslation } from 'react-i18next'
 import cn from 'classnames'
@@ -13,7 +12,7 @@ import { hitTesting } from '@/service/datasets'
 import { asyncRunSafe } from '@/utils'
 import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
 
-type Props = {
+type TextAreaWithButtonIProps = {
   datasetId: string
   onUpdateList: () => void
   setHitResult: (res: HitTestingResponse) => void
@@ -24,9 +23,10 @@ type Props = {
   onClickRetrievalMethod: () => void
   retrievalConfig: RetrievalConfig
   isEconomy: boolean
+  onSubmit?: () => void
 }
 
-const TextAreaWithButton: FC<Props> = ({
+const TextAreaWithButton = ({
   datasetId,
   onUpdateList,
   setHitResult,
@@ -37,7 +37,8 @@ const TextAreaWithButton: FC<Props> = ({
   onClickRetrievalMethod,
   retrievalConfig,
   isEconomy,
-}) => {
+  onSubmit: _onSubmit,
+}: TextAreaWithButtonIProps) => {
   const { t } = useTranslation()
   const { indexingTechnique } = useContext(DatasetDetailContext)
 
@@ -55,6 +56,7 @@ const TextAreaWithButton: FC<Props> = ({
       onUpdateList?.()
     }
     setLoading(false)
+    _onSubmit && _onSubmit()
   }
 
   const retrievalMethod = isEconomy ? RETRIEVE_METHOD.invertedIndex : retrievalConfig.search_method

+ 7 - 7
web/app/components/datasets/settings/form/index.tsx

@@ -24,13 +24,13 @@ import { useModalContext } from '@/context/modal-context'
 import { useProviderContext } from '@/context/provider-context'
 import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
 const rowClass = `
-  flex justify-between py-4
+  flex justify-between py-4 flex-wrap gap-y-2
 `
 const labelClass = `
   flex items-center w-[168px] h-9
 `
 const inputClass = `
-  w-[480px] px-3 bg-gray-100 text-sm text-gray-800 rounded-lg outline-none appearance-none
+  w-full max-w-[480px] px-3 bg-gray-100 text-sm text-gray-800 rounded-lg outline-none appearance-none
 `
 const useInitialValue: <T>(depend: T, dispatch: Dispatch<T>) => void = (depend, dispatch) => {
   useEffect(() => {
@@ -118,14 +118,14 @@ const Form = () => {
   useInitialValue<DataSet['indexing_technique'] | undefined>(currentDataset?.indexing_technique, setIndexMethod)
 
   return (
-    <div className='w-[800px] px-16 py-6'>
+    <div className='w-full sm:w-[800px] p-4 sm:px-16 sm:py-6'>
       <div className={rowClass}>
         <div className={labelClass}>
           <div>{t('datasetSettings.form.name')}</div>
         </div>
         <input
           disabled={!currentDataset?.embedding_available}
-          className={cn(inputClass, !currentDataset?.embedding_available && 'opacity-60')}
+          className={cn(inputClass, !currentDataset?.embedding_available && 'opacity-60', 'h-9')}
           value={name}
           onChange={e => setName(e.target.value)}
         />
@@ -134,7 +134,7 @@ const Form = () => {
         <div className={labelClass}>
           <div>{t('datasetSettings.form.desc')}</div>
         </div>
-        <div>
+        <div className='w-full max-w-[480px]'>
           <textarea
             disabled={!currentDataset?.embedding_available}
             className={cn(`${inputClass} block mb-2 h-[120px] py-2 resize-none`, !currentDataset?.embedding_available && 'opacity-60')}
@@ -152,7 +152,7 @@ const Form = () => {
         <div className={labelClass}>
           <div>{t('datasetSettings.form.permissions')}</div>
         </div>
-        <div className='w-[480px]'>
+        <div className='w-full sm:w-[480px]'>
           <PermissionsRadio
             disable={!currentDataset?.embedding_available}
             value={permission}
@@ -167,7 +167,7 @@ const Form = () => {
             <div className={labelClass}>
               <div>{t('datasetSettings.form.indexMethod')}</div>
             </div>
-            <div className='w-[480px]'>
+            <div className='w-full sm:w-[480px]'>
               <IndexMethodRadio
                 disable={!currentDataset?.embedding_available}
                 value={indexMethod}

+ 2 - 2
web/app/components/datasets/settings/index-method-radio/index.tsx

@@ -5,7 +5,7 @@ import s from './index.module.css'
 import type { DataSet } from '@/models/datasets'
 
 const itemClass = `
-  w-[234px] p-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
+  w-full sm:w-[234px] p-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
 `
 const radioClass = `
   w-4 h-4 border-[2px] border-gray-200 rounded-full
@@ -40,7 +40,7 @@ const IndexMethodRadio = ({
   ]
 
   return (
-    <div className={classNames(s.wrapper, 'flex justify-between w-full')}>
+    <div className={classNames(s.wrapper, 'flex justify-between w-full flex-wrap gap-y-2')}>
       {
         options.map(option => (
           <div

+ 2 - 2
web/app/components/datasets/settings/permissions-radio/index.tsx

@@ -5,7 +5,7 @@ import s from './index.module.css'
 import type { DataSet } from '@/models/datasets'
 
 const itemClass = `
-  flex items-center w-[234px] h-12 px-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
+  flex items-center w-full sm:w-[234px] h-12 px-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
 `
 const radioClass = `
   w-4 h-4 border-[2px] border-gray-200 rounded-full
@@ -36,7 +36,7 @@ const PermissionsRadio = ({
   ]
 
   return (
-    <div className={classNames(s.wrapper, 'flex justify-between w-full')}>
+    <div className={classNames(s.wrapper, 'flex justify-between w-full flex-wrap gap-y-2')}>
       {
         options.map(option => (
           <div

+ 5 - 5
web/app/components/develop/index.tsx

@@ -1,11 +1,11 @@
 'use client'
 import { useTranslation } from 'react-i18next'
 import useSWR from 'swr'
+import s from './secret-key/style.module.css'
 import Doc from '@/app/components/develop/doc'
 import InputCopy from '@/app/components/develop/secret-key/input-copy'
 import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
 import { fetchAppDetail } from '@/service/apps'
-import s from './secret-key/style.module.css'
 
 type IDevelopMainProps = {
   appId: string
@@ -21,10 +21,10 @@ const DevelopMain = ({ appId, dictionary }: IDevelopMainProps) => {
 
   return (
     <div className='relative flex flex-col h-full overflow-hidden'>
-      <div className='flex items-center justify-between flex-shrink-0 px-6 border-b border-solid h-14 border-b-gray-100'>
+      <div className='flex items-center justify-between flex-shrink-0 px-6 border-b border-solid py-2 border-b-gray-100'>
         <div className='text-lg font-medium text-gray-900'>{dictionary.app?.develop?.title}</div>
-        <div className='flex items-center'>
-          <InputCopy className={`flex-shrink-0 mr-1 w-60 ${s.w320}`} value={appDetail?.api_base_url}>
+        <div className='flex items-center flex-wrap gap-y-1'>
+          <InputCopy className='flex-shrink-0 mr-1 w-52 sm:w-80' value={appDetail?.api_base_url}>
             <div className={`ml-2 border border-gray-200 border-solid flex-shrink-0 px-2 py-0.5 rounded-[6px] text-gray-500 text-[0.625rem] ${s.customApi}`}>
               {t('appApi.apiServer')}
             </div>
@@ -37,7 +37,7 @@ const DevelopMain = ({ appId, dictionary }: IDevelopMainProps) => {
           <SecretKeyButton className='flex-shrink-0' appId={appId} />
         </div>
       </div>
-      <div className='px-10 py-4 overflow-auto grow'>
+      <div className='px-4 sm:px-10 py-4 overflow-auto grow'>
         <Doc appDetail={appDetail} />
       </div>
     </div>

+ 0 - 4
web/app/components/develop/secret-key/style.module.css

@@ -16,10 +16,6 @@
     width: 4rem;
 }
 
-.w320 {
-    width: 20rem;
-}
-
 .customApi {
     font-size: 11px;
 }

+ 1 - 1
web/app/components/explore/installed-app/index.tsx

@@ -26,7 +26,7 @@ const InstalledApp: FC<IInstalledAppProps> = ({
   }
 
   return (
-    <div className='h-full p-2'>
+    <div className='h-full py-2 pl-0 pr-2 sm:p-2'>
       {installedApp?.app.mode === 'chat'
         ? (
           <ChatApp isInstalledApp installedAppInfo={installedApp} />

+ 21 - 16
web/app/components/explore/sidebar/app-nav-item/index.tsx

@@ -9,6 +9,7 @@ import ItemOperation from '@/app/components/explore/item-operation'
 import AppIcon from '@/app/components/base/app-icon'
 
 export type IAppNavItemProps = {
+  isMobile: boolean
   name: string
   id: string
   icon: string
@@ -21,6 +22,7 @@ export type IAppNavItemProps = {
 }
 
 export default function AppNavItem({
+  isMobile,
   name,
   id,
   icon,
@@ -42,27 +44,30 @@ export default function AppNavItem({
       className={cn(
         s.item,
         isSelected ? s.active : 'hover:bg-gray-200',
-        'flex h-8 items-center justify-between px-2 rounded-lg text-sm font-normal ',
+        'flex h-8 items-center justify-between mobile:justify-center px-2 mobile:px-1 rounded-lg text-sm font-normal',
       )}
       onClick={() => {
         router.push(url) // use Link causes popup item always trigger jump. Can not be solved by e.stopPropagation().
       }}
     >
-      <div className='flex items-center space-x-2 w-0 grow'>
-        <AppIcon size='tiny' icon={icon} background={icon_background} />
-        <div className='overflow-hidden text-ellipsis whitespace-nowrap'>{name}</div>
-      </div>
-      {
-        <div className='shrink-0 h-6' onClick={e => e.stopPropagation()}>
-          <ItemOperation
-            isPinned={isPinned}
-            isItemHovering={isHovering}
-            togglePin={togglePin}
-            isShowDelete={!uninstallable && !isSelected}
-            onDelete={() => onDelete(id)}
-          />
-        </div>
-      }
+      {isMobile && <AppIcon size='tiny' icon={icon} background={icon_background} />}
+      {!isMobile && (
+        <>
+          <div className='flex items-center space-x-2 w-0 grow'>
+            <AppIcon size='tiny' icon={icon} background={icon_background} />
+            <div className='overflow-hidden text-ellipsis whitespace-nowrap'>{name}</div>
+          </div>
+          <div className='shrink-0 h-6' onClick={e => e.stopPropagation()}>
+            <ItemOperation
+              isPinned={isPinned}
+              isItemHovering={isHovering}
+              togglePin={togglePin}
+              isShowDelete={!uninstallable && !isSelected}
+              onDelete={() => onDelete(id)}
+            />
+          </div>
+        </>
+      )}
     </div>
   )
 }

+ 15 - 8
web/app/components/explore/sidebar/index.tsx

@@ -11,6 +11,7 @@ import Item from './app-nav-item'
 import { fetchInstalledAppList as doFetchInstalledAppList, uninstallApp, updatePinStatus } from '@/service/explore'
 import ExploreContext from '@/context/explore-context'
 import Confirm from '@/app/components/base/confirm'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 const SelectedDiscoveryIcon = () => (
   <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -36,9 +37,11 @@ const ChatIcon = () => (
   </svg>
 )
 
-const SideBar: FC<{
+export type IExploreSideBarProps = {
   controlUpdateInstalledApps: number
-}> = ({
+}
+
+const SideBar: FC<IExploreSideBarProps> = ({
   controlUpdateInstalledApps,
 }) => {
   const { t } = useTranslation()
@@ -48,6 +51,9 @@ const SideBar: FC<{
   const isChatSelected = lastSegment === 'chat'
   const { installedApps, setInstalledApps } = useContext(ExploreContext)
 
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   const fetchInstalledAppList = async () => {
     const { installed_apps }: any = await doFetchInstalledAppList()
     setInstalledApps(installed_apps)
@@ -84,28 +90,28 @@ const SideBar: FC<{
   }, [controlUpdateInstalledApps])
 
   return (
-    <div className='w-[216px] shrink-0 pt-6 px-4 border-gray-200 cursor-pointer'>
+    <div className='w-fit sm:w-[216px] shrink-0 pt-6 px-4 border-gray-200 cursor-pointer'>
       <div>
         <Link
           href='/explore/apps'
-          className={cn(isDiscoverySelected ? 'text-primary-600  bg-white font-semibold' : 'text-gray-700 font-medium', 'flex items-center h-9 pl-3 space-x-2 rounded-lg')}
+          className={cn(isDiscoverySelected ? 'text-primary-600  bg-white font-semibold' : 'text-gray-700 font-medium', 'flex items-center mobile:justify-center mobile:w-fit h-9 px-3 mobile:px-2 gap-2 rounded-lg')}
           style={isDiscoverySelected ? { boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)' } : {}}
         >
           {isDiscoverySelected ? <SelectedDiscoveryIcon /> : <DiscoveryIcon />}
-          <div className='text-sm'>{t('explore.sidebar.discovery')}</div>
+          {!isMobile && <div className='text-sm'>{t('explore.sidebar.discovery')}</div>}
         </Link>
         <Link
           href='/explore/chat'
-          className={cn(isChatSelected ? 'text-primary-600  bg-white font-semibold' : 'text-gray-700 font-medium', 'flex items-center h-9 pl-3 space-x-2 rounded-lg')}
+          className={cn(isChatSelected ? 'text-primary-600  bg-white font-semibold' : 'text-gray-700 font-medium', 'flex items-center mobile:justify-center mobile:w-fit h-9 px-3 mobile:px-2 gap-2 rounded-lg')}
           style={isChatSelected ? { boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)' } : {}}
         >
           {isChatSelected ? <SelectedChatIcon /> : <ChatIcon />}
-          <div className='text-sm'>{t('explore.sidebar.chat')}</div>
+          {!isMobile && <div className='text-sm'>{t('explore.sidebar.chat')}</div>}
         </Link>
       </div>
       {installedApps.length > 0 && (
         <div className='mt-10'>
-          <div className='pl-2 text-xs text-gray-500 font-medium uppercase'>{t('explore.sidebar.workspace')}</div>
+          <p className='pl-2 mobile:px-0 text-xs text-gray-500 break-all font-medium uppercase'>{t('explore.sidebar.workspace')}</p>
           <div className='mt-3 space-y-1 overflow-y-auto overflow-x-hidden'
             style={{
               height: 'calc(100vh - 250px)',
@@ -115,6 +121,7 @@ const SideBar: FC<{
               return (
                 <Item
                   key={id}
+                  isMobile={isMobile}
                   name={name}
                   icon={icon}
                   icon_background={icon_background}

+ 3 - 5
web/app/components/explore/universal-chat/index.tsx

@@ -10,7 +10,6 @@ import produce from 'immer'
 import { useBoolean, useGetState } from 'ahooks'
 import AppUnavailable from '../../base/app-unavailable'
 import useConversation from './hooks/use-conversation'
-import s from './style.module.css'
 import Init from './init'
 import { ToastContext } from '@/app/components/base/toast'
 import Sidebar from '@/app/components/share/chat/sidebar'
@@ -721,10 +720,10 @@ const Main: FC<IMainProps> = () => {
     return <Loading type='app' />
 
   return (
-    <div className='bg-gray-100'>
+    <div className='bg-gray-100 h-full'>
       <div
         className={cn(
-          'flex rounded-t-2xl bg-white overflow-hidden rounded-b-2xl',
+          'flex rounded-t-2xl bg-white overflow-hidden rounded-b-2xl h-full',
         )}
         style={{
           boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)',
@@ -744,8 +743,7 @@ const Main: FC<IMainProps> = () => {
         )}
         {/* main */}
         <div className={cn(
-          s.installedApp,
-          'flex-grow flex flex-col overflow-y-auto',
+          'h-full flex-grow flex flex-col overflow-y-auto',
         )
         }>
           {(!isNewConversation || isResponsing || errorHappened) && (

+ 5 - 5
web/app/components/explore/universal-chat/init/index.tsx

@@ -8,7 +8,7 @@ import Config from '../config'
 import s from './style.module.css'
 
 const Line = (
-  <svg width="720" height="1" viewBox="0 0 720 1" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <svg width="100%" height="1" viewBox="0 0 720 1" fill="none" xmlns="http://www.w3.org/2000/svg">
     <line y1="0.5" x2="720" y2="0.5" stroke="url(#paint0_linear_6845_53470)"/>
     <defs>
       <linearGradient id="paint0_linear_6845_53470" x1="0" y1="1" x2="720" y2="1" gradientUnits="userSpaceOnUse">
@@ -26,16 +26,16 @@ const Init: FC<IConfigProps> = ({
   const { t } = useTranslation()
 
   return (
-    <div className='h-full flex items-center'>
+    <div className='h-full flex items-center justify-center'>
       <div>
-        <div className='w-[480px] mx-auto text-center'>
+        <div className='text-center'>
           <div className={cn(s.textGradient, 'mb-2 leading-[32px] font-semibold text-[24px]')}>{t('explore.universalChat.welcome')}</div>
           <div className='mb-2 font-normal text-sm text-gray-500'>{t('explore.universalChat.welcomeDescribe')}</div>
         </div>
-        <div className='flex mb-2 mx-auto h-8 items-center'>
+        <div className='flex mb-2 h-8 items-center'>
           {Line}
         </div>
-        <Config className='w-[480px] mx-auto' {...configProps} />
+        <Config {...configProps} />
       </div>
     </div>
   )

+ 0 - 3
web/app/components/explore/universal-chat/style.module.css

@@ -1,3 +0,0 @@
-.installedApp {
-  height: calc(100vh - 74px);
-}

+ 2 - 9
web/app/components/header/HeaderWrapper.tsx

@@ -2,7 +2,6 @@
 import classNames from 'classnames'
 import { usePathname } from 'next/navigation'
 import s from './index.module.css'
-import { useAppContext } from '@/context/app-context'
 
 type HeaderWrapperProps = {
   children: React.ReactNode
@@ -12,22 +11,16 @@ const HeaderWrapper = ({
   children,
 }: HeaderWrapperProps) => {
   const pathname = usePathname()
-  const { langeniusVersionInfo } = useAppContext()
   const isBordered = ['/apps', '/datasets'].includes(pathname)
 
   return (
     <div className={classNames(
-      'sticky top-0 left-0 right-0 z-20 flex bg-gray-100 grow-0 shrink-0 basis-auto h-14',
+      'sticky top-0 left-0 right-0 z-20 flex flex-col bg-gray-100 grow-0 shrink-0 basis-auto min-h-[56px]',
       s.header,
       isBordered ? 'border-b border-gray-200' : '',
     )}
     >
-      <div className={classNames(
-        s[`header-${langeniusVersionInfo.current_env}`],
-        'flex flex-1 items-center justify-between px-4',
-      )}>
-        {children}
-      </div>
+      {children}
     </div>
   )
 }

+ 11 - 4
web/app/components/header/account-dropdown/index.tsx

@@ -17,7 +17,11 @@ import { ArrowUpRight, ChevronDown } from '@/app/components/base/icons/src/vende
 import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general'
 import { useModalContext } from '@/context/modal-context'
 
-export default function AppSelector() {
+export type IAppSelecotr = {
+  isMobile: boolean
+}
+
+export default function AppSelector({ isMobile }: IAppSelecotr) {
   const itemClassName = `
     flex items-center w-full h-9 px-3 text-gray-700 text-[14px]
     rounded-lg font-normal hover:bg-gray-50 cursor-pointer
@@ -50,12 +54,15 @@ export default function AppSelector() {
                     inline-flex items-center
                     rounded-[20px] py-1 pr-2.5 pl-1 text-sm
                   text-gray-700 hover:bg-gray-200
+                    mobile:px-1
                     ${open && 'bg-gray-200'}
                   `}
                 >
-                  <Avatar name={userProfile.name} className='mr-2' size={32} />
-                  {userProfile.name}
-                  <ChevronDown className="w-3 h-3 ml-1 text-gray-700"/>
+                  <Avatar name={userProfile.name} className='sm:mr-2 mr-0' size={32} />
+                  {!isMobile && <>
+                    {userProfile.name}
+                    <ChevronDown className="w-3 h-3 ml-1 text-gray-700"/>
+                  </>}
                 </Menu.Button>
               </div>
               <Transition

+ 1 - 1
web/app/components/header/account-setting/api-based-extension-page/selector.tsx

@@ -70,7 +70,7 @@ const ApiBasedExtensionSelector: FC<ApiBasedExtensionSelectorProps> = ({
             )
         }
       </PortalToFollowElemTrigger>
-      <PortalToFollowElemContent className='w-[576px] z-[11]'>
+      <PortalToFollowElemContent className='w-[calc(100%-32px)] max-w-[576px] z-[11]'>
         <div className='w-full rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg z-10'>
           <div className='p-1'>
             <div className='flex items-center justify-between px-3 pt-2 pb-1'>

+ 1 - 1
web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx

@@ -76,7 +76,7 @@ const DataSourceNotion = ({
             : (
               <div
                 className={
-                  `flex items-center px-3 h-7 bg-white border-[0.5px] border-gray-200 text-xs font-medium text-primary-600 rounded-md
+                  `flex items-center px-3 py-1 min-h-7 bg-white border-[0.5px] border-gray-200 text-xs font-medium text-primary-600 rounded-md
                   ${isCurrentWorkspaceManager ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
                 }
                 onClick={handleConnectNotion}

+ 10 - 5
web/app/components/header/account-setting/index.tsx

@@ -23,6 +23,7 @@ import { User01 as User01Solid, Users01 as Users01Solid } from '@/app/components
 import { Globe01 } from '@/app/components/base/icons/src/vender/line/mapsAndTravel'
 import { AtSign, XClose } from '@/app/components/base/icons/src/vender/line/general'
 import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 const iconClassName = `
   w-4 h-4 ml-3 mr-2
@@ -42,6 +43,10 @@ export default function AccountSetting({
 }: IAccountSettingProps) {
   const [activeMenu, setActiveMenu] = useState(activeTab)
   const { t } = useTranslation()
+
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+
   const menuItems = [
     {
       key: 'workspace-group',
@@ -130,9 +135,9 @@ export default function AccountSetting({
       wrapperClassName='!z-20 pt-[60px]'
     >
       <div className='flex'>
-        <div className='w-[200px] p-4 border border-gray-100'>
-          <div className='mb-8 ml-2 text-base font-medium leading-6 text-gray-900'>{t('common.userProfile.settings')}</div>
-          <div>
+        <div className='w-[44px] sm:w-[200px] px-[1px] py-4 sm:p-4 border border-gray-100 shrink-0 sm:shrink-1 flex flex-col items-center sm:items-start'>
+          <div className='mb-8 ml-0 sm:ml-2 text-sm sm:text-base font-medium leading-6 text-gray-900'>{t('common.userProfile.settings')}</div>
+          <div className='w-full'>
             {
               menuItems.map(menuItem => (
                 <div key={menuItem.key} className='mb-4'>
@@ -150,7 +155,7 @@ export default function AccountSetting({
                           onClick={() => setActiveMenu(item.key)}
                         >
                           {activeMenu === item.key ? item.activeIcon : item.icon}
-                          <div className='truncate'>{item.name}</div>
+                          {!isMobile && <div className='truncate'>{item.name}</div>}
                         </div>
                       ))
                     }
@@ -167,7 +172,7 @@ export default function AccountSetting({
               <XClose className='w-4 h-4 text-gray-500' />
             </div>
           </div>
-          <div className='px-8 pt-2'>
+          <div className='px-4 sm:px-8 pt-2'>
             {activeMenu === 'account' && <AccountPage />}
             {activeMenu === 'members' && <MembersPage />}
             {activeMenu === 'integrations' && <IntegrationsPage />}

+ 3 - 3
web/app/components/header/account-setting/members-page/index.tsx

@@ -51,13 +51,13 @@ const MembersPage = () => {
             {t('common.members.invite')}
           </div>
         </div>
-        <div>
-          <div className='flex items-center py-[7px] border-b border-gray-200'>
+        <div className='overflow-x-auto'>
+          <div className='flex items-center py-[7px] border-b border-gray-200 min-w-[480px]'>
             <div className='grow px-3 text-xs font-medium text-gray-500'>{t('common.members.name')}</div>
             <div className='shrink-0 w-[104px] text-xs font-medium text-gray-500'>{t('common.members.lastActive')}</div>
             <div className='shrink-0 w-[96px] px-3 text-xs font-medium text-gray-500'>{t('common.members.role')}</div>
           </div>
-          <div>
+          <div className='min-w-[480px]'>
             {
               accounts.map(account => (
                 <div key={account.id} className='flex border-b border-gray-100'>

+ 1 - 1
web/app/components/header/account-setting/model-page/index.tsx

@@ -218,7 +218,7 @@ const ModelPage = () => {
         }
         <SystemModel onUpdate={() => mutateProviders()} />
       </div>
-      <div className='grid grid-cols-2 gap-4 mb-6'>
+      <div className='grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6'>
         {
           MODEL_CARD_LIST.map((model, index) => (
             <ModelCard

+ 1 - 1
web/app/components/header/account-setting/model-page/model-item/index.tsx

@@ -34,7 +34,7 @@ const ModelItem: FC<ModelItemProps> = ({
 
   return (
     <div className='mb-2 bg-gray-50 rounded-xl'>
-      <div className='flex justify-between items-center px-4 h-14'>
+      <div className='flex justify-between items-center p-4 min-h-[56px] flex-wrap gap-y-1'>
         <div className='flex items-center'>
           {modelItem.titleIcon[locale]}
           {

+ 1 - 1
web/app/components/header/account-setting/model-page/model-modal/Form.tsx

@@ -159,7 +159,7 @@ const Form: FC<FormProps> = ({
               options?.map(option => (
                 <div
                   className={`
-                    flex items-center px-3 h-9 rounded-lg border border-gray-100 bg-gray-25 cursor-pointer
+                    flex items-center px-3 py-2 rounded-lg border border-gray-100 bg-gray-25 cursor-pointer
                     ${value?.[field.key] === option.key && 'bg-white border-[1.5px] border-primary-400 shadow-sm'}
                   `}
                   onClick={() => handleFormChange(field.key, option.key)}

+ 2 - 2
web/app/components/header/account-setting/model-page/model-modal/index.tsx

@@ -96,7 +96,7 @@ const ModelModal: FC<ModelModalProps> = ({
     <PortalToFollowElem open>
       <PortalToFollowElemContent className='w-full h-full z-[60]'>
         <div className='fixed inset-0 flex items-center justify-center bg-black/[.25]'>
-          <div className='w-[640px] max-h-[calc(100vh-120px)] bg-white shadow-xl rounded-2xl overflow-y-auto'>
+          <div className='mx-2 w-[640px] max-h-[calc(100vh-120px)] bg-white shadow-xl rounded-2xl overflow-y-auto'>
             <div className='px-8 pt-8'>
               <div className='flex justify-between items-center mb-2'>
                 <div className='text-xl font-semibold text-gray-900'>{renderTitlePrefix()}</div>
@@ -113,7 +113,7 @@ const ModelModal: FC<ModelModalProps> = ({
                 onClearedChange={setCleared}
                 onValidating={handleValidating}
               />
-              <div className='flex justify-between items-center py-6'>
+              <div className='flex justify-between items-center py-6 flex-wrap gap-y-2'>
                 <a
                   href={modelModal?.link.href}
                   target='_blank'

+ 65 - 18
web/app/components/header/index.tsx

@@ -1,43 +1,90 @@
 'use client'
 
 import Link from 'next/link'
+import { useSelectedLayoutSegment } from 'next/navigation'
+import classNames from 'classnames'
+import { useEffect } from 'react'
+import { Bars3Icon } from '@heroicons/react/20/solid'
+import { useBoolean } from 'ahooks'
 import AccountDropdown from './account-dropdown'
 import AppNav from './app-nav'
 import DatasetNav from './dataset-nav'
 import EnvNav from './env-nav'
 import ExploreNav from './explore-nav'
 import GithubStar from './github-star'
+import s from './index.module.css'
 import { WorkspaceProvider } from '@/context/workspace-context'
 import { useAppContext } from '@/context/app-context'
 import LogoSite from '@/app/components/base/logo/logo-site'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
 
 const navClassName = `
-  flex items-center relative mr-3 px-3 h-8 rounded-xl
+  flex items-center relative mr-3 px-3 h-9 rounded-xl
   font-medium text-sm
   cursor-pointer
 `
 
 const Header = () => {
-  const { isCurrentWorkspaceManager } = useAppContext()
+  const selectedSegment = useSelectedLayoutSegment()
+  const { isCurrentWorkspaceManager, langeniusVersionInfo } = useAppContext()
+  const media = useBreakpoints()
+  const isMobile = media === MediaType.mobile
+  const [isShowNavMenu, { toggle, setFalse: hideNavMenu }] = useBoolean(false)
+
+  useEffect(() => {
+    hideNavMenu()
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [selectedSegment])
+
   return (
     <>
-      <div className='flex items-center'>
-        <Link href="/apps" className='flex items-center mr-4'>
-          <LogoSite />
-        </Link>
-        <GithubStar />
-      </div>
-      <div className='flex items-center'>
-        <ExploreNav className={navClassName} />
-        <AppNav />
-        {isCurrentWorkspaceManager && <DatasetNav />}
-      </div>
-      <div className='flex items-center flex-shrink-0'>
-        <EnvNav />
-        <WorkspaceProvider>
-          <AccountDropdown />
-        </WorkspaceProvider>
+      <div className={classNames(
+        s[`header-${langeniusVersionInfo.current_env}`],
+        'flex flex-1 items-center justify-between px-4',
+      )}>
+        <div className='flex items-center'>
+          {isMobile && <div
+            className='flex items-center justify-center h-8 w-8 cursor-pointer'
+            onClick={toggle}
+          >
+            <Bars3Icon className="h-4 w-4 text-gray-500" />
+          </div>}
+          {!isMobile && <>
+            <Link href="/apps" className='flex items-center mr-4'>
+              <LogoSite />
+            </Link>
+            <GithubStar />
+          </>}
+        </div>
+        {isMobile && (
+          <div className='flex'>
+            <Link href="/apps" className='flex items-center mr-4'>
+              <LogoSite />
+            </Link>
+            <GithubStar />
+          </div>
+        )}
+        {!isMobile && (
+          <div className='flex items-center'>
+            <ExploreNav className={navClassName} />
+            <AppNav />
+            {isCurrentWorkspaceManager && <DatasetNav />}
+          </div>
+        )}
+        <div className='flex items-center flex-shrink-0'>
+          <EnvNav />
+          <WorkspaceProvider>
+            <AccountDropdown isMobile={isMobile} />
+          </WorkspaceProvider>
+        </div>
       </div>
+      {(isMobile && isShowNavMenu) && (
+        <div className='w-full flex flex-col p-2 gap-y-1'>
+          <ExploreNav className={navClassName} />
+          <AppNav />
+          {isCurrentWorkspaceManager && <DatasetNav />}
+        </div>
+      )}
     </>
   )
 }

+ 1 - 1
web/app/components/header/nav/index.tsx

@@ -34,7 +34,7 @@ const Nav = ({
 
   return (
     <div className={`
-      flex items-center h-8 mr-3 px-0.5 rounded-xl text-sm shrink-0 font-medium
+      flex items-center h-8 mr-0 sm:mr-3 px-0.5 rounded-xl text-sm shrink-0 font-medium
       ${isActived && 'bg-white shadow-md font-semibold'}
       ${!curNav && !isActived && 'hover:bg-gray-200'}
     `}>

+ 2 - 3
web/app/components/share/chat/index.tsx

@@ -11,7 +11,6 @@ import { useBoolean, useGetState } from 'ahooks'
 import AppUnavailable from '../../base/app-unavailable'
 import { checkOrSetAccessToken } from '../utils'
 import useConversation from './hooks/use-conversation'
-import s from './style.module.css'
 import { ToastContext } from '@/app/components/base/toast'
 import Sidebar from '@/app/components/share/chat/sidebar'
 import ConfigSence from '@/app/components/share/chat/config-scence'
@@ -683,7 +682,7 @@ const Main: FC<IMainProps> = ({
   }
 
   return (
-    <div className='bg-gray-100'>
+    <div className='bg-gray-100 h-full'>
       {!isInstalledApp && (
         <Header
           title={siteInfo.title}
@@ -720,7 +719,7 @@ const Main: FC<IMainProps> = ({
         )}
         {/* main */}
         <div className={cn(
-          isInstalledApp ? s.installedApp : 'h-[calc(100vh_-_3rem)] tablet:h-screen',
+          isInstalledApp ? 'h-full' : 'h-[calc(100vh_-_3rem)] tablet:h-screen',
           'flex-grow flex flex-col overflow-y-auto',
         )
         }>

+ 0 - 3
web/app/components/share/chat/style.module.css

@@ -1,3 +0,0 @@
-.installedApp {
-  height: calc(100vh - 74px);
-}

+ 1 - 2
web/app/components/share/chatbot/index.tsx

@@ -10,7 +10,6 @@ import { useBoolean, useGetState } from 'ahooks'
 import { checkOrSetAccessToken } from '../utils'
 import AppUnavailable from '../../base/app-unavailable'
 import useConversation from './hooks/use-conversation'
-import s from './style.module.css'
 import { ToastContext } from '@/app/components/base/toast'
 import ConfigScene from '@/app/components/share/chatbot/config-scence'
 import Header from '@/app/components/share/header'
@@ -561,7 +560,7 @@ const Main: FC<IMainProps> = ({
 
       <div className={'flex bg-white overflow-hidden'}>
         <div className={cn(
-          isInstalledApp ? s.installedApp : 'h-[calc(100vh_-_3rem)]',
+          isInstalledApp ? 'h-full' : 'h-[calc(100vh_-_3rem)]',
           'flex-grow flex flex-col overflow-y-auto',
         )
         }>

+ 0 - 3
web/app/components/share/chatbot/style.module.css

@@ -1,3 +0,0 @@
-.installedApp {
-  height: calc(100vh - 74px);
-}

+ 2 - 2
web/app/components/share/header.tsx

@@ -33,7 +33,7 @@ const Header: FC<IHeaderProps> = ({
     return (
       <div
         className={`
-          shrink-0 flex items-center justify-between h-12 px-3 bg-gray-100 
+          shrink-0 flex items-center justify-between h-14 px-4 bg-gray-100 
           bg-gradient-to-r from-blue-600 to-sky-500
         `}
       >
@@ -52,7 +52,7 @@ const Header: FC<IHeaderProps> = ({
   }
 
   return (
-    <div className="shrink-0 flex items-center justify-between h-12 px-3 bg-gray-100">
+    <div className="shrink-0 flex items-center justify-between h-14 px-4 bg-gray-100">
       <div
         className='flex items-center justify-center h-8 w-8 cursor-pointer'
         onClick={() => onShowSideBar?.()}

+ 1 - 1
web/app/components/share/text-generation/style.module.css

@@ -1,5 +1,5 @@
 .installedApp {
-  height: calc(100vh - 74px);
+  height: 100%;
   border-radius: 16px;
   box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
 }

+ 2 - 2
web/context/app-context.tsx

@@ -120,9 +120,9 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
       isCurrentWorkspaceManager,
       mutateCurrentWorkspace,
     }}>
-      <div className='flex flex-col h-full'>
+      <div className='flex flex-col h-full overflow-y-auto'>
         {globalThis.document?.body?.getAttribute('data-public-maintenance-notice') && <MaintenanceNotice />}
-        <div ref={pageContainerRef} className='grow relative flex flex-col overflow-auto bg-gray-100'>
+        <div ref={pageContainerRef} className='grow relative flex flex-col overflow-y-auto overflow-x-hidden bg-gray-100'>
           {children}
         </div>
       </div>

+ 1 - 0
web/i18n/lang/app-debug.en.ts

@@ -21,6 +21,7 @@ const translation = {
   operation: {
     applyConfig: 'Publish',
     resetConfig: 'Reset',
+    debugConfig: 'Debug',
     addFeature: 'Add Feature',
     automatic: 'Automatic',
     stopResponding: 'Stop responding',

+ 1 - 0
web/i18n/lang/app-debug.zh.ts

@@ -21,6 +21,7 @@ const translation = {
   operation: {
     applyConfig: '发布',
     resetConfig: '重置',
+    debugConfig: '调试',
     addFeature: '添加功能',
     automatic: '自动编排',
     stopResponding: '停止响应',

+ 1 - 0
web/i18n/lang/dataset-creation.en.ts

@@ -96,6 +96,7 @@ const translation = {
     sideTipP3: 'Cleaning removes unnecessary characters and formats, making datasets cleaner and easier to parse.',
     sideTipP4: 'Proper segmentation and cleaning improve model performance, providing more accurate and valuable results.',
     previewTitle: 'Preview',
+    previewTitleButton: 'Preview',
     previewButton: 'Switching to Q&A format',
     previewSwitchTipStart: 'The current segment preview is in text format, switching to a question-and-answer format preview will',
     previewSwitchTipEnd: ' consume additional tokens',

+ 1 - 0
web/i18n/lang/dataset-creation.zh.ts

@@ -96,6 +96,7 @@ const translation = {
     sideTipP3: '清洗则是对文本进行预处理,删除不必要的字符、符号或格式,使数据集更加干净、整洁,便于模型解析。',
     sideTipP4: '通过对数据集进行适当的分段和清洗,可以提高模型在实际应用中的表现,从而为用户提供更准确、更有价值的结果。',
     previewTitle: '分段预览',
+    previewTitleButton: '预览',
     previewButton: '切换至 Q&A 形式',
     previewSwitchTipStart: '当前分段预览是文本模式,切换到 Q&A 模式将会',
     previewSwitchTipEnd: '消耗额外的 token',