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'
import type { FC } from 'react'
import React from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import {
......@@ -13,6 +13,7 @@ import { AppType } from '@/types/app'
import Select from '@/app/components/base/select'
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
import Button from '@/app/components/base/button'
import { ChevronDown, ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
export type IPromptValuePanelProps = {
appType: AppType
......@@ -37,6 +38,8 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
}) => {
const { t } = useTranslation()
const { modelConfig, inputs, setInputs } = useContext(ConfigContext)
const [promptPreviewCollapse, setPromptPreviewCollapse] = useState(false)
const [userInputFieldCollapse, setUserInputFieldCollapse] = useState(false)
const promptTemplate = modelConfig.configs.prompt_template
const promptVariables = modelConfig.configs.prompt_variables.filter(({ key, name }) => {
return key && key?.trim() && name && name?.trim()
......@@ -63,93 +66,115 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
}
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="flex items-center space-x-1">
<div className="flex items-center space-x-1 cursor-pointer" onClick={() => setPromptPreviewCollapse(!promptPreviewCollapse)}>
{starIcon}
<div className="text-xs font-medium text-indigo-600 uppercase">{t('appDebug.inputs.previewTitle')}</div>
</div>
<div className='mt-2 leading-normal'>
{
(promptTemplate && promptTemplate?.trim())
? (
<div
className="max-h-48 overflow-y-auto text-sm text-gray-700 break-all"
dangerouslySetInnerHTML={{
__html: format(replaceStringWithValuesWithFormat(promptTemplate.replace(/</g, '&lt;').replace(/>/g, '&gt;'), promptVariables, inputs)),
}}
>
</div>
)
: (
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noPrompt')}</div>
)
promptPreviewCollapse
? <ChevronRight className='w-3 h-3 text-gray-700' />
: <ChevronDown className='w-3 h-3 text-gray-700' />
}
</div>
{
!promptPreviewCollapse && (
<div className='mt-2 leading-normal'>
{
(promptTemplate && promptTemplate?.trim())
? (
<div
className="max-h-48 overflow-y-auto text-sm text-gray-700 break-all"
dangerouslySetInnerHTML={{
__html: format(replaceStringWithValuesWithFormat(promptTemplate.replace(/</g, '&lt;').replace(/>/g, '&gt;'), promptVariables, inputs)),
}}
>
</div>
)
: (
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noPrompt')}</div>
)
}
</div>
)
}
</div>
</div>
)
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)',
}}>
{promptPreview}
<div className="mt-5 px-4">
<div className='mb-4 '>
<div className='flex items-center space-x-1'>
<div className={'mt-3 px-4 bg-white'}>
<div className={
`${!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='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>
{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>
{
promptVariables.length > 0
? (
<div className="space-y-3 ">
{promptVariables.map(({ key, name, type, options, max_length, required }) => (
<div key={key} className="flex items-center justify-between">
<div className="mr-1 shrink-0 w-[120px] text-sm text-gray-900">{name || key}</div>
{type === 'select'
? (
<Select
className='w-full'
defaultValue={inputs[key] as string}
onSelect={(i) => { handleInputValueChange(key, i.value as string) }}
items={(options || []).map(i => ({ name: i, value: i }))}
allowSearch={false}
bgClassName='bg-gray-50'
overlayClassName='z-[11]'
/>
)
: (
<input
className="w-full px-3 text-sm leading-9 text-gray-900 border-0 rounded-lg grow h-9 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
placeholder={`${name}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
type="text"
value={inputs[key] ? `${inputs[key]}` : ''}
onChange={(e) => { handleInputValueChange(key, e.target.value) }}
maxLength={max_length || DEFAULT_VALUE_MAX_LEN}
/>
)}
</div>
))}
</div>
)
: (
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noVar')}</div>
)
!userInputFieldCollapse && (
<>
{
promptVariables.length > 0
? (
<div className="space-y-3 ">
{promptVariables.map(({ key, name, type, options, max_length, required }) => (
<div key={key} className="flex items-center justify-between">
<div className="mr-1 shrink-0 w-[120px] text-sm text-gray-900">{name || key}</div>
{type === 'select'
? (
<Select
className='w-full'
defaultValue={inputs[key] as string}
onSelect={(i) => { handleInputValueChange(key, i.value as string) }}
items={(options || []).map(i => ({ name: i, value: i }))}
allowSearch={false}
bgClassName='bg-gray-50'
overlayClassName='z-[11]'
/>
)
: (
<input
className="w-full px-3 text-sm leading-9 text-gray-900 border-0 rounded-lg grow h-9 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
placeholder={`${name}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
type="text"
value={inputs[key] ? `${inputs[key]}` : ''}
onChange={(e) => { handleInputValueChange(key, e.target.value) }}
maxLength={max_length || DEFAULT_VALUE_MAX_LEN}
/>
)}
</div>
))}
</div>
)
: (
<div className='text-xs text-gray-500'>{t('appDebug.inputs.noVar')}</div>
)
}
</>
)
}
</div>
{
appType === AppType.completion && (
<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>
<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 cn from 'classnames'
import style from './style.module.css'
......@@ -43,10 +43,15 @@ const prefixEmbedded = 'appOverview.overview.appInfo.embedded'
type Option = keyof typeof OPTION_MAP
type OptionStatus = {
iframe: boolean
scripts: boolean
}
const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
const { t } = useTranslation()
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 { langeniusVersionInfo } = useAppContext()
......@@ -56,6 +61,19 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
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 (
<Modal
title={t(`${prefixEmbedded}.title`)}
......@@ -77,7 +95,10 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
style[`${v}Icon`],
option === v && style.active,
)}
onClick={() => setOption(v as Option)}
onClick={() => {
setOption(v as Option)
resetCopyStatus()
}}
></div>
)
})}
......
......@@ -15,6 +15,7 @@ export type Attrs = {
export function normalizeAttrs(attrs: Attrs = {}): Attrs {
return Object.keys(attrs).reduce((acc: Attrs, key) => {
const val = attrs[key]
key = key.replace(/([-]\w)/g, (g: string) => g[1].toUpperCase())
switch (key) {
case 'class':
acc.className = val
......
'use client'
import React, { useEffect, useState } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import copy from 'copy-to-clipboard'
import { t } from 'i18next'
import s from './style.module.css'
import { randomString } from '@/app/components/app-sidebar/basic'
import Tooltip from '@/app/components/base/tooltip'
type IInputCopyProps = {
......@@ -13,13 +14,15 @@ type IInputCopyProps = {
}
const InputCopy = ({
value,
value = '',
className,
readOnly = true,
children,
}: IInputCopyProps) => {
const [isCopied, setIsCopied] = useState(false)
const selector = useRef(`input-tooltip-${randomString(4)}`)
useEffect(() => {
if (isCopied) {
const timeout = setTimeout(() => {
......@@ -38,7 +41,7 @@ const InputCopy = ({
{children}
<div className='flex-grow bg-gray-50 text-[13px] relative h-full'>
<Tooltip
selector="top-uniq"
selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......@@ -50,7 +53,7 @@ const InputCopy = ({
</div>
<div className="flex-shrink-0 h-4 bg-gray-200 border" />
<Tooltip
selector="top-uniq"
selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......
......@@ -18,7 +18,7 @@ const SecretKeyGenerateModal = ({
isShow = false,
onClose,
newKey,
className
className,
}: ISecretKeyGenerateModalProps) => {
const { t } = useTranslation()
return (
......
......@@ -6,6 +6,7 @@ import {
import { useTranslation } from 'react-i18next'
import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid'
import useSWR, { useSWRConfig } from 'swr'
import { useContext } from 'use-context-selector'
import SecretKeyGenerateModal from './secret-key-generate'
import s from './style.module.css'
import Modal from '@/app/components/base/modal'
......@@ -16,7 +17,6 @@ import Tooltip from '@/app/components/base/tooltip'
import Loading from '@/app/components/base/loading'
import Confirm from '@/app/components/base/confirm'
import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'
import { useContext } from 'use-context-selector'
import I18n from '@/context/i18n'
type ISecretKeyModalProps = {
......@@ -58,12 +58,11 @@ const SecretKeyModal = ({
}
}, [copyValue])
const onDel = async () => {
setShowConfirmDelete(false)
if (!delKeyID) {
if (!delKeyID)
return
}
await delApikey({ url: `/apps/${appId}/api-keys/${delKeyID}`, params: {} })
mutate(commonParams)
}
......@@ -80,11 +79,10 @@ const SecretKeyModal = ({
}
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)
} else {
else
return new Intl.DateTimeFormat('fr-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format((+timestamp) * 1000)
}
}
return (
......@@ -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 flex-grow px-3'>
<Tooltip
selector="top-uniq"
selector={`key-${api.token}`}
content={copyValue === api.token ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......
'use client'
import React, { useCallback, useEffect, useState } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { t } from 'i18next'
import s from './index.module.css'
import Tooltip from '@/app/components/base/tooltip'
import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'
import { randomString } from '@/app/components/app-sidebar/basic'
type IInvitationLinkProps = {
value?: string
......@@ -13,6 +14,7 @@ const InvitationLink = ({
value = '',
}: IInvitationLinkProps) => {
const [isCopied, setIsCopied] = useState(false)
const selector = useRef(`invite-link-${randomString(4)}`)
const [_, copy] = useCopyToClipboard()
const copyHandle = useCallback(() => {
......@@ -37,7 +39,7 @@ const InvitationLink = ({
<div className="flex items-center flex-grow h-5">
<div className='flex-grow bg-gray-100 text-[13px] relative h-full'>
<Tooltip
selector="top-uniq"
selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......@@ -46,7 +48,7 @@ const InvitationLink = ({
</div>
<div className="flex-shrink-0 h-4 bg-gray-200 border" />
<Tooltip
selector="top-uniq"
selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......
......@@ -43,7 +43,7 @@ const translation = {
'较高的 Temperature 设置将导致更多样和创造性的输出,而较低的 Temperature 将产生更保守的输出并且类似于训练数据。',
topP: '采样范围',
topPTip:
'Top P值越高,输出与训练文本越相似,Top P值越低,输出越有创意和变化。它可用于使输出更适合特定用例。',
'Top P值越低,输出与训练文本越相似,Top P值越高,输出越有创意和变化。它可用于使输出更适合特定用例。',
presencePenalty: '词汇控制',
presencePenaltyTip:
'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