| @@ -11,7 +11,7 @@ import { useQuery } from '@tanstack/react-query' | |||
| import ExternalAPIPanel from '../../components/datasets/external-api/external-api-panel' | |||
| import Datasets from './Datasets' | |||
| import DatasetFooter from './DatasetFooter' | |||
| import ApiServer from './ApiServer' | |||
| import ApiServer from '../../components/develop/ApiServer' | |||
| import Doc from './Doc' | |||
| import TabSliderNew from '@/app/components/base/tab-slider-new' | |||
| import TagManagementModal from '@/app/components/base/tag-management' | |||
| @@ -9,6 +9,9 @@ import TemplateZh from './template/template.zh.mdx' | |||
| import TemplateJa from './template/template.ja.mdx' | |||
| import I18n from '@/context/i18n' | |||
| import { LanguagesSupported } from '@/i18n/language' | |||
| import useTheme from '@/hooks/use-theme' | |||
| import { Theme } from '@/types/app' | |||
| import cn from '@/utils/classnames' | |||
| type DocProps = { | |||
| apiBaseUrl: string | |||
| @@ -19,6 +22,7 @@ const Doc = ({ apiBaseUrl }: DocProps) => { | |||
| const { t } = useTranslation() | |||
| const [toc, setToc] = useState<Array<{ href: string; text: string }>>([]) | |||
| const [isTocExpanded, setIsTocExpanded] = useState(false) | |||
| const { theme } = useTheme() | |||
| // Set initial TOC expanded state based on screen width | |||
| useEffect(() => { | |||
| @@ -83,12 +87,12 @@ const Doc = ({ apiBaseUrl }: DocProps) => { | |||
| <div className={`fixed right-20 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}> | |||
| {isTocExpanded | |||
| ? ( | |||
| <nav className="toc max-h-[calc(100vh-150px)] w-full overflow-y-auto rounded-lg bg-gray-50 p-4 shadow-md"> | |||
| <nav className="toc max-h-[calc(100vh-150px)] w-full overflow-y-auto rounded-lg bg-components-panel-bg p-4 shadow-md"> | |||
| <div className="mb-4 flex items-center justify-between"> | |||
| <h3 className="text-lg font-semibold">{t('appApi.develop.toc')}</h3> | |||
| <h3 className="text-lg font-semibold text-text-primary">{t('appApi.develop.toc')}</h3> | |||
| <button | |||
| onClick={() => setIsTocExpanded(false)} | |||
| className="text-gray-500 hover:text-gray-700" | |||
| className="text-text-tertiary hover:text-text-secondary" | |||
| > | |||
| ✕ | |||
| </button> | |||
| @@ -98,7 +102,7 @@ const Doc = ({ apiBaseUrl }: DocProps) => { | |||
| <li key={index}> | |||
| <a | |||
| href={item.href} | |||
| className="text-gray-600 transition-colors duration-200 hover:text-gray-900 hover:underline" | |||
| className="text-text-secondary transition-colors duration-200 hover:text-text-primary hover:underline" | |||
| onClick={e => handleTocClick(e, item)} | |||
| > | |||
| {item.text} | |||
| @@ -111,13 +115,13 @@ const Doc = ({ apiBaseUrl }: DocProps) => { | |||
| : ( | |||
| <button | |||
| onClick={() => setIsTocExpanded(true)} | |||
| className="flex h-10 w-10 items-center justify-center rounded-full bg-gray-50 shadow-md transition-colors duration-200 hover:bg-gray-100" | |||
| className="flex h-10 w-10 items-center justify-center rounded-full bg-components-button-secondary-bg shadow-md transition-colors duration-200 hover:bg-components-button-secondary-bg-hover" | |||
| > | |||
| <RiListUnordered className="h-6 w-6" /> | |||
| <RiListUnordered className="h-6 w-6 text-components-button-secondary-text" /> | |||
| </button> | |||
| )} | |||
| </div> | |||
| <article className='prose-xl prose mx-1 rounded-t-xl bg-white px-4 pt-16 sm:mx-12'> | |||
| <article className={cn('prose-xl prose mx-1 rounded-t-xl bg-background-default px-4 pt-16 sm:mx-12', theme === Theme.dark && 'dark:prose-invert')}> | |||
| {Template} | |||
| </article> | |||
| </div> | |||
| @@ -4,7 +4,6 @@ import type { FC } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import CopyFeedback from '@/app/components/base/copy-feedback' | |||
| import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button' | |||
| import { randomString } from '@/utils' | |||
| type ApiServerProps = { | |||
| apiBaseUrl: string | |||
| @@ -16,21 +15,17 @@ const ApiServer: FC<ApiServerProps> = ({ | |||
| return ( | |||
| <div className='flex flex-wrap items-center gap-y-2'> | |||
| <div className='mr-2 flex h-8 items-center rounded-lg border-[0.5px] border-white bg-white/80 pl-1.5 pr-1 leading-5'> | |||
| <div className='mr-0.5 h-5 shrink-0 rounded-md border border-gray-200 px-1.5 text-[11px] text-gray-500'>{t('appApi.apiServer')}</div> | |||
| <div className='w-fit truncate px-1 text-[13px] font-medium text-gray-800 sm:w-[248px]'>{apiBaseUrl}</div> | |||
| <div className='mx-1 h-[14px] w-[1px] bg-gray-200'></div> | |||
| <CopyFeedback | |||
| content={apiBaseUrl} | |||
| selectorId={randomString(8)} | |||
| className={'!h-6 !w-6 hover:bg-gray-200'} | |||
| /> | |||
| <div className='mr-2 flex h-8 items-center rounded-lg border-[0.5px] border-components-input-border-active bg-components-input-bg-normal pl-1.5 pr-1 leading-5'> | |||
| <div className='mr-0.5 h-5 shrink-0 rounded-md border border-divider-subtle px-1.5 text-[11px] text-text-tertiary'>{t('appApi.apiServer')}</div> | |||
| <div className='w-fit truncate px-1 text-[13px] font-medium text-text-secondary sm:w-[248px]'>{apiBaseUrl}</div> | |||
| <div className='mx-1 h-[14px] w-[1px] bg-divider-regular'></div> | |||
| <CopyFeedback content={apiBaseUrl}/> | |||
| </div> | |||
| <div className='mr-2 flex h-8 items-center rounded-lg border-[0.5px] border-[#D1FADF] bg-[#ECFDF3] px-3 text-xs font-semibold text-[#039855]'> | |||
| {t('appApi.ok')} | |||
| </div> | |||
| <SecretKeyButton | |||
| className='!h-8 shrink-0 bg-white' | |||
| className='!h-8 shrink-0' | |||
| /> | |||
| </div> | |||
| ) | |||
| @@ -17,6 +17,9 @@ import TemplateChatZh from './template/template_chat.zh.mdx' | |||
| import TemplateChatJa from './template/template_chat.ja.mdx' | |||
| import I18n from '@/context/i18n' | |||
| import { LanguagesSupported } from '@/i18n/language' | |||
| import useTheme from '@/hooks/use-theme' | |||
| import { Theme } from '@/types/app' | |||
| import cn from '@/utils/classnames' | |||
| type IDocProps = { | |||
| appDetail: any | |||
| @@ -27,6 +30,7 @@ const Doc = ({ appDetail }: IDocProps) => { | |||
| const { t } = useTranslation() | |||
| const [toc, setToc] = useState<Array<{ href: string; text: string }>>([]) | |||
| const [isTocExpanded, setIsTocExpanded] = useState(false) | |||
| const { theme } = useTheme() | |||
| const variables = appDetail?.model_config?.configs?.prompt_variables || [] | |||
| const inputs = variables.reduce((res: any, variable: any) => { | |||
| @@ -83,12 +87,12 @@ const Doc = ({ appDetail }: IDocProps) => { | |||
| <div className={`fixed right-8 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}> | |||
| {isTocExpanded | |||
| ? ( | |||
| <nav className="toc w-full rounded-lg bg-gray-50 p-4 shadow-md"> | |||
| <nav className="toc w-full rounded-lg bg-components-panel-bg p-4 shadow-md"> | |||
| <div className="mb-4 flex items-center justify-between"> | |||
| <h3 className="text-lg font-semibold">{t('appApi.develop.toc')}</h3> | |||
| <h3 className="text-lg font-semibold text-text-primary">{t('appApi.develop.toc')}</h3> | |||
| <button | |||
| onClick={() => setIsTocExpanded(false)} | |||
| className="text-gray-500 hover:text-gray-700" | |||
| className="text-text-tertiary hover:text-text-secondary" | |||
| > | |||
| ✕ | |||
| </button> | |||
| @@ -98,7 +102,7 @@ const Doc = ({ appDetail }: IDocProps) => { | |||
| <li key={index}> | |||
| <a | |||
| href={item.href} | |||
| className="text-gray-600 transition-colors duration-200 hover:text-gray-900 hover:underline" | |||
| className="text-text-secondary transition-colors duration-200 hover:text-text-primary hover:underline" | |||
| onClick={e => handleTocClick(e, item)} | |||
| > | |||
| {item.text} | |||
| @@ -111,13 +115,13 @@ const Doc = ({ appDetail }: IDocProps) => { | |||
| : ( | |||
| <button | |||
| onClick={() => setIsTocExpanded(true)} | |||
| className="flex h-10 w-10 items-center justify-center rounded-full bg-gray-50 shadow-md transition-colors duration-200 hover:bg-gray-100" | |||
| className="flex h-10 w-10 items-center justify-center rounded-full bg-components-button-secondary-bg shadow-md transition-colors duration-200 hover:bg-components-button-secondary-bg-hover" | |||
| > | |||
| <RiListUnordered className="h-6 w-6" /> | |||
| <RiListUnordered className="h-6 w-6 text-components-button-secondary-text" /> | |||
| </button> | |||
| )} | |||
| </div> | |||
| <article className="prose-xl prose" > | |||
| <article className={cn('prose-xl prose', theme === Theme.dark && 'dark:prose-invert')} > | |||
| {(appDetail?.mode === 'chat' || appDetail?.mode === 'agent-chat') && ( | |||
| (() => { | |||
| switch (locale) { | |||
| @@ -1,10 +1,7 @@ | |||
| 'use client' | |||
| import { useTranslation } from 'react-i18next' | |||
| import s from './secret-key/style.module.css' | |||
| import Doc from '@/app/components/develop/doc' | |||
| import Loading from '@/app/components/base/loading' | |||
| import InputCopy from '@/app/components/develop/secret-key/input-copy' | |||
| import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button' | |||
| import ApiServer from '@/app/components/develop/ApiServer' | |||
| import { useStore as useAppStore } from '@/app/components/app/store' | |||
| type IDevelopMainProps = { | |||
| @@ -13,11 +10,10 @@ type IDevelopMainProps = { | |||
| const DevelopMain = ({ appId }: IDevelopMainProps) => { | |||
| const appDetail = useAppStore(state => state.appDetail) | |||
| const { t } = useTranslation() | |||
| if (!appDetail) { | |||
| return ( | |||
| <div className='flex h-full items-center justify-center bg-white'> | |||
| <div className='flex h-full items-center justify-center bg-background-default'> | |||
| <Loading /> | |||
| </div> | |||
| ) | |||
| @@ -25,21 +21,9 @@ const DevelopMain = ({ appId }: IDevelopMainProps) => { | |||
| return ( | |||
| <div className='relative flex h-full flex-col overflow-hidden'> | |||
| <div className='flex shrink-0 items-center justify-between border-b border-solid border-b-gray-100 px-6 py-2'> | |||
| <div className='text-lg font-medium text-gray-900'></div> | |||
| <div className='flex flex-wrap items-center gap-y-1'> | |||
| <InputCopy className='mr-1 w-52 shrink-0 sm:w-80' value={appDetail.api_base_url}> | |||
| <div className={`ml-2 shrink-0 rounded-[6px] border border-solid border-gray-200 px-2 py-0.5 text-[0.625rem] text-gray-500 ${s.customApi}`}> | |||
| {t('appApi.apiServer')} | |||
| </div> | |||
| </InputCopy> | |||
| <div className={`mr-2 flex h-9 items-center rounded-lg | |||
| px-3 text-[13px] font-normal ${appDetail.enable_api ? 'bg-green-50 text-green-500' : 'bg-yellow-50 text-yellow-500'}`}> | |||
| <div className='mr-1'>{t('appApi.status')}</div> | |||
| <div className='font-semibold'>{appDetail.enable_api ? `${t('appApi.ok')}` : `${t('appApi.disabled')}`}</div> | |||
| </div> | |||
| <SecretKeyButton className='shrink-0' appId={appId} /> | |||
| </div> | |||
| <div className='flex shrink-0 items-center justify-between border-b border-solid border-b-divider-regular px-6 py-2'> | |||
| <div className='text-lg font-medium text-text-primary'></div> | |||
| <ApiServer apiBaseUrl={appDetail.api_base_url} /> | |||
| </div> | |||
| <div className='grow overflow-auto px-4 py-4 sm:px-10'> | |||
| <Doc appDetail={appDetail} /> | |||
| @@ -1,16 +1,17 @@ | |||
| 'use client' | |||
| import { | |||
| useEffect, | |||
| useState, | |||
| } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { RiDeleteBinLine } from '@remixicon/react' | |||
| import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid' | |||
| import useSWR, { useSWRConfig } from 'swr' | |||
| import copy from 'copy-to-clipboard' | |||
| import SecretKeyGenerateModal from './secret-key-generate' | |||
| import s from './style.module.css' | |||
| import ActionButton from '@/app/components/base/action-button' | |||
| import Modal from '@/app/components/base/modal' | |||
| import Button from '@/app/components/base/button' | |||
| import CopyFeedback from '@/app/components/base/copy-feedback' | |||
| import { | |||
| createApikey as createAppApikey, | |||
| delApikey as delAppApikey, | |||
| @@ -22,7 +23,6 @@ import { | |||
| fetchApiKeysList as fetchDatasetApiKeysList, | |||
| } from '@/service/datasets' | |||
| import type { CreateApiKeyResponse } from '@/models/app' | |||
| import Tooltip from '@/app/components/base/tooltip' | |||
| import Loading from '@/app/components/base/loading' | |||
| import Confirm from '@/app/components/base/confirm' | |||
| import useTimestamp from '@/hooks/use-timestamp' | |||
| @@ -54,20 +54,6 @@ const SecretKeyModal = ({ | |||
| const [delKeyID, setDelKeyId] = useState('') | |||
| const [copyValue, setCopyValue] = useState('') | |||
| useEffect(() => { | |||
| if (copyValue) { | |||
| const timeout = setTimeout(() => { | |||
| setCopyValue('') | |||
| }, 1000) | |||
| return () => { | |||
| clearTimeout(timeout) | |||
| } | |||
| } | |||
| }, [copyValue]) | |||
| const onDel = async () => { | |||
| setShowConfirmDelete(false) | |||
| if (!delKeyID) | |||
| @@ -104,7 +90,7 @@ const SecretKeyModal = ({ | |||
| { | |||
| !!apiKeysList?.data?.length && ( | |||
| <div className='mt-4 flex grow flex-col overflow-hidden'> | |||
| <div className='flex h-9 shrink-0 items-center border-b border-solid text-xs font-semibold text-text-tertiary'> | |||
| <div className='flex h-9 shrink-0 items-center border-b border-divider-regular text-xs font-semibold text-text-tertiary'> | |||
| <div className='w-64 shrink-0 px-3'>{t('appApi.apiKeyModal.secretKey')}</div> | |||
| <div className='w-[200px] shrink-0 px-3'>{t('appApi.apiKeyModal.created')}</div> | |||
| <div className='w-[200px] shrink-0 px-3'>{t('appApi.apiKeyModal.lastUsed')}</div> | |||
| @@ -112,28 +98,22 @@ const SecretKeyModal = ({ | |||
| </div> | |||
| <div className='grow overflow-auto'> | |||
| {apiKeysList.data.map(api => ( | |||
| <div className='flex h-9 items-center border-b border-solid text-sm font-normal text-text-secondary' key={api.id}> | |||
| <div className='flex h-9 items-center border-b border-divider-regular text-sm font-normal text-text-secondary' key={api.id}> | |||
| <div className='w-64 shrink-0 truncate px-3 font-mono'>{generateToken(api.token)}</div> | |||
| <div className='w-[200px] shrink-0 truncate px-3'>{formatTime(Number(api.created_at), t('appLog.dateTimeFormat') as string)}</div> | |||
| <div className='w-[200px] shrink-0 truncate px-3'>{api.last_used_at ? formatTime(Number(api.last_used_at), t('appLog.dateTimeFormat') as string) : t('appApi.never')}</div> | |||
| <div className='flex grow px-3'> | |||
| <Tooltip | |||
| popupContent={copyValue === api.token ? `${t('appApi.copied')}` : `${t('appApi.copy')}`} | |||
| popupClassName='mr-1' | |||
| > | |||
| <div className={`mr-1 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg hover:bg-state-base-hover ${s.copyIcon} ${copyValue === api.token ? s.copied : ''}`} onClick={() => { | |||
| // setIsCopied(true) | |||
| copy(api.token) | |||
| setCopyValue(api.token) | |||
| }}></div> | |||
| </Tooltip> | |||
| {isCurrentWorkspaceManager | |||
| && <div className={`flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg ${s.trashIcon}`} onClick={() => { | |||
| setDelKeyId(api.id) | |||
| setShowConfirmDelete(true) | |||
| }}> | |||
| </div> | |||
| } | |||
| <div className='flex grow space-x-2 px-3'> | |||
| <CopyFeedback content={api.token} /> | |||
| {isCurrentWorkspaceManager && ( | |||
| <ActionButton | |||
| onClick={() => { | |||
| setDelKeyId(api.id) | |||
| setShowConfirmDelete(true) | |||
| }} | |||
| > | |||
| <RiDeleteBinLine className='h-4 w-4' /> | |||
| </ActionButton> | |||
| )} | |||
| </div> | |||
| </div> | |||
| ))} | |||