Commit c935edad authored by StyleZhang's avatar StyleZhang

Merge branch 'feat/application-config-user-input-field-collapse' into deploy/dev

parents ab108d61 d2f5098a
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React, { useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector' import { useContext } from 'use-context-selector'
import { import {
...@@ -13,6 +13,7 @@ import { AppType } from '@/types/app' ...@@ -13,6 +13,7 @@ import { AppType } from '@/types/app'
import Select from '@/app/components/base/select' import Select from '@/app/components/base/select'
import { DEFAULT_VALUE_MAX_LEN } from '@/config' import { DEFAULT_VALUE_MAX_LEN } from '@/config'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import { ChevronDown, ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
export type IPromptValuePanelProps = { export type IPromptValuePanelProps = {
appType: AppType appType: AppType
...@@ -37,6 +38,8 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({ ...@@ -37,6 +38,8 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { modelConfig, inputs, setInputs } = useContext(ConfigContext) const { modelConfig, inputs, setInputs } = useContext(ConfigContext)
const [promptPreviewCollapse, setPromptPreviewCollapse] = useState(false)
const [userInputFieldCollapse, setUserInputFieldCollapse] = useState(false)
const promptTemplate = modelConfig.configs.prompt_template const promptTemplate = modelConfig.configs.prompt_template
const promptVariables = modelConfig.configs.prompt_variables.filter(({ key, name }) => { const promptVariables = modelConfig.configs.prompt_variables.filter(({ key, name }) => {
return key && key?.trim() && name && name?.trim() return key && key?.trim() && name && name?.trim()
...@@ -63,12 +66,19 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({ ...@@ -63,12 +66,19 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
} }
const promptPreview = ( const promptPreview = (
<div className='pt-3 pb-4 rounded-t-xl bg-indigo-25'> <div className='py-3 rounded-t-xl bg-indigo-25'>
<div className="px-4"> <div className="px-4">
<div className="flex items-center space-x-1"> <div className="flex items-center space-x-1 cursor-pointer" onClick={() => setPromptPreviewCollapse(!promptPreviewCollapse)}>
{starIcon} {starIcon}
<div className="text-xs font-medium text-indigo-600 uppercase">{t('appDebug.inputs.previewTitle')}</div> <div className="text-xs font-medium text-indigo-600 uppercase">{t('appDebug.inputs.previewTitle')}</div>
{
promptPreviewCollapse
? <ChevronRight className='w-3 h-3 text-gray-700' />
: <ChevronDown className='w-3 h-3 text-gray-700' />
}
</div> </div>
{
!promptPreviewCollapse && (
<div className='mt-2 leading-normal'> <div className='mt-2 leading-normal'>
{ {
(promptTemplate && promptTemplate?.trim()) (promptTemplate && promptTemplate?.trim())
...@@ -86,26 +96,38 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({ ...@@ -86,26 +96,38 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
) )
} }
</div> </div>
)
}
</div> </div>
</div> </div>
) )
return ( return (
<div className="pb-5 border border-gray-200 bg-white rounded-xl" style={{ <div className="pb-3 border border-gray-200 bg-white rounded-xl" style={{
boxShadow: '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)', boxShadow: '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)',
}}> }}>
{promptPreview} {promptPreview}
<div className="mt-5 px-4"> <div className={'mt-3 px-4 bg-white'}>
<div className='mb-4 '> <div className={
<div className='flex items-center space-x-1'> `${!userInputFieldCollapse && 'mb-2'}`
}>
<div className='flex items-center space-x-1 cursor-pointer' onClick={() => setUserInputFieldCollapse(!userInputFieldCollapse)}>
<div className='flex items-center justify-center w-4 h-4'><VarIcon /></div> <div className='flex items-center justify-center w-4 h-4'><VarIcon /></div>
<div className='text-sm font-semibold text-gray-800'>{t('appDebug.inputs.userInputField')}</div> <div className='text-xs font-medium text-gray-800'>{t('appDebug.inputs.userInputField')}</div>
{
userInputFieldCollapse
? <ChevronRight className='w-3 h-3 text-gray-700' />
: <ChevronDown className='w-3 h-3 text-gray-700' />
}
</div> </div>
{appType === AppType.completion && promptVariables.length > 0 && ( {appType === AppType.completion && promptVariables.length > 0 && !userInputFieldCollapse && (
<div className="mt-1 text-xs leading-normal text-gray-500">{t('appDebug.inputs.completionVarTip')}</div> <div className="mt-1 text-xs leading-normal text-gray-500">{t('appDebug.inputs.completionVarTip')}</div>
)} )}
</div> </div>
{
!userInputFieldCollapse && (
<>
{ {
promptVariables.length > 0 promptVariables.length > 0
? ( ? (
...@@ -144,12 +166,15 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({ ...@@ -144,12 +166,15 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noVar')}</div> <div className='text-xs text-gray-500'>{t('appDebug.inputs.noVar')}</div>
) )
} }
</>
)
}
</div> </div>
{ {
appType === AppType.completion && ( appType === AppType.completion && (
<div className='px-4'> <div className='px-4'>
<div className="mt-5 border-b border-gray-100"></div> <div className="mt-3 border-b border-gray-100"></div>
<div className="mt-4"> <div className="mt-4">
<div> <div>
<div className="text-[13px] text-gray-900 font-medium">{t('appDebug.inputs.queryTitle')}</div> <div className="text-[13px] text-gray-900 font-medium">{t('appDebug.inputs.queryTitle')}</div>
......
import React, { useState } from 'react' import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import cn from 'classnames' import cn from 'classnames'
import style from './style.module.css' import style from './style.module.css'
...@@ -43,10 +43,15 @@ const prefixEmbedded = 'appOverview.overview.appInfo.embedded' ...@@ -43,10 +43,15 @@ const prefixEmbedded = 'appOverview.overview.appInfo.embedded'
type Option = keyof typeof OPTION_MAP type Option = keyof typeof OPTION_MAP
type OptionStatus = {
iframe: boolean
scripts: boolean
}
const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => { const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
const { t } = useTranslation() const { t } = useTranslation()
const [option, setOption] = useState<Option>('iframe') const [option, setOption] = useState<Option>('iframe')
const [isCopied, setIsCopied] = useState({ iframe: false, scripts: false }) const [isCopied, setIsCopied] = useState<OptionStatus>({ iframe: false, scripts: false })
const [_, copy] = useCopyToClipboard() const [_, copy] = useCopyToClipboard()
const { langeniusVersionInfo } = useAppContext() const { langeniusVersionInfo } = useAppContext()
...@@ -56,6 +61,19 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => { ...@@ -56,6 +61,19 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
setIsCopied({ ...isCopied, [option]: true }) setIsCopied({ ...isCopied, [option]: true })
} }
// when toggle option, reset then copy status
const resetCopyStatus = () => {
const cache = { ...isCopied }
Object.keys(cache).forEach((key) => {
cache[key as keyof OptionStatus] = false
})
setIsCopied(cache)
}
useEffect(() => {
resetCopyStatus()
}, [isShow])
return ( return (
<Modal <Modal
title={t(`${prefixEmbedded}.title`)} title={t(`${prefixEmbedded}.title`)}
...@@ -77,7 +95,10 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => { ...@@ -77,7 +95,10 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
style[`${v}Icon`], style[`${v}Icon`],
option === v && style.active, option === v && style.active,
)} )}
onClick={() => setOption(v as Option)} onClick={() => {
setOption(v as Option)
resetCopyStatus()
}}
></div> ></div>
) )
})} })}
......
...@@ -15,6 +15,7 @@ export type Attrs = { ...@@ -15,6 +15,7 @@ export type Attrs = {
export function normalizeAttrs(attrs: Attrs = {}): Attrs { export function normalizeAttrs(attrs: Attrs = {}): Attrs {
return Object.keys(attrs).reduce((acc: Attrs, key) => { return Object.keys(attrs).reduce((acc: Attrs, key) => {
const val = attrs[key] const val = attrs[key]
key = key.replace(/([-]\w)/g, (g: string) => g[1].toUpperCase())
switch (key) { switch (key) {
case 'class': case 'class':
acc.className = val acc.className = val
......
'use client' 'use client'
import React, { useEffect, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import copy from 'copy-to-clipboard' import copy from 'copy-to-clipboard'
import { t } from 'i18next' import { t } from 'i18next'
import s from './style.module.css' import s from './style.module.css'
import { randomString } from '@/app/components/app-sidebar/basic'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
type IInputCopyProps = { type IInputCopyProps = {
...@@ -13,13 +14,15 @@ type IInputCopyProps = { ...@@ -13,13 +14,15 @@ type IInputCopyProps = {
} }
const InputCopy = ({ const InputCopy = ({
value, value = '',
className, className,
readOnly = true, readOnly = true,
children, children,
}: IInputCopyProps) => { }: IInputCopyProps) => {
const [isCopied, setIsCopied] = useState(false) const [isCopied, setIsCopied] = useState(false)
const selector = useRef(`input-tooltip-${randomString(4)}`)
useEffect(() => { useEffect(() => {
if (isCopied) { if (isCopied) {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
...@@ -38,7 +41,7 @@ const InputCopy = ({ ...@@ -38,7 +41,7 @@ const InputCopy = ({
{children} {children}
<div className='flex-grow bg-gray-50 text-[13px] relative h-full'> <div className='flex-grow bg-gray-50 text-[13px] relative h-full'>
<Tooltip <Tooltip
selector="top-uniq" selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`} content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10' className='z-10'
> >
...@@ -50,7 +53,7 @@ const InputCopy = ({ ...@@ -50,7 +53,7 @@ const InputCopy = ({
</div> </div>
<div className="flex-shrink-0 h-4 bg-gray-200 border" /> <div className="flex-shrink-0 h-4 bg-gray-200 border" />
<Tooltip <Tooltip
selector="top-uniq" selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`} content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10' className='z-10'
> >
......
...@@ -18,7 +18,7 @@ const SecretKeyGenerateModal = ({ ...@@ -18,7 +18,7 @@ const SecretKeyGenerateModal = ({
isShow = false, isShow = false,
onClose, onClose,
newKey, newKey,
className className,
}: ISecretKeyGenerateModalProps) => { }: ISecretKeyGenerateModalProps) => {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
......
...@@ -6,6 +6,7 @@ import { ...@@ -6,6 +6,7 @@ import {
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid' import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid'
import useSWR, { useSWRConfig } from 'swr' import useSWR, { useSWRConfig } from 'swr'
import { useContext } from 'use-context-selector'
import SecretKeyGenerateModal from './secret-key-generate' import SecretKeyGenerateModal from './secret-key-generate'
import s from './style.module.css' import s from './style.module.css'
import Modal from '@/app/components/base/modal' import Modal from '@/app/components/base/modal'
...@@ -16,7 +17,6 @@ import Tooltip from '@/app/components/base/tooltip' ...@@ -16,7 +17,6 @@ import Tooltip from '@/app/components/base/tooltip'
import Loading from '@/app/components/base/loading' import Loading from '@/app/components/base/loading'
import Confirm from '@/app/components/base/confirm' import Confirm from '@/app/components/base/confirm'
import useCopyToClipboard from '@/hooks/use-copy-to-clipboard' import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'
import { useContext } from 'use-context-selector'
import I18n from '@/context/i18n' import I18n from '@/context/i18n'
type ISecretKeyModalProps = { type ISecretKeyModalProps = {
...@@ -58,12 +58,11 @@ const SecretKeyModal = ({ ...@@ -58,12 +58,11 @@ const SecretKeyModal = ({
} }
}, [copyValue]) }, [copyValue])
const onDel = async () => { const onDel = async () => {
setShowConfirmDelete(false) setShowConfirmDelete(false)
if (!delKeyID) { if (!delKeyID)
return return
}
await delApikey({ url: `/apps/${appId}/api-keys/${delKeyID}`, params: {} }) await delApikey({ url: `/apps/${appId}/api-keys/${delKeyID}`, params: {} })
mutate(commonParams) mutate(commonParams)
} }
...@@ -80,12 +79,11 @@ const SecretKeyModal = ({ ...@@ -80,12 +79,11 @@ const SecretKeyModal = ({
} }
const formatDate = (timestamp: any) => { const formatDate = (timestamp: any) => {
if (locale === 'en') { if (locale === 'en')
return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format((+timestamp) * 1000) return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format((+timestamp) * 1000)
} else { else
return new Intl.DateTimeFormat('fr-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format((+timestamp) * 1000) return new Intl.DateTimeFormat('fr-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format((+timestamp) * 1000)
} }
}
return ( return (
<Modal isShow={isShow} onClose={onClose} title={`${t('appApi.apiKeyModal.apiSecretKey')}`} className={`${s.customModal} px-8 flex flex-col`}> <Modal isShow={isShow} onClose={onClose} title={`${t('appApi.apiKeyModal.apiSecretKey')}`} className={`${s.customModal} px-8 flex flex-col`}>
...@@ -111,7 +109,7 @@ const SecretKeyModal = ({ ...@@ -111,7 +109,7 @@ const SecretKeyModal = ({
<div className='flex-shrink-0 px-3 truncate w-28'>{api.last_used_at ? formatDate(api.last_used_at) : t('appApi.never')}</div> <div className='flex-shrink-0 px-3 truncate w-28'>{api.last_used_at ? formatDate(api.last_used_at) : t('appApi.never')}</div>
<div className='flex flex-grow px-3'> <div className='flex flex-grow px-3'>
<Tooltip <Tooltip
selector="top-uniq" selector={`key-${api.token}`}
content={copyValue === api.token ? `${t('appApi.copied')}` : `${t('appApi.copy')}`} content={copyValue === api.token ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10' className='z-10'
> >
......
'use client' 'use client'
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useRef, useState } from 'react'
import { t } from 'i18next' import { t } from 'i18next'
import s from './index.module.css' import s from './index.module.css'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import useCopyToClipboard from '@/hooks/use-copy-to-clipboard' import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'
import { randomString } from '@/app/components/app-sidebar/basic'
type IInvitationLinkProps = { type IInvitationLinkProps = {
value?: string value?: string
...@@ -13,6 +14,7 @@ const InvitationLink = ({ ...@@ -13,6 +14,7 @@ const InvitationLink = ({
value = '', value = '',
}: IInvitationLinkProps) => { }: IInvitationLinkProps) => {
const [isCopied, setIsCopied] = useState(false) const [isCopied, setIsCopied] = useState(false)
const selector = useRef(`invite-link-${randomString(4)}`)
const [_, copy] = useCopyToClipboard() const [_, copy] = useCopyToClipboard()
const copyHandle = useCallback(() => { const copyHandle = useCallback(() => {
...@@ -37,7 +39,7 @@ const InvitationLink = ({ ...@@ -37,7 +39,7 @@ const InvitationLink = ({
<div className="flex items-center flex-grow h-5"> <div className="flex items-center flex-grow h-5">
<div className='flex-grow bg-gray-100 text-[13px] relative h-full'> <div className='flex-grow bg-gray-100 text-[13px] relative h-full'>
<Tooltip <Tooltip
selector="top-uniq" selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`} content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10' className='z-10'
> >
...@@ -46,7 +48,7 @@ const InvitationLink = ({ ...@@ -46,7 +48,7 @@ const InvitationLink = ({
</div> </div>
<div className="flex-shrink-0 h-4 bg-gray-200 border" /> <div className="flex-shrink-0 h-4 bg-gray-200 border" />
<Tooltip <Tooltip
selector="top-uniq" selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`} content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10' className='z-10'
> >
......
...@@ -43,7 +43,7 @@ const translation = { ...@@ -43,7 +43,7 @@ const translation = {
'较高的 Temperature 设置将导致更多样和创造性的输出,而较低的 Temperature 将产生更保守的输出并且类似于训练数据。', '较高的 Temperature 设置将导致更多样和创造性的输出,而较低的 Temperature 将产生更保守的输出并且类似于训练数据。',
topP: '采样范围', topP: '采样范围',
topPTip: topPTip:
'Top P值越高,输出与训练文本越相似,Top P值越低,输出越有创意和变化。它可用于使输出更适合特定用例。', 'Top P值越低,输出与训练文本越相似,Top P值越高,输出越有创意和变化。它可用于使输出更适合特定用例。',
presencePenalty: '词汇控制', presencePenalty: '词汇控制',
presencePenaltyTip: presencePenaltyTip:
'Presence penalty 是根据新词是否出现在目前的文本中来对其进行惩罚。正值将降低模型谈论新话题的可能性。', 'Presence penalty 是根据新词是否出现在目前的文本中来对其进行惩罚。正值将降低模型谈论新话题的可能性。',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment